diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..32079c6f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,17 @@
+# EditorConfig is awesome:http://EditorConfig.org
+
+# ignore .editorconfig files above this
+root = true
+
+[*]
+charset = utf-8
+
+# source code
+[*.{cpp,h,c}]
+end_of_line = lf
+indent_style = space
+indent_size = 2
+
+# windows-specific project files
+[*.{vcxproj,sln,vcxproj.filters,props}]
+charset = utf-8-bom
diff --git a/example_convert/example_convert.cpp b/example_convert/example_convert.cpp
index 9e03ebe6..12dbb35c 100644
--- a/example_convert/example_convert.cpp
+++ b/example_convert/example_convert.cpp
@@ -30,13 +30,13 @@ static bool HasErrorsOrWarnings(ON_TextLog* log, const char* operation)
ON_String msg;
if (ON_GetErrorCount() > 0)
{
- msg.Format("%d errors: %s", ON_GetErrorCount(), operation);
+ msg.Format("%d errors: %s\n", ON_GetErrorCount(), operation);
log->Print(msg);
return true;
}
if (ON_GetWarningCount() > 0)
{
- msg.Format("%d warnings: %s", ON_GetErrorCount(), operation);
+ msg.Format("%d warnings: %s\n", ON_GetErrorCount(), operation);
log->Print(msg);
return true;
}
@@ -162,7 +162,7 @@ int main(int argc, const char *argv[])
// Write file
model.m_sStartSectionComments = "Converted by example_convert.exe";
bool outrc = model.Write(output, version, dump);
- if (HasErrorsOrWarnings(dump, "writing output file"))
+ if (HasErrorsOrWarnings(dump, "writing output file\n"))
return 1;
if (outrc)
diff --git a/example_test/example_test.cpp b/example_test/example_test.cpp
index f2e2d638..c7c3a287 100644
--- a/example_test/example_test.cpp
+++ b/example_test/example_test.cpp
@@ -564,6 +564,8 @@ static ON_String Internal_PlatformId(bool bVerbose)
"Apple"
#elif defined(ON_RUNTIME_ANDROID)
"Android"
+#elif defined(ON_RUNTIME_LINUX)
+ "Linux"
#else
"Runtime"
#endif
diff --git a/example_test/example_test.vcxproj b/example_test/example_test.vcxproj
index 6399073f..902b2161 100644
--- a/example_test/example_test.vcxproj
+++ b/example_test/example_test.vcxproj
@@ -23,7 +23,6 @@
{865E8D8D-8E03-4601-A7B5-A8C4094ECB8B}
Win32Proj
exampletest
- 10.0.16299.0
diff --git a/makefile b/makefile
index 753fc03b..3a48e468 100644
--- a/makefile
+++ b/makefile
@@ -368,6 +368,7 @@ ON_SRC = opennurbs_3dm_attributes.cpp \
opennurbs_subd_ref.cpp \
opennurbs_subd_ring.cpp \
opennurbs_subd_sector.cpp \
+ opennurbs_subd_texture.cpp \
opennurbs_sum.cpp \
opennurbs_sumsurface.cpp \
opennurbs_surface.cpp \
@@ -543,6 +544,7 @@ ON_OBJ = opennurbs_3dm_attributes.o \
opennurbs_subd_ref.o \
opennurbs_subd_ring.o \
opennurbs_subd_sector.o \
+ opennurbs_subd_texture.o \
opennurbs_sum.o \
opennurbs_sumsurface.o \
opennurbs_surface.o \
diff --git a/opennurbs.additional.manifest b/opennurbs.additional.manifest
new file mode 100644
index 00000000..4fd0be92
--- /dev/null
+++ b/opennurbs.additional.manifest
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/opennurbs.h b/opennurbs.h
index 73b9d744..e91c76f3 100644
--- a/opennurbs.h
+++ b/opennurbs.h
@@ -88,6 +88,7 @@
#include "opennurbs_sphere.h" // simple 3d sphere
#include "opennurbs_box.h" // simple 3d box
#include "opennurbs_torus.h" // simple 3d torus
+#include "opennurbs_convex_poly.h" // simple 3d simplex and 3d convex polyhedra
#include "opennurbs_bezier.h" // simple bezier and polynomial curves and surfaces
#include "opennurbs_math.h" // utilities for performing simple calculations
#include "opennurbs_intersect.h" // utilities for performing simple intersections
@@ -119,7 +120,9 @@
#include "opennurbs_pointcloud.h" // point set
#include "opennurbs_curveproxy.h" // proxy curve provides a way to use an existing curve
#include "opennurbs_surfaceproxy.h" // proxy surface provides a way to use another surface
-#include "opennurbs_mesh.h" // render mesh object
+#include "opennurbs_mesh.h" // mesh object
+
+
#include "opennurbs_pointgrid.h" // point grid object
#include "opennurbs_linecurve.h" // line as a paramtric curve object
#include "opennurbs_arccurve.h" // arc/circle as a paramtric curve object
diff --git a/opennurbs_3dm_properties.cpp b/opennurbs_3dm_properties.cpp
index 899d6e58..f475fb38 100644
--- a/opennurbs_3dm_properties.cpp
+++ b/opennurbs_3dm_properties.cpp
@@ -514,7 +514,7 @@ bool ON_3dmProperties::Write(ON_BinaryArchive& file) const
= file.ArchiveFullPath().IsEmpty()
? m_3dmArchiveFullPathName
: file.ArchiveFullPath();
- if (archive_full_path.IsNotEmpty())
+ if (rc && archive_full_path.IsNotEmpty())
{
if (!file.BeginWrite3dmChunk(TCODE_PROPERTIES_AS_FILE_NAME, 0))
return false;
diff --git a/opennurbs_annotationbase.cpp b/opennurbs_annotationbase.cpp
index c39acf10..7a197890 100644
--- a/opennurbs_annotationbase.cpp
+++ b/opennurbs_annotationbase.cpp
@@ -24,6 +24,7 @@
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif
+#include "opennurbs_textiterator.h"
ON_VIRTUAL_OBJECT_IMPLEMENT(ON_Annotation, ON_Geometry, "B5802E0C-5B16-43C9-BD43-A3E0AC18203B");
ON::AnnotationType ON::AnnotationTypeFromUnsigned(
@@ -589,22 +590,113 @@ ON_3dVector ON_Annotation::GetDefaultHorizontal(const ON_Plane& plane)
return ON_3dVector::XAxis;
}
+void ON_Annotation::CalcTextFlip(
+ const ON_3dVector& text_xdir, const ON_3dVector& text_ydir, const ON_3dVector& text_zdir,
+ const ON_3dVector& view_xdir, const ON_3dVector& view_ydir, const ON_3dVector& view_zdir,
+ const ON_Xform* model_xform,
+ const double fliptol,
+ bool& flip_x,
+ bool& flip_y)
+{
+
+ double XoX = text_xdir * view_xdir;
+ double XoY = text_xdir * view_ydir;
+ double YoX = text_ydir * view_xdir;
+ double YoY = text_ydir * view_ydir;
+ bool from_the_back = (view_zdir * text_zdir < 0.0);
+ if (nullptr != model_xform && model_xform->Determinant() < 0.0)
+ from_the_back = !from_the_back;
+
+ double upsign = 1.0;
+
+ // This part shifts text to the correct side of the dimension line
+ if (fabs(XoX) > fabs(XoY)) // more horizontal
+ {
+ if (YoY > 0.0)
+ upsign = 1.0;
+ else
+ upsign = -1.0;
+ }
+ else // more vertical
+ {
+ if (from_the_back)
+ {
+ if (YoX < 0.0)
+ {
+ if (XoX < fliptol)
+ upsign = 1.0;
+ else
+ upsign = -1.0;
+ }
+ else
+ {
+ if (XoX > -fliptol)
+ upsign = -1.0;
+ else
+ upsign = 1.0;
+ }
+ }
+ else
+ {
+ if (YoX > 0.0)
+ {
+ if (XoX > fliptol)
+ upsign = 1.0;
+ else
+ upsign = -1.0;
+ }
+ else
+ {
+ if (XoX < -fliptol)
+ upsign = -1.0;
+ else
+ upsign = 1.0;
+ }
+ }
+ }
+ flip_x = false;
+ flip_y = false;
+ if (from_the_back)
+ upsign = -upsign;
+ flip_x = upsign < 0.0;
+ if (from_the_back)
+ flip_y = !flip_x;
+ else
+ flip_y = flip_x;
+}
+
+
const ON_wString ON_Annotation::PlainText() const
{
- return (nullptr == m_text)
- ? ON_wString::EmptyString
- : m_text->PlainText();
+ if (nullptr == m_text)
+ return ON_wString::EmptyString;
+ const ON_TextRunArray* runs = m_text->TextRuns(true);
+ if (nullptr != runs && 0 == runs->Count())
+ BoundingBox(); // Side effect of building text runs
+ return m_text->PlainText();
}
const ON_wString ON_Annotation::PlainTextWithFields() const
{
- const ON_TextContent* text = Text();
-
- return (nullptr == text)
- ? ON_wString::EmptyString
- : text->PlainTextWithFields();
+ if (nullptr == m_text)
+ return ON_wString::EmptyString;
+ const ON_TextRunArray* runs = m_text->TextRuns(true);
+ if (nullptr != runs && 0 == runs->Count())
+ BoundingBox();
+ return m_text->PlainTextWithFields();
}
+const ON_wString ON_Annotation::PlainTextWithFields(ON_SimpleArray* runmap) const
+{
+ if (nullptr == m_text)
+ return ON_wString::EmptyString;
+ const ON_TextRunArray* runs = m_text->TextRuns(true);
+ if (nullptr != runs && 0 == runs->Count())
+ BoundingBox();
+ return m_text->PlainTextWithFields(runmap);
+}
+
+
const ON_wString ON_Annotation::RichText() const
{
return (nullptr == m_text)
@@ -1250,6 +1342,29 @@ bool ON_Annotation::ReplaceTextString(
return text->ReplaceTextString(RtfString, Type(), dimstyle);
}
+
+bool ON_Annotation::RunReplaceString(
+ const ON_DimStyle* dimstyle,
+ const wchar_t* repl_str,
+ int start_run_idx,
+ int start_run_pos,
+ int end_run_idx,
+ int end_run_pos)
+{
+ ON_TextContent* text_content = Text();
+ if (nullptr == text_content)
+ return false;
+ bool rc = text_content->RunReplaceString(repl_str, start_run_idx, start_run_pos, end_run_idx, end_run_pos);
+
+ text_content->ComposeText();
+
+ text_content->RebuildRuns(Type(), dimstyle);
+
+
+ return rc;
+}
+
+
////double ON_Annotation::Height() const
////{
//// //// Delete this function ASAP
@@ -2823,6 +2938,23 @@ void ON_Annotation::SetDimScale(const ON_DimStyle* parent_style, double value)
}
}
+wchar_t ON_Annotation::DecimalSeparator(const ON_DimStyle* parent_style) const
+{
+ return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::DecimalSeparator).DecimalSeparator();
+}
+
+void ON_Annotation::SetDecimalSeparator(const ON_DimStyle* parent_style, wchar_t separator)
+{
+ parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style);
+ bool bCreate = (separator != parent_style->DecimalSeparator());
+ ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate);
+ if (nullptr != override_style)
+ {
+ override_style->SetDecimalSeparator(separator);
+ override_style->SetFieldOverride(ON_DimStyle::field::DecimalSeparator, bCreate);
+ }
+}
+
const class ON_Font& ON_Annotation::Font(const ON_DimStyle* parent_style) const
{
return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::Font).Font();
@@ -2909,7 +3041,6 @@ ON::LengthUnitSystem ON_Annotation::AlternateDimensionLengthDisplayUnit(
model_sn = parent_style->ModelSerialNumber();
return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::AlternateDimensionLengthDisplay).DimensionLengthDisplayUnit(model_sn);
}
-
//--------------------------------
bool ON_Annotation::ClearRtfFmt(const wchar_t* fmt_str_on, const wchar_t* fmt_str_off, ON_wString& rtf_in)
diff --git a/opennurbs_annotationbase.h b/opennurbs_annotationbase.h
index 189fa3e8..c299d608 100644
--- a/opennurbs_annotationbase.h
+++ b/opennurbs_annotationbase.h
@@ -183,6 +183,16 @@ public:
// Use this function when you don't have a known horizontal direction
static ON_3dVector GetDefaultHorizontal(const ON_Plane& plane);
+
+ static void CalcTextFlip(
+ const ON_3dVector& text_xdir, const ON_3dVector& text_ydir, const ON_3dVector& text_zdir,
+ const ON_3dVector& view_xdir, const ON_3dVector& view_ydir, const ON_3dVector& view_zdir,
+ const ON_Xform* model_xform,
+ const double flip_tol,
+ bool& flip_x,
+ bool& flip_y);
+
+
/*
Returns:
Rich text that can contain rich text formatting instructions.
@@ -204,6 +214,18 @@ public:
*/
const ON_wString PlainTextWithFields() const;
+ /*
+ Finds the positions and lengths of substrings in the string returned by PlainTextWithFields()
+ That string is the plain text (no rtf formatting) with field source unevaluated
+ Each 3dex in the array is
+ i: run index,
+ j: position in the string where text from run[i] starts,
+ k: length of text from run[i]
+
+ Returns the same string that PlainTextWithFields() returns
+ */
+ const ON_wString PlainTextWithFields(ON_SimpleArray* runmap) const;
+
// Return the id of the main (parent) dimstyle used by this object.
// The style with this id should not be used directly if there is
// an override dimstyle present.
@@ -377,6 +399,15 @@ public:
const ON_DimStyle* dimstyle
);
+ bool RunReplaceString(
+ const ON_DimStyle* dimstyle,
+ const wchar_t* str,
+ int start_run_idx,
+ int start_run_pos,
+ int end_run_idx,
+ int end_run_pos);
+
+
void GetAlignment(ON::TextHorizontalAlignment& horz, ON::TextVerticalAlignment& vert) const;
void SetAlignment(ON::TextHorizontalAlignment horz, ON::TextVerticalAlignment vert);
@@ -746,6 +777,9 @@ public:
double DimScale(const ON_DimStyle* parent_style) const;
void SetDimScale(const ON_DimStyle* parent_style, double scale);
+ wchar_t DecimalSeparator(const ON_DimStyle* parent_style) const;
+ void SetDecimalSeparator(const ON_DimStyle* parent_style, wchar_t separator);
+
ON_DimStyle::LengthDisplay DimensionLengthDisplay(const ON_DimStyle* parent_style) const;
void SetDimensionLengthDisplay(const ON_DimStyle* parent_style, ON_DimStyle::LengthDisplay length_display);
@@ -842,6 +876,8 @@ public:
static bool FirstCharTextProperties(const wchar_t* rtf_in, bool& bold, bool& italic, bool& underline, ON_wString& facename);
const ON_Font* FirstCharFont() const;
+
+ friend class ON_Dimension;
};
diff --git a/opennurbs_apple_nsfont.cpp b/opennurbs_apple_nsfont.cpp
index 2e763f1f..34954018 100644
--- a/opennurbs_apple_nsfont.cpp
+++ b/opennurbs_apple_nsfont.cpp
@@ -184,9 +184,8 @@ bool ON_Font::SetFromAppleCTFont(CTFontRef apple_font, bool bAnnotationFont)
const ON_wString face_name = ON_Font::AppleCTFontFaceName(apple_font);
// Set Windows LOGFONT.lfFaceName to something not empty there is some hope this
- // font might work in Rhino 5 for Windows too.
- // https://mcneel.myjetbrains.com/youtrack/issue/RH-37074
- const ON_wString windows_logfont_name = family_name;
+ // font might work in Rhino 6/7 for Windows too.
+ const ON_wString windows_logfont_name = ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames(family_name, postscript_name);
const bool rc = postscript_name.IsNotEmpty() || family_name.IsNotEmpty();
if (rc)
@@ -259,7 +258,7 @@ bool ON_Font::SetFromAppleCTFont(CTFontRef apple_font, bool bAnnotationFont)
if (nullptr != appleWidth)
{
- double x = apple_font_weight_trait;
+ double x = apple_font_width_trait;
if ( CFNumberGetValue(appleWidth, kCFNumberFloat64Type, &x) && -1.0 <= x && x <= 1.0 )
{
// Use the kCTFontWidthTrait key to access the normalized proportion trait from the font traits dictionary.
@@ -321,6 +320,14 @@ bool ON_Font::SetFromAppleCTFont(CTFontRef apple_font, bool bAnnotationFont)
m_panose1 = ON_Font::AppleCTFontPANOSE1(apple_font);
SetFontOrigin(ON_Font::Origin::AppleFont);
+
+ if (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0)
+ m_apple_font_weight_trait = apple_font_weight_trait;
+
+ if (-1.0 <= apple_font_width_trait && apple_font_width_trait <= 1.0)
+ m_apple_font_width_trait = apple_font_width_trait;
+ else
+ m_apple_font_width_trait = ON_UNSET_VALUE;
}
return rc;
diff --git a/opennurbs_archive.cpp b/opennurbs_archive.cpp
index efe62154..4c1cf582 100644
--- a/opennurbs_archive.cpp
+++ b/opennurbs_archive.cpp
@@ -1187,6 +1187,34 @@ ON_BinaryArchive::ReadColor( ON_Color& color )
return rc;
}
+bool
+ON_BinaryArchive::ReadColor(ON_4fColor& color)
+{
+ float f = 0.f;
+
+ bool rc = ReadFloat(&f);
+ if (rc)
+ {
+ color.SetRed(f);
+ rc = ReadFloat(&f);
+ }
+ if (rc)
+ {
+ color.SetGreen(f);
+ rc = ReadFloat(&f);
+ }
+ if (rc)
+ {
+ color.SetBlue(f);
+ rc = ReadFloat(&f);
+ }
+ if (rc)
+ {
+ color.SetAlpha(f);
+ }
+ return rc;
+}
+
bool
ON_BinaryArchive::ReadPoint (
ON_2dPoint& p
@@ -3202,6 +3230,16 @@ ON_BinaryArchive::WriteColor( const ON_Color& color )
return WriteByte( 4, (const unsigned char*)&colorref ); // WriteByte prevents big endian swaps
}
+bool
+ON_BinaryArchive::WriteColor(const ON_4fColor& color)
+{
+ if (!WriteFloat(color.Red())) return false;
+ if (!WriteFloat(color.Green())) return false;
+ if (!WriteFloat(color.Blue())) return false;
+ if (!WriteFloat(color.Alpha())) return false;
+ return true;
+}
+
bool
ON_BinaryArchive::WritePoint (
const ON_2dPoint& p
@@ -7065,15 +7103,6 @@ bool ON_BinaryArchive::Save3dmPreviewImage() const
return m_bSave3dmPreviewImage;
}
-
-
-int ON_BinaryArchive::CurrentArchiveVersion()
-{
- // Latest version of opennurbs binary archives supported by
- // this version of opennurbs.
- return ON::VersionMajor()*10; // *10 means 64-bit chunk values
-}
-
void ON_BinaryArchive::SetModelSerialNumber(
unsigned int model_serial_number,
unsigned int reference_model_serial_number,
@@ -7211,7 +7240,7 @@ bool ON_BinaryArchive::Begin3dmTable(
if ( table == ON_3dmArchiveTableType::start_section )
{
// m_3dm_version is set during reading of the start section.
- if (0 != m_3dm_version)
+ if (0 != m_3dm_version && ON::archive_mode::read3dm == Mode())
{
ON_ERROR("Archive m_3dm_version is set during start section reading.");
return End3dmTable(table,false);
@@ -7498,7 +7527,6 @@ bool ON_BinaryArchive::Write3dmStartSection( int version, const char* sInformati
return End3dmTable(ON_3dmArchiveTableType::start_section,false);
}
-
m_crc_error_count = 0;
m_critical_error_count = 0;
m_3dm_version = version;
@@ -16541,7 +16569,7 @@ ON_BinaryArchive::SetArchive3dmVersion(int v)
// 5 was used for early V5 betas with 4 byte chunk lengths
// 50 is used for V5 files with 8 bytes chunk lengths.
// 60, 70, ... will be used for Rhino V6, V7, etc.
- if ( (v >= 1 && v <= 5) || ( v >= 50 && 0 == (v % 10) ) )
+ if ( (v >= 1 && v <= 5) || ( v >= 50 && 0 == (v % 10) && v <= ON_BinaryArchive::CurrentArchiveVersion() ) )
{
m_3dm_version = v;
rc = true;
@@ -16970,8 +16998,13 @@ bool ON_WriteMultipleObjectArchive(
model.AddManagedModelComponent( mg, bResolveIdAndNameConflicts );
}
- if (version < ON_BinaryArchive::CurrentArchiveVersion()-1 || version > ON_BinaryArchive::CurrentArchiveVersion())
+ if ( (0 != version % 10)
+ || version < ON_BinaryArchive::CurrentArchiveVersion() - 10
+ || version > ON_BinaryArchive::CurrentArchiveVersion()
+ )
+ {
version = ON_BinaryArchive::CurrentArchiveVersion();
+ }
model.m_sStartSectionComments = "Archive created by ON_WriteMultipleObjectArchive";
bool rc = model.Write(archive, version );
diff --git a/opennurbs_archive.h b/opennurbs_archive.h
index 4fa351e4..59a386e9 100644
--- a/opennurbs_archive.h
+++ b/opennurbs_archive.h
@@ -2216,6 +2216,10 @@ public:
ON_Color&
);
+ bool ReadColor(
+ ON_4fColor&
+ );
+
bool ReadPoint (
ON_2dPoint&
);
@@ -2512,6 +2516,10 @@ public:
const ON_Color&
);
+ bool WriteColor(
+ const ON_4fColor&
+ );
+
bool WritePoint (
const ON_2dPoint&
);
@@ -3030,13 +3038,12 @@ public:
) const;
/*
+ Remarks:
+ In a stable commercially released Rhino version N, CurrentArchiveVersion() = 10*N.
+ In "early" Rhino N WIP, CurrentArchiveVersion() = 10*(N-1).
+ In "later" Rhino N WIP, CurrentArchiveVersion() = 10*N.
Returns:
- 10*OPENNURBS_VERSION_MAJOR
- (The Rhino opennurbs file version.)
- This is the value of version to pass to ON_BinaryArchive
- functions like Write3dmStartSection() when you want to use the
- the current opennurbs version number and you do not want to have
- to update your code when this version number changes.
+ The current 3dm archive version that is saved by Rhino.
*/
static int CurrentArchiveVersion();
diff --git a/opennurbs_array.h b/opennurbs_array.h
index dae53784..577c492e 100644
--- a/opennurbs_array.h
+++ b/opennurbs_array.h
@@ -211,6 +211,11 @@ public:
// See Also: ON_CompareIncreasing and ON_CompareDeccreasing
bool QuickSort( int (*)(const T*,const T*) );
+ //////////
+ // Sorts the array using the quick sort algorithma and then removes duplicates.
+ // See Also: ON_CompareIncreasing and ON_CompareDeccreasing
+ bool QuickSortAndRemoveDuplicates( int (*)(const T*,const T*) );
+
/*
Description:
Sort() fills in the index[] array so that
diff --git a/opennurbs_array_defs.h b/opennurbs_array_defs.h
index 320b0891..a77a4e2d 100644
--- a/opennurbs_array_defs.h
+++ b/opennurbs_array_defs.h
@@ -782,6 +782,36 @@ bool ON_SimpleArray::QuickSort( int (*compar)(const T*,const T*) )
return rc;
}
+template
+bool ON_SimpleArray::QuickSortAndRemoveDuplicates( int (*compar)(const T*,const T*) )
+{
+ bool rc = false;
+ if ( m_a && m_count > 0 && compar )
+ {
+ if (m_count > 1)
+ {
+ ON_qsort(m_a, m_count, sizeof(T), (int(*)(const void*, const void*))compar);
+ const T* prev_ele = &m_a[0];
+ int clean_count = 1;
+ for (int i = 1; i < m_count; ++i)
+ {
+ if (0 == compar(prev_ele, &m_a[i]))
+ continue; // duplicate
+ if (i > clean_count)
+ m_a[clean_count] = m_a[i];
+ ++clean_count;
+ }
+ if (clean_count < m_count)
+ {
+ memset( (void*)(&m_a[clean_count]), 0, (m_count-clean_count)*sizeof(T) );
+ SetCount(clean_count);
+ }
+ }
+ rc = true;
+ }
+ return rc;
+}
+
template
bool ON_SimpleArray::Sort( ON::sort_algorithm sa, int* index, int (*compar)(const T*,const T*) ) const
{
@@ -1154,6 +1184,11 @@ ON_ClassArray& ON_ClassArray::operator=( ON_ClassArray&& src ) ON_NOEXC
{
if( this != &src )
{
+ // TODO - investigate why we should use std::move(src)
+ // instead of the code below
+ //ON_ClassArray::operator=(std::move(src));
+ // Then investigate why the change was requested only for class array.
+ // What about the other dynamic array classes?
this->Destroy();
m_a = src.m_a;
m_count = src.m_count;
diff --git a/opennurbs_bounding_box.cpp b/opennurbs_bounding_box.cpp
index 29571e21..ce0651d2 100644
--- a/opennurbs_bounding_box.cpp
+++ b/opennurbs_bounding_box.cpp
@@ -991,16 +991,13 @@ unsigned int ON_ClippingRegion::TransformPoint(
if (y < -w) out |= 0x04; else if (y > w) out |= 0x08;
z = xform[8]*cv[0] + xform[9]*cv[1] + xform[10]*cv[2] + xform[11];
if (z < -w) out |= 0x10; else if (z > w) out |= 0x20;
- if ( w <= 0.0 )
+ if ( false == (w > 0.0) )
{
- w = (0.0==w) ? 1.0 : 1.0/w;
+ if (0.0 == w || false == ON_IsValid(w))
+ w = 1.0;
out |= 0x80000000;
}
- else
- {
- w = 1.0/w;
- }
- Q.x = x*w; Q.y = y*w; Q.z = z*w;
+ Q.x = x/w; Q.y = y/w; Q.z = z/w;
return out;
}
@@ -1413,6 +1410,7 @@ bool ON_ClippingRegionPoints::AppendClipPoints(
return true;
}
+
bool ON_ClippingRegionPoints::AppendClipPoint(
ON_3dPoint clip_point,
unsigned int clip_flag
diff --git a/opennurbs_brep.h b/opennurbs_brep.h
index 078b4c76..0dc12c80 100644
--- a/opennurbs_brep.h
+++ b/opennurbs_brep.h
@@ -118,6 +118,7 @@ public:
// number of edges that begin or end at this vertex.
int EdgeCount() const;
+
/////////////////////////////////////////////////////////////////
// Implementation
@@ -332,6 +333,7 @@ public:
*/
void UnsetPlineEdgeParameters();
+
// index of 3d curve in m_C3[] array
// (edge.m_curve also points to m_C3[m_c3i])
int m_c3i = -1;
@@ -1144,6 +1146,7 @@ public:
const ON_Surface* SurfaceOf() const;
+
ON_SimpleArray m_li; // loop indices (outer loop is m_li[0])
int m_si = -1; // index of surface in b-rep m_S[] array
bool m_bRev = false; // true if face orientation is opposite
diff --git a/opennurbs_brep_tools.cpp b/opennurbs_brep_tools.cpp
index aa714bff..5789cb77 100644
--- a/opennurbs_brep_tools.cpp
+++ b/opennurbs_brep_tools.cpp
@@ -408,6 +408,9 @@ void ON_Brep::SetTolerancesBoxesAndFlags(
static
bool CheckForMatchingVertexIndices( int i, int j, int corner_vi[4] )
{
+ if (corner_vi[i] == corner_vi[j])
+ return true;
+
bool rc = false;
if ( corner_vi[i] >= 0 || corner_vi[j] >= 0 )
{
@@ -421,12 +424,9 @@ bool CheckForMatchingVertexIndices( int i, int j, int corner_vi[4] )
corner_vi[j] = corner_vi[i];
rc = true;
}
- else if ( corner_vi[i] == corner_vi[j] )
- {
- rc = true;
- }
}
- return true;
+
+ return rc;
}
@@ -3274,6 +3274,7 @@ static bool ON_BrepRemoveSlits(ON_BrepLoop& L)
}
}
}
+ pB->MatchTrimEnds(nL);
pB->SetTrimBoundingBoxes(nL, true);
}
return true;
diff --git a/opennurbs_color.cpp b/opennurbs_color.cpp
index eaf71d5a..62d8de8b 100644
--- a/opennurbs_color.cpp
+++ b/opennurbs_color.cpp
@@ -8,7 +8,7 @@
// 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 .
//
////////////////////////////////////////////////////////////////
@@ -19,16 +19,16 @@
#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
+// 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_Color::ON_Color(unsigned int colorref)
- : m_color(colorref)
+ : m_color(colorref)
{
- // No adjustments are required on big endian computers because all
- // unsigned int conversion and all IO preserves the order of the
+ // No adjustments are required on big endian computers because all
+ // unsigned int conversion and all IO preserves the order of the
// ON_Color::m_RGBA[4] bytes.
}
@@ -50,8 +50,8 @@ unsigned int ON_Color::WindowsRGB() const
ON_Color::operator unsigned int() const
{
- // No adjustments are required on big endian computers because all
- // unsigned int conversion and all IO preserves the order of the
+ // No adjustments are required on big endian computers because all
+ // unsigned int conversion and all IO preserves the order of the
// ON_Color::m_RGBA[4] bytes.
return m_color;
}
@@ -84,25 +84,25 @@ int ON_Color::Alpha() const
{ return m_RGBA[ON_Color::kAlphaByteIndex];}
double ON_Color::FractionRed() const
-{
+{
//return Red()/255.0;
return m_RGBA[ON_Color::kRedByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer
}
-double ON_Color::FractionGreen() const
-{
+double ON_Color::FractionGreen() const
+{
//return Green()/255.0;
return m_RGBA[ON_Color::kGreenByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer
}
-double ON_Color::FractionBlue() const
-{
+double ON_Color::FractionBlue() const
+{
//return Blue()/255.0;
return m_RGBA[ON_Color::kBlueByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer
}
-double ON_Color::FractionAlpha() const
-{
+double ON_Color::FractionAlpha() const
+{
//return Alpha()/255.0;
return m_RGBA[ON_Color::kAlphaByteIndex]*0.003921568627450980392156862745; // better fodder for optimizer
}
@@ -119,23 +119,23 @@ void ON_Color::SetFractionalRGB(double r,double g,double b)
void ON_Color::SetAlpha(int alpha)
{
- if (alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255;
+ if (alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255;
m_RGBA[ON_Color::kAlphaByteIndex] = (unsigned char)alpha;
}
void ON_Color::SetFractionalAlpha(double alpha)
{
- if (alpha < 0.0 ) alpha = 0.0; else if ( alpha > 1.0 ) alpha = 1.0;
+ if (alpha < 0.0 ) alpha = 0.0; else if ( alpha > 1.0 ) alpha = 1.0;
SetAlpha((int)(alpha*255.0));
}
void
ON_Color::SetRGBA( int red, int green, int blue, int alpha )
{
- if (red < 0 ) red = 0; else if ( red > 255 ) red = 255;
- if (green < 0 ) green = 0; else if ( green > 255 ) green = 255;
- if (blue < 0 ) blue = 0; else if ( blue > 255 ) blue = 255;
- if (alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255;
+ if (red < 0 ) red = 0; else if ( red > 255 ) red = 255;
+ if (green < 0 ) green = 0; else if ( green > 255 ) green = 255;
+ if (blue < 0 ) blue = 0; else if ( blue > 255 ) blue = 255;
+ if (alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255;
m_RGBA[ON_Color::kRedByteIndex] = (unsigned char)red;
m_RGBA[ON_Color::kGreenByteIndex] = (unsigned char)green;
m_RGBA[ON_Color::kBlueByteIndex] = (unsigned char)blue;
@@ -146,9 +146,9 @@ void
ON_Color::SetFractionalRGBA( double red, double green, double blue, double alpha )
{
int r,g,b,a;
- if (red < 0.0 ) red = 0.0; else if ( red > 1.0 ) red = 1.0;
- if (green < 0.0 ) green = 0.0; else if ( green > 1.0 ) green = 1.0;
- if (blue < 0.0 ) blue = 0.0; else if ( blue > 1.0 ) blue = 1.0;
+ if (red < 0.0 ) red = 0.0; else if ( red > 1.0 ) red = 1.0;
+ if (green < 0.0 ) green = 0.0; else if ( green > 1.0 ) green = 1.0;
+ if (blue < 0.0 ) blue = 0.0; else if ( blue > 1.0 ) blue = 1.0;
if (alpha < 0.0 ) alpha = 0.0; else if ( alpha > 1.0 ) alpha = 1.0;
red *= 255.0;
@@ -172,8 +172,8 @@ ON_Color::SetFractionalRGBA( double red, double green, double blue, double alpha
double ON_Color::Hue() const
{
- // returns 0 to 2*pi
- // 0 = red, pi/3 = yellow, 2*pi/3 = green,
+ // returns 0 to 2*pi
+ // 0 = red, pi/3 = yellow, 2*pi/3 = green,
// pi = cyan, 4*pi/3 = blue, 5*pi/3 = magenta,
// 2*pi = red
double h;
@@ -192,7 +192,7 @@ double ON_Color::Hue() const
}
else if ( g == maxrgb)
h = 2.0 + (b - r)*d;
- else
+ else
h = 4.0 + (r - g)*d;
h *= ON_PI/3.0;
}
@@ -229,10 +229,10 @@ double ON_Color::Value() const
return (maxrgb/255.0);
}
-void ON_Color::SetHSV(
- double hue, // hue in radians
+void ON_Color::SetHSV(
+ double hue, // hue in radians
double saturation, // satuation 0.0 = gray, 1.0 = saturated
- double value // value
+ double value // value
)
{
int i;
@@ -250,15 +250,15 @@ void ON_Color::SetHSV(
if ( hue < 0.0 )
hue += 6.0;
i = (int)floor(hue);
- }
- f = hue - i;
+ }
+ f = hue - i;
p = value * ( 1.0 - saturation);
q = value * ( 1.0 - ( saturation * f) );
t = value * ( 1.0 - ( saturation * ( 1.0 - f) ) );
switch( i)
{
case 0:
- r = value; g = t; b = p; break;
+ r = value; g = t; b = p; break;
case 1:
r = q; g = value; b = p; break;
case 2:
@@ -268,11 +268,170 @@ void ON_Color::SetHSV(
case 4:
r = t; g = p; b = value; break;
case 5:
- r = value; g = p; b = q; break;
+ r = value; g = p; b = q; break;
default:
r = 0; g = 0; b = 0; break; // to keep lint quiet
- }
+ }
}
SetFractionalRGB(r,g,b);
}
+ON_ColorStop::ON_ColorStop(const ON_Color& color, double position)
+ : m_color(color)
+ , m_position(position)
+{
+}
+
+ON_4fColor::ON_4fColor()
+{
+ for (int i = 0; i < 4; i++)
+ {
+ m_color[i] = ON_UNSET_FLOAT;
+ }
+}
+
+//Note that these function will set the alpha correctly from ON_Colors "inverted" alpha.
+ON_4fColor::ON_4fColor(const ON_Color& in)
+{
+ *this = in;
+}
+
+ON_4fColor& ON_4fColor::operator=(const ON_Color& in)
+{
+ SetRed((float)in.FractionRed());
+ SetGreen((float)in.FractionGreen());
+ SetBlue((float)in.FractionBlue());
+ SetAlpha(1.0f - (float)in.FractionAlpha());
+ return *this;
+}
+
+//Will invert the opacity alpha to transparency.
+ON_4fColor::operator ON_Color(void) const
+{
+ ON_Color out;
+ out.SetFractionalRGBA(Red(), Green(), Blue(), 1.0 - Alpha());
+ return out;
+}
+
+float ON_4fColor::Red(void) const { return m_color[0];}
+void ON_4fColor::SetRed(float f) { m_color[0] = f;}
+
+float ON_4fColor::Green(void) const { return m_color[1];}
+void ON_4fColor::SetGreen(float f) { m_color[1] = f;}
+
+float ON_4fColor::Blue(void) const { return m_color[2]; }
+void ON_4fColor::SetBlue(float f) { m_color[2] = f; }
+
+float ON_4fColor::Alpha(void) const { return m_color[3]; }
+void ON_4fColor::SetAlpha(float f) { m_color[3] = f; }
+
+void ON_4fColor::SetRGBA(float r, float g, float b, float a)
+{
+ m_color[0] = r;
+ m_color[1] = g;
+ m_color[2] = b;
+ m_color[3] = a;
+}
+
+bool ON_4fColor::IsValid(class ON_TextLog* text_log) const
+{
+ for (int i = 0; i < 4; i++)
+ {
+ if (ON_IS_UNSET_FLOAT(m_color[i]))
+ return false;
+ }
+ return true;
+}
+
+
+
+bool ON_ColorStop::Write(ON_BinaryArchive& archive) const
+{
+ bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0);
+ if (!rc)
+ return false;
+
+ for (;;)
+ {
+ rc = archive.WriteColor(m_color);
+ if (!rc) break;
+ rc = archive.WriteDouble(m_position);
+ if (!rc) break;
+
+ break;
+ }
+
+ if (!archive.EndWrite3dmChunk())
+ rc = false;
+
+ return rc;
+}
+
+bool ON_ColorStop::Read(ON_BinaryArchive& file)
+{
+ int major_version = 0;
+ int minor_version = 0;
+ bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version);
+ if (!rc)
+ return false;
+ for (;;)
+ {
+ rc = (1 == major_version);
+ if (!rc) break;
+ rc = file.ReadColor(m_color);
+ if (!rc) break;
+ rc = file.ReadDouble(&m_position);
+ if (!rc) break;
+
+ break;
+ }
+ if (!file.EndRead3dmChunk())
+ rc = false;
+
+ return rc;
+}
+
+
+static int CompareNans(double a, double b)
+{
+ if (a == a)
+ {
+ if (b == b)
+ {
+ return ((a < b) ? -1 : ((a > b) ? 1 : 0));
+ }
+ // a is not a NAN, b is a NAN
+ return -1; // a < b - NAN's are last
+ }
+ else if (b == b)
+ {
+ // a is a NAN, b is not a NAN
+ return -1; // b < a - NAN's are last
+ }
+ return 0; // a and b are both NaNs
+}
+
+static int CompareDouble(double a, double b)
+{
+ return ((a < b) ? -1 : ((a > b) ? 1 : (a == b ? 0 : CompareNans(a, b))));
+}
+
+// < 0 if this < arg, 0 ir this==arg, > 0 if this > arg
+int ON_4fColor::Compare(const ON_4fColor& b) const
+{
+ int rc = CompareDouble(Red(), b.Red());
+ if (0 != rc)
+ return rc;
+
+ rc = CompareDouble(Green(), b.Green());
+ if (0 != rc)
+ return rc;
+
+ rc = CompareDouble(Blue(), b.Blue());
+ if (0 != rc)
+ return rc;
+
+ rc = CompareDouble(Alpha(), b.Alpha());
+
+ return rc;
+}
diff --git a/opennurbs_color.h b/opennurbs_color.h
index 82b4436d..6f76a3c5 100644
--- a/opennurbs_color.h
+++ b/opennurbs_color.h
@@ -214,4 +214,74 @@ private:
};
};
+///////////////////////////////////////////////////////////////////////////////
+//
+// Class ON_ColorStop
+//
+// Combination of a color and a single value. Typically used for defining
+// gradient fills over a series of colors.
+class ON_CLASS ON_ColorStop
+{
+public:
+ ON_ColorStop() = default;
+ ON_ColorStop(const ON_Color& color, double position);
+
+ bool Write(class ON_BinaryArchive& archive) const;
+ bool Read(class ON_BinaryArchive& archive);
+
+ ON_Color m_color = ON_Color::UnsetColor;
+ double m_position = 0;
+};
+
+#if defined(ON_DLL_TEMPLATE)
+ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray;
+#endif
+
+
+class ON_CLASS ON_4fColor
+{
+public:
+ ON_4fColor();
+ ~ON_4fColor() = default;
+ ON_4fColor(const ON_4fColor&) = default;
+ ON_4fColor& operator=(const ON_4fColor&) = default;
+
+ static const ON_4fColor Unset;
+
+ //Note that these function will set the alpha correctly from ON_Colors "inverted" alpha.
+ ON_4fColor(const ON_Color&);
+ ON_4fColor& operator=(const ON_Color&);
+
+ //Will invert the opacity alpha to transparency.
+ operator ON_Color(void) const;
+
+ float Red(void) const;
+ void SetRed(float);
+
+ float Green(void) const;
+ void SetGreen(float);
+
+ float Blue(void) const;
+ void SetBlue(float);
+
+ //Alpha in ON_4fColor is OPACITY - not transparency as in ON_Color.
+ float Alpha(void) const;
+ void SetAlpha(float);
+
+ void SetRGBA(float r, float g, float b, float a);
+
+ bool IsValid(class ON_TextLog* text_log = nullptr) const;
+
+ // < 0 if this < arg, 0 ir this==arg, > 0 if this > arg
+ int Compare(const ON_4fColor&) const;
+
+private:
+ float m_color[4];
+};
+
+#if defined(ON_DLL_TEMPLATE)
+ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray;
+#endif
+
+
#endif
diff --git a/opennurbs_compstat.cpp b/opennurbs_compstat.cpp
index 4d5c8109..65fa7bbe 100644
--- a/opennurbs_compstat.cpp
+++ b/opennurbs_compstat.cpp
@@ -283,8 +283,12 @@ unsigned int ON_ComponentStatus::ClearStates(
s1 &= ~HIGHLIGHTED_BIT;
}
+ // preserve value of runtime mark bit on m_status_flags
const unsigned char mark = (m_status_flags&RUNTIME_MARK_BIT);
+
+ // change m_status_flags to new value
m_status_flags = (s1|mark);
+
return 1;
}
@@ -388,33 +392,33 @@ bool ON_ComponentStatus::RuntimeMark() const
}
-unsigned int ON_ComponentStatus::SetRuntimeMark(
+bool ON_ComponentStatus::SetRuntimeMark(
bool bRuntimeMark
)
{
return bRuntimeMark ? SetRuntimeMark() : ClearRuntimeMark();
}
-unsigned int ON_ComponentStatus::SetRuntimeMark()
+bool ON_ComponentStatus::SetRuntimeMark()
{
const unsigned char c = (m_status_flags | RUNTIME_MARK_BIT);
if (c != m_status_flags)
{
m_status_flags = c;
- return 1;
+ return true;
}
- return 0;
+ return false;
}
-unsigned int ON_ComponentStatus::ClearRuntimeMark()
+bool ON_ComponentStatus::ClearRuntimeMark()
{
const unsigned char c = (m_status_flags & ~RUNTIME_MARK_BIT);
if (c != m_status_flags)
{
m_status_flags = c;
- return 1;
+ return true;
}
- return 0;
+ return false;
}
diff --git a/opennurbs_compstat.h b/opennurbs_compstat.h
index 50fe1bfa..80fd4d20 100644
--- a/opennurbs_compstat.h
+++ b/opennurbs_compstat.h
@@ -247,11 +247,26 @@ public:
// RuntimeMark
//
bool RuntimeMark() const;
- unsigned int SetRuntimeMark(
+
+ /*
+ Returns:
+ Input value of RuntimeMark();
+ */
+ bool SetRuntimeMark(
bool bRuntimeMark
);
- unsigned int SetRuntimeMark();
- unsigned int ClearRuntimeMark();
+
+ /*
+ Returns:
+ Input value of RuntimeMark();
+ */
+ bool SetRuntimeMark();
+
+ /*
+ Returns:
+ Input value of RuntimeMark();
+ */
+ bool ClearRuntimeMark();
//////////////////////////////////////////////////////////////////////////
//
diff --git a/opennurbs_convex_poly.cpp b/opennurbs_convex_poly.cpp
new file mode 100644
index 00000000..84075397
--- /dev/null
+++ b/opennurbs_convex_poly.cpp
@@ -0,0 +1,1028 @@
+/* $NoKeywords: $ */
+/*
+//
+// Copyright (c) 1993-2012 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_3dSimplex::ON_3dSimplex() { m_n = 0; }
+
+/* this function checks the validity of ClosetPoint results*/
+bool ClosestPointIsValid(const ON_ConvexPoly& AHull, const ON_ConvexPoly& BHull,
+ ON_4dex Adex, ON_4dex Bdex, ON_4dPoint ABBary, double atmost, ON_TextLog* log = nullptr);
+
+
+ON_3dSimplex::ON_3dSimplex(const ON_3dPoint& a) { m_n = 1; m_V[0] = a; }
+ON_3dSimplex::ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b) { m_n = 2; m_V[0] = a; m_V[1] = b; }
+ON_3dSimplex::ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c) { m_n = 3; m_V[0] = a; m_V[1] = b; m_V[2] = c; }
+ON_3dSimplex::ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c, const ON_3dPoint& d) { m_n = 4; m_V[0] = a; m_V[1] = b; m_V[2] = c; m_V[3] = d; }
+
+int ON_3dSimplex::Count() const { return m_n; };
+
+bool ON_3dSimplex::IsValid(double eps) const // true if the Verticies are affinely independent
+{
+ bool rc = true;
+ if (m_n >= 2)
+ {
+ ON_3dVector V = m_V[1] - m_V[0];
+ if( m_n==2)
+ rc = ( V.Length() > eps );
+ else
+ {
+ ON_3dVector W = m_V[2] - m_V[0];
+ ON_3dVector X = ON_CrossProduct(V, W);
+ // TODO put something smart here....
+ if( m_n==3)
+ rc = (X.Length() > eps);
+ else
+ {
+ // TODO and here....
+ double triple = X * (m_V[3] - m_V[0]);
+ rc = (fabs(triple) > eps);
+ }
+ }
+ }
+ return rc;
+}
+
+const ON_3dPoint& ON_3dSimplex::operator[](int i) const {
+ return *reinterpret_cast(m_V + i);
+}
+
+ON_3dPoint& ON_3dSimplex::operator[](int i) {
+ return *reinterpret_cast(m_V + i);
+}
+
+
+ON_3dPoint ON_3dSimplex::Vertex(int i) const { return ON_3dPoint(m_V[i]); }
+ON_3dPoint& ON_3dSimplex::Vertex(int i) { return *reinterpret_cast(&m_V[i]); }
+
+
+
+ON_3dPoint ON_3dSimplex::Evaluate(const double* b) const
+{
+ ON_3dVector p(0, 0, 0);
+ for (int i = 0; i < m_n; i++)
+ p += b[i] * m_V[i];
+ return p;
+}
+
+ON_3dPoint ON_3dSimplex::Evaluate(const ON_4dPoint& b) const { return Evaluate(&b.x); }
+
+double ON_3dSimplex::Volume() const
+{
+ double vol = 0.0;
+ int n = Count();
+ if (n >= 2)
+ {
+ ON_3dVector V = m_V[1] - m_V[0];
+ if (n == 2)
+ vol = V.Length();
+ else
+ {
+ ON_3dVector X = ON_CrossProduct(V, m_V[2]-m_V[0]);
+ if (n == 3)
+ vol = 0.5 * X.Length();
+ else
+ vol = 1.0/6.0 * fabs(X*(m_V[3] - m_V[0]));
+ }
+ }
+ return vol;
+}
+
+double ON_3dSimplex::SignedVolume() const
+{
+ double vol = ON_UNSET_VALUE;
+ if (Count() == 3)
+ {
+ ON_3dVector V = m_V[1] - m_V[0];
+ ON_3dVector X = ON_CrossProduct(V, m_V[2] - m_V[0]);
+ vol = 1.0 / 6.0 * (X*(m_V[3] - m_V[0]));
+ }
+ return vol;
+}
+
+ON_BoundingBox ON_3dSimplex::BoundingBox() const
+{
+ ON_BoundingBox box;
+ box.Set(3, false, m_n, 3, m_V[0], false);
+ return box;
+}
+
+bool ON_3dSimplex::GetBoundingBox(
+ ON_BoundingBox& bbox, int bGrowBox ) const
+{
+ return bbox.Set(3, false, m_n, 3, m_V[0], bGrowBox);
+}
+
+bool ON_3dSimplex::GetTightBoundingBox(
+ ON_BoundingBox& tight_bbox, bool bGrowBox,
+ const ON_Xform* xform ) const
+{
+ if (bGrowBox && !tight_bbox.IsValid())
+ {
+ bGrowBox = false;
+ }
+ if (!bGrowBox)
+ {
+ tight_bbox.Destroy();
+ }
+
+ int i;
+ for (i = 0; i < m_n; i++)
+ {
+ if (ON_GetPointListBoundingBox(3, 0, m_n, 3, m_V[i], tight_bbox, bGrowBox, xform))
+ bGrowBox = true;
+ }
+ return bGrowBox ? true : false;
+}
+
+bool ON_3dSimplex::Transform(
+ const ON_Xform& xform)
+{
+ for (int i = 0; i < m_n; i++)
+ m_V[i].Transform(xform);
+ return true;
+}
+
+bool ON_3dSimplex::Rotate(
+ double sin_angle,
+ double cos_angle,
+ const ON_3dVector& axis_of_rotation,
+ const ON_3dPoint& center_of_rotation)
+{
+ ON_Xform R;
+ R.Rotation(sin_angle, cos_angle, axis_of_rotation, center_of_rotation);
+ return Transform(R);
+}
+
+bool ON_3dSimplex::Rotate(
+ double angle_in_radians,
+ const ON_3dVector& axis_of_rotation,
+ const ON_3dPoint& center_of_rotation )
+{
+ ON_Xform R;
+ R.Rotation(angle_in_radians, axis_of_rotation, center_of_rotation);
+ return Transform(R);
+}
+
+bool ON_3dSimplex::Translate( const ON_3dVector& delta )
+{
+ ON_Xform T = ON_Xform::TranslationTransformation(delta);
+ return Transform(T);
+}
+
+
+bool ON_3dSimplex::RemoveVertex(int i)
+{
+ bool rc = false;
+ if (i < m_n)
+ {
+ m_n--;
+ for (/**/; i < m_n; i++)
+ m_V[i] = m_V[i + 1];
+ }
+ return rc;
+}
+
+bool ON_3dSimplex::AddVertex(const ON_3dPoint& P)
+{
+ bool rc = false;
+ if (m_n < 4)
+ {
+ m_V[m_n++] = P;
+ }
+ return rc;
+}
+
+
+bool ON_3dSimplex::SetVertex(int i, ON_3dPoint P0)
+{
+ bool rc = false;
+ if (i >= 0 && i < Count())
+ {
+ m_V[i] = P0;
+ // todo clear any cashed data
+ rc = true;
+ }
+ return rc;
+}
+
+ON_3dVector ON_3dSimplex::Edge(int e0, int e1) const
+{
+ ON_3dVector V = ON_3dVector::UnsetVector;
+ if (e0 < Count() && e1 < Count())
+ {
+ V = Vertex(e1) - Vertex(e0);
+ }
+ return V;
+}
+
+
+/*
+ This is a carefull implementation of cross product that tries to get an accurate result
+*/
+ON_3dVector ON_CrossProductwCare(const ON_3dVector& A, ON_3dVector& B)
+{
+ double norm[3];
+ norm[0] = A.MaximumCoordinate();
+ norm[1] = B.MaximumCoordinate();
+ ON_3dVector AxB = ON_CrossProduct(A, B);
+ const double thresh = 1.0e-8; // sin(A,B) ~< thresh^(1/2)
+ double ab = norm[0] * norm[1];
+ double ab2 = ab * ab;
+ if (AxB.LengthSquared() < ab2*thresh)
+ {
+ // TODO - this is a good start but we could do something better...
+ ON_3dVector V[3] = { A, B, A - B };
+ norm[2] = V[2].MaximumCoordinate();
+ int maxi = (norm[0] > norm[1]) ? 0 : 1;
+ if (norm[2] < norm[maxi]) // else C is longest so we are done.
+ {
+ AxB = ON_CrossProduct(V[(maxi + 1) % 3], V[(maxi + 2) % 3]);
+ if (maxi == 0)
+ AxB = - AxB;
+ }
+ }
+ return AxB;
+}
+
+
+ON_3dVector ON_3dSimplex::FaceNormal(int noti) const
+{
+ ON_3dVector N = ON_3dVector::UnsetVector;
+ if (Count() == 3 || (Count() == 4 && noti <= 3 && noti >= 0))
+ {
+ int ind[3] = { 0,1,2 };
+ if (Count() == 4 && noti < 3)
+ {
+ for (int ii = 0; ii < 3; ii++)
+ ind[ii] = (noti + 1 + ii) % 4;
+ }
+ ON_3dVector A = Vertex(ind[1]) - Vertex(ind[0]);
+ ON_3dVector B = Vertex(ind[2]) - Vertex(ind[0]);
+ N = ON_CrossProductwCare(A, B );
+ }
+ return N;
+}
+
+ON_3dVector ON_3dSimplex::FaceUnitNormal(int noti) const
+{
+ ON_3dVector N = FaceNormal(noti);
+ if (N != ON_3dVector::UnsetVector && N != ON_3dVector::ZeroVector)
+ N.Unitize();
+ return N;
+}
+
+bool ON_3dSimplex::GetClosestPoint(const ON_3dPoint& P0, ON_4dPoint& Bary, double atmost) const
+{
+ ON_3dVector V = P0;
+ ON_3dSimplex Trans;
+ bool toofar = ((atmost <= 0.0) ? false : true);
+ bool rval = false;
+ for (int i = 0; i < Count(); i++)
+ {
+
+ Trans.AddVertex(Vertex(i) - V);
+ if (toofar && Trans[i].MaximumCoordinate() < .5 * atmost)
+ toofar = false;
+ }
+ if (!toofar)
+ {
+ rval = Trans.GetClosestPointToOrigin(Bary);
+ if (rval && atmost >= 0)
+ {
+ ON_3dVector CP = Trans.Evaluate(Bary);
+ if (CP.LengthSquared() > atmost*atmost)
+ rval = false;
+ }
+ }
+ return rval;
+}
+
+bool ON_3dSimplex::GetClosestPointToOrigin(ON_4dPoint& Bary) const
+{
+ bool rc = false;
+ if (m_n == 4)
+ rc = Closest3plex(Bary);
+ else if (m_n == 3)
+ rc = Closest2plex(Bary);
+ else if (m_n == 2)
+ rc = Closest1plex(Bary);
+ else if (m_n == 1)
+ {
+ Bary = ON_4dPoint(1.0, 0, 0, 0);
+ rc = true;
+ }
+ return rc;
+}
+
+// return true if a and b are non zero and of same sign
+static bool SameSign(double a, double b)
+{
+ return (a*b) > 0;
+}
+
+
+// closest point to a 1-simplex
+bool ON_3dSimplex::Closest1plex(ON_4dPoint& Bary) const
+{
+ bool rc = false;
+ ON_3dVector Del = m_V[1] - m_V[0];
+ double Del2 = Del.LengthSquared();
+ if (Del2 > 0.0)
+ {
+ rc = true;
+ double dot = -m_V[0] * Del;
+ if (dot >= Del2)
+ Bary = ON_4dPoint(0, 1, 0, 0);
+ else if (dot <= 0)
+ Bary = ON_4dPoint(1, 0, 0, 0);
+ else
+ {
+ double b0 = dot / Del2;
+ Bary = ON_4dPoint(1 - b0, b0, 0, 0);
+ }
+ }
+ return rc;
+}
+
+// closest point to origin for a 2-simplex
+bool ON_3dSimplex::Closest2plex(ON_4dPoint& Bary) const
+{
+ bool rc = false;
+ ON_3dVector N = FaceNormal(0);
+ double N2 = N.LengthSquared();
+ if (N2 > 0)
+ {
+ ON_3dPoint P3 = (m_V[0] * N)*N / N2; // origin projected to Affine_Span < V_0, V_1, V_2 >
+ int J = N.MaximumCoordinateIndex();
+
+ ON_3dPoint Planar[3]; // We reduce to a planar closest point to origin problem
+ for (int i = 0; i < 3; i++)
+ Planar[i] = Vertex(i) - P3;
+
+ // Finding the barycentric coordintes of the origin will guild the rest of the algorithm
+ // We simplify this by projecting to Not J plane
+
+ double DetM = 0.0;
+ double C3[3];
+ int j0 = (J + 1) % 3;
+ int j1 = (J + 2) % 3;
+ for (int i = 0; i < 3; i++)
+ {
+ int i0 = (i + 1) % 3;
+ int i1 = (i + 2) % 3;
+ C3[i] = Planar[i0][j0] * Planar[i1][j1] - Planar[i1][j0] * Planar[i0][j1];
+ DetM += C3[i];
+ }
+
+ if (DetM != 0.0)
+ {
+ bool interior = true;
+ for (int j = 0; interior && j < 3; j++)
+ interior = SameSign(DetM, C3[j]);
+ if (interior)
+ {
+ for (int i = 0; i < 3; i++)
+ Bary[i] = C3[i] / DetM;
+ rc = true;
+ }
+ else
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ if (!SameSign(DetM, C3[j]))
+ {
+ ON_3dSimplex S(Planar[(j + 1) % 3], Planar[(j + 2) % 3]); // S is a 1-simplex
+ ON_4dPoint bary;
+ if (S.GetClosestPointToOrigin(bary))
+ {
+ rc = true;
+ bool OnEnd = (bary[0] == 1.0 || bary[1] == 1.0);
+ Bary[j] = 0.0;
+ Bary[3] = 0.0;
+ for (int i = 0; i < 2; i++)
+ Bary[(j + 1 + i) % 3] = bary[i];
+ if (!OnEnd)
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+// closest point to a 3-simplex
+bool ON_3dSimplex::Closest3plex(ON_4dPoint& Bary) const
+{
+ bool rc = false;
+ // Solving
+ // [ V_0 V_1 V_2 V_3 ] [ 0 ]
+ // M*B = [ 1 1 1 1 ] * B = [ 1 ]
+
+ int ind[3] = { 1,2,3 };
+ double detM = 0.0;
+ double C4[4]; // C4[j] = C_{4,j} is a cofactor of M
+ double sign = 1.0;
+ for (int j = 0; j < 4; j++)
+ {
+ C4[j] = sign * ON_TripleProduct(m_V[ind[0]], m_V[ind[1]], m_V[ind[2]]);
+ if (j < 3)
+ {
+ ind[j] = j; // {1,2,3}, {0,2,3}, {0,1,3}, {0,1,2}
+ sign *= -1.0;
+ }
+ detM += C4[j];
+ }
+
+
+ if (detM != 0.0)
+ {
+ bool interior = true;
+ int j = 0;
+ for (j = 0; interior && j < 4; j++)
+ interior = SameSign(detM, C4[j]);
+ if (interior)
+ {
+ for (int i = 0; i < 4; i++)
+ Bary[i] = C4[i] / detM;
+ rc = true;
+ }
+ else
+ {
+ j--;
+ double D2 = ON_DBL_MAX; // best answer so far
+ int N = 5; // size of support
+ do
+ {
+ if (!SameSign(detM, C4[j]))
+ {
+ ON_3dSimplex S = (*this);
+ S.RemoveVertex(j);
+ ON_4dPoint bary;
+ if (S.Closest2plex(bary))
+ {
+ int n = 0; // size of support
+ for (int i = 0; i < 3; i++) if (bary[i] > 0)n++;
+ if (n == 3)
+ {
+ for (int i = 3; i > j; i--)
+ bary[i] = bary[i - 1];
+ bary[j] = 0.0;
+ Bary = bary;
+ rc = true;
+ break;
+ }
+ else
+ {
+ ON_3dVector cp = S.Evaluate(bary);
+ double d2 = cp.LengthSquared();
+
+ if (d2 < D2 || d2 == D2 && n < N)
+ {
+ D2 = d2;
+ N = n;
+ for (int i = 3; i > j; i--)
+ bary[i] = bary[i - 1];
+ bary[j] = 0.0;
+ rc = true;
+ Bary = bary;
+ }
+ }
+ }
+ else
+ rc = false;
+ }
+ } while (++j < 4);
+ }
+ }
+ return rc;
+}
+
+bool ON_ConvexPoly::Standardize(ON_4dex& dex, ON_4dPoint& B)
+{
+ bool rc = true;
+ ON_4dex rdex = { -1,-1,-1,-1 }; // results
+ ON_4dPoint rB(0, 0, 0, 0);
+ int ri = 0; // index into result
+ for (int ii = 0; ii < 4; ii++) // index in input
+ {
+ while ((dex[ii] < 0 || B[ii] == 0.0) && ii < 4) ii++;
+ if (ii == 4)
+ break;
+ int j = 0;
+ while (j < ri && rdex[j] != dex[ii]) j++;
+ if (j == ri)
+ {
+ rdex[ri] = dex[ii];
+ rB[ri++] = 0.0;
+ }
+ rB[j] += B[ii];
+ }
+
+ if (rc)
+ {
+ dex = rdex;
+ B = rB;
+ }
+ return rc;
+}
+
+
+void ON_ConvexHullRef::Initialize(const ON_3dVector* V0, int n)
+{
+ m_n = n;
+ m_v = *V0;
+ m_is_rat = false;
+ m_stride = 3;
+}
+
+void ON_ConvexHullRef::Initialize(const ON_4dPoint* V0, int n)
+{
+ m_n = n;
+ m_v = *V0;
+ m_is_rat = true;
+ m_stride = 4;
+}
+
+// style must be either not_rational or homogeneous_rational = 2,
+void ON_ConvexHullRef::Initialize(const double* V0, ON::point_style style, int count)
+{
+ if (style == ON::homogeneous_rational)
+ Initialize(reinterpret_cast(V0), count);
+ else
+ Initialize(reinterpret_cast(V0), count);
+}
+
+ON_ConvexHullRef::ON_ConvexHullRef(const ON_3dVector* V0, int n)
+{
+ m_n = n;
+ m_v = *V0;
+ m_is_rat = false;
+ m_stride = 3;
+}
+
+ON_ConvexHullRef::ON_ConvexHullRef(const ON_3dPoint* P0, int n)
+{
+ m_n = n;
+ m_v = *P0;
+ m_is_rat = false;
+ m_stride = 3;
+}
+
+ON_ConvexHullRef::ON_ConvexHullRef(const ON_4dPoint* V0, int n)
+{
+ m_n = n;
+ m_v = *V0;
+ m_is_rat = true;
+ m_stride = 4;
+}
+
+ON_ConvexHullRef::ON_ConvexHullRef(const double* V0, bool is_rat, int n)
+{
+ m_n = n;
+ m_v = V0;
+ m_is_rat = is_rat;
+ m_stride = is_rat ? 4 : 3;
+}
+
+ON_ConvexHullRef::ON_ConvexHullRef(const double* V0, bool is_rat, int n, int stride)
+{
+ m_n = n;
+ m_v = V0;
+ m_is_rat = is_rat;
+ m_stride = stride;
+}
+
+ON_3dVector ON_ConvexHullRef::Vertex(int j) const
+{
+ ON_3dVector v;
+ if (m_is_rat)
+ {
+ ON_4dPoint hv = *(reinterpret_cast(m_v + m_stride*j));
+ v = ON_3dVector(hv.EuclideanX(), hv.EuclideanY(), hv.EuclideanZ());
+ }
+ else
+ {
+ v = *(reinterpret_cast(m_v + m_stride*j));
+ }
+ return v;
+}
+
+int ON_ConvexHullRef::SupportIndex(ON_3dVector W, int) const
+{
+ int j0 = 0;
+ double dot = Vertex(0)*W;
+ for (int j = 1; j < m_n; j++)
+ {
+ ON_3dVector v = Vertex(j);
+ double d = v * W;
+ if (d > dot)
+ {
+ dot = d;
+ j0 = j;
+ }
+ }
+ return j0;
+}
+
+double ON_ConvexHullRef::MaximumCoordinate() const
+{
+ return ON_MaximumCoordinate(m_v, 3, m_is_rat, m_n);
+}
+
+int ON_ConvexHullPoint2::AppendVertex(const ON_3dPoint& P) // return index of new vertex. must set Adjacent Indicies.
+{
+ m_Vert.Append(P);
+ Ref.Initialize(m_Vert, m_Vert.Count());
+ return m_Vert.Count()-1;
+}
+
+void ON_ConvexHullPoint2::Empty()
+{
+ Ref.Initialize(m_Vert, 0);
+ m_Vert.Empty();
+}
+
+double ON_ConvexHullPoint2::MaximumCoordinate() const
+{
+ return ON_MaximumCoordinate(m_Vert[0], 3, false, m_Vert.Count());
+}
+
+bool ClosestPoint(const ON_3dPoint P0, const ON_ConvexPoly& poly,
+ ON_4dex& dex, ON_4dPoint& Bary, double atmost )
+{
+ ON_ConvexHullRef CvxPt(&P0, 1);
+ // Set pdex to match the support of dex
+ ON_4dex pdex = dex;
+ for (int i = 0; i < 4; i++)
+ {
+ if (dex[i] >= 0)
+ pdex[i] = 0;
+ }
+ bool rc = ClosestPoint(CvxPt, poly, pdex, dex, Bary, atmost);
+ ON_ConvexPoly::Standardize(dex, Bary);
+ return rc;
+}
+
+// MatchingSupport(A, B) retuns a positive number if
+// A[i]<0 iff B[i]<0 and at least one coordinate pair has valid indicies A[i]>=0 and B[i]>=0.
+static int MatchingSupport(const ON_4dex& A, const ON_4dex& B)
+{
+ int nsup = 0;
+ int i =0;
+ for (; i < 4; i++)
+ {
+ if ((A[i] < 0) != (B[i] < 0))
+ break;
+ if (A[i] >= 0)
+ nsup++;
+ }
+ return (i == 4) ? nsup : -1;
+}
+
+static int Gregdebugcounter = 0;
+
+
+static void DebugCounter()
+{
+#ifdef ON_DEBUG
+ Gregdebugcounter++;
+#endif
+}
+
+// Gilbert Johnson Keerthi algorithm
+
+// To supply an inital seed simplex Adex and Bdex must be valid and
+// have matching support specifically
+// Adex[i]=0 for some i for some i
+// By satisfying this condition Adex and Bdex will define a simplex in A - B
+// Note the result of a ClosestPoint calculation Adex and Bdex satisfy these conditions
+bool ClosestPoint(const ON_ConvexPoly& A, const ON_ConvexPoly& B,
+ ON_4dex& Adex, ON_4dex& Bdex, ON_4dPoint& Bary, double atmost)
+{
+ bool rc = false;
+ if (A.Count() == 0 || B.Count() == 0)
+ return false;
+
+ int Aind[4] = { -1,-1,-1,-1 };
+ int Bind[4] = { -1,-1,-1,-1 };
+ ON_3dSimplex Simp;
+ Bary = ON_4dPoint(1, 0, 0, 0);
+ ON_3dVector v(0,0,0);
+
+
+ // If Adex and Bdex are valid on entry we use them as an inital
+ // seed for the trial simplex. This case is indicated by setting
+ // bFirstPass
+ bool bFirstPass = false;
+ if (A.IsValid4Dex(Adex) && B.IsValid4Dex(Bdex) && MatchingSupport(Adex, Bdex)>0 )
+ {
+ // Set the initial condition for Aind, Bind and Simp from Adex and Bdex
+ int j = 0;
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (Adex[i] < 0 || Bdex[i] < 0)
+ continue;
+ int jj;
+ for (jj = 0; jj < j; jj++)
+ {
+ if (Aind[jj] == Adex[i] && Bind[jj] == Bdex[i])
+ break;
+ }
+ if (jj < j)
+ break;
+ Aind[j] = Adex[i];
+ Bind[j] = Bdex[i];
+ ON_3dVector vert = A.Vertex(Aind[j]) - B.Vertex(Bind[j]);
+ Simp.AddVertex(vert);
+ j++;
+ }
+
+ if( i==4)
+ bFirstPass = true;
+ else
+ bFirstPass = false;
+ }
+
+ bool done = false;
+ double vlen = ON_DBL_MAX;
+ double vlenlast = ON_DBL_MAX;
+ while (!done)
+ {
+ if (!bFirstPass)
+ {
+ // Default initial simplex is a point A.Vertex(0) - B.Vertex(0);
+ Aind[0] = Bind[0] = 0;
+ Aind[1] = Aind[2] = Aind[3] = -1;
+ Bind[1] = Bind[2] = Bind[3] = -1;
+ v = A.Vertex(0) - B.Vertex(0);
+ vlenlast = ON_DBL_MAX;
+ vlen = v.Length();
+
+ Simp = ON_3dSimplex();
+ Simp.AddVertex(v);
+ }
+ // The key to the implemetation of this algorithm is contained in Simplex::GetClosestPointToOrigin()
+
+ // These lines are for ON_3dSimplex
+ const bool SimplexReindexing = true;
+
+ // These lines are for ON_JSimplex
+ //const bool SimplexReindexing = false;
+ //ON_JSimplex Simp;
+
+ double mu = 0.0;
+ const double epsilon = 10000.0 * ON_EPSILON;
+
+ int wA = 0, wB = 0;
+ while (!done && (bFirstPass || vlen > 0))
+ {
+ ON_3dVector W;
+ if (!bFirstPass)
+ {
+ wA = A.SupportIndex(-v, wA);
+ wB = B.SupportIndex(v, wB);
+ W = A.Vertex(wA) - B.Vertex(wB);
+ ON_3dVector unit_v = 1 / vlen * v;
+ double del = unit_v * W ; // this is lower bound on the distance
+ if (del > mu)
+ mu = del;
+
+ // 18-July-19 Considered Adding vlen>=vlenlast to ensure distance is decreasing
+ // See RH-47044 for a case that got hung up here
+ // See RH-30343 for a case where the 2.0 factor is needed
+ // WRONG!!! RH-30343 again. the 2.0 factor doest't work either
+ // TODO: testing... If the support vertex is already in simplex were done
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (Aind[i] == wA && Bind[i] == wB) break;
+ }
+
+ // See 100818MatchPoints.3dm if Bary>0 then we are done since closest point is
+ // in the interior of a Simplex
+ if (i < 4 || Simp.Count()==4)
+ done = true;
+ else
+ done = ((vlen - mu) <= 2.0* mu *epsilon) || mu > atmost || vlen >= vlenlast ;//TODO "1+" ?? got rid of it
+ }
+ if (!done)
+ {
+ // n0 is the index of the newly added vertex
+ int n0;
+ if (!bFirstPass)
+ {
+ n0 = Simp.Count(); // this is specific to ON_3dSimplex
+ Simp.AddVertex(W);
+ Aind[n0] = wA;
+ Bind[n0] = wB;
+ }
+ else
+ n0 = Simp.Count() - 1;
+
+
+
+ if (Simp.GetClosestPointToOrigin(Bary))
+ {
+ DebugCounter();
+ bFirstPass = false;
+ v = Simp.Evaluate(Bary);
+ vlenlast = vlen;
+ vlen = v.Length();
+ int imax = 3;
+ if (SimplexReindexing)
+ imax = n0;
+ for (int i = imax; i >= 0; i--)
+ {
+ if (Bary[i] == 0.0)
+ {
+ Simp.RemoveVertex(i);
+ if (SimplexReindexing)
+ {
+ /* The JSimplex the indicies are fixed so the following step is not needed.
+ the ON_3dSimplex type does change the index of verticies and this piece of
+ code adjusts for this.*/
+ for (int j = i; j < n0; j++)
+ {
+ Bary[j] = Bary[j + 1];
+ Aind[j] = Aind[j + 1];
+ Bind[j] = Bind[j + 1];
+ }
+ Bary[n0] = 0.0;
+ n0--;
+ }
+ }
+ }
+ }
+ else
+ {
+ // In this case I am going to terminte the iteration. If this was a FirstPass with user supplied initial guess
+ // then we restart the algorithm without the initial guess.
+ break;
+ }
+ }
+ }
+ if (!done)
+ {
+ if (bFirstPass)
+ bFirstPass = false;
+ else
+ done = true;
+
+ }
+
+ /* TODO
+ RH-54751
+ vlen is nearly 0. but it should be 0.0
+ If (0,0,0) s in the interior of A-B then solution is vlen == 0.0
+ */
+ if (Simp.Count() == 4 && Simp.Volume() > ON_SQRT_EPSILON)
+ {
+ vlen = 0.0;
+ }
+ rc = (vlen <= atmost);
+ if (rc)
+ {
+ if (SimplexReindexing)
+ {
+ if (Simp.Count() > 0)
+ {
+ for (int i = Simp.Count(); i < 4; i++)
+ {
+ Bary[i] = 0.0;
+ Aind[i] = Bind[i] = -1;
+ }
+ }
+ }
+
+
+ Adex = ON_4dex(Aind[0], Aind[1], Aind[2], Aind[3]);
+ Bdex = ON_4dex(Bind[0], Bind[1], Bind[2], Bind[3]);
+ }
+ }
+ return rc;
+}
+
+
+// Is PQR a right hnd turn
+static bool IsLeftTurn(const ON_2dPoint& P, const ON_2dPoint& Q, const ON_2dPoint& R)
+{
+ ON_2dVector A = R - Q;
+ ON_2dVector B = P - Q;
+ double det = A.x* B.y - B.x*A.y;
+ return det > 0.0;
+}
+
+
+int ON_ConvexHull2d(const ON_SimpleArray& Pnt, ON_SimpleArray& Hull, ON_SimpleArray< int>* PntInd)
+{
+ int rval = -1;
+ Hull.Empty();
+ ON_SimpleArray Ind(Pnt.Count());
+ Ind.SetCount(Pnt.Count());
+ if (PntInd)
+ PntInd->Empty();
+ if (!Pnt.Sort(ON::sort_algorithm::quick_sort, Ind,
+ [](const ON_2dPoint* A, const ON_2dPoint* B) { return ON_2dPoint::Compare(*A, *B); }))
+ return rval;
+ if (Pnt.Count() == 0)
+ return rval;
+
+ Hull.Append(Pnt[Ind[0]]);
+ if (PntInd)
+ PntInd->Append(Ind[0]);
+ int fixed = 1; // This is the count of Hull that is fixed
+ int ri = 1;
+
+ for (int inc = 1; inc >= -1; inc -= 2)
+ {
+ for ( /*empty*/; ri < Pnt.Count() && ri >= 0; ri += inc)
+ {
+ ON_2dPoint R = Pnt[Ind[ri]];
+
+ if (Hull.Count() == fixed)
+ {
+ if (R != *Hull.Last())
+ {
+ Hull.Append(R);
+ if (PntInd)
+ PntInd->Append(Ind[ri]);
+ }
+ }
+ else
+ {
+ int pi = Hull.Count() - 2;
+ ON_2dPoint P = Hull[pi];
+ ON_2dPoint Q = *Hull.Last();
+ if (IsLeftTurn(P, Q, R))
+ {
+ Hull.Append(R);
+ if (PntInd)
+ PntInd->Append(Ind[ri]);
+ }
+ else
+ {
+ bool done = false;
+ while (!done)
+ {
+ Hull.Remove();
+ if (PntInd)
+ PntInd->Remove();
+ Q = P;
+ done = (pi == ((inc==1)?0:fixed-1));
+ if (!done)
+ {
+ P = Hull[--pi];
+ done = IsLeftTurn(P, Q, R);
+ }
+
+ }
+ Hull.Append(R);
+ if (PntInd)
+ PntInd->Append(Ind[ri]);
+ }
+ }
+ }
+ if (Hull.Count() == 1) {
+ rval = 0;
+ break;
+ }
+ fixed = Hull.Count();
+ ri = Pnt.Count() - 2;
+ }
+
+ if (Hull.Count() == 2)
+ rval = 1;
+ else
+ rval = 2;
+ return rval;
+}
+
+
+
diff --git a/opennurbs_convex_poly.h b/opennurbs_convex_poly.h
new file mode 100644
index 00000000..3635038e
--- /dev/null
+++ b/opennurbs_convex_poly.h
@@ -0,0 +1,398 @@
+/* $NoKeywords: $ */
+/*
+//
+// Copyright (c) 1993-2012 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(ON_CONVEX_POLY_INC_)
+#define ON_CONVEX_POLY_INC_
+
+
+// A Simplex in 3d
+class ON_CLASS ON_3dSimplex
+{
+public:
+ ON_3dSimplex(); // An empty simplex
+ explicit ON_3dSimplex(const ON_3dPoint& a); // 0-simplex in 3d
+ ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b); // 1-simplex
+ ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c); // 2-simplex
+ ON_3dSimplex(const ON_3dPoint& a, const ON_3dPoint& b, const ON_3dPoint& c, const ON_3dPoint& d); // 3-simplex
+
+ ON_3dSimplex(const ON_3dSimplex& rhs) = default;
+ ON_3dSimplex& operator=(const ON_3dSimplex& rhs) = default;
+ ~ON_3dSimplex() = default;
+
+ int Count() const; // Number of Verticies <=4
+ bool IsValid(double eps) const; // true if the Verticies are affinely independent
+
+ /*
+ Description:
+ Evaluate a point in a Simplex from a barycentric coordinate b.
+ Returns:
+ The point
+ b[0] * Vertex[0] + ... + b[Count()-1] * Vertex[Count()-1]
+ Notes:
+ If b[0] + ... + b[Count()-1] = 1 and b[i]>=0 for i=0 to Count()-1 then the
+ returned point is on the simplex
+ */
+ ON_3dPoint Evaluate(const double* b) const;
+ ON_3dPoint Evaluate(const ON_4dPoint& b) const;
+
+ /*
+ Description:
+ Find Closest Point to this simplex from a base point P0 or the Origin.
+ If true is retuned then Evaluate(Bary) is the closest point on the Simplex.
+ maximum_distance - optional upperbound on closest point. If maximum_distance>=0 is specified and
+ Dist(P0, Simplex)>maximum_distance then false is returned.
+ */
+ bool GetClosestPoint(const ON_3dPoint& P0, ON_4dPoint& Bary, double maximum_distance = ON_DBL_MAX) const;
+ bool GetClosestPointToOrigin(ON_4dPoint& Bary) const;
+
+ /*
+ Count() Volume() returns
+ 0 0.0
+ 1 0.0
+ 2 length >=0
+ 3 area >=0
+ 4 volume >=0
+ */
+ double Volume() const;
+ double SignedVolume() const; // returns ON_UNSET_VALUE if Count()<4 else the signed volume
+
+ /*
+ FaceNormal(noti) is the oriented face normal obtained by omitting vertex noti.
+ FaceNormal returns ON_UNSET_VALUE if Count()<3 or Count()==4 noti not 0,1,2 or 3.
+ FaceUnitNormal returns ON_UNSET_VALUE if Count()<3 or Count()==4 noti not 0,1,2 or 3 or if FaceNormal(noti)=Zero_Vector
+ */
+ ON_3dVector FaceNormal(int noti = 0) const;
+ ON_3dVector FaceUnitNormal(int noti = 0) const;
+
+ /*
+ Edge vector from Vertex(e0) to Vertex(e1)
+ */
+ ON_3dVector Edge(int e0, int e1)const;
+
+ /* If 0<=i=0
+ */
+ virtual int Count() const = 0;
+ /*
+ Returns: Vertex[i] for i=0,...,Count()-1
+ */
+ virtual ON_3dVector Vertex(int i) const = 0;
+
+ /*
+ Description:
+ Let K be this ON_ConvexPoly then for a non-zero vector W the support Support(W) is a point x in K that maximizes
+ min arg x * W
+ x \in K
+ i0 is an optional initial index seed value. It may provide a performance enhancement toward finding
+ a minimizer.
+ */
+ ON_3dPoint Support(ON_3dVector W, int i0 =0) const
+ {
+ return Vertex(SupportIndex(W, i0));
+ }
+
+ /*
+ Description:
+ For any vector W there is a vetex that is Support(W)
+ SupportIndex( W, i0) returns a vertex index for a vertex that is the support.
+ Veretx( K.SupportIndex( W )) = K.Support(W );
+ */
+ virtual int SupportIndex(ON_3dVector W, int i0=0) const = 0;
+
+ /*
+ Description:
+ Points in a Convex Polytope are parameterized , not necessaily uniquely,
+ by an ON_4dex of vertex indicies and a 4d barycentric point B
+ Evaluate(Ind, B ) = Sum_{i=0,..,3} Vertex(Ind[i])*B[i], where the sum is taken over i such that Ind[i]>=0
+ If B is a barycentric coordinte
+ B[i]>=0 and B[0] + B[1] + B[2] + B[3] = 1.0
+ then Evaluate( Ind, B) is a point in the convex polytope
+ */
+ ON_3dPoint Evaluate(ON_4dex dex, ON_4dPoint B)const
+ {
+ ON_3dVector v(0, 0, 0);
+ if (dex.i >= 0)
+ v = B[0] * Vertex(dex.i);
+ if (dex.j >= 0)
+ v += B[1] * Vertex(dex.j);
+ if (dex.k >= 0)
+ v += B[2] * Vertex(dex.k);
+ if (dex.l >= 0)
+ v += B[3] * Vertex(dex.l);
+ return v;
+ };
+
+ /*
+ Description:
+ This is a bound on the collection of verticies.
+ Vertex(i).MaximumCoordinate()<= MaximumCoordinate() for all i
+ */
+ virtual double MaximumCoordinate() const = 0;
+
+ /*
+ Description:
+ A point represented by a ON_4dex D and a barycentric coordinate B
+ can be put in a standard form so that non-negative elements of D are unique and
+ corresponding coordinates are positive. Furthemore, the non-negative
+ indicies are all listed before the unset ( neagative ) values
+ */
+ static bool Standardize(ON_4dex& D, ON_4dPoint& B);
+
+ /*
+ Returns:
+ true if d[i] n) return false;
+ }
+ return true;
+ }
+ bool IsValid4Dex(const ON_4dex& D) const { return IsValid4DexN(D, Count()); };
+
+ virtual ~ON_ConvexPoly() {};
+};
+
+// 3d convex hull defined by an explicit collection of points called verticies.
+// Note: verticies need not be extreme points
+
+// WARNING: Points are referenced not stored for optimal performance in'
+// some applications.
+// The list of points must remain alive and in there initial location
+// For the duration of this object.
+class ON_CLASS ON_ConvexHullRef : public ON_ConvexPoly
+{
+public:
+ ON_ConvexHullRef() { m_n = 0; m_is_rat = false; m_stride = 3; };
+ ON_ConvexHullRef(const ON_3dVector* V0, int count); // a 3d point array
+ ON_ConvexHullRef(const ON_3dPoint* V0, int count); // a 3d point array
+ ON_ConvexHullRef(const ON_4dPoint* V0, int count); // a array of homogeneous points
+ ON_ConvexHullRef(const double* v0, bool is_rat, int n); // v0 is an array of 3dpoints or homo 4d points
+ ON_ConvexHullRef(const double* v0, bool is_rat, int n, int stride); // v0 is an array of 3dpoints or homo 4d points
+
+ void Initialize(const ON_3dVector* V0, int count);
+ void Initialize(const ON_4dPoint* V0, int count);
+ void Initialize(const double* V0, ON::point_style style, int count); // style must be either not_rational or homogeneous_rational = 2,
+
+ int Count() const override { return m_n; }
+ ON_3dVector Vertex(int j) const override;
+
+ // Support map O( Vertes.Count
+ virtual int SupportIndex(ON_3dVector W, int i0) const override;
+ virtual double MaximumCoordinate() const override;
+
+ virtual ~ON_ConvexHullRef() override {};
+private:
+
+ int m_n = 0;
+ bool m_is_rat= false;
+ const double* m_v = nullptr;
+ int m_stride=3;
+};
+
+// 3d convex hull defined by an explicit collection of points called verticies.
+// Note: verticies need not be extreme points
+class ON_CLASS ON_ConvexHullPoint2 : public ON_ConvexPoly
+{
+public:
+ ON_ConvexHullPoint2() = default;
+ ON_ConvexHullPoint2(int init_capacity) : m_Vert(init_capacity) {};
+
+ virtual int Count() const override { return m_Vert.Count(); }
+ virtual ON_3dVector Vertex(int j) const override { return m_Vert[j]; }
+
+ // Support map
+ virtual int SupportIndex(ON_3dVector W, int i0) const override {
+ return Ref.SupportIndex(W, i0);
+ };
+
+ virtual double MaximumCoordinate() const override;
+
+ virtual ~ON_ConvexHullPoint2() override {};
+
+ int AppendVertex(const ON_3dPoint& P); // return index of new vertex. must set Adjacent Indicies.
+ void Empty();
+
+ bool SetCapacity(int vcnt) {
+ m_Vert.SetCapacity(vcnt);
+ return true;
+ };
+
+private:
+ ON_ConvexHullRef Ref;
+ ON_SimpleArray m_Vert;
+};
+
+
+/*
+
+Computes a closest point between convex polytopes AHull and BHull.
+Returns true if a closest point is found and it is within optional maximum_distance bound;
+
+Specifically, when true is returned parameters (Adex, ABbary) on AHull
+and (Bdex, ABbary) on BHull are found such that:
+if a* = AHull.Evaluate(Adex,ABbary), and
+ b* = BHull.Evaluate(Bdex,ABbary),
+then
+ d = dist(a*, b*) <= maximum_distance, and
+for any a in AHull and b in BHull
+ dist(a,b) => d.
+
+
+Setting maximum_distance = tol can speedup the calculation in cases where d>tol
+
+On input Adex and Bdex are used to define an intial simplex
+use Adex = Bdex = ON_4dex::Unset if you have no good initial guess
+Notice the result is indepentent of the initial guess.
+
+*/
+ON_DECL
+bool ClosestPoint(const ON_ConvexPoly& AHull, const ON_ConvexPoly& BHull,
+ ON_4dex& Adex, ON_4dex& Bdex, ON_4dPoint& ABbary, double maximum_distance = ON_DBL_MAX);
+
+ON_DECL
+bool ClosestPoint(const ON_3dPoint P0, const ON_ConvexPoly& poly,
+ ON_4dex& dex, ON_4dPoint& Bary, double maximum_distance = ON_DBL_MAX);
+
+/*
+ Compute Convex hull of 2d points
+ Parameters:
+ Pnt - array of points, this is array of working data. The points are sorted in place as part of the algorithm
+ HUll - the sequence Hull[0], HUll[1]... ,*Hull.Last() == Hull[0] defines the convex hull with a positive orientation retuns 2.
+ PntInd - otional array to be filled in so that Hull[i] = Pnt[ PntInd[i]] where Pnt is the original input point
+ Returns
+ dimension of the convex hull
+ 2 - Hull is 2 dimensional
+ 1 - Hull is a line segments
+ 0 - hull is a point
+ <0 error
+*/
+ON_DECL
+int ON_ConvexHull2d(const ON_SimpleArray& Pnt, ON_SimpleArray& Hull, ON_SimpleArray< int>* PntInd = nullptr);
+
+#endif
+
+
diff --git a/opennurbs_curve.cpp b/opennurbs_curve.cpp
index a7676bb2..203fdb5e 100644
--- a/opennurbs_curve.cpp
+++ b/opennurbs_curve.cpp
@@ -585,7 +585,10 @@ bool ON_Curve::IsClosed() const
{
// Note: The point compare test should be the same
// as the one used in ON_PolyCurve::HasGap().
- //
+ // June 2019 - sometime in the past decade ON_PolyCurve::HasGap()
+ // changed and the test there is different from this test.
+ // The initial "Note" no longer applies becaue it's no longer
+ // clear why the current ON_PolyCurve::HasGap() was changed.
if ( ON_PointsAreCoincident( dim, false, a, p ) )
{
if ( Evaluate( d.ParameterAt(1.0/3.0), 0, dim, b, 0 )
@@ -593,7 +596,7 @@ bool ON_Curve::IsClosed() const
)
{
if ( false == ON_PointsAreCoincident( dim, false, a, b )
- && false == ON_PointsAreCoincident( dim, false, p, c )
+ && false == ON_PointsAreCoincident( dim, false, a, c )
&& false == ON_PointsAreCoincident( dim, false, p, b )
&& false == ON_PointsAreCoincident( dim, false, p, c )
)
@@ -2515,6 +2518,10 @@ static int CompareJoinEnds(void* ctext, const void* aA, const void* bB)
if (a->tan_dot <= context->dot_tol && b->tan_dot > context->dot_tol) return 1;
if (a->dist < b->dist) return -1;
if (a->dist > b->dist) return 1;
+ if (a->id[0] < b->id[0]) return -1;
+ if (a->id[0] > b->id[0]) return 1;
+ if (a->id[1] < b->id[1]) return -1;
+ if (a->id[1] > b->id[1]) return 1;
return 0;
}
else {
@@ -2522,6 +2529,10 @@ static int CompareJoinEnds(void* ctext, const void* aA, const void* bB)
if (a->dist > b->dist) return 1;
if (a->tan_dot > b->tan_dot) return -1;
if (a->tan_dot < b->tan_dot) return 1;
+ if (a->id[0] < b->id[0]) return -1;
+ if (a->id[0] > b->id[0]) return 1;
+ if (a->id[1] < b->id[1]) return -1;
+ if (a->id[1] > b->id[1]) return 1;
return 0;
}
}
@@ -2841,6 +2852,8 @@ static bool GetCurveEndData(int count,
ON_SimpleArray& EData)
{
+ join_tol = join_tol * join_tol;
+
EData.Reserve(count);
bool bHaveTans = (StartTans && EndTans) ? true : false;
if (dot_tol < 0.0)
@@ -2863,7 +2876,7 @@ static bool GetCurveEndData(int count,
const ON_3dPoint& Pi = (endi) ? EndPoints[i] : StartPoints[i];
for (int endj=0; endj<2; endj++){
const ON_3dPoint& Pj = (endj) ? EndPoints[j] : StartPoints[j];
- dist[endi][endj] = Pi.DistanceTo(Pj);
+ dist[endi][endj] = (Pi-Pj).LengthSquared();
if (dist[endi][endj] >= join_tol)
bDoIt[endi][endj] = false;
}
diff --git a/opennurbs_curve.h b/opennurbs_curve.h
index bb8576d3..5c078328 100644
--- a/opennurbs_curve.h
+++ b/opennurbs_curve.h
@@ -261,13 +261,13 @@ public:
// Description:
// Get number of parameters of "knots".
// Parameters:
- // knots - [out] an array of length SpanCount()+1 is filled in
+ // span_parameters - [out] an array of length SpanCount()+1 is filled in
// with the parameters where the curve is not smooth (C-infinity).
// Returns:
// true if successful
virtual
bool GetSpanVector(
- double* knots
+ double* span_parameters
) const = 0; //
//////////
diff --git a/opennurbs_defines.cpp b/opennurbs_defines.cpp
index dfc1af6b..be404ed9 100644
--- a/opennurbs_defines.cpp
+++ b/opennurbs_defines.cpp
@@ -2317,6 +2317,32 @@ ON_4dex::ON_4dex(
, l(lValue)
{}
+int ON_4dex::operator[](int ind) const
+{
+ switch (ind) {
+ case 0:
+ return i;
+ case 1:
+ return j;
+ case 2:
+ return k;
+ }
+ return l;
+}
+
+int& ON_4dex::operator[](int ind)
+{
+ switch (ind) {
+ case 0:
+ return i;
+ case 1:
+ return j;
+ case 2:
+ return k;
+ }
+ return l;
+}
+
ON_2udex::ON_2udex(
unsigned int iValue,
unsigned int jValue)
diff --git a/opennurbs_defines.h b/opennurbs_defines.h
index 284ae850..d61426d0 100644
--- a/opennurbs_defines.h
+++ b/opennurbs_defines.h
@@ -135,9 +135,11 @@
#if defined(PI)
#define ON_PI PI
#else
-#define ON_PI 3.141592653589793238462643
+#define ON_PI 3.141592653589793238462643
#endif
+#define ON_2PI (2.0*ON_PI)
+
#define ON_DEGREES_TO_RADIANS (ON_PI/180.0)
#define ON_RADIANS_TO_DEGREES (180.0/ON_PI)
@@ -324,6 +326,13 @@ double ON_DoubleFromFloat(
float x
);
+/*
+Returns:
+ A nonzero runtime unsigned that is incremented every call to ON_NextContentSerialNumber().
+ This value is useful as a "content serial number" that can be used to detect
+ when the content of an object has changed.
+*/
+ON__UINT64 ON_NextContentSerialNumber();
ON_END_EXTERNC
@@ -399,10 +408,13 @@ bool ON_IsNullPtr(const ON__INT_PTR ptr);
#define ON_RELATIVE_CURVATURE_TOLERANCE 0.05
/* default value for angle tolerances = 1 degree */
-#define ON_DEFAULT_ANGLE_TOLERANCE (ON_PI/180.0)
+#define ON_DEFAULT_ANGLE_TOLERANCE_RADIANS (ON_PI/180.0)
+#define ON_DEFAULT_ANGLE_TOLERANCE_DEGREES (ON_DEFAULT_ANGLE_TOLERANCE_RADIANS * 180.0/ON_PI)
+#define ON_DEFAULT_ANGLE_TOLERANCE ON_DEFAULT_ANGLE_TOLERANCE_RADIANS
#define ON_DEFAULT_ANGLE_TOLERANCE_COSINE 0.99984769515639123915701155881391
#define ON_MINIMUM_ANGLE_TOLERANCE (ON_DEFAULT_ANGLE_TOLERANCE/10.0)
+#define ON_DEFAULT_DISTANCE_TOLERANCE_MM 0.01
/*
*/
@@ -553,6 +565,9 @@ public:
ON_4dex(const ON_4dex&) = default;
ON_4dex& operator=(const ON_4dex&) = default;
+
+ int operator[](int i) const;
+ int& operator[](int i);
public:
// do not initialize i, j, k, l for performance reasons
int i;
@@ -673,6 +688,24 @@ enum class ON_ChainDirection : unsigned char
Both = 3
};
+///
+///Style of color gradient
+///
+enum class ON_GradientType : int
+{
+ ///No gradient
+ None = 0,
+ ///Linear (or axial) gradient between two points
+ Linear = 1,
+ ///Radial (or spherical) gradient using a center point and a radius
+ Radial = 2,
+ ///Disabled linear gradient. Useful for keeping gradient information around, but not having it displayed
+ LinearDisabled = 3,
+ ///Disabled radial gradient. Useful for keeping gradient information around, but not having it displayed
+ RadialDisabled = 4
+};
+
+
// OpenNurbs enums
class ON_CLASS ON
{
@@ -1972,14 +2005,22 @@ public:
morph_control_object = 0x20000, // some type of ON_MorphControl
subd_object = 0x40000, // some type of ON_SubD, ON_SubDRef, ON_SubDComponentRef, ON_SubD....
loop_object = 0x80000, // some type of ON_BrepLoop
- brepvertex_filter = 0x100000, // selection filter value - not a real object type (ON_BrepEdge, ON_SubDVertex)
+ brepvertex_filter = 0x100000, // selection filter value - not a real object type (ON_BrepVertex)
polysrf_filter = 0x200000, // selection filter value - not a real object type
- edge_filter = 0x400000, // selection filter value - not a real object type (ON_BrepEdge, ON_SubDEdge)
+ edge_filter = 0x400000, // selection filter value - not a real object type (ON_BrepEdge with associated ON_BrepTrim)
polyedge_filter = 0x800000, // selection filter value - not a real object type
+
+
+ // NOTE WELL:
+ // The "mesh" vertex/edge/face filters and "meshcomponent_reference"
+ // are used to identify ON_Mesh and ON_SubD components.
+ // By the time subd_object was added, there were not enough unused bits
+ // for separate subd component filters.
meshvertex_filter = 0x01000000, // selection filter value - not a real object type (ON_MeshTopologyVertex, ON_SubDVertex)
meshedge_filter = 0x02000000, // selection filter value - not a real object type (ON_MeshTopologyEdge, ON_SubDEdge)
meshface_filter = 0x04000000, // selection filter for ON_Mesh triangle, quad, ngon, or ON_SubDFace - not a real object type
- meshcomponent_reference = 0x07000000, // an ON_MeshComponentRef or ON_SubDComponentRef
+ meshcomponent_reference = 0x07000000, // an ON_MeshComponentRef or ON_SubDComponentRef)
+
cage_object = 0x08000000, // some type of ON_NurbsCage
phantom_object = 0x10000000,
clipplane_object = 0x20000000,
@@ -2237,6 +2278,13 @@ public:
/// Attach point at right text horizontal advance (not glyph bounding box)
///
Right = 2,
+ ///
+ /// Used for Leaders only
+ /// Attach point adjusts to Right or Left depending on leader tail direction in view
+ /// If tail direction is to the Left, alignment is Right
+ /// If tail direction is to the Right, alignment is Left
+ ///
+ Auto = 3,
};
#pragma endregion
@@ -2353,6 +2401,8 @@ public:
subd_edge = 72, // m_index = ON_SubDEdge.m_id
subd_face = 73, // m_index = ON_SubDFace.m_id
+ hatch_loop = 81, // m_index = ON_Hatch::m_loops[] array index
+
dim_linear_point = 100,
dim_radial_point = 101,
dim_angular_point = 102,
@@ -2377,6 +2427,13 @@ public:
static
ON_COMPONENT_INDEX::TYPE Type(int i);
+ /*
+ Description:
+ Compare on m_type (as an int).
+ */
+ static
+ int CompareType( const ON_COMPONENT_INDEX* lhs, const ON_COMPONENT_INDEX* rhs);
+
/*
Description:
Dictionary compare on m_type, m_index as ints.
@@ -2427,7 +2484,7 @@ public:
/*
Returns:
True if m_type is set to a TYPE enum value between
- brep_vertex and polycurve_segment.
+ brep_vertex and dim_leader_point.
*/
bool IsSet() const;
@@ -2524,6 +2581,12 @@ public:
*/
bool IsAnnotationComponentIndex() const;
+ /*
+ Returns:
+ True if m_type = hatch_loop and m_index >= 0.
+ */
+ bool IsHatchLoopComponentIndex() const;
+
void Dump(
class ON_TextLog& text_log
)const;
@@ -2565,6 +2628,8 @@ public:
extrusion_cap_surface 0 = bottom cap, 1 = top cap
extrusion_path -1 = entire path, 0 = start of path, 1 = end of path
+ hatch_loop ON_Hatch::m_loops[] array index
+
dim_linear_point linear dimension point index
dim_radial_point radial dimension point index
dim_angular_point angular dimension point index
diff --git a/opennurbs_dimension.cpp b/opennurbs_dimension.cpp
index 6a804f3e..226fbe13 100644
--- a/opennurbs_dimension.cpp
+++ b/opennurbs_dimension.cpp
@@ -77,8 +77,6 @@ void ON_Dimension::Internal_CopyFrom(const ON_Dimension& src)
m_detail_measured = src.m_detail_measured;
m_flip_arrow_1 = src.m_flip_arrow_1;
m_flip_arrow_2 = src.m_flip_arrow_2;
- m_force_arrows = src.m_force_arrows;
- m_force_textpos = src.m_force_textpos;
}
bool ON_Dimension::IsValid(ON_TextLog* text_log) const
@@ -294,23 +292,90 @@ void ON_Dimension::SetDetailMeasured(ON_UUID uuid)
ON_Dimension::ForceArrow ON_Dimension::ForceArrowPosition() const
{
- return m_force_arrows;
+ ON_ERROR("Use ON_Dimension::ArrowFit(const ON_DimStyle* parent_style)");
+ return ON_Dimension::ForceArrow::Auto;
}
void ON_Dimension::SetForceArrowPosition(ON_Dimension::ForceArrow force)
{
- m_force_arrows = force;
+ //
+ ON_ERROR("Use ON_Dimension::SetArrowFit(const ON_DimStyle* parent_style,ON_DimStyle::arrow_fit arrowfit)");
}
ON_Dimension::ForceText ON_Dimension::ForceTextPosition() const
{
- return m_force_textpos;
+ ON_ERROR("Use ON_Dimension::TextFit(const ON_DimStyle* parent_style)");
+ return ON_Dimension::ForceText::Auto;
}
void ON_Dimension::SetForceTextPosition(ON_Dimension::ForceText force)
{
- m_force_textpos = force;
+ ON_ERROR("Use ON_Dimension::SetTextFit(const ON_DimStyle* parent_style,ON_DimStyle::text_fit textfit)");
}
+//--------------------------------
+
+void ON_Dimension::SetForceDimLine(
+ const ON_DimStyle* parent_style,
+ bool force_dimline
+)
+{
+ parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style);
+ bool bCreate = (force_dimline != parent_style->ForceDimLine());
+ ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate);
+ if (nullptr != override_style)
+ {
+ override_style->SetForceDimLine(force_dimline);
+ override_style->SetFieldOverride(ON_DimStyle::field::ForceDimLine, bCreate);
+ }
+}
+
+bool ON_Dimension::ForceDimLine(
+ const ON_DimStyle* parent_style) const
+{
+ return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::ForceDimLine).ForceDimLine();
+
+}
+
+void ON_Dimension::SetTextFit(
+ const ON_DimStyle* parent_style,
+ ON_DimStyle::text_fit textfit)
+{
+ parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style);
+ bool bCreate = (textfit != parent_style->TextFit());
+ ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate);
+ if (nullptr != override_style)
+ {
+ override_style->SetTextFit(textfit);
+ override_style->SetFieldOverride(ON_DimStyle::field::TextFit, bCreate);
+ }
+}
+
+ON_DimStyle::text_fit ON_Dimension::TextFit(
+ const ON_DimStyle* parent_style) const
+{
+ return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::TextFit).TextFit();
+}
+
+void ON_Dimension::SetArrowFit(
+ const ON_DimStyle* parent_style,
+ ON_DimStyle::arrow_fit arrowfit)
+{
+ parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style);
+ bool bCreate = (arrowfit != parent_style->ArrowFit());
+ ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate);
+ if (nullptr != override_style)
+ {
+ override_style->SetArrowFit(arrowfit);
+ override_style->SetFieldOverride(ON_DimStyle::field::TextFit, bCreate);
+ }
+}
+
+ON_DimStyle::arrow_fit ON_Dimension::ArrowFit(
+ const ON_DimStyle* parent_style) const
+{
+ return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::ArrowFit).ArrowFit();
+}
+
//----------------------------------------------------------
// Class ON_DimLinear
@@ -365,6 +430,9 @@ bool ON_Dimension::Internal_WriteDimension(
if (!ON_Annotation::Internal_WriteAnnotation(archive))
break;
+ const ON_DimStyle& ds = archive.ArchiveCurrentDimStyle();
+
+
if (!archive.WriteString(m_user_text))
break;
if (!archive.WriteDouble(0.0)) // OBSOLETE m_text_rotation
@@ -377,8 +445,8 @@ bool ON_Dimension::Internal_WriteDimension(
break;
if (!archive.WriteBool(m_flip_arrow_2))
break;
- unsigned int u = static_cast(m_force_arrows);
- if (!archive.WriteInt(u))
+ const unsigned int legacy_arrow_fit = static_cast(ArrowFit(&ds));
+ if (!archive.WriteInt(legacy_arrow_fit))
break;
if (!archive.WriteUuid(m_detail_measured))
break;
@@ -386,8 +454,8 @@ bool ON_Dimension::Internal_WriteDimension(
break;
// content_version 1
- const unsigned int force_textpos_as_unsigned = static_cast(m_force_textpos);
- if (!archive.WriteInt(force_textpos_as_unsigned))
+ const unsigned int legacy_text_fit = static_cast(TextFit(&ds));
+ if (!archive.WriteInt(legacy_text_fit))
break;
rc = true;
@@ -410,6 +478,9 @@ bool ON_Dimension::Internal_ReadDimension(
if (false == archive.BeginRead3dmAnonymousChunk(&content_version))
return false;
+ unsigned int legacy_arrow_fit = 0;
+ unsigned int legacy_text_fit = 0;
+
bool rc = false;
for (;;)
{
@@ -431,14 +502,14 @@ bool ON_Dimension::Internal_ReadDimension(
break;
if (!archive.ReadBool(&m_flip_arrow_2))
break;
- unsigned int u = static_cast(m_force_arrows);
- if (!archive.ReadInt(&u))
+ if (!archive.ReadInt(&legacy_arrow_fit))
break;
- m_force_arrows = ON_Dimension::ForceArrowFromUnsigned(u);
if (!archive.ReadUuid(m_detail_measured))
break;
if (!archive.ReadDouble(&m_distance_scale))
break;
+ if (ON_nil_uuid == m_detail_measured)
+ m_distance_scale = 1.0;
if (content_version <= 0)
{
@@ -447,10 +518,8 @@ bool ON_Dimension::Internal_ReadDimension(
}
// content_version 1
- unsigned int force_textpos_as_unsigned = static_cast(m_force_textpos);
- if (!archive.ReadInt(&force_textpos_as_unsigned))
+ if (!archive.ReadInt(&legacy_text_fit))
break;
- m_force_textpos = ON_Dimension::ForceTextFromUnsigned(force_textpos_as_unsigned);
rc = true;
break;
@@ -459,6 +528,23 @@ bool ON_Dimension::Internal_ReadDimension(
if (!archive.EndRead3dmChunk())
rc = false;
+ const unsigned int version_v7_may_8_2019 = ON_VersionNumberConstruct(7, 0, 2019, 5, 8, 0);
+ if (rc && archive.ArchiveOpenNURBSVersion() < version_v7_may_8_2019 )
+ {
+ // may 2019 - "arrow fit" and "text fit" moved from member settings on ON_Dimension
+ // to settings on ON_DimStyle.
+ // The file being read is older than the change.
+ const ON_DimStyle::arrow_fit new_arrow_fit = ON_DimStyle::ArrowFitFromUnsigned((unsigned int)legacy_arrow_fit);
+ const ON_DimStyle::text_fit new_text_fit = ON_DimStyle::TextFitFromUnsigned((unsigned int)legacy_text_fit);
+ const ON_DimStyle& ds = archive.ArchiveCurrentDimStyle();
+ bool bSetArrowFit = (new_arrow_fit != ArrowFit(&ds));
+ if (bSetArrowFit)
+ SetArrowFit(&ds, new_arrow_fit);
+ bool bSetTextFit = (new_text_fit != TextFit(&ds));
+ if (bSetTextFit)
+ SetTextFit(&ds, new_text_fit);
+ }
+
return rc;
}
@@ -640,7 +726,7 @@ bool ON_DimLinear::GetTextXform(
ON_Xform text_rotation(1.0); // Text rotation around text plane origin point
// The amount past vertical where text flips to the other orientation
- const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : cos(80.0*ON_PI / 180.0);
+ const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : cos(80.001 * ON_DEGREES_TO_RADIANS);
ON_3dPoint text_center = ON_3dPoint::Origin;
// Text starts out approximately centered at origin
@@ -665,12 +751,12 @@ bool ON_DimLinear::GetTextXform(
// See if arrows and text will all fit inside extension lines
// or what has to be moved outside
bool arrowflipped[2] = { false, false };
- //{ ArrowIsFlipped(0), ArrowIsFlipped(1) }; // manual override
- ON_Dimension::ForceArrow force_arrow = ForceArrowPosition();
- if (ForceArrow::Outside == force_arrow)
+
+ ON_DimStyle::arrow_fit arrow_fit = dimstyle->ArrowFit();
+ if (ON_DimStyle::arrow_fit::ArrowsOutside == arrow_fit)
arrowflipped[0] = arrowflipped[1] = true;
- ON_Dimension::ForceText force_text = ForceTextPosition();
+ ON_DimStyle::text_fit text_fit = dimstyle->TextFit();
bool text_outside = false;
double dist = Measurement();
@@ -687,12 +773,13 @@ bool ON_DimLinear::GetTextXform(
double total_text_width = (ON_DimStyle::ContentAngleStyle::Horizontal == text_angle_style) ? text_height : text_width;
- if (force_text == ON_Dimension::ForceText::Left || force_text == ON_Dimension::ForceText::Right)
+ if (text_fit == ON_DimStyle::text_fit::TextLeft || text_fit == ON_DimStyle::text_fit::TextRight)
{
total_text_width = 0.0;
text_outside = true;
}
- else if (force_text == ON_Dimension::ForceText::Inside)
+ //else if (force_text == ON_Dimension::ForceText::Inside)
+ else if (text_fit == ON_DimStyle::text_fit::TextInside)
{
total_text_width = 0.0;
text_outside = false;
@@ -702,7 +789,7 @@ bool ON_DimLinear::GetTextXform(
static double arrow_width_factor = 1.1;
double total_arrow_width = asz * arrow_width_factor * 2;
- if (ForceArrow::Outside == force_arrow)
+ if (ON_DimStyle::arrow_fit::ArrowsOutside == arrow_fit)
total_arrow_width = 0.0;
if (total_arrow_width + total_text_width > dist) // arrows + text dont fit
@@ -712,13 +799,14 @@ bool ON_DimLinear::GetTextXform(
{
// move text outside
text_outside = true;
- if (total_arrow_width > dist && ForceArrow::Auto == force_arrow) // arrows dont fit either
+ if (total_arrow_width > dist && ON_DimStyle::arrow_fit::Auto == arrow_fit) // arrows dont fit either
{
arrowflipped[0] = true;
arrowflipped[1] = true;
}
}
- else if (ForceArrow::Auto == force_arrow) // text fits
+ //else if (ForceArrow::Auto == force_arrow) // text fits
+ else if (ON_DimStyle::arrow_fit::Auto == arrow_fit) // text fits
{
// flip arrows
arrowflipped[0] = true;
@@ -735,9 +823,9 @@ bool ON_DimLinear::GetTextXform(
{
// move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width
double x = (text_width * 0.5) + (text_gap * 3.0);
- if (force_text == ON_Dimension::ForceText::Left || force_text == ON_Dimension::ForceText::HintLeft)
+ if (text_fit == ON_DimStyle::text_fit::TextLeft || text_fit == ON_DimStyle::text_fit::TextHintLeft)
{
- if (arrowflipped[0])
+ if (arrowflipped[0])
x += (asz * arrow_width_factor);
text_pt = ArrowPoint1().x < ArrowPoint2().x ? ArrowPoint1() : ArrowPoint2();
text_pt.x -= x;
@@ -745,7 +833,7 @@ bool ON_DimLinear::GetTextXform(
else // right or auto
{
if (arrowflipped[1])
- x += asz * (arrow_width_factor);
+ x += (asz * arrow_width_factor);
text_pt = ArrowPoint1().x < ArrowPoint2().x ? ArrowPoint2() : ArrowPoint1();
text_pt.x += x;
}
@@ -795,71 +883,26 @@ bool ON_DimLinear::GetTextXform(
}
}
- double XoX = dim_xaxis * view_xdir;
- double XoY = dim_xaxis * view_ydir;
- double YoX = dim_yaxis * view_xdir;
- double YoY = dim_yaxis * view_ydir;
- bool from_the_back = (view_zdir * dim_zaxis < 0.0);
- if (nullptr != model_xform && model_xform->Determinant() < 0.0)
- from_the_back = !from_the_back;
+ bool flip_x = false;
+ bool flip_y = false;
- double upsign = 1.0;
-
- // This part shifts text to the correct side of the dimension line
- if (fabs(XoX) > fabs(XoY)) // more horizontal
- {
- if (YoY > 0.0)
- upsign = 1.0;
- else
- upsign = -1.0;
- }
- else // more vertical
- {
- if (from_the_back)
- {
- if (YoX < 0.0)
- {
- if (XoX < fliptol)
- upsign = 1.0;
- else
- upsign = -1.0;
- }
- else
- {
- if (XoX > -fliptol)
- upsign = -1.0;
- else
- upsign = 1.0;
- }
- }
- else
- {
- if (YoX > 0.0)
- {
- if (XoX > fliptol)
- upsign = 1.0;
- else
- upsign = -1.0;
- }
- else
- {
- if (XoX < -fliptol)
- upsign = -1.0;
- else
- upsign = 1.0;
- }
- }
- }
+ CalcTextFlip(
+ dim_xaxis, dim_yaxis, dim_zaxis,
+ view_xdir, view_ydir, view_zdir,
+ model_xform,
+ fliptol,
+ flip_x,
+ flip_y);
if (ON_DimStyle::TextLocation::AboveDimLine == text_location)
{
// Moves the text to AboveLine if that's the alignment mode
- double d = (text_height * 0.5 + text_gap) * upsign;
- //if (from_the_back)
- // d = -d;
+ double dy = flip_y ? -1.0 : 1.0;
+ double d = (text_height * 0.5 + text_gap) * dy;
text_pt.y += d;
}
+
ON_3dPoint text_point_3d = Plane().PointAt(text_pt.x, text_pt.y); // 3d text point
dimplane_to_textpoint = ON_Xform::TranslationTransformation(text_point_3d - Plane().origin); // Move from dimplane origin to text point
@@ -893,23 +936,13 @@ bool ON_DimLinear::GetTextXform(
}
else if (draw_forward)
{
- bool fx = false;
- bool fy = false;
- if (from_the_back)
- upsign = -upsign;
- fx = upsign < 0.0;
- if (from_the_back)
- fy = !fx;
- else
- fy = fx;
-
ON_Xform mxf; // Mirror xform for backwards text to adjust DrawForward
- if (fx)
+ if (flip_x)
{
mxf.Mirror(text_center, ON_3dVector::XAxis);
text_xform_out = text_xform_out * mxf;
}
- if (fy)
+ if (flip_y)
{
mxf.Mirror(text_center, ON_3dVector::YAxis);
text_xform_out = text_xform_out * mxf;
@@ -1859,32 +1892,36 @@ bool ON_DimLinear::GetDisplayLines(
isline[2] = true;
isline[3] = false;
- if (UseDefaultTextPoint() && ON_DimStyle::TextLocation::InDimLine != text_location)
+ if (/*UseDefaultTextPoint() &&*/ ON_DimStyle::TextLocation::InDimLine != text_location)
{
- // If the dimline is under the text, and the text extends past the end of the dimline,
- // make the dim line as long as the text if the text is offset sideways from the
- // extension lines.
- // If the text overlaps the extensions in both directions, it is centered and the
- // dimension line will hang out just a little each way, so don't do it in that case
- double t0, t1;
- lines[2].ClosestPointTo(text_rect[0], &t0);
- lines[2].ClosestPointTo(text_rect[1], &t1);
- if (fabs(t0 - t1) > 0.00001) // if text rect has some width
+ if (m_use_default_text_point || fabs(m_user_text_point.y - m_dimline_pt.y) < style->TextGap() * dimscale * 0.75)
{
- if (t0 > t1)
+ // If the dimline is under the text, and the text extends past the end of the dimline,
+ // make the dim line as long as the text if the text is offset sideways from the
+ // extension lines.
+ // If the text is within 3/4 * text gap of default vertical position, draw the extended line
+ // If the text overlaps the extensions in both directions, it is centered and the
+ // dimension line will hang out just a little each way, so don't do it in that case
+ double t0, t1;
+ lines[2].ClosestPointTo(text_rect[0], &t0);
+ lines[2].ClosestPointTo(text_rect[1], &t1);
+ if (fabs(t0 - t1) > 0.00001) // if text rect has some width
{
- double t = t0; t0 = t1; t1 = t;
+ if (t0 > t1)
+ {
+ double t = t0; t0 = t1; t1 = t;
+ }
+ ON_Line l = lines[2];
+ if (t0 < 0.0 && t1 < 1.0)
+ l.from = lines[2].PointAt(t0);
+ if (t1 > 1.0 && t0 > 0.0)
+ l.to = lines[2].PointAt(t1);
+ lines[2] = l;
}
- ON_Line l = lines[2];
- if (t0 < 0.0 && t1 < 1.0)
- l.from = lines[2].PointAt(t0);
- if (t1 > 1.0 && t0 > 0.0)
- l.to = lines[2].PointAt(t1);
- lines[2] = l;
}
}
- if (ArrowIsFlipped(0) && ArrowIsFlipped(1))
+ if (ArrowIsFlipped(0) && ArrowIsFlipped(1) && !style->ForceDimLine())
{
// Don't draw dimline between extensions if arrows are flipped
lines[3].from = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y);
@@ -1960,6 +1997,7 @@ void ON_DimLinear::GetArrowXform(
arrow_xform_out = xf;
}
+
//----------------------------------------------------------
// Class ON_DimAngular
@@ -2853,7 +2891,7 @@ bool ON_DimAngular::GetTextXform(
if (!text->Get3dCorners(cp))
return false;
- text_center = (cp[0] * dimscale + cp[2] * dimscale) / 2.0;
+ text_center = (cp[0] + cp[2]) / 2.0;
text_width = (cp[1].x - cp[0].x) * dimscale;
text_height = (cp[3].y - cp[0].y) * dimscale;
@@ -2866,93 +2904,90 @@ bool ON_DimAngular::GetTextXform(
bool arrowflipped[2] = { false, false };
bool text_outside = false;
- ON_Dimension::ForceText force_text = ForceTextPosition();
- ON_Dimension::ForceArrow force_arrow = ForceArrowPosition();
- if (ForceArrow::Outside == force_arrow)
+
+ ON_DimStyle::arrow_fit arrow_fit = dimstyle->ArrowFit();
+ if (ON_DimStyle::arrow_fit::ArrowsOutside == arrow_fit)
arrowflipped[0] = arrowflipped[1] = true;
- else if (ForceArrow::Inside == force_arrow)
- arrowflipped[0] = arrowflipped[1] = false;
- //else if (ON_PI < Measurement()) // No flipping
- // arrowflipped[0] = arrowflipped[1] = false;
- //else // Flipping arrows won't happen on more than half-circle angles
+
+ ON_DimStyle::text_fit text_fit = dimstyle->TextFit();
+
+ // See if arrows and text will all fit inside extension lines
+ // or what has to be moved outside
+ double asz = dimstyle->ArrowSize() * dimscale;
+ double dist = Radius() * Measurement();
+
+ double total_text_width = text_width;
+ if (text_fit != ON_DimStyle::text_fit::Auto)
+ total_text_width = 0.0;
+ else if (0.0 < total_text_width)
+ total_text_width += text_gap;
+
+ if (text_fit != ON_DimStyle::text_fit::Auto &&
+ text_fit != ON_DimStyle::text_fit::TextInside)
+ text_outside = true;
+
+ static double arrow_width_factor = 1.5;
+ double total_arrow_width = asz * arrow_width_factor * 2; // min arrow tail space is asz/2
+
+ if (arrowflipped[0])
+ total_arrow_width -= (asz * arrow_width_factor);
+ if (arrowflipped[1])
+ total_arrow_width -= (asz * arrow_width_factor);
+
+ if (total_arrow_width + total_text_width > dist) // arrows + text dont fit
{
- // See if arrows and text will all fit inside extension lines
- // or what has to be moved outside
- double asz = dimstyle->ArrowSize() * dimscale;
- double dist = Radius() * Measurement();
-
- double total_text_width = text_width;
- if (force_text != ON_Dimension::ForceText::Auto)
- total_text_width = 0.0;
- else if (0.0 < total_text_width)
- total_text_width += text_gap;
-
- if (force_text != ON_Dimension::ForceText::Auto &&
- force_text != ON_Dimension::ForceText::Inside)
- text_outside = true;
-
- static double arrow_width_factor = 1.5;
- double total_arrow_width = asz * arrow_width_factor * 2; // min arrow tail space is asz/2
-
- if (arrowflipped[0])
- total_arrow_width -= (asz * arrow_width_factor);
- if (arrowflipped[1])
- total_arrow_width -= (asz * arrow_width_factor);
-
- if (total_arrow_width + total_text_width > dist) // arrows + text dont fit
+ if (total_text_width > dist) // text doesnt fit
{
- if (total_text_width > dist) // text doesnt fit
+ // move text outside
+ text_outside = true;
+ if (total_arrow_width > dist && ON_DimStyle::arrow_fit::Auto == arrow_fit) // arrows dont fit either
{
- // move text outside
- text_outside = true;
- if (total_arrow_width > dist && ForceArrow::Auto == force_arrow) // arrows dont fit either
- {
- arrowflipped[0] = true;
- arrowflipped[1] = true;
- }
- }
- else if (ForceArrow::Auto == force_arrow) // text fits
- {
- // flip arrows
arrowflipped[0] = true;
arrowflipped[1] = true;
}
}
-
- // Dimension's ON_TextContent display text is stored at wcs origin coords until it is drawn
- // text_xform positions text at the 3d point and rotation to draw
- if (fabs(text_pt_2d.x) < ON_SQRT_EPSILON && fabs(text_pt_2d.y) < ON_SQRT_EPSILON)
- text_pt_2d.Set(0.0, 0.0);
-
- if (text_outside && ON_DimStyle::ContentAngleStyle::Horizontal != text_angle_style && UseDefaultTextPoint())
+ else if (ON_DimStyle::arrow_fit::Auto == arrow_fit) // text fits
{
- double radius = Radius();
- // move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width
- double x = text_width * 0.5 + text_gap;
- if (force_text == ON_Dimension::ForceText::Left)
+ // flip arrows
+ arrowflipped[0] = true;
+ arrowflipped[1] = true;
+ }
+ }
+
+ // Dimension's ON_TextContent display text is stored at wcs origin coords until it is drawn
+ // text_xform positions text at the 3d point and rotation to draw
+ if (fabs(text_pt_2d.x) < ON_SQRT_EPSILON && fabs(text_pt_2d.y) < ON_SQRT_EPSILON)
+ text_pt_2d.Set(0.0, 0.0);
+
+ if (text_outside && ON_DimStyle::ContentAngleStyle::Horizontal != text_angle_style && UseDefaultTextPoint())
+ {
+ double radius = Radius();
+ // move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width
+ double x = text_width * 0.5 + text_gap;
+ if (text_fit == ON_DimStyle::text_fit::TextLeft)
+ {
+ //if (arrowflipped[0])
+ x += 1.5 * asz * arrow_width_factor;
+ text_pt_2d = ArrowPoint1();
+ if (0.0 < radius)
{
- //if (arrowflipped[0])
- x += 1.5 * asz * arrow_width_factor;
- text_pt_2d = ArrowPoint1();
- if (0.0 < radius)
- {
- double d_ang = x / radius;
- text_pt_2d.Rotate(-d_ang, ON_2dPoint::Origin);
- }
+ double d_ang = x / radius;
+ text_pt_2d.Rotate(-d_ang, ON_2dPoint::Origin);
}
- else
+ }
+ else
+ {
+ //if (arrowflipped[1])
+ x += asz * arrow_width_factor;
+ text_pt_2d = ArrowPoint2();
+ if (0.0 < radius)
{
- //if (arrowflipped[1])
- x += asz * arrow_width_factor;
- text_pt_2d = ArrowPoint2();
- if (0.0 < radius)
- {
- double d_ang = x / radius;
- text_pt_2d.Rotate(d_ang, ON_2dPoint::Origin);
- }
+ double d_ang = x / radius;
+ text_pt_2d.Rotate(d_ang, ON_2dPoint::Origin);
}
}
}
+
FlipArrow(0, arrowflipped[0]);
FlipArrow(1, arrowflipped[1]);
@@ -4195,13 +4230,17 @@ bool ON_DimRadial::GetTextXform(
if (fx)
{
mxf.Mirror(text_center, textplane.xaxis);
- text_xform_out = text_xform_out * mxf;
+ textpt_xf = textpt_xf * mxf;
}
if (fy)
{
mxf.Mirror(ON_3dPoint::Origin, textplane.yaxis);
- text_xform_out = text_xform_out * mxf;
+ textpt_xf = textpt_xf * mxf;
}
+ text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale);
+ text_xform_out = textrot_xf * text_xform_out;
+ text_xform_out = textpt_xf * text_xform_out;
+ text_xform_out = dimplane_xf * text_xform_out;
}
}
}
diff --git a/opennurbs_dimension.h b/opennurbs_dimension.h
index e59b0a59..4d71c71b 100644
--- a/opennurbs_dimension.h
+++ b/opennurbs_dimension.h
@@ -28,7 +28,7 @@ class ON_CLASS ON_Dimension : public ON_Annotation
public:
#pragma region RH_C_SHARED_ENUM [ON_Dimension::ForceArrow] [Rhino.Geometry.Dimension.ForceArrow] [nested:int]
///
- /// Arrowheads forced Inside, or Outside of extension lines, or moved to fit.
+ /// OBSOLETE enum do not use.
///
enum class ForceArrow : unsigned int
{
@@ -46,7 +46,7 @@ public:
#pragma region RH_C_SHARED_ENUM [ON_Dimension::ForceText] [Rhino.Geometry.Dimension.ForceText] [nested:int]
///
- /// Text forced Inside, Right or Left of extension lines, or moved to fit (Auto).
+ /// OBSOLETE enum do not use.
///
enum class ForceText : unsigned int
{
@@ -58,9 +58,9 @@ public:
Right = 2,
///
Left = 3,
- /// If override isn't specified and text doesn't fit, move it right
+ ///
HintRight = 4,
- /// If override isn't specified and text doesn't fit, move it left
+ ///
HintLeft = 5,
};
#pragma endregion
@@ -170,13 +170,43 @@ public:
ON_3dPoint points[13],
bool ispoint[13]);
-
+ // Obsolete
+ ON_DEPRECATED_MSG("ON_Dimension::ArrowFit(const ON_DimStyle* parent_style)")
ON_Dimension::ForceArrow ForceArrowPosition() const;
+
+ ON_DEPRECATED_MSG("ON_Dimension::SetArrowFit(const ON_DimStyle* parent_style,ON_DimStyle::arrow_fit arrowfit)")
void SetForceArrowPosition(ForceArrow force);
+ ON_DEPRECATED_MSG("ON_Dimension::TextFit(const ON_DimStyle* parent_style)")
ON_Dimension::ForceText ForceTextPosition() const;
+
+ ON_DEPRECATED_MSG("ON_Dimension::SetTextFit(const ON_DimStyle* parent_style,ON_DimStyle::text_fit textfit)")
void SetForceTextPosition(ForceText force);
+ void SetForceDimLine(
+ const ON_DimStyle* parent_style,
+ bool forcedimline
+ );
+
+ bool ForceDimLine(
+ const ON_DimStyle* parent_style) const;
+
+ void SetTextFit(
+ const ON_DimStyle* parent_style,
+ ON_DimStyle::text_fit textfit);
+
+ ON_DimStyle::text_fit TextFit(
+ const ON_DimStyle* parent_style) const;
+
+ void SetArrowFit(
+ const ON_DimStyle* parent_style,
+ ON_DimStyle::arrow_fit arrowfit);
+
+ ON_DimStyle::arrow_fit ArrowFit(
+ const ON_DimStyle* parent_style) const;
+
+
+
protected:
ON_wString m_user_text = L"<>"; // If user overridden, or "<>" to use default
double m_reserved = 0.0;
@@ -188,8 +218,8 @@ protected:
mutable bool m_flip_arrow_1 = false;
mutable bool m_flip_arrow_2 = false;
mutable bool m_text_outside = false;
- ForceArrow m_force_arrows = ForceArrow::Auto;
- ForceText m_force_textpos = ForceText::Auto;
+ unsigned int m_reserved98 = 0;
+ unsigned int m_reserved99 = 0;
// UUID of detail if dimension is in page space measuring model space geometry
@@ -416,6 +446,7 @@ public:
bool from_the_back,
ON_Xform& arrow_xform_out) const;
+
protected:
ON_2dPoint m_def_pt_2 = ON_2dPoint::UnsetPoint;
ON_2dPoint m_dimline_pt = ON_2dPoint::UnsetPoint;
diff --git a/opennurbs_dimensionformat.cpp b/opennurbs_dimensionformat.cpp
index a312331b..cd1b93c8 100644
--- a/opennurbs_dimensionformat.cpp
+++ b/opennurbs_dimensionformat.cpp
@@ -229,7 +229,10 @@ bool ON_NumberFormatter::FormatNumber(
{
if (0 != wholenumber)
{
- sFormat.Format(L"%d ", (int)wholenumber);
+ if (0 != numerator)
+ sFormat.Format(L"%d ", (int)wholenumber);
+ else
+ sFormat.Format(L"%d", (int)wholenumber);
}
if (0 != numerator)
diff --git a/opennurbs_dimensionstyle.cpp b/opennurbs_dimensionstyle.cpp
index 5c8aba37..41f80718 100644
--- a/opennurbs_dimensionstyle.cpp
+++ b/opennurbs_dimensionstyle.cpp
@@ -421,6 +421,10 @@ ON_DimStyle::field ON_DimStyle::FieldFromUnsigned(
// OBSOLETE // //ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::OBSOLETE_AlternateDimensionUnitSystem_);
ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimensionLengthDisplay);
ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AlternateDimensionLengthDisplay);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ForceDimLine);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextFit);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::ArrowFit);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DecimalSeparator);
}
if (field_as_unsigned > static_cast(ON_DimStyle::field::AlternateDimensionLengthDisplay))
{
@@ -488,6 +492,38 @@ ON_DimStyle::OBSOLETE_length_format ON_DimStyle::OBSOLETE_LengthFormatFromUnsign
return (ON_DimStyle::OBSOLETE_length_format::Decimal);
}
+ON_DimStyle::arrow_fit ON_DimStyle::ArrowFitFromUnsigned(
+ unsigned int arrow_fit_as_unsigned
+)
+{
+ switch (arrow_fit_as_unsigned)
+ {
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::arrow_fit::Auto);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::arrow_fit::ArrowsInside);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::arrow_fit::ArrowsOutside);
+ }
+ ON_ERROR("invalid arrow_fit_as_unsigned parameter.");
+ return (ON_DimStyle::arrow_fit::Auto);
+}
+
+ON_DimStyle::text_fit ON_DimStyle::TextFitFromUnsigned(
+ unsigned int text_fit_as_unsigned
+)
+{
+ switch (text_fit_as_unsigned)
+ {
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::Auto);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::TextInside);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::TextRight);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::TextLeft);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::TextHintRight);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::text_fit::TextHintLeft);
+ }
+ ON_ERROR("invalid text_fit_as_unsigned parameter.");
+ return (ON_DimStyle::text_fit::Auto);
+}
+
+
ON_DimStyle::OBSOLETE_length_format ON_DimStyle::OBSOLETE_LengthFormatFromLengthDisplay(
ON_DimStyle::LengthDisplay dimension_length_display,
ON::LengthUnitSystem model_unit_system
@@ -551,7 +587,7 @@ ON_DimStyle::suppress_zero ON_DimStyle::ZeroSuppressFromUnsigned(
ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::suppress_zero::SuppressZeroFeetAndZeroInches);
break;
}
- ON_ERROR("invalid suppress_ero_as_unsigned parameter.");
+ ON_ERROR("invalid suppress_zero_as_unsigned parameter.");
return ON_DimStyle::suppress_zero::None;
}
@@ -1239,7 +1275,8 @@ void ON_DimStyle::Internal_ContentChange() const
ON_DimStyle::ON_DimStyle()
: ON_ModelComponent(ON_ModelComponent::Type::DimStyle)
-{}
+{
+}
struct V5_to_V6_field_id_map
{
@@ -2102,7 +2139,12 @@ bool ON_DimStyle::CompareFields(const ON_DimStyle& style) const
m_dimradial_text_orientation == style.m_dimradial_text_orientation &&
m_dim_text_angle_style == style.m_dim_text_angle_style &&
m_dimradial_text_angle_style == style.m_dimradial_text_angle_style &&
- m_text_underlined == style.m_text_underlined
+ m_text_underlined == style.m_text_underlined &&
+
+ m_bForceDimLine == style.m_bForceDimLine &&
+ m_ArrowFit == style.m_ArrowFit &&
+ m_TextFit == style.m_TextFit &&
+ m_decimal_separator == style.m_decimal_separator
)
return true;
else
@@ -2142,7 +2184,7 @@ bool ON_DimStyle::Write(
ON_BinaryArchive& file // serialize definition to binary archive
) const
{
- if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 7))
+ if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 9))
return false;
bool rc = false;
@@ -2471,6 +2513,19 @@ bool ON_DimStyle::Write(
if (!file.WriteInt(u)) break;
// END chunk version 1.7 information
+ if (!file.WriteBool(m_bForceDimLine)) break;
+
+ u = static_cast(m_TextFit);
+ if (!file.WriteInt(u)) break;
+
+ u = static_cast(m_ArrowFit);
+ if (!file.WriteInt(u)) break;
+ // END chunk version 1.8 information
+
+ u = static_cast(m_decimal_separator);
+ if (!file.WriteInt(u)) break;
+ // END chunk version 1.9 information
+
rc = true;
break;
}
@@ -2998,6 +3053,33 @@ bool ON_DimStyle::Read(
m_centermark_style = ON_DimStyle::CentermarkStyleFromUnsigned(u);
// END chunk version 1.7 information
+ if (minor_version <= 7)
+ {
+ rc = true;
+ break;
+ }
+
+ if (!file.ReadBool(&m_bForceDimLine)) break;
+
+ u = static_cast(m_TextFit);
+ if (!file.ReadInt(&u)) break;
+ m_TextFit = ON_DimStyle::TextFitFromUnsigned(u);
+
+ u = static_cast(m_ArrowFit);
+ if (!file.ReadInt(&u)) break;
+ m_ArrowFit = ON_DimStyle::ArrowFitFromUnsigned(u);
+ // END chunk version 1.8 information
+
+ if (minor_version <= 8)
+ {
+ rc = true;
+ break;
+ }
+ u = static_cast(m_decimal_separator);
+ if (!file.ReadInt(&u)) break;
+ m_decimal_separator = (wchar_t)u;
+ // END chunk version 1.9 information
+
rc = true;
break;
}
@@ -3502,6 +3584,11 @@ const class ON_SHA1_Hash ON_DimStyle::TextPositionPropertiesHash() const
sha1.AccumulateUnsigned32(static_cast(m_dimradial_text_angle_style));
sha1.AccumulateUnsigned32(static_cast(m_text_underlined));
+ sha1.AccumulateUnsigned32(static_cast(m_ArrowFit));
+ sha1.AccumulateUnsigned32(static_cast(m_TextFit));
+
+ sha1.AccumulateUnsigned32(static_cast(m_decimal_separator));
+
// Save hash in mutable m_text_position_properties_hash
m_text_position_properties_hash = sha1.Hash();
}
@@ -3576,7 +3663,13 @@ const class ON_SHA1_Hash& ON_DimStyle::ContentHash() const
sha1.AccumulateBool(m_draw_forward);
sha1.AccumulateBool(m_signed_ordinate);
-
+
+ sha1.AccumulateBool(static_cast(m_bForceDimLine));
+ sha1.AccumulateUnsigned32(static_cast(m_ArrowFit));
+ sha1.AccumulateUnsigned32(static_cast(m_TextFit));
+
+ sha1.AccumulateUnsigned32(static_cast(m_decimal_separator));
+
// Save hash in mutable m_content_hash
m_content_hash = sha1.Hash();
}
@@ -4804,6 +4897,62 @@ void ON_DimStyle::SetTextUnderlined(bool underlined)
Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::TextUnderlined);
}
+bool ON_DimStyle::ForceDimLine() const
+{
+ return m_bForceDimLine;
+}
+void ON_DimStyle::SetForceDimLine(bool forcedimline)
+{
+ if (m_bForceDimLine != forcedimline)
+ {
+ m_bForceDimLine = forcedimline;
+ }
+ Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::ForceDimLine);
+}
+
+void ON_DimStyle::SetArrowFit(ON_DimStyle::arrow_fit arrowfit)
+{
+ if (m_ArrowFit != arrowfit)
+ {
+ m_ArrowFit = arrowfit;
+ Internal_TextPositionPropertiesChange();
+ }
+ Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::ArrowFit);
+}
+
+ON_DimStyle::arrow_fit ON_DimStyle::ArrowFit() const
+{
+ return m_ArrowFit;
+}
+
+void ON_DimStyle::SetTextFit(ON_DimStyle::text_fit textfit)
+{
+ if (m_TextFit != textfit)
+ {
+ m_TextFit = textfit;
+ Internal_TextPositionPropertiesChange();
+ }
+ Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::TextFit);
+}
+
+ON_DimStyle::text_fit ON_DimStyle::TextFit() const
+{
+ return m_TextFit;
+}
+
+void ON_DimStyle::SetDecimalSeparator(wchar_t separator)
+{
+ if(separator == ON_wString::DecimalAsComma)
+ m_decimal_separator = ON_wString::DecimalAsComma;
+ else
+ m_decimal_separator = ON_wString::DecimalAsPeriod;
+}
+
+wchar_t ON_DimStyle::DecimalSeparator() const
+{
+ return m_decimal_separator;
+}
+
ON__UINT32* ON_DimStyle::Internal_GetOverrideParentBit(ON_DimStyle::field field_id, ON__UINT32* mask) const
{
unsigned int bitdex = 0;
@@ -5377,7 +5526,18 @@ void ON_DimStyle::OverrideFields(const ON_DimStyle& source, const ON_DimStyle& p
case ON_DimStyle::field::AlternateDimensionLengthDisplay:
ON_INTERNAL_UPDATE_PROPERTY(AlternateDimensionLengthDisplay);
break;
-
+ case ON_DimStyle::field::ForceDimLine:
+ ON_INTERNAL_UPDATE_PROPERTY(ForceDimLine);
+ break;
+ case ON_DimStyle::field::TextFit:
+ ON_INTERNAL_UPDATE_PROPERTY(TextFit);
+ break;
+ case ON_DimStyle::field::ArrowFit:
+ ON_INTERNAL_UPDATE_PROPERTY(ArrowFit);
+ break;
+ case ON_DimStyle::field::DecimalSeparator:
+ ON_INTERNAL_UPDATE_PROPERTY(DecimalSeparator);
+ break;
default:
ON_ERROR("The switch statement in this function has gaps!");
SetFieldOverride(field_id, false);
diff --git a/opennurbs_dimensionstyle.h b/opennurbs_dimensionstyle.h
index 6edadb01..ed596bd1 100644
--- a/opennurbs_dimensionstyle.h
+++ b/opennurbs_dimensionstyle.h
@@ -856,8 +856,28 @@ public:
///
AlternateDimensionLengthDisplay = 110,
+ ///
+ /// Force dimension line to draw when text is moved outside
+ ///
+ ForceDimLine = 111,
+
+ ///
+ /// Arrow position when arrows won't fit between extensions
+ ///
+ ArrowFit = 112,
+
+ ///
+ /// Text position when text won't fit between extensions
+ ///
+ TextFit = 113,
+
+ ///
+ /// Character to use for decimal separator in dimension text
+ ///
+ DecimalSeparator = 114,
+
/// Every enum UINT value that identifies a valid dimension style property is less than the UINT value of Count.
- Count = 111
+ Count = 115
};
#pragma endregion
@@ -1038,6 +1058,50 @@ public:
);
+#pragma region RH_C_SHARED_ENUM [ON_DimStyle::arrow_fit] [Rhino.DocObjects.DimensionStyle.ArrowFit] [nested:byte]
+ ///
+ /// Arrow display position inside or outside extension lines
+ ///
+ enum class arrow_fit : unsigned char
+ {
+ /// Auto - Display when space permits
+ Auto = 0,
+ /// Force arrows inside extensions
+ ArrowsInside = 1,
+ /// Force arrows outside extensions
+ ArrowsOutside = 2,
+ };
+#pragma endregion
+
+ static ON_DimStyle::arrow_fit ArrowFitFromUnsigned(
+ unsigned int arrow_fit_as_unsigned
+ );
+
+#pragma region RH_C_SHARED_ENUM [ON_DimStyle::text_fit] [Rhino.DocObjects.DimensionStyle.TextFit] [nested:byte]
+ ///
+ /// Text display position inside or outside extension lines
+ ///
+ enum class text_fit : unsigned char
+ {
+ /// Auto - Display inside when space permits
+ Auto = 0,
+ /// Force text inside extensions
+ TextInside = 1,
+ /// Force text outside to the right of extensions
+ TextRight = 2,
+ /// Force text outside to the left of extensions
+ TextLeft = 3,
+ /// Move text outside to the right of extensions when it won't fit inside
+ TextHintRight = 4,
+ /// Move text outside to the left of extensions when it won't fit inside
+ TextHintLeft = 5,
+ };
+#pragma endregion
+
+ static ON_DimStyle::text_fit TextFitFromUnsigned(
+ unsigned int text_fit_as_unsigned
+ );
+
static ON_DimStyle::LengthDisplay LengthDisplayFromUnitsAndFormat(
ON::LengthUnitSystem units,
ON_DimStyle::OBSOLETE_length_format lengthformat
@@ -1095,6 +1159,7 @@ public:
unsigned int model_sn
) const;
+
private:
/*
Returns:
@@ -1797,13 +1862,13 @@ public:
True if zero_suppression is a valid setting when
DimensionLengthDiplay = dimension_length_display
Remarks:
- LengthDisplay: Inch fractional No zero suppression matches
- LengthDisplay : FeetAndInches Zero suppress can be
+ LengthDisplay: Inch fractional – No zero suppression matches
+ LengthDisplay : FeetAndInches – Zero suppress can be
None,
Suppress zero feet,
Suppress zero inches or
Suppress zero feet and zero inches.
- LengthDisplay : ModelUnits or any Decimal mode Zero suppress can be
+ LengthDisplay : ModelUnits or any Decimal mode – Zero suppress can be
None,
Suppress leading,
Suppress trailing or
@@ -1932,6 +1997,18 @@ public:
bool TextUnderlined() const;
void SetTextUnderlined(bool underlined);
+ bool ForceDimLine() const;
+ void SetForceDimLine(bool forcedimline);
+
+ void SetArrowFit(ON_DimStyle::arrow_fit arrowfit);
+ ON_DimStyle::arrow_fit ArrowFit() const;
+
+ void SetTextFit(ON_DimStyle::text_fit textfit);
+ ON_DimStyle::text_fit TextFit() const;
+
+ void SetDecimalSeparator(wchar_t separator);
+ wchar_t DecimalSeparator() const;
+
//double ModelSize() const;
//void SetModelSize(double size);
//double PaperSize() const;
@@ -2051,7 +2128,11 @@ private:
bool m_bAlternate = false; // (dimalt) display alternate dimension string (or not)
-
+
+ bool m_bForceDimLine = true; // 4/30/2019
+ ON_DimStyle::arrow_fit m_ArrowFit = ON_DimStyle::arrow_fit::Auto; // 4/30/2019
+ ON_DimStyle::text_fit m_TextFit = ON_DimStyle::text_fit::Auto; // 4/30/2019
+ wchar_t m_decimal_separator = ON_wString::DecimalAsPeriod;
ON_wString m_prefix; // string preceding dimension value string
ON_wString m_suffix; // string following dimension value string
@@ -2063,8 +2144,8 @@ private:
bool m_bSuppressExtension1 = false; // flag to not draw extension lines
bool m_bSuppressExtension2 = false; // flag to not draw extension lines
bool m_bReserved1 = false;
- bool m_bReserved2 = false;
-
+ bool m_bReserved2 = false;
+
// m_field_override_count
// number of ON_DimStyle::field settings that are independent of the parent dimension style.
// (not inherited from)
@@ -2153,6 +2234,10 @@ private:
double m_fixed_extension_len = 1.0; // Fixed extension line length if m_fixed_extension_len_on is true
bool m_fixed_extension_len_on = false; // true: use fixed_extension_len, false: don't use m_fixed_extension_len
+
+ unsigned char m_ReservedChar1 = 0;
+ unsigned short m_ReservedShort1 = 0;
+ unsigned int m_ReservedInt1 = 0;
double m_text_rotation = 0.0; // Dimension text rotation around text point (radians)
int m_alternate_tolerance_resolution = 4; // for decimal, digits past the decimal point, fractions: 1/2^n
@@ -2160,6 +2245,9 @@ private:
bool m_suppress_arrow1 = false; // false: dont suppress, true: suppress
bool m_suppress_arrow2 = false; // false: dont suppress, true: suppress
+
+ unsigned short m_ReservedShort2 = 0;
+
int m_textmove_leader = 0; // 0: move text anywhere, 1: add leader when moving text
int m_arclength_sym = 0; // 0: symbol before dim text, 1: symbol above dim text, no symbol
double m_stack_textheight_fraction = 0.7; // fraction of main text height
@@ -2174,7 +2262,8 @@ private:
ON_DimStyle::suppress_zero m_ang_zero_suppress = ON_DimStyle::suppress_zero::None;
bool m_alt_below = false; // true: display alternate text below main text
- // false: display alternate text after main text
+
+ // false: display alternate text after main text
ON_Arrowhead::arrow_type m_arrow_type_1 = ON_Arrowhead::arrow_type::SolidTriangle; // Arrow types for ON_Dimension derived dimensions
ON_Arrowhead::arrow_type m_arrow_type_2 = ON_Arrowhead::arrow_type::SolidTriangle;
ON_Arrowhead::arrow_type m_leader_arrow_type = ON_Arrowhead::arrow_type::SolidTriangle;
@@ -2194,10 +2283,12 @@ private:
ON_DimStyle::leader_curve_type m_leader_curve_type = ON_DimStyle::leader_curve_type::Polyline;
double m_leader_content_angle = 0.0;
bool m_leader_has_landing = true;
+
double m_leader_landing_length = 1.0;
bool m_draw_forward = true;
bool m_signed_ordinate = true;
+
ON_ScaleValue m_scale_value = ON_ScaleValue::OneToOne;
// Unit system for dimension rendering sizes like text height, and arrow head length.
diff --git a/opennurbs_dll.cpp b/opennurbs_dll.cpp
index 4ac2bed9..d6ab1f29 100644
--- a/opennurbs_dll.cpp
+++ b/opennurbs_dll.cpp
@@ -40,7 +40,8 @@ int APIENTRY DllMain( HANDLE hModule,
bRunning = true;
}
- switch( ul_reason_for_call ) {
+ switch( ul_reason_for_call )
+ {
case DLL_PROCESS_ATTACH:
ON_ClassId::IncrementMark(); // make sure each DLL that each process that
diff --git a/opennurbs_error.cpp b/opennurbs_error.cpp
index 9c546bdd..4ffcc36e 100644
--- a/opennurbs_error.cpp
+++ b/opennurbs_error.cpp
@@ -124,6 +124,13 @@ static void ON_IncrementWarningCount()
ON_WARNING_COUNT++;
}
+void ON_SubDIncrementErrorCount()
+{
+ ON_ERROR_COUNT++;
+ ON_SubD::ErrorCount++;
+}
+
+
bool ON_IsNotValid()
{
return false;
diff --git a/opennurbs_extensions.cpp b/opennurbs_extensions.cpp
index e3b7dab6..b976689e 100644
--- a/opennurbs_extensions.cpp
+++ b/opennurbs_extensions.cpp
@@ -2173,20 +2173,10 @@ bool ONX_Model::IncrementalReadBegin(
rc = archive.Read3dmBitmap(&bitmap);
if ( rc==0 )
break; // end of bitmap table
-
- for (;;)
- {
- if ( rc < 0 )
- break;
-
- if ( AddModelComponentForExperts(bitmap,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
- {
- delete bitmap;
- continue;
- }
-
+ if (rc < 0)
break;
- }
+ if ( AddModelComponentForExperts(bitmap,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
+ delete bitmap;
}
}
@@ -2209,19 +2199,10 @@ bool ONX_Model::IncrementalReadBegin(
rc = archive.Read3dmTextureMapping(&texture_mapping);
if ( rc==0 )
break; // end of texture_mapping table
- for (;;)
- {
- if ( rc < 0 )
- break;
-
- if (AddModelComponentForExperts(texture_mapping, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
- {
- delete texture_mapping;
- continue;
- }
-
+ if ( rc < 0 )
break;
- }
+ if (AddModelComponentForExperts(texture_mapping, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
+ delete texture_mapping;
}
}
@@ -2251,36 +2232,22 @@ bool ONX_Model::IncrementalReadBegin(
rc = archive.Read3dmMaterial(&material);
if ( rc==0 )
break; // end of material table
- for (;;)
- {
- if ( rc < 0 )
- {
- break;
- }
-
- bool bSetAsCurrent =
- (bSetCurrentById && settings_current_id == material->Id())
- || (bSetCurrentByIndex && settings_current_index == material->Index());
-
- if ( AddModelComponentForExperts(material,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
- {
- delete material;
- continue;
- }
-
- if (bSetAsCurrent)
- {
- m_settings.SetCurrentMaterialId(material->Id());
- bSetCurrentById = false;
- bSetCurrentByIndex = false;
- }
-
- material = nullptr;
-
+ if (rc < 0)
break;
- }
- if ( nullptr != material)
+ // index or id might be modified by AddModelComponentForExperts()
+ const bool bSetAsCurrent =
+ (bSetCurrentById && settings_current_id == material->Id())
+ || (bSetCurrentByIndex && settings_current_index == material->Index());
+ if ( AddModelComponentForExperts(material,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
+ {
delete material;
+ }
+ else if (bSetAsCurrent)
+ {
+ m_settings.SetCurrentMaterialId(material->Id());
+ bSetCurrentById = false;
+ bSetCurrentByIndex = false;
+ }
}
}
@@ -2310,22 +2277,17 @@ bool ONX_Model::IncrementalReadBegin(
rc = archive.Read3dmLinetype(&line_pattern);
if ( rc==0 )
break; // end of linetype table
- if ( rc < 0 )
- {
- continue;
- }
-
- bool bSetAsCurrent =
+ if (rc < 0)
+ break;
+ // index or id might be modified by AddModelComponentForExperts()
+ const bool bSetAsCurrent =
(bSetCurrentById && settings_current_id == line_pattern->Id())
|| (bSetCurrentByIndex && settings_current_index == line_pattern->Index());
-
if ( AddModelComponentForExperts(line_pattern,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
{
delete line_pattern;
- continue;
}
-
- if (bSetAsCurrent)
+ else if (bSetAsCurrent)
{
m_settings.SetCurrentLinePatternId(line_pattern->Id());
bSetCurrentById = false;
@@ -2359,22 +2321,17 @@ bool ONX_Model::IncrementalReadBegin(
rc = archive.Read3dmLayer(&layer);
if ( rc==0 )
break; // end of layer table
- if ( rc < 0 )
- {
- continue;
- }
-
- bool bSetAsCurrent =
+ if (rc < 0)
+ break;
+ // index or id might be modified by AddModelComponentForExperts()
+ const bool bSetAsCurrent =
(bSetCurrentById && settings_current_id == layer->Id())
|| (bSetCurrentByIndex && settings_current_index == layer->Index());
-
if (AddModelComponentForExperts(layer, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
{
delete layer;
- continue;
}
-
- if (bSetAsCurrent)
+ else if (bSetAsCurrent)
{
m_settings.SetCurrentLayerId(layer->Id());
bSetCurrentById = false;
@@ -2432,21 +2389,10 @@ bool ONX_Model::IncrementalReadBegin(
rc = archive.Read3dmGroup(&group);
if ( rc==0 )
break; // end of group table
-
- for (;;)
- {
- if ( rc < 0 )
- {
- break;
- }
-
- if (AddModelComponentForExperts(group, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
- {
- delete group;
- continue;
- }
+ if (rc < 0)
break;
- }
+ if (AddModelComponentForExperts(group, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
+ delete group;
}
}
@@ -2476,21 +2422,16 @@ bool ONX_Model::IncrementalReadBegin(
if ( rc==0 )
break; // end of dimstyle table
if ( rc < 0 )
- {
break;
- }
-
- bool bSetAsCurrent =
+ // index or id might be modified by AddModelComponentForExperts()
+ const bool bSetAsCurrent =
(bSetCurrentById && settings_current_id == dimension_style->Id())
|| (bSetCurrentByIndex && settings_current_index == dimension_style->Index());
-
if (AddModelComponentForExperts(dimension_style, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
{
delete dimension_style;
- continue;
}
-
- if (bSetAsCurrent)
+ else if (bSetAsCurrent)
{
m_settings.SetCurrentDimensionStyleId(dimension_style->Id());
bSetCurrentById = false;
@@ -2517,24 +2458,11 @@ bool ONX_Model::IncrementalReadBegin(
ON_ModelGeometryComponent* model_light = nullptr;
rc = archive.Read3dmModelLight(&model_light);
if (rc == 0)
- {
break; // end of light table
- }
- for (;;)
- {
- if ( rc < 0 )
- {
- break;
- }
-
- if (AddModelComponentForExperts(model_light, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
- {
- delete model_light;
- continue;
- }
-
+ if ( rc < 0 )
break;
- }
+ if (AddModelComponentForExperts(model_light, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
+ delete model_light;
}
}
@@ -2565,18 +2493,15 @@ bool ONX_Model::IncrementalReadBegin(
break; // end of hatchpattern table
if ( rc < 0 )
break;
-
- bool bSetAsCurrent =
+ // index or id might be modified by AddModelComponentForExperts()
+ const bool bSetAsCurrent =
(bSetCurrentById && settings_current_id == hatch_pattern->Id())
|| (bSetCurrentByIndex && settings_current_index == hatch_pattern->Index());
-
if (AddModelComponentForExperts(hatch_pattern, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
{
delete hatch_pattern;
- continue;
}
-
- if (bSetAsCurrent)
+ else if (bSetAsCurrent)
{
m_settings.SetCurrentHatchPatternId(hatch_pattern->Id());
bSetCurrentById = false;
@@ -2606,12 +2531,8 @@ bool ONX_Model::IncrementalReadBegin(
break; // end of instance definition table
if ( rc < 0 )
break;
-
if (AddModelComponentForExperts(instance_definition, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
- {
delete instance_definition;
- continue;
- }
}
}
@@ -2757,9 +2678,7 @@ bool ONX_Model::IncrementalReadFinish(
if ( rc == 0 )
break; // end of history record table
if ( rc < 0 )
- {
break;
- }
if ( AddModelComponentForExperts(pHistoryRecord,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
{
delete pHistoryRecord;
@@ -3020,13 +2939,13 @@ bool ONX_Model::Write(
{
if ( 0 != version )
{
- if ( version < 2
- || version > ON_BinaryArchive::CurrentArchiveVersion()
- || (version >= 50 && 0 != (version%10))
- || (version < 50 && version > ON_BinaryArchive::CurrentArchiveVersion()/10)
- )
+ if (
+ version < 2
+ || version > ON_BinaryArchive::CurrentArchiveVersion()
+ || (version < 50 && version > ON_BinaryArchive::CurrentArchiveVersion()/10)
+ || (version >= 50 && 0 != (version % 10))
+ )
{
- // version must be 0, 2, 3, 4, 5 or 50
version = 0;
if ( error_log) error_log->Print("ONX_Model::Write version parameter = %d; it must be 0, or >= 2 and <= %d, or a multiple of 10 >= 50 and <= %d.\n",
version,ON_BinaryArchive::CurrentArchiveVersion()/10,ON_BinaryArchive::CurrentArchiveVersion());
diff --git a/opennurbs_font.cpp b/opennurbs_font.cpp
index 6598be9c..8c7902d1 100644
--- a/opennurbs_font.cpp
+++ b/opennurbs_font.cpp
@@ -1687,6 +1687,114 @@ const ON_Font* ON_FontFaceQuartet::BoldItalicFace() const
return m_bold_italic;
}
+ON_FontFaceQuartet::Member ON_FontFaceQuartet::QuartetMember(
+ const ON_Font* font
+) const
+{
+ if (nullptr == font || m_quartet_name.IsEmpty())
+ return ON_FontFaceQuartet::Member::Unset;
+
+ if (
+ false == m_quartet_name.EqualOrdinal(font->QuartetName(ON_Font::NameLocale::Localized), true)
+ && false == m_quartet_name.EqualOrdinal(font->QuartetName(ON_Font::NameLocale::English), true)
+ )
+ return ON_FontFaceQuartet::Member::Unset;
+
+ ON_FontFaceQuartet::Member m[2] = { ON_FontFaceQuartet::Member ::Unset,ON_FontFaceQuartet::Member::Unset};
+ if ( font->IsItalicOrOblique() )
+ {
+ m[0] = ON_FontFaceQuartet::Member::Italic;
+ m[1] = ON_FontFaceQuartet::Member::BoldItalic;
+ }
+ else
+ {
+ m[0] = ON_FontFaceQuartet::Member::Regular;
+ m[1] = ON_FontFaceQuartet::Member::Bold;
+ }
+
+ const ON_Font* q[2] = { Face(m[0]),Face(m[1]) };
+
+ if (font == q[0])
+ return m[0];
+ if (font == q[1])
+ return m[1];
+
+ ON_wString s[4];
+ for (int i = 0; i < 2; i++)
+ {
+ if (nullptr == q[i])
+ continue;
+
+#if defined(ON_RUNTIME_WIN)
+ // style, weight, and LOGFONT name is the most reliable way to uniquely identify quartet members on Windows.
+ // We've already matched style.
+ if (q[i]->FontWeight() != font->FontWeight())
+ continue;
+ s[0] = q[i]->WindowsLogfontName(ON_Font::NameLocale::Localized);
+ s[1] = q[i]->WindowsLogfontName(ON_Font::NameLocale::English);
+ s[2] = font->WindowsLogfontName(ON_Font::NameLocale::Localized);
+ s[3] = font->WindowsLogfontName(ON_Font::NameLocale::English);
+#else
+ // style and PostScript name is the most reliable way to uniquely identify quartet members on Mac OS.
+ // We've already matched style.
+ s[0] = q[i]->PostScriptName(ON_Font::NameLocale::Localized);
+ s[1] = q[i]->PostScriptName(ON_Font::NameLocale::English);
+ s[2] = font->PostScriptName(ON_Font::NameLocale::Localized);
+ s[3] = font->PostScriptName(ON_Font::NameLocale::English);
+#endif
+ if (s[0].IsNotEmpty() && (s[0].EqualOrdinal(s[2], true) || s[0].EqualOrdinal(s[3], true)))
+ return m[i];
+ if (s[1].IsNotEmpty() && (s[1].EqualOrdinal(s[2], true) || s[1].EqualOrdinal(s[3], true)))
+ return m[i];
+ }
+ return ON_FontFaceQuartet::Member::Unset;
+}
+
+const ON_Font* ON_FontFaceQuartet::Face(
+ ON_FontFaceQuartet::Member member
+) const
+{
+ switch (member)
+ {
+ case ON_FontFaceQuartet::Member::Regular:
+ return m_regular;
+ case ON_FontFaceQuartet::Member::Bold:
+ return m_bold;
+ case ON_FontFaceQuartet::Member::Italic:
+ return m_italic;
+ case ON_FontFaceQuartet::Member::BoldItalic:
+ return m_bold_italic;
+ }
+ return nullptr;
+}
+
+const ON_Font* ON_FontFaceQuartet::ClosestFace(
+ ON_FontFaceQuartet::Member member
+) const
+{
+ bool bPreferBold = false;
+ bool bPreferItalic = false;
+ switch (member)
+ {
+ case ON_FontFaceQuartet::Member::Regular:
+ break;
+ case ON_FontFaceQuartet::Member::Bold:
+ bPreferBold = true;
+ break;
+ case ON_FontFaceQuartet::Member::Italic:
+ bPreferItalic = true;
+ break;
+ case ON_FontFaceQuartet::Member::BoldItalic:
+ bPreferItalic = true;
+ bPreferBold = true;
+ break;
+ default:
+ return nullptr;
+ }
+ return ClosestFace(bPreferBold,bPreferItalic);
+}
+
+
const ON_Font* ON_FontFaceQuartet::Face(
bool bBold,
bool bItalic
@@ -1820,6 +1928,23 @@ int ON_FontList::CompareFamilyAndFaceName(ON_Font const* const* lhs, ON_Font con
return rc;
}
+int ON_FontList::CompareFamilyAndWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs)
+{
+ ON_ManagedFonts_CompareFontPointer(lhs, rhs);
+ int rc = ON_wString::CompareOrdinal(
+ lhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst),
+ rhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst),
+ true
+ );
+ if (0 == rc)
+ rc = ON_wString::CompareOrdinal(
+ lhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst),
+ rhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst),
+ true
+ );
+ return rc;
+}
+
int ON_FontList::CompareWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs)
{
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
@@ -1925,6 +2050,19 @@ int ON_FontList::CompareWeightStretchStyle(
return 0;
}
+
+int ON_FontList::CompareStretch(
+ ON_Font const* const* lhs,
+ ON_Font const* const* rhs
+ )
+{
+ ON_ManagedFonts_CompareFontPointer(lhs, rhs);
+ const int lhs_font_stretch = (int)static_cast(lhs_font->FontStretch());
+ const int rhs_font_stretch = (int)static_cast(rhs_font->FontStretch());
+ const int rc = (lhs_font_stretch - rhs_font_stretch);
+ return rc;
+}
+
int ON_FontList::CompareUnderlinedStrikethroughPointSize(
ON_Font const* const* lhs,
ON_Font const* const* rhs
@@ -4171,10 +4309,18 @@ const ON_ClassArray< ON_FontFaceQuartet >& ON_FontList::QuartetList() const
m_quartet_list.Reserve(32 + font_count / 4);
const ON_Font* f = nullptr;
- const ON_Font* upright6[6] = {};
- const ON_Font* italic6[6] = {};
+ const unsigned max_stretch_dex = 10;
+ const unsigned max_weight_dex = 10;
+ // quartet_fonts[stretch_dex][upright,italic][weight_dex]
+ // = all fonts in the quartet arranged by stretch, slant, and weight.
+ const ON_Font* quartet_fonts[11][2][11] = {};
+ // count[stretch_dex][upright,italic] = number of weights available for that stretch and slant
+ unsigned int count[10][2] = {};
- for (unsigned int i = 0; i < font_count; i++)
+ unsigned stretch_dex_range[2] = { 0U,0U };
+
+ unsigned next_i = 0U;
+ for (unsigned i = 0; i < font_count; i = (i < next_i) ? next_i : (i+1))
{
f = a[i];
if (nullptr == f)
@@ -4184,115 +4330,138 @@ const ON_ClassArray< ON_FontFaceQuartet >& ON_FontList::QuartetList() const
if (quartet_name.IsEmpty())
continue;
- memset(upright6, 0, sizeof(upright6));
- memset(italic6, 0, sizeof(italic6));
-
- const ON_Font** sextet = (f->IsItalic()) ? italic6 : upright6;
- if (ON_Font::Weight::Normal == f->FontWeight())
- sextet[2] = f;
- else if (ON_Font::Weight::Medium == f->FontWeight())
- sextet[3] = f;
- else if (f->IsBold())
- sextet[4] = f;
- else if (f->IsLight())
- sextet[1] = f;
- else
- continue;
-
- while(i+1 < font_count)
+ memset(quartet_fonts, 0, sizeof(quartet_fonts));
+ memset(count, 0, sizeof(count));
+ const unsigned medium_stretch_dex = (unsigned)static_cast(ON_Font::Stretch::Medium);
+ unsigned stretch_dex = medium_stretch_dex;
+ unsigned slant_dex;
+ unsigned weight_dex;
+ unsigned quartet_count = 0;
+ for ( next_i = i; next_i < font_count; ++next_i)
{
- f = a[i + 1];
+ f = a[next_i];
if (nullptr == f)
break;
if (false == quartet_name.EqualOrdinal(f->QuartetName(), true))
break;
- i++;
- const unsigned int f_weight = static_cast(f->FontWeight());
- sextet = (f->IsItalic()) ? italic6 : upright6;
- if (ON_Font::Weight::Normal == f->FontWeight())
- sextet[2] = f;
- else if (ON_Font::Weight::Medium == f->FontWeight())
- sextet[3] = f;
- else if (f->IsBold())
+ stretch_dex = static_cast(f->FontStretch());
+ if (stretch_dex < 1 || stretch_dex >= max_stretch_dex)
+ continue;
+ weight_dex = static_cast(f->FontWeight());
+ if (weight_dex < 1 || weight_dex >= max_weight_dex)
+ continue;
+ slant_dex = f->IsItalicOrOblique() ? 1U : 0U;
+ if (nullptr != quartet_fonts[stretch_dex][slant_dex][weight_dex])
+ continue;
+ if (0 == quartet_count)
{
- if (nullptr == sextet[4])
- sextet[4] = f;
- else if (f_weight < static_cast(sextet[4]->FontWeight()))
- {
- if (nullptr == sextet[5])
- sextet[5] = sextet[4];
- sextet[4] = f;
- }
- else if (nullptr != sextet[5] && f_weight > static_cast(sextet[5]->FontWeight()))
- sextet[5] = f;
- }
- else if (f->IsLight())
- {
- if (nullptr == sextet[1])
- sextet[1] = f;
- else if (f_weight > static_cast(sextet[1]->FontWeight()))
- {
- if ( nullptr == sextet[0])
- sextet[0] = sextet[1];
- sextet[1] = f;
- }
- else if (nullptr != sextet[0] && f_weight < static_cast(sextet[0]->FontWeight()))
- sextet[0] = f;
+ stretch_dex_range[0] = stretch_dex;
+ stretch_dex_range[1] = stretch_dex;
}
+ else if (stretch_dex < stretch_dex_range[0])
+ stretch_dex_range[0] = stretch_dex;
+ else if (stretch_dex > stretch_dex_range[1])
+ stretch_dex_range[1] = stretch_dex;
+ quartet_fonts[stretch_dex][slant_dex][weight_dex] = f;
+ ++count[stretch_dex][slant_dex];
+ ++quartet_count;
}
+ if (0 == quartet_count)
+ continue;
- for (int sex_dex = 0; sex_dex < 2; sex_dex++)
+ if (stretch_dex_range[0] < stretch_dex_range[1])
{
- sextet = (1==sex_dex) ? italic6 : upright6;
-
- if (nullptr == sextet[2])
+ // Need to pick the stretch_dex with the most members.
+ // This happens on Mac OS (where no reliable "LOGFONT" name exists) and
+ // with damaged Windows fonts that don't have a "LOFGONT" name set.
+ // Pick the one closest to ON_Font::Stretch::Medium with the most faces
+ stretch_dex = medium_stretch_dex;
+ for (unsigned k = 1; k <= medium_stretch_dex; ++k)
{
- sextet[2] = sextet[3];
- sextet[3] = nullptr;
- }
- else if (nullptr == sextet[4])
- {
- sextet[4] = sextet[3];
- sextet[3] = nullptr;
- }
-
- if (nullptr != sextet[2])
- {
- if (nullptr != sextet[5])
- sextet[4] = sextet[5];
- continue;
- }
-
- if (nullptr != sextet[1] && nullptr != sextet[4])
- {
- sextet[2] = sextet[1];
- continue;
- }
-
- if (nullptr != sextet[1])
- {
- if (nullptr != sextet[0])
- {
- sextet[2] = sextet[0];
- sextet[4] = sextet[1];
- }
- else
- {
- sextet[2] = sextet[1];
- }
- continue;
- }
-
- if (nullptr != sextet[4])
- {
- sextet[2] = sextet[4];
- sextet[4] = sextet[5];
- continue;
+ const unsigned k0 = medium_stretch_dex - k;
+ const unsigned k1 = medium_stretch_dex + k;
+ if (k0 > 0 && (count[k0][0] + count[k0][1]) > (count[stretch_dex][0] + count[stretch_dex][1]))
+ stretch_dex = k0;
+ if (k1 < max_stretch_dex && (count[k1][0] + count[k1][1]) >(count[stretch_dex][0] + count[stretch_dex][1]))
+ stretch_dex = k1;
}
}
+ else
+ stretch_dex = stretch_dex_range[0];
- ON_FontFaceQuartet q(quartet_name,upright6[2],upright6[4],italic6[2],italic6[4]);
+ if (count[stretch_dex][0] + count[stretch_dex][1] <= 0)
+ continue;
+
+ const unsigned normal_weight_dex = (unsigned)static_cast(ON_Font::Weight::Normal);
+ const unsigned medium_weight_dex = (unsigned)static_cast(ON_Font::Weight::Medium);
+ const unsigned bold_weight_dex = (unsigned)static_cast(ON_Font::Weight::Bold);
+
+ const ON_Font* pairs[2][2] = {};
+ for (slant_dex = 0; slant_dex < 2; slant_dex++)
+ {
+ if ( count[stretch_dex][slant_dex] <= 2 )
+ {
+ // 0, 1 or 2 available weights.
+ // If there is 1 face it must be the "Regular" face.
+ // If there are 2 faces, the lightest will be "Regular" and the heaviest will be bold.
+ int pair_dex = 0;
+ for (int j = 1; j < max_weight_dex && pair_dex < 2; ++j)
+ {
+ if (nullptr != quartet_fonts[stretch_dex][slant_dex][j])
+ pairs[slant_dex][pair_dex++] = quartet_fonts[stretch_dex][slant_dex][j];
+ }
+ continue;
+ }
+
+ // 3 or more available weights (Bahnshrift, Helvetica Neue, ...)
+ unsigned regular_dex
+ = (nullptr != quartet_fonts[stretch_dex][slant_dex][normal_weight_dex])
+ ? normal_weight_dex
+ : medium_weight_dex;
+ while (nullptr == quartet_fonts[stretch_dex][slant_dex][regular_dex] && regular_dex > 0)
+ --regular_dex;
+
+ unsigned bold_dex
+ = (nullptr != quartet_fonts[stretch_dex][slant_dex][bold_weight_dex])
+ ? bold_weight_dex
+ : regular_dex+1;
+ while (nullptr == quartet_fonts[stretch_dex][slant_dex][bold_dex] && bold_dex < max_weight_dex)
+ ++bold_dex;
+
+ if (nullptr != quartet_fonts[stretch_dex][slant_dex][regular_dex] && nullptr == quartet_fonts[stretch_dex][slant_dex][bold_dex] )
+ {
+ if (regular_dex > 0)
+ {
+ for (unsigned j = regular_dex - 1; j > 0; --j)
+ {
+ if (nullptr == quartet_fonts[stretch_dex][slant_dex][j])
+ continue;
+ bold_dex = regular_dex;
+ regular_dex = j;
+ break;
+ }
+ }
+ }
+ else if (nullptr == quartet_fonts[stretch_dex][slant_dex][regular_dex] && nullptr != quartet_fonts[stretch_dex][slant_dex][bold_dex] )
+ {
+ if (bold_dex > 0)
+ {
+ for (unsigned j = bold_dex - 1; j > 0; --j)
+ {
+ if (nullptr == quartet_fonts[stretch_dex][slant_dex][j])
+ continue;
+ regular_dex = j;
+ break;
+ }
+ }
+ }
+
+ pairs[slant_dex][0] = quartet_fonts[stretch_dex][slant_dex][regular_dex];
+ pairs[slant_dex][1] = quartet_fonts[stretch_dex][slant_dex][bold_dex];
+ }
+
+ ON_FontFaceQuartet q(quartet_name,pairs[0][0],pairs[0][1],pairs[1][0],pairs[1][1]);
if (q.IsEmpty())
continue;
@@ -6125,11 +6294,367 @@ const ON_wString ON_Font::WindowsLogfontName() const
return WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst);
}
+class Internal_FakeWindowsLogfontName
+{
+public:
+ ~Internal_FakeWindowsLogfontName() = default;
+ Internal_FakeWindowsLogfontName(const Internal_FakeWindowsLogfontName&) = default;
+ Internal_FakeWindowsLogfontName() = default;
+ Internal_FakeWindowsLogfontName& operator=(const Internal_FakeWindowsLogfontName&) = default;
+
+ Internal_FakeWindowsLogfontName(
+ const wchar_t* family_name,
+ const wchar_t* postscript_name,
+ const wchar_t* fake_logfont_name,
+ ON_FontFaceQuartet::Member quartet_member
+ )
+ : m_fake_logfont_name(fake_logfont_name)
+ , m_family_and_postcript_name_hash(Internal_FakeWindowsLogfontName::NameHash(family_name, postscript_name))
+ , m_quartet_member(quartet_member)
+ // for debugging
+ //, m_family_name(family_name)
+ //, m_postscript_name(postscript_name)
+ {
+ m_fake_logfont_name.TrimLeftAndRight();
+ if (
+ m_fake_logfont_name.EqualOrdinal(family_name, true)
+ || (ON_FontFaceQuartet::Member::Unset != quartet_member && m_fake_logfont_name.IsEmpty())
+ || m_family_and_postcript_name_hash.IsZeroDigentOrEmptyContentHash()
+ )
+ {
+ ON_ERROR("Invalid input.");
+ m_fake_logfont_name = ON_wString::EmptyString;
+ m_family_and_postcript_name_hash = ON_SHA1_Hash::EmptyContentHash;
+ m_quartet_member = ON_FontFaceQuartet::Member::Unset;
+ }
+ }
+
+ static const ON_SHA1_Hash NameHash(
+ const wchar_t* family_name,
+ const wchar_t* postscript_name
+ )
+ {
+ if (nullptr == family_name || 0 == family_name[0])
+ return ON_SHA1_Hash::EmptyContentHash;
+ if (nullptr == postscript_name || 0 == postscript_name[0])
+ return ON_SHA1_Hash::EmptyContentHash;
+
+ ON_wString s1(family_name);
+ s1.Remove(ON_wString::Space);
+ s1.Remove(ON_wString::HyphenMinus);
+ s1.TrimLeftAndRight();
+ if (s1.IsEmpty())
+ return ON_SHA1_Hash::EmptyContentHash;
+
+ ON_wString s2(postscript_name);
+ s2.Remove(ON_wString::Space);
+ s2.Remove(ON_wString::HyphenMinus);
+ s2.TrimLeftAndRight();
+ if (s2.IsEmpty())
+ return ON_SHA1_Hash::EmptyContentHash;
+
+ // add a hypen between family and postscript name
+ // to insure hash for Family = A and postscript = BC
+ // is different from hash for Family = AB and postscript = C
+ s1 += ON_wString::HyphenMinus;
+ s1 += s2;
+
+ return s1.ContentHash(ON_StringMapOrdinalType::MinimumOrdinal);
+ }
+
+ static int CompareFamilyAndPostscriptNameHash(
+ const Internal_FakeWindowsLogfontName* lhs,
+ const Internal_FakeWindowsLogfontName* rhs
+ )
+ {
+ if (lhs == rhs)
+ return 0;
+ // nulls sort last
+ if (nullptr == lhs)
+ return 1;
+ if (nullptr == rhs)
+ return -1;
+ return ON_SHA1_Hash::Compare(lhs->m_family_and_postcript_name_hash, rhs->m_family_and_postcript_name_hash);
+ }
+
+ const ON_SHA1_Hash QuartetFamilyAndPostscriptNameHash() const
+ {
+ return m_family_and_postcript_name_hash;
+ }
+
+ ON_FontFaceQuartet::Member QuartetMember() const
+ {
+ return m_quartet_member;
+ }
+
+ const ON_wString FakeWindowsLogfontName() const
+ {
+ return m_fake_logfont_name;
+ }
+
+private:
+ ON_SHA1_Hash m_family_and_postcript_name_hash = ON_SHA1_Hash::EmptyContentHash;
+ ON_wString m_fake_logfont_name = ON_wString::EmptyString;
+ ON_FontFaceQuartet::Member m_quartet_member = ON_FontFaceQuartet::Member::Unset;
+
+ // for debugging
+ //const ON_wString m_family_name = ON_wString::EmptyString;
+ //const ON_wString m_postscript_name = ON_wString::EmptyString;
+};
+
+const ON_wString ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames(
+ ON_wString family_name,
+ ON_wString postscript_name
+)
+{
+ // This function is used to partition Apple fonts into groups with
+ // at most 4 faces and assign a face windows logfont name to the group.
+ //
+ // This is required for archaic font selection interfaces, like ones in Rhino 6 and 7,
+ // that use quartets of regular/italic/bold/bold-italic to specify fonts.
+ //
+ // Because modern fonts can have many faces (Helvetica Neue has 14 as of 2019)
+ // and those faces dont' fit the 1970's LOGFONT model of regular/italic/bold/bold-italic.
+ // American Typewriter and Avenir are other example of commonly used fonts that have
+ // more than 4 faces and don't have fit well into the regular/italic/bold/bold-italic UI.
+ //
+ // It fixes bugs like
+ // https://mcneel.myjetbrains.com/youtrack/issue/RH-37074
+ // https://mcneel.myjetbrains.com/youtrack/issue/RH-53129
+ // https://mcneel.myjetbrains.com/youtrack/issue/RH-53430
+ //
+
+ family_name.TrimLeftAndRight();
+ postscript_name.TrimLeftAndRight();
+ if (family_name.IsEmpty() || postscript_name.IsEmpty())
+ return ON_wString::EmptyString;
+
+ static Internal_FakeWindowsLogfontName fake_names[] =
+ {
+ // Quartets that need a fake LOGFONT name different from family_name in order to
+ // appear in an archaic quartet name + regular/bold/italic/bold-italic UI
+ //
+ // Internal_FakeWindowsLogfontName(family_name,postscript_name,fake_logfont_name,member);
+
+ // Cominations that are not explicitly specified are correctly handled by using the family name as the fake logfont name.
+ // The explicity fake logfont name should always be different from the family name.
+ Internal_FakeWindowsLogfontName(L"American Typewriter", L"AmericanTypewriter-Light", L"American Typewriter Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"American Typewriter", L"AmericanTypewriter-Semibold", L"American Typewriter Semibold", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"American Typewriter", L"AmericanTypewriter-CondensedLight", L"American Typewriter Condensed Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"American Typewriter", L"AmericanTypewriter-Condensed", L"American Typewriter Condensed", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"American Typewriter", L"AmericanTypewriter-CondensedBold", L"American Typewriter Condensed", ON_FontFaceQuartet::Member::Bold),
+
+ Internal_FakeWindowsLogfontName(L"Apple Braille", L"AppleBraille-Outline6Dot", L"Apple Braille Outline 6 Dot", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Apple Braille", L"AppleBraille-Outline8Dot", L"Apple Braille Outline 8 Dot", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Apple Braille", L"AppleBraille-Pinpoint6Dot", L"Apple Braille Pinpoint 6 Dot", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Apple Braille", L"AppleBraille-Pinpoint8Dot", L"Apple Braille Pinpoint 8 Dot", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-Thin", L"Apple SD Gothic Neo Thin", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-UltraLight", L"Apple SD Gothic Neo Ultralight", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-Light", L"Apple SD Gothic Neo Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-Medium", L"Apple SD Gothic Neo Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-SemiBold", L"Apple SD Gothic Neo Semibold", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-ExtraBold", L"Apple SD Gothic Neo Extra Bold", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Apple SD Gothic Neo", L"AppleSDGothicNeo-Heavy", L"Apple SD Gothic Neo Heavy", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Arial Hebrew", L"ArialHebrew-Light", L"Arial Hebrew Light", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Arial Hebrew Scholar", L"ArialHebrewScholar-Light", L"Arial Hebrew Scholar Light", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Book", L"Avenir Book", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-BookOblique", L"Avenir Book", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Light", L"Avenir Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-LightOblique", L"Avenir Light", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Medium", L"Avenir Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-MediumOblique", L"Avenir Medium", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Heavy", L"Avenir Heavy", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-HeavyOblique", L"Avenir Heavy", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Black", L"Avenir Black", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-BlackOblique", L"Avenir-Black", ON_FontFaceQuartet::Member::Italic),
+
+ Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-UltraLight", L"Avenir Next Ultralight", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-UltraLightItalic", L"Avenir Next Ultralight", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-Medium", L"Avenir Next Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-MediumItalic", L"Avenir Next Medium", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-DemiBold", L"Avenir Next Demibold", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-DemiBoldItalic", L"Avenir Next Demibold", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-Heavy", L"Avenir Next Heavy", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-HeavyItalic", L"Avenir Next Heavy", ON_FontFaceQuartet::Member::Italic),
+
+ Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-UltraLight", L"Avenir Next Condensed Ultralight", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-UltraLightItalic", L"Avenir Next Condensed Ultralight", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-Medium", L"Avenir Next Condensed Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-MediumItalic", L"Avenir Next Condensed Medium", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-DemiBold", L"Avenir Next Condensed Demibold", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-DemiBoldItalic", L"Avenir Next Condensed Demibold", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-Heavy", L"Avenir Next Condensed Heavy", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Avenir Next Condensed", L"AvenirNextCondensed-HeavyItalic", L"Avenir Next Condensed Heavy", ON_FontFaceQuartet::Member::Italic),
+
+ Internal_FakeWindowsLogfontName(L"Baskerville", L"Baskerville-SemiBold", L"Baskerville Semibold", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Baskerville", L"Baskerville-SemiBoldItalic", L"Baskerville Semibold", ON_FontFaceQuartet::Member::Italic),
+
+ Internal_FakeWindowsLogfontName(L"Chalkboard SE", L"ChalkboardSE-Light", L"Chalkboard SE Light", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Charter", L"Charter-Black", L"Charter Black", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Charter", L"Charter-BlackItalic", L"Charter Black", ON_FontFaceQuartet::Member::Italic),
+
+ Internal_FakeWindowsLogfontName(L"Copperplate", L"Copperplate-Light", L"Copperplate Light", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Damascus", L"DamascusLight", L"Damascus Light", ON_FontFaceQuartet::Member::Regular),
+ 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),
+
+ 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),
+ Internal_FakeWindowsLogfontName(L"Gill Sans", L"GillSans-SemiBold", L"Gill Sans Semibold", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Gill Sans", L"GillSans-SemiBoldItalic", L"Gill Sans Semibold", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Gill Sans", L"GillSans-UltraBold", L"Gill Sans Ultrabold", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Helvetica", L"Helvetica-Light", L"Helvetica Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Helvetica", L"Helvetica-LightOblique", L"Helvetica Light", ON_FontFaceQuartet::Member::Italic),
+
+ Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-UltraLight", L"Helvetica Neue Ultralight", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-UltraLightItalic", L"Helvetica Neue Ultralight", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-Thin", L"Helvetica Neue Thin", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-ThinItalic", L"Helvetica Neue Thin", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-Light", L"Helvetica Neue Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-LightItalic", L"Helvetica Neue Light", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-Medium", L"Helvetica Neue Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-MediumItalic", L"Helvetica Neue Medium", ON_FontFaceQuartet::Member::Italic),
+ Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-CondensedBold", L"Helvetica Neue Condensed", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Helvetica Neue", L"HelveticaNeue-CondensedBlack", L"Helvetica Neue Condensed", ON_FontFaceQuartet::Member::Bold),
+
+ Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W3", L"Hiragino Sans W3", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W6", L"Hiragino Sans W6", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W0", L"Hiragino Sans W0", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W1", L"Hiragino Sans W1", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W2", L"Hiragino Sans W2", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W4", L"Hiragino Sans W4", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W5", L"Hiragino Sans W5", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W7", L"Hiragino Sans W7", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W8", L"Hiragino Sans W8", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Hiragino Sans", L"HiraginoSans-W9", L"Hiragino Sans W9", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Hoefler Text", L"HoeflerText-Ornaments", L"Hoefler Text Ornaments", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"ITF Devanagari", L"ITFDevanagari-Light", L"ITF Devanagari Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"ITF Devanagari", L"ITFDevanagari-Medium", L"ITF Devanagari Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"ITF Devanagari", L"ITFDevanagari-Demi", L"ITF Devanagari Demibold", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"ITF Devanagari Marathi", L"ITFDevanagariMarathi-Light", L"ITF Devanagari Marathi Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"ITF Devanagari Marathi", L"ITFDevanagariMarathi-Medium", L"ITF Devanagari Marathi Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"ITF Devanagari Marathi", L"ITFDevanagariMarathi-Demi", L"ITF Devanagari Marathi Demibold", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Kohinoor Bangla", L"KohinoorBangla-Light", L"Kohinoor Bangla Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Kohinoor Bangla", L"KohinoorBangla-Medium", L"Kohinoor Bangla Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Kohinoor Bangla", L"KohinoorBangla-Semibold", L"Kohinoor Bangla Semibold", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Kohinoor Devanagari", L"KohinoorDevanagari-Light", L"Kohinoor Devanagari Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Kohinoor Devanagari", L"KohinoorDevanagari-Medium", L"Kohinoor Devanagari Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Kohinoor Devanagari", L"KohinoorDevanagari-Semibold", L"Kohinoor Devanagari Semibold", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Kohinoor Telugu", L"KohinoorTelugu-Light", L"Kohinoor Telugu Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Kohinoor Telugu", L"KohinoorTelugu-Medium", L"Kohinoor Telugu Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Kohinoor Telugu", L"KohinoorTelugu-Semibold", L"Kohinoor Telugu Semibold", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Muna", L"MunaBlack", L"Muna Black", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Optima", L"Optima-ExtraBlack", L"Optima Black", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Papyrus", L"Papyrus-Condensed", L"Papyrus Condensed", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Phosphate", L"Phosphate-Inline", L"Phosphate Inline", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Phosphate", L"Phosphate-Solid", L"Phosphate Solid", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"PingFang HK", L"PingFangHK-Ultralight", L"PingFang HK Ultralight", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"PingFang HK", L"PingFangHK-Thin", L"PingFang HK Thin", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"PingFang HK", L"PingFangHK-Light", L"PingFang HK Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"PingFang HK", L"PingFangHK-Medium", L"PingFang HK Medium", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"PingFang SC", L"PingFangSC-Ultralight", L"PingFang SC Ultralight", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"PingFang SC", L"PingFangSC-Thin", L"PingFang SC Thin", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"PingFang SC", L"PingFangSC-Light", L"PingFang SC Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"PingFang SC", L"PingFangSC-Medium", L"PingFang SC Medium", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"PingFang TC", L"PingFangTC-Ultralight", L"PingFang TC Ultralight", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"PingFang TC", L"PingFangTC-Thin", L"PingFang TC Thin", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"PingFang TC", L"PingFangTC-Light", L"PingFang TC Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"PingFang TC", L"PingFangTC-Medium", L"PingFang TC Medium", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Light", L"Skia Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Black", L"Skia Black", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Condensed", L"Skia Condensed", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Black-Condensed", L"Skia Condensed Black", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Light-Condensed", L"Skia Condensed Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Extended", L"Skia Extended", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Black-Extended", L"Skia Extended Black", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Skia", L"Skia-Regular_Light-Extended", L"Skia Extended Light", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Songti SC", L"STSongti-SC-Light", L"Songti SC Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Songti SC", L"STSongti-SC-Black", L"Songti SC Black", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Songti TC", L"STSongti-TC-Light", L"Songti TC Light", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Sukhumvit Set", L"SukhumvitSet-Light", L"Sukhumvit Set Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Sukhumvit Set", L"SukhumvitSet-Medium", L"Sukhumvit Set Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Sukhumvit Set", L"SukhumvitSet-SemiBold", L"Sukhumvit Set Semibold", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Sukhumvit Set", L"SukhumvitSet-Thin", L"Sukhumvit Set Thin", ON_FontFaceQuartet::Member::Regular),
+
+ Internal_FakeWindowsLogfontName(L"Thonburi", L"Thonburi-Light", L"Thonburi Light", ON_FontFaceQuartet::Member::Regular),
+ };
+
+ static bool bFakeNamesAreSorted = false;
+ if (false == bFakeNamesAreSorted)
+ {
+ bFakeNamesAreSorted = true;
+ qsort(fake_names, sizeof(fake_names) / sizeof(fake_names[0]), sizeof(fake_names[0]), (int(*)(const void*,const void*))Internal_FakeWindowsLogfontName::CompareFamilyAndPostscriptNameHash);
+ }
+
+ const Internal_FakeWindowsLogfontName key(family_name, postscript_name, L"", ON_FontFaceQuartet::Member::Unset);
+ const Internal_FakeWindowsLogfontName* fake_name
+ = (const Internal_FakeWindowsLogfontName*)bsearch((const void*)&key, (const void*)fake_names, sizeof(fake_names) / sizeof(fake_names[0]), sizeof(fake_names[0]), (int(*)(const void*, const void*))Internal_FakeWindowsLogfontName::CompareFamilyAndPostscriptNameHash);
+ if (
+ fake_name
+ && ON_FontFaceQuartet::Member::Unset != fake_name->QuartetMember()
+ && false == fake_name->QuartetFamilyAndPostscriptNameHash().IsZeroDigentOrEmptyContentHash()
+ && fake_name->FakeWindowsLogfontName().IsNotEmpty()
+ )
+ {
+ // this family is complicated enough that it
+ // has to be partitioned into multiple fake Windows LOGFONT quartets.
+ return fake_name->FakeWindowsLogfontName();
+ }
+
+ return family_name; // use family_name as the fake LOGFONT name
+}
+
const ON_wString ON_Font::QuartetName(
ON_Font::NameLocale name_locale
) const
{
- // This may need adjustment for MacOS.
+ // The Windows OS partitions a font family (which can have many face) into
+ // subsets with at most 4 faces that can be tagged regular/italic/bold/bold-italic
+ // and assigns each subset a unique "LOGFONT" name. Note that some subsets may
+ // have fewer than 4 faces because there isn't an italic or bold member.
+ // There are Windows OS font picking tools that use
+ // LOGFONT name + regular/italic/bold/bold-italic to select a particular face
+ // and these work ok because Windows OS generates the quartets and all applications
+ // choosing to use the archaic regular/italic/bold/bold-italic font UI have consistent
+ // quartets.
+ //
+ // Modern Mac OS does not have native font selection UI that uses the
+ // archaic regular/italic/bold/bold-italic approach.
+ //
+ // On Mac OS, opennurbs generates a fake LOGFONT name because Rhino 6 and 7 use
+ // the archaic regular/italic/bold/bold-italic font UI on oth Windows and Mac.
+ // Helvetica Neue, American Typewriter, and Avenir are examples of common
+ // Mac OS font families that do not fit well with the
+ // archaic regular/italic/bold/bold-italic font UI. Code in
+ // opennurbs_apple_nsfont.cpp is used to assign fake LOGFONT names for fonts
+ // like these.
return WindowsLogfontName(name_locale);
}
@@ -6163,7 +6688,12 @@ bool ON_Font::IsBoldInQuartet() const
if (nullptr == regular)
return true; // no regular in this quartet.
-
+
+ if (this == bold)
+ return true;
+ if (this == regular)
+ return false;
+
const unsigned int font_weight = static_cast(FontWeight());
const unsigned int regular_weight = static_cast(regular->FontWeight());
const unsigned int bold_weight = static_cast(bold->FontWeight());
@@ -7569,43 +8099,28 @@ void ON_Font::Dump(ON_TextLog& dump) const
dump.Print(L"PointSize = annotation default\n");
}
- bool bInQuartet = false;
- for(;;)
+ const ON_FontFaceQuartet q = this->InstalledFontQuartet();
+ const ON_FontFaceQuartet::Member m = q.QuartetMember(this);
+ switch (m)
{
- const ON_FontFaceQuartet q = this->InstalledFontQuartet();
- if (q.QuartetName().IsEmpty())
- break;
-
- const bool bQuartetItalic = (ON_Font::Style::Italic == m_font_style || ON_Font::Style::Oblique == m_font_style);
- const ON_Font* regular = bQuartetItalic ? q.ItalicFace() : q.RegularFace();
- const ON_Font* bold = bQuartetItalic ? q.BoldItalicFace() : q.BoldFace();
- const ON_Font* quartet_face;
- if (nullptr != regular && FontWeight() == regular->FontWeight())
- quartet_face = regular;
- else if (nullptr != bold && FontWeight() == bold->FontWeight())
- quartet_face = bold;
- else
- quartet_face = nullptr;
- if (nullptr == quartet_face)
- break;
-
- bInQuartet = true;
- if (quartet_face == q.RegularFace())
- dump.Print("Quartet: %ls (Regular member)\n", static_cast(q.QuartetName()));
- else if (quartet_face == q.BoldFace())
- dump.Print("Quartet: %ls (Bold member)\n", static_cast(q.QuartetName()));
- else if (quartet_face == q.ItalicFace())
- dump.Print("Quartet: %ls (Italic member)\n", static_cast(q.QuartetName()));
- else if (quartet_face == q.BoldItalicFace())
- dump.Print("Quartet: %ls (Bold-Italic member)\n", static_cast(q.QuartetName()));
- else
- bInQuartet = false;
-
+ case ON_FontFaceQuartet::Member::Regular:
+ dump.Print("Quartet: %ls (Regular member)\n", static_cast(q.QuartetName()));
+ break;
+ case ON_FontFaceQuartet::Member::Bold:
+ dump.Print("Quartet: %ls (Bold member)\n", static_cast(q.QuartetName()));
+ break;
+ case ON_FontFaceQuartet::Member::Italic:
+ dump.Print("Quartet: %ls (Italic member)\n", static_cast(q.QuartetName()));
+ break;
+ case ON_FontFaceQuartet::Member::BoldItalic:
+ dump.Print("Quartet: %ls (Bold-Italic member)\n", static_cast(q.QuartetName()));
+ break;
+ case ON_FontFaceQuartet::Member::Unset:
+ default:
+ dump.Print("Quartet: None\n");
break;
}
- if (false == bInQuartet)
- dump.Print("Quartet: None\n");
s = ON_Font::WeightToWideString(FontWeight());
if( s.IsEmpty())
@@ -7717,6 +8232,13 @@ void ON_Font::Dump(ON_TextLog& dump) const
dump.PushIndent();
ON_Font::DumpCTFont(apple_font, dump);
dump.PopIndent();
+ if (nullptr != apple_font && ON_Font::Origin::AppleFont == m_font_origin)
+ {
+ if (m_apple_font_weight_trait >= -1.0 && m_apple_font_weight_trait <= 1.0)
+ dump.Print(L"Apple font weight trait = %g\n", m_apple_font_weight_trait);
+ if (m_apple_font_width_trait >= -1.0 && m_apple_font_width_trait <= 1.0)
+ dump.Print(L"Apple font width trait = %g\n", m_apple_font_width_trait);
+ }
}
#elif defined(OPENNURBS_FREETYPE_SUPPORT)
// Look in opennurbs_system_rumtime.h for the correct place to define OPENNURBS_FREETYPE_SUPPORT.
@@ -9363,6 +9885,11 @@ bool ON_Font::IsUpright() const
return (ON_Font::Style::Upright == m_font_style);
}
+bool ON_Font::IsItalicOrOblique() const
+{
+ return (ON_Font::Style::Italic == m_font_style || ON_Font::Style::Oblique == m_font_style);
+}
+
bool ON_Font::IsOblique()
{
return (ON_Font::Style::Oblique == m_font_style);
@@ -9550,12 +10077,33 @@ bool ON_Font::SetAppleFontWeightTrait(
double ON_Font::AppleFontWeightTrait() const
{
- return
+ // old function that cannot be changed because it would break the SDK.
+ return
(m_apple_font_weight_trait >= -1.0 && m_apple_font_weight_trait <= 1.0)
? m_apple_font_weight_trait
: ON_Font::AppleFontWeightTraitFromWeight(this->FontWeight());
}
+double ON_Font::AppleFontWeightTraitEx() const
+{
+ return
+ ON_Font::Origin::AppleFont == FontOrigin()
+ && m_apple_font_weight_trait >= -1.0
+ && m_apple_font_weight_trait <= 1.0
+ ? m_apple_font_weight_trait
+ : ON_UNSET_VALUE;
+}
+
+double ON_Font::AppleFontWidthTrait() const
+{
+ return
+ ON_Font::Origin::AppleFont == FontOrigin()
+ && m_apple_font_width_trait >= -1.0
+ && m_apple_font_width_trait <= 1.0
+ ? m_apple_font_width_trait
+ : ON_UNSET_VALUE;
+}
+
bool ON_Font::IsValidPointSize(
double point_size
)
@@ -11345,6 +11893,153 @@ bool ON_ManagedFonts::GetFontMetricsInFontDesignUnits(
return false;
}
+void ON_ManagedFonts::Internal_SetFakeWindowsLogfontName(
+ const ON_Font * font,
+ const ON_wString fake_loc_logfont_name,
+ const ON_wString fake_en_logfont_name
+)
+{
+ if (nullptr == font)
+ return;
+ const_cast(font)->m_loc_windows_logfont_name = fake_loc_logfont_name;
+ const_cast(font)->m_en_windows_logfont_name = fake_en_logfont_name;
+}
+
+void ON_ManagedFonts::Internal_SetFakeWindowsLogfontNames(
+ ON_SimpleArray& device_list
+)
+{
+ // Windows divides font families (which can have many faces)
+ // into LOGFONT subsets identified by a logfont name.
+ // A LOGFONT subset can have at most 4 faces and typically
+ // they are regular/bold/italic/bold-italic variations of
+ // a face and all have matching stretch.
+ //
+ // This all goes back to the 1970's and early user interfaces
+ // that selected fonts based on a name, bold=true/false, italic=true/false
+ // style inteface. This type of archaic font selection interface is way
+ // too simple to accomodate the rich font families are currently in use.
+ // Newer font families can have dozens of faces with severeal weights,
+ // several widths, and an upright and italic version of each of these.
+ // However, that does not prevent applications like Rhino 6/7
+ // from attempting to use the inadequate and archic bold/italic font
+ // selection interface to select a font from a rich list of modern fonts.
+ //
+ // This function is used on non Windows platforms to partition
+ // the installed font families into subsets with the same
+ // width and set a unique fake LOGFONT name. Later sorting will
+ // choose up to four faces from each subset to use as the
+ // regular/bold/italic/bold-italic representatives.
+
+ // Assign a fake logfont name.
+ const unsigned int font_count = device_list.UnsignedCount();
+ for (unsigned int i = 0; i < font_count; ++i)
+ {
+ const ON_Font* f0 = device_list[i];
+ if (nullptr == f0)
+ continue;
+
+ ON_wString fake_loc_logfont_name = ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames(
+ f0->FamilyName(ON_Font::NameLocale::LocalizedFirst),
+ f0->PostScriptName(ON_Font::NameLocale::LocalizedFirst)
+ );
+ if (fake_loc_logfont_name.IsEmpty())
+ continue;
+ ON_wString fake_en_logfont_name = ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames(
+ f0->FamilyName(ON_Font::NameLocale::English),
+ f0->PostScriptName(ON_Font::NameLocale::English)
+ );
+ if (fake_en_logfont_name.IsEmpty())
+ fake_en_logfont_name = fake_loc_logfont_name;
+ else if (false == fake_loc_logfont_name.EqualOrdinal(fake_en_logfont_name, true))
+ {
+ const bool bLocalFaceIsFamilyName = fake_loc_logfont_name.EqualOrdinal(f0->FamilyName(ON_Font::NameLocale::LocalizedFirst), true);
+ const bool bEnglishFaceIsFamilyName = fake_en_logfont_name.EqualOrdinal(f0->FamilyName(ON_Font::NameLocale::English), true);
+ if (bLocalFaceIsFamilyName && false == bEnglishFaceIsFamilyName)
+ fake_loc_logfont_name = fake_en_logfont_name;
+ else if (bEnglishFaceIsFamilyName && false == bLocalFaceIsFamilyName)
+ fake_en_logfont_name = fake_loc_logfont_name;
+ }
+
+ ON_ManagedFonts::Internal_SetFakeWindowsLogfontName(
+ f0,
+ fake_loc_logfont_name,
+ fake_en_logfont_name
+ );
+ }
+
+ // Sort device_list by font family and current fake LOGFONT name
+ device_list.QuickSort(ON_FontList::CompareFamilyAndWindowsLogfontName);
+ ON_SimpleArray quartet_candidates(128);
+ bool bSortAgain = false;
+ for (unsigned int i = 0; i < font_count; ++i)
+ {
+ const ON_Font* f0 = device_list[i];
+ if (nullptr == f0)
+ continue;
+
+ quartet_candidates.SetCount(0);
+ quartet_candidates.Append(f0);
+ while( (i + 1) < font_count )
+ {
+ const ON_Font* f = device_list[i + 1];
+ if (nullptr == f || 0 != ON_FontList::CompareFamilyAndWindowsLogfontName(&f0, &f))
+ break;
+ quartet_candidates.Append(f);
+ ++i;
+ }
+ const unsigned int quartet_candidate_count = quartet_candidates.UnsignedCount();
+ if (quartet_candidate_count < 2)
+ continue;
+
+ // members in a fake LOGFONT quartet should have the same stretch
+ quartet_candidates.QuickSort(ON_FontList::CompareStretch);
+
+ // See which subset of family gets to use the family name as
+ // the fake logfont name.
+ const int medium_stretch = (int)((unsigned)(ON_Font::Stretch::Medium));
+ ON_Font::Stretch stretch0 = quartet_candidates[0]->FontStretch();
+ int delta_stretch = abs(medium_stretch - ((int)((unsigned)(stretch0))));
+ bool bNeedToModifyFakeName = false;
+ for (unsigned int j = 0; j < quartet_candidate_count; ++j)
+ {
+ const ON_Font::Stretch stretch = quartet_candidates[j]->FontStretch();
+ int ds = abs(medium_stretch - ((int)((unsigned)(stretch))));
+ if (ds < delta_stretch)
+ {
+ stretch0 = stretch;
+ delta_stretch = ds;
+ bNeedToModifyFakeName = true;
+ }
+ }
+
+ if (false == bNeedToModifyFakeName)
+ continue; // we cannot use stretch to distinguish between family members
+
+ // If a family member's stretch is not stretch0, modify its names.
+ for (unsigned int j = 0; j < quartet_candidate_count; ++j)
+ {
+ const ON_Font* f = quartet_candidates[j];
+ const ON_Font::Stretch stretch = f->FontStretch();
+ if (stretch == stretch0)
+ continue;
+ ON_wString suffix = L" (";
+ suffix += ON_Font::StretchToWideString(stretch);
+ if (suffix.Length() < 3)
+ continue;
+ suffix += L")";
+ const ON_wString loc_name = f->m_loc_windows_logfont_name+suffix;
+ const ON_wString en_name = f->m_en_windows_logfont_name+suffix;
+ ON_ManagedFonts::Internal_SetFakeWindowsLogfontName(f,loc_name,en_name);
+ bSortAgain = true;
+ }
+ }
+
+ if (bSortAgain)
+ device_list.QuickSort(ON_FontList::CompareFamilyAndWindowsLogfontName);
+
+}
+
const ON_FontList& ON_ManagedFonts::InstalledFonts()
{
if (0 == List.m_installed_fonts.Count())
@@ -11355,6 +12050,7 @@ const ON_FontList& ON_ManagedFonts::InstalledFonts()
ON_ManagedFonts::Internal_GetWindowsInstalledFonts(device_list);
#elif defined (ON_RUNTIME_APPLE_CORE_TEXT_AVAILABLE)
ON_ManagedFonts::Internal_GetAppleInstalledCTFonts(device_list);
+ ON_ManagedFonts::Internal_SetFakeWindowsLogfontNames(device_list);
#endif
if (device_list.Count() > 0)
{
@@ -11364,3 +12060,5 @@ const ON_FontList& ON_ManagedFonts::InstalledFonts()
}
return List.m_installed_fonts;
}
+
+
diff --git a/opennurbs_font.h b/opennurbs_font.h
index 18fb953d..4b47a4a5 100644
--- a/opennurbs_font.h
+++ b/opennurbs_font.h
@@ -2152,6 +2152,16 @@ public:
class ON_CLASS ON_FontFaceQuartet
{
public:
+
+ enum class Member : unsigned char
+ {
+ Unset = 0,
+ Regular = 1,
+ Bold = 2,
+ Italic = 3,
+ BoldItalic = 4
+ };
+
ON_FontFaceQuartet() = default;
~ON_FontFaceQuartet() = default;
ON_FontFaceQuartet(const ON_FontFaceQuartet&) = default;
@@ -2193,6 +2203,38 @@ public:
const class ON_Font* ItalicFace() const;
const class ON_Font* BoldItalicFace() const;
+ /*
+ Parameters:
+ font - [in]
+ Font to test
+ Returns:
+ If font exactly matches a quartet member, that member is identified.
+ Otherwise, ON_FontFaceQuartet::Member::Unset is returned.
+ */
+ ON_FontFaceQuartet::Member QuartetMember(
+ const ON_Font* font
+ ) const;
+
+ /*
+ Parameters:
+ member - [in]
+ Returns:
+ Specified quartet member.
+ */
+ const ON_Font* Face(
+ ON_FontFaceQuartet::Member member
+ ) const;
+
+ /*
+ Parameters:
+ member - [in]
+ Returns:
+ Closest quartet member.
+ */
+ const ON_Font* ClosestFace(
+ ON_FontFaceQuartet::Member member
+ ) const;
+
const ON_Font* Face(
bool bBold,
bool bItalic
@@ -3589,6 +3631,24 @@ public:
*/
const ON_wString WindowsLogfontName() const;
+ /*
+ Description:
+ On non-WIndows platforms like Mac OS, iOS, and Android, this function can
+ be used to generate fake windows logfont names.
+ For fonts that have at most 4 faces with the same stretch and variations
+ in weight and slant, the family_name is a good choice.
+ For fonts that have many faces, like Helvetica Neue on Mac OS,
+ this funciton will generate names that act like a Windows LOGFONT name
+ for use in archaic name + regular/bold/italic/bold-italic font selction
+ user interfaces.
+ Returns:
+ A fake windows logfont name.
+ */
+ static const ON_wString FakeWindowsLogfontNameFromFamilyAndPostScriptNames(
+ ON_wString family_name,
+ ON_wString postscript_name
+ );
+
/*
Returns:
Name of the quartet for this font. See ON_FontFaceQuartet for more details.
@@ -4754,6 +4814,13 @@ public:
int AppleWeightOfFont() const;
double AppleFontWeightTrait() const;
+ /*
+ Returns:
+ If the font is created from a CTFont, the weight trait,
+ otherwise ON_UNSET_VALUE;
+ */
+ double AppleFontWeightTraitEx() const;
+
/*
Description:
Don't use this old function. If you have a font and want a face in
@@ -4943,20 +5010,35 @@ public:
*/
bool IsUpright() const;
+ /*
+ Returns:
+ true if FontStyle() is ON_Font::Style::Italic or is ON_Font::Style::Oblique.
+ Otherwise false.
+ Remarks:
+ The face is sloped so the top is to the left of the base. This is extremely rare.
+ NOTE WELL:
+ When the term "oblique" appears in a face names or descriptions,
+ it generally means the face is an italic face.
+ */
+ bool IsItalicOrOblique() const;
+
/*
Returns:
true if FontStyle() is ON_Font::Style::Oblique.
false if FontStyle() is ON_Font::Style::Upright or .ON_Font::Style::Italic.
Remarks:
The face is sloped so the top is to the left of the base. This is extremely rare.
- NOTE WELL:
+ NOTE WELL:
When the term "oblique" appears in a face names or descriptions,
it generally means the face is an italic face.
*/
bool IsOblique(); // ERROR - missing const
-
+
+
ON_Font::Stretch FontStretch() const;
+ double AppleFontWidthTrait() const;
+
/*
Description:
Don't use this old function. If you have a font and want a face in
@@ -5330,7 +5412,7 @@ private:
mutable ON_SHA1_Hash m_font_characteristics_hash;
private:
- double m_reserved2 = 0.0;
+ double m_apple_font_width_trait = ON_UNSET_VALUE;
double m_reserved3 = 0.0;
double m_reserved4 = 0.0;
@@ -5766,6 +5848,7 @@ public:
static int CompareFamilyName(ON_Font const* const* lhs, ON_Font const* const* rhs);
static int CompareFamilyAndFaceName(ON_Font const* const* lhs, ON_Font const* const* rhs);
static int CompareWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs);
+ static int CompareFamilyAndWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs);
static int CompareEnglishPostScriptName(ON_Font const* const* lhs, ON_Font const* const* rhs);
static int CompareEnglishFamilyName(ON_Font const* const* lhs, ON_Font const* const* rhs);
@@ -5775,6 +5858,7 @@ public:
static int CompareQuartetName(ON_Font const* const* lhs, ON_Font const* const* rhs);
static int CompareWeightStretchStyle(ON_Font const* const* lhs, ON_Font const* const* rhs);
+ static int CompareStretch(ON_Font const* const* lhs, ON_Font const* const* rhs);
static int CompareUnderlinedStrikethroughPointSize(ON_Font const* const* lhs, ON_Font const* const* rhs);
unsigned int AddFont(
diff --git a/opennurbs_fpoint.h b/opennurbs_fpoint.h
index 8517f0b7..ab9159d6 100644
--- a/opennurbs_fpoint.h
+++ b/opennurbs_fpoint.h
@@ -176,7 +176,7 @@ public:
/*
Returns:
- true if at least one coordinate is not zero and no coordinates are nans.
+ true if at lease one coordinate is not zero and no coordinates are unset or nans.
*/
bool IsNotZero() const;
@@ -353,7 +353,7 @@ public:
/*
Returns:
- true if at lease one coordinate is not zero and no coordinates are nans.
+ true if at lease one coordinate is not zero and no coordinates are unset or nans.
*/
bool IsNotZero() const;
@@ -774,7 +774,7 @@ public:
/*
Returns:
- true if at lease one coordinate is not zero and no coordinates are nans.
+ true if at lease one coordinate is not zero and no coordinates are unset or nans.
*/
bool IsNotZero() const;
@@ -1036,7 +1036,7 @@ public:
/*
Returns:
- true if at lease one coordinate is not zero and no coordinates are nans.
+ true if at lease one coordinate is not zero and no coordinates are unset or nans.
*/
bool IsNotZero() const;
diff --git a/opennurbs_freetype.cpp b/opennurbs_freetype.cpp
index c61144c1..89d1e1be 100644
--- a/opennurbs_freetype.cpp
+++ b/opennurbs_freetype.cpp
@@ -7,7 +7,7 @@
// 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 .
//
////////////////////////////////////////////////////////////////
@@ -18,7 +18,7 @@
#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
+// 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
@@ -33,18 +33,18 @@
// FreeType Licensing:
//
-//// Retrieved March 22, 2017
+//// Retrieved March 22, 2017
//// https://www.freetype.org/freetype2/docs/index.html
////What is FreeType?
////
-////FreeType is a software font engine that is designed to be small, efficient,
-////highly customizable, and portable while capable of producing high-quality
+////FreeType is a software font engine that is designed to be small, efficient,
+////highly customizable, and portable while capable of producing high-quality
////output (glyph images). It can be used in graphics libraries, display servers,
////font conversion tools, text image generation tools, and many other products as well.
////
-////Note that FreeType is a font service and doesn't provide APIs to perform
-////higher-level features like text layout or graphics processing
-////(e.g., colored text rendering, hollowing, etc.). However, it greatly
+////Note that FreeType is a font service and doesn't provide APIs to perform
+////higher-level features like text layout or graphics processing
+////(e.g., colored text rendering, ‘hollowing’, etc.). However, it greatly
////simplifies these tasks by providing a simple, easy to use, and uniform
////interface to access the content of font files.
////
@@ -52,12 +52,12 @@
////FreeType License and the GNU Public License, Version 2. It can thus
////be used by any kind of projects, be they proprietary or not.
////
-////Please note that FreeType is also called FreeType 2, to
-////distinguish it from the old, deprecated FreeType 1 library,
+////Please note that ‘FreeType’ is also called ‘FreeType 2’, to
+////distinguish it from the old, deprecated ‘FreeType 1’ library,
////a predecessor no longer maintained and supported.
////
//// http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT
-////
+////
//// The FreeType Project LICENSE
//// ----------------------------
////
@@ -108,7 +108,7 @@
//// encourage you to use the following text:
////
//// """
-//// Portions of this software are copyright The FreeType
+//// Portions of this software are copyright © The FreeType
//// Project (www.freetype.org). All rights reserved.
//// """
////
@@ -266,7 +266,7 @@ public:
ON_FontFileBuffer(const ON_FontFileBuffer& src)
{
Internal_CopyFrom(src);
- }
+ }
ON_FontFileBuffer& operator=(const ON_FontFileBuffer& src)
{
@@ -499,9 +499,9 @@ bool ON_FreeType::IsDamagedCharMap(
{
if (nullptr == cmap || nullptr == cmap->face)
return true;
-
+
bool rc = false;
-
+
switch (cmap->encoding)
{
case FT_ENCODING_APPLE_ROMAN:
@@ -555,7 +555,7 @@ bool ON_FreeType::UseUnicodeAsAppleRomanCharCode(FT_Face face)
//
// It appears that for these fonts, passing a Unicode code point value
// to the cmap[] idenfied as "FT_ENCODING_APPLE_ROMAN" will get the correct
- // glyph id.
+ // glyph id.
//
// I have verified that the way opennurbs handles FT_ENCODING_APPLE_ROMAN charmaps
// and the funciton ON_MapUnicodeToAppleRoman() works correctly with all the
@@ -583,7 +583,7 @@ bool ON_FreeType::UseUnicodeAsAppleRomanCharCode(FT_Face face)
// Linguist's Software Romantic 2.0 generated with Altsys Fontographer 4.1 9/17/96
// Linguist's Software SansSerif 2.0 generated with Altsys Fontographer 4.1 9/17/96
// Linguist's Software Technic 2.0 generated with Altsys Fontographer 4.1 9/17/96
- //
+ //
// So far, all styles with these face names have the buggy cmap[].
if (ON_String::EqualOrdinal(face->family_name, "CityBlueprint", false))
return true;
@@ -602,7 +602,7 @@ bool ON_FreeType::UseUnicodeAsAppleRomanCharCode(FT_Face face)
const ON_wString ON_FreeType::EncodingTypeToString( FT_Encoding charmap_encoding )
{
ON_wString e;
-
+
switch (charmap_encoding)
{
case FT_ENCODING_NONE: e = L"FT_ENCODING_NONE"; break;
@@ -619,14 +619,14 @@ const ON_wString ON_FreeType::EncodingTypeToString( FT_Encoding charmap_encoding
case FT_ENCODING_ADOBE_EXPERT: e = L"FT_ENCODING_ADOBE_EXPERT"; break;
case FT_ENCODING_ADOBE_CUSTOM: e = L"FT_ENCODING_ADOBE_CUSTOM"; break;
case FT_ENCODING_APPLE_ROMAN: e = L"FT_ENCODING_APPLE_ROMAN"; break;
- default:
+ default:
e = ON_wString::FormatToString(
L"((FT_Encoding)%u)",
static_cast(charmap_encoding)
);
break;
}
-
+
return e;
}
@@ -639,8 +639,8 @@ const ON_wString ON_FreeType::CharmapPlatformEncodingDescription( const FT_CharM
ON_wString encoding;
switch (cmap->platform_id)
{
- case 0:
- platform = L"Unicode";
+ case 0:
+ platform = L"Unicode";
switch (cmap->encoding_id)
{
case 0: encoding = L"Unicode 1.0 semantics [deprecated]"; break;
@@ -653,7 +653,7 @@ const ON_wString ON_FreeType::CharmapPlatformEncodingDescription( const FT_CharM
}
break;
- case 1:
+ case 1:
platform = L"Apple Script Manager";
switch (cmap->encoding_id)
{
@@ -704,7 +704,7 @@ const ON_wString ON_FreeType::CharmapPlatformEncodingDescription( const FT_CharM
break;
case 3:
- platform = L"Windows";
+ platform = L"Windows";
switch (cmap->encoding_id)
{
case 0: encoding = L"Symbol"; break;
@@ -747,7 +747,7 @@ bool ON_FontGlyph::TestFreeTypeFaceCharMaps(
ON_TextLog* text_log
) const
{
- // In order for false to be returned, charmaps[] have to exist and
+ // In order for false to be returned, charmaps[] have to exist and
// an explicit error has to be detected. Otherwise, true is returned.
const ON_Font* font = Font();
if (nullptr == font)
@@ -847,7 +847,7 @@ bool ON_FontGlyph::TestFreeTypeFaceCharMaps(
if (bHaveCharCode)
{
- bHaveCharMap
+ bHaveCharMap
= FT_Err_Ok == FT_Set_Charmap(face, charmap)
&& charmap == face->charmap;
if (bHaveCharMap)
@@ -896,7 +896,7 @@ bool ON_FontGlyph::TestFreeTypeFaceCharMaps(
if (glyph_index != gid)
{
s += ON_wString::FormatToString(L"ERROR(expected glyph index %u)",glyph_index);
- }
+ }
}
text_log->Print(L"%ls\n", static_cast(s));
}
@@ -904,7 +904,7 @@ bool ON_FontGlyph::TestFreeTypeFaceCharMaps(
// restore face charmap state
FT_Set_Charmap(face, charmap0);
-
+
return rc;
}
@@ -944,15 +944,15 @@ unsigned int ON_FreeType::GlyphId(
// and they can map different unicode code points. These typically are
// Windows UCS-2 and Windows UCS-4 cmaps. UCS-2 and UCS-4 values subsets of Unicode.
//
- // In fonts like CityBlueprint and CountryBlueprint (which many customers use), there
+ // In fonts like CityBlueprint and CountryBlueprint (which many customers use), there
// is no FT_ENCODING_UNICODE charmap but there is a viable FT_ENCODING_APPLE_ROMAN charmap.
//
// As we discover fonts that customers use, we will add support for their charmaps.
//
// TrueType platform_id and encoding_id. The encoding id is platform specific.
- // platform_id-encoding_id
+ // platform_id-encoding_id
//
- // 0-* "Apple *code" - encoding id varies
+ // 0-* "Apple *code" - encoding id varies
//
// 1-0 Apple Roman (256 codes - see ON_MapAppleRomanToUnicode())
// 1-* Apple (encoding id = script manager)
@@ -971,7 +971,7 @@ unsigned int ON_FreeType::GlyphId(
// 4-* Custom
//
// http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-chapter08
- //
+ //
const FT_Encoding encoding_pass[]
{
FT_ENCODING_UNICODE,
@@ -1100,7 +1100,7 @@ static bool Internal_CreateFontBufferFromDirectWrite(
ON_WARNING("Multiple font files.");
numfiles = 1;
}
-
+
Microsoft::WRL::ComPtr dwriteFontFile = nullptr;
hr = dwriteFontFace->GetFiles(&numfiles, &dwriteFontFile);
if (FAILED(hr))
@@ -1193,11 +1193,11 @@ static bool Internal_CreateFontBufferFromDirectWrite(
cleanLogFont.lfOrientation = 0;
if (cleanLogFont.lfWeight < 1 || cleanLogFont.lfWeight > 999)
cleanLogFont.lfWeight = 400;
- if (0 != cleanLogFont.lfItalic)
+ if (0 != cleanLogFont.lfItalic)
cleanLogFont.lfItalic = 1;
- if (0 != cleanLogFont.lfUnderline)
+ if (0 != cleanLogFont.lfUnderline)
cleanLogFont.lfUnderline = 1;
- if (0 != cleanLogFont.lfStrikeOut)
+ if (0 != cleanLogFont.lfStrikeOut)
cleanLogFont.lfStrikeOut = 1;
if (ON_Font::WindowsConstants::logfont_symbol_charset != cleanLogFont.lfCharSet)
cleanLogFont.lfCharSet = ON_Font::WindowsConstants::logfont_default_charset;
@@ -1265,7 +1265,7 @@ static bool Internal_CreateFontBufferFromGDI(
{
ON_ERROR("onmalloc(buffer_capacity) failed.");
break;
- }
+ }
memset(buffer, 0, buffer_capacity);
const DWORD buffer_size = ::GetFontData(font_hdc, dwTable, dwOffset, buffer, buffer_capacity);
@@ -1317,7 +1317,7 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromWindowsFont(
// The DirectWrite approach yields better results in some cases.
// For example, the Yu Gothic in Windows 10
// The font_buffer created by DirectWrite results in an FT_Face
- // with 3 charmaps (2 Uniocde) and the font_buffer created by
+ // with 3 charmaps (2 Uniocde) and the font_buffer created by
// GDI has 0 charmaps which means getting a glyph from a code point
// is not possible.
const bool bHaveBuffer
@@ -1331,7 +1331,7 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromWindowsFont(
continue;
if (nullptr == font_buffer.Buffer())
continue;
-
+
int font_face_index = 0;
FT_Face face = nullptr;
FT_Error rc = FT_New_Memory_Face(
@@ -1371,19 +1371,19 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromAppleFont (CTFontRef fontRe
{
if (nullptr == fontRef)
return nullptr;
-
+
// determine file path for CTFont
CFURLRef fontURLRef = (CFURLRef) CTFontCopyAttribute (fontRef, kCTFontURLAttribute);
-
+
NSURL* fontURL = (NSURL*) CFBridgingRelease (fontURLRef);
const char* path = fontURL.path.UTF8String;
-
+
// Search all the faces in this font file for a face that matches the NSFont family and style
FT_Face ftFace;
FT_Error err = FT_New_Face (ON_FreeType::Library(), path, 0, &ftFace); // get first face
if (err)
return nullptr; // that didn't work
-
+
const int numFaces = (int)ftFace->num_faces; // determine number of faces in the font file
ON_FreeTypeFace* rc = new ON_FreeTypeFace();
@@ -1392,7 +1392,7 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromAppleFont (CTFontRef fontRe
rc->m_face = ftFace;
return rc; // only one face, so this must be the right one
}
-
+
int faceIndex = 0;
for (;;)
{
@@ -1409,10 +1409,10 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromAppleFont (CTFontRef fontRe
rc->m_face = ftFace;
return rc;
}
-
+
// No match. Step to next face.
FT_Done_Face (ftFace);
-
+
FT_Error err = FT_New_Face (ON_FreeType::Library(), path, ++faceIndex, &ftFace);
if (ftFace == nullptr || err || faceIndex >= numFaces) {
// Ran out of faces to inspect or FT_New_Face returned an error.
@@ -1420,7 +1420,7 @@ ON_FreeTypeFace* ON_FreeType::Internal_CreateFaceFromAppleFont (CTFontRef fontRe
break;
}
}
-
+
// When no match found, use first face in font file as the default face.
FT_New_Face (ON_FreeType::Library(), path, 0, &ftFace); // get first face
@@ -1513,20 +1513,20 @@ unsigned int ON_FreeTypeGetFontUnitsPerM(
font = font->ManagedFont();
if (nullptr == font)
break;
-
+
const ON__UINT_PTR ft_face_as_uint = ON_Font::FreeTypeFace(font);
if (0 == ft_face_as_uint)
break;
-
+
FT_Face ft_face = (FT_Face)ft_face_as_uint;
-
+
unsigned int freetypeUPM = (unsigned int)ft_face->units_per_EM;
if (freetypeUPM > 0 && freetypeUPM < 0xFFFFFFF)
return freetypeUPM;
-
+
break;
}
-
+
return 0;
}
@@ -1564,15 +1564,15 @@ void ON_FreeTypeGetFontMetrics(
return 0;
-
- const unsigned int glyph_id
+
+ const unsigned int glyph_id
= glyph->FontGlyphIdIsSet()
? (unsigned int)glyph->FontGlyphId()
: ON_FreeType::GlyphId(face, glyph->CodePoint());
if (0 == glyph_id)
return 0;
*/
-
+
// Turns out that checking H and I doesn't work very well for some
// fonts designed for Asian languages, symbol fonts, and emoji fonts.
const ON_FontGlyph Iglyph(font, 'I');
@@ -1625,8 +1625,8 @@ public:
~ON_FreeTypeOutlineAccumlator() = default;
bool AddFreeTypeFiguresToOutline(
- FT_Face ft_face,
- FT_UInt font_glyph_id,
+ FT_Face ft_face,
+ FT_UInt font_glyph_id,
ON_Outline& outline
);
@@ -1641,7 +1641,7 @@ private:
// ON_Outline* destination_outline,
// FT_Face ft_face, FT_UInt font_glyph_id, FT_Orientation* ft_orientation
// );
-//
+//
// bool BeginGlyphOutline(
// ON__UINT32 font_units_per_em,
// bool bSingleStrokeFont,
@@ -1714,8 +1714,8 @@ const ON_TextBox ON_TextBox_CreateFromFreeTypeGlyphMetrics(
}
bool ON_FreeTypeOutlineAccumlator::AddFreeTypeFiguresToOutline(
- FT_Face ft_face,
- FT_UInt font_glyph_id,
+ FT_Face ft_face,
+ FT_UInt font_glyph_id,
ON_Outline& outline
)
{
@@ -1742,7 +1742,7 @@ bool ON_FreeTypeOutlineAccumlator::AddFreeTypeFiguresToOutline(
if (FT_Err_Ok != FT_Get_Glyph(ft_face->glyph, &ft_glyph))
break;
if (nullptr == ft_glyph)
- break;
+ break;
if (FT_GLYPH_FORMAT_OUTLINE != ft_glyph->format)
break;
@@ -1758,7 +1758,7 @@ bool ON_FreeTypeOutlineAccumlator::AddFreeTypeFiguresToOutline(
}
m_ft_orientation = FT_Outline_Get_Orientation(&ft_outline);
- m_end_figure_point_type
+ m_end_figure_point_type
= (ON_OutlineFigure::Type::SingleStroke == outline.FigureType())
? ON_OutlineFigurePoint::Type::EndFigureOpen
: ON_OutlineFigurePoint::Type::EndFigureClosed;
@@ -1786,9 +1786,9 @@ bool ON_FreeTypeOutlineAccumlator::AddFreeTypeFiguresToOutline(
return rc;
}
-int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineMoveToFunc(
- const FT_Vector* to,
- void* user
+int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineMoveToFunc(
+ const FT_Vector* to,
+ void* user
)
{
ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user;
@@ -1806,8 +1806,8 @@ int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineMoveToFunc(
}
int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToFunc(
- const FT_Vector* to,
- void* user
+ const FT_Vector* to,
+ void* user
)
{
ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user;
@@ -1821,8 +1821,8 @@ int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToFunc(
}
int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToCloseContourFunc(
- const FT_Vector* to,
- void* user
+ const FT_Vector* to,
+ void* user
)
{
ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user;
@@ -1831,19 +1831,19 @@ int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineLineToCloseContourFunc
a->EndFigure(a->m_end_figure_point_type);
a->m_prev_point = *to;
-
+
return FT_Err_Ok;
}
int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineConicToFunc(
const FT_Vector* control,
- const FT_Vector* to,
- void* user
+ const FT_Vector* to,
+ void* user
)
{
ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user;
if (nullptr == a)
- return FT_Err_Invalid_Argument;
+ return FT_Err_Invalid_Argument;
a->AppendQuadraticBezier(Internal_To2fPoint(control),Internal_To2fPoint(to));
a->m_prev_point = *to;
@@ -1854,8 +1854,8 @@ int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineConicToFunc(
int ON_FreeTypeOutlineAccumlator::Internal_FreeTypeOutlineCubicToFunc(
const FT_Vector* control1,
const FT_Vector* control2,
- const FT_Vector* to,
- void* user
+ const FT_Vector* to,
+ void* user
)
{
ON_FreeTypeOutlineAccumlator* a = (ON_FreeTypeOutlineAccumlator*)user;
@@ -1890,7 +1890,7 @@ bool ON_FreeTypeLoadGlyph(
#endif
for (int pass = bLoadRenderBitmap ? 1 : pass0; pass < 2; pass++)
{
- FT_Int32 ft_face_load_no_scale_flag
+ FT_Int32 ft_face_load_no_scale_flag
= (0 == pass)
? 0
: FT_LOAD_NO_SCALE;
@@ -1905,10 +1905,10 @@ bool ON_FreeTypeLoadGlyph(
/*
Avoid use of FT_LOAD_NO_SCALE
https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_LOAD_NO_SCALE
- If the font is tricky (see FT_FACE_FLAG_TRICKY for more), using FT_LOAD_NO_SCALE
- usually yields meaningless outlines because the subglyphs must be scaled and
- positioned with hinting instructions. This can be solved by loading the font
- without FT_LOAD_NO_SCALE and setting the character size to font->units_per_EM.
+ If the font is ‘tricky’ (see FT_FACE_FLAG_TRICKY for more), using FT_LOAD_NO_SCALE
+ usually yields meaningless outlines because the subglyphs must be scaled and
+ positioned with hinting instructions. This can be solved by loading the font
+ without FT_LOAD_NO_SCALE and setting the character size to ‘font->units_per_EM’.
*/
#if defined(ON_RUNTIME_WIN)
// Unit Systems:
@@ -1974,7 +1974,7 @@ bool ON_FreeTypeLoadGlyph(
unsigned int ON_FreeTypeGetGlyphMetrics(
const ON_FontGlyph* glyph,
class ON_TextBox& glyph_metrics_in_font_design_units
-)
+)
{
glyph_metrics_in_font_design_units = ON_TextBox::Unset;
@@ -1994,7 +1994,7 @@ unsigned int ON_FreeTypeGetGlyphMetrics(
FT_Face face = (FT_Face)ft_face_as_uint;
- const unsigned int glyph_index
+ const unsigned int glyph_index
= glyph->FontGlyphIndexIsSet()
? glyph->FontGlyphIndex()
: ON_FreeType::GlyphId(face, glyph->CodePoint());
@@ -2015,7 +2015,7 @@ unsigned int ON_FreeTypeGetGlyphMetrics(
//// }
////#endif
-
+
const bool bLoadRenderBitmap = false;
// bLoadRenderBitmap = false means we load using FT_LOAD_NO_SCALE
// This won't work for "tricky" font faces that render glyphs using composites.
@@ -2024,7 +2024,7 @@ unsigned int ON_FreeTypeGetGlyphMetrics(
if ( nullptr == face->glyph)
return 0;
-
+
// Because ft_load_flags includes FT_LOAD_NO_SCALE, the
// face->glyph->metrics units are expressed in font design units.
glyph_metrics_in_font_design_units = ON_TextBox_CreateFromFreeTypeGlyphMetrics(&face->glyph->metrics);
@@ -2079,7 +2079,7 @@ bool ON_FreeTypeGetGlyphOutline(
if (glyph_index <= 0)
return false;
}
-
+
FT_Face ft_face = (FT_Face)(ON_Font::FreeTypeFace(font));
if (nullptr == ft_face)
return false;
@@ -2133,10 +2133,10 @@ void ON_Font::DumpFreeTypeFace(
s = face->family_name;
text_log.Print("Family name = %ls\n", static_cast(s));
-
+
s = face->style_name;
text_log.Print("Style name = %ls\n", static_cast(s));
-
+
FT_Long style_mask = 0xFFFF;
s = ON_FreeType::StyleFlagsToString(face->style_flags);
if ( 0 != (style_mask&face->style_flags) || s.IsNotEmpty() )
@@ -2171,7 +2171,7 @@ void ON_Font::DumpFreeTypeFace(
}
}
text_log.PopIndent();
-
+
text_log.PopIndent();
return;
diff --git a/opennurbs_freetype_include.h b/opennurbs_freetype_include.h
index 78bb8f95..b976a2ab 100644
--- a/opennurbs_freetype_include.h
+++ b/opennurbs_freetype_include.h
@@ -7,7 +7,7 @@
// 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 .
//
////////////////////////////////////////////////////////////////
@@ -17,18 +17,18 @@
// FreeType Licensing:
//
-//// Retrieved March 22, 2017
+//// Retrieved March 22, 2017
//// https://www.freetype.org/freetype2/docs/index.html
////What is FreeType?
////
-////FreeType is a software font engine that is designed to be small, efficient,
-////highly customizable, and portable while capable of producing high-quality
+////FreeType is a software font engine that is designed to be small, efficient,
+////highly customizable, and portable while capable of producing high-quality
////output (glyph images). It can be used in graphics libraries, display servers,
////font conversion tools, text image generation tools, and many other products as well.
////
-////Note that FreeType is a font service and doesn't provide APIs to perform
-////higher-level features like text layout or graphics processing
-////(e.g., colored text rendering, hollowing, etc.). However, it greatly
+////Note that FreeType is a font service and doesn't provide APIs to perform
+////higher-level features like text layout or graphics processing
+////(e.g., colored text rendering, ‘hollowing’, etc.). However, it greatly
////simplifies these tasks by providing a simple, easy to use, and uniform
////interface to access the content of font files.
////
@@ -36,12 +36,12 @@
////FreeType License and the GNU Public License, Version 2. It can thus
////be used by any kind of projects, be they proprietary or not.
////
-////Please note that FreeType is also called FreeType 2, to
-////distinguish it from the old, deprecated FreeType 1 library,
+////Please note that ‘FreeType’ is also called ‘FreeType 2’, to
+////distinguish it from the old, deprecated ‘FreeType 1’ library,
////a predecessor no longer maintained and supported.
////
//// http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT
-////
+////
//// The FreeType Project LICENSE
//// ----------------------------
////
@@ -92,7 +92,7 @@
//// encourage you to use the following text:
////
//// """
-//// Portions of this software are copyright The FreeType
+//// Portions of this software are copyright © The FreeType
//// Project (www.freetype.org). All rights reserved.
//// """
////
@@ -219,8 +219,8 @@
// This header file is not included in opennurbs.h because
// FreeType 2.6.3 has deeply nested includes and uses angle brackets
// in its include files (instead of double quotes and relative paths like opennurbs),
-// the directory ./freetype263/include must be in the "system" includes path.
-// It is not feasable or reasonable for all projects that include opennurbs.h to have the
+// the directory ./freetype263/include must be in the "system" includes path.
+// It is not feasable or reasonable for all projects that include opennurbs.h to have the
// freetype includes directory in the system includes path.
#if defined(OPENNURBS_FREETYPE_SUPPORT)
diff --git a/opennurbs_fsp.cpp b/opennurbs_fsp.cpp
index 3c31831b..30650dc4 100644
--- a/opennurbs_fsp.cpp
+++ b/opennurbs_fsp.cpp
@@ -3,7 +3,7 @@
#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
+// 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
@@ -46,7 +46,7 @@ ON_FixedSizePool& ON_FixedSizePool::operator=(ON_FixedSizePool&& src)
{
if (this != &src)
{
- Destroy();
+ Destroy();
m_first_block = src.m_first_block;
m_al_element_stack = src.m_al_element_stack;
m_al_block = src.m_al_block;
@@ -69,8 +69,8 @@ size_t ON_FixedSizePool::SizeofElement() const
return m_sizeof_element;
}
-bool ON_FixedSizePool::Create(
- size_t sizeof_element,
+bool ON_FixedSizePool::Create(
+ size_t sizeof_element,
size_t element_count_estimate,
size_t block_element_capacity
)
@@ -121,14 +121,14 @@ bool ON_FixedSizePool::Create(
// Set m_al_count = capacity of the first block.
- // If the estimated number of elements is not too big,
+ // If the estimated number of elements is not too big,
// then make the first block that size.
if ( element_count_estimate > 0 )
{
// this is the first block and it has a custom size
if ( 8*m_block_element_count >= element_count_estimate )
m_al_count = element_count_estimate;
- else
+ else
m_al_count = 8*m_block_element_count; // first block will be large
}
else
@@ -149,7 +149,7 @@ void ON_FixedSizePool::ReturnAll()
//////m_qwerty_it_element = 0;
m_al_block = m_first_block;
m_al_element_array = (void*)(((char*)m_al_block) + 2*sizeof(void*));
- m_al_count = BlockElementCapacity(m_first_block);
+ m_al_count = BlockElementCapacity(m_first_block);
m_active_element_count = 0;
m_total_element_count = 0;
}
@@ -180,7 +180,7 @@ size_t ON_FixedSizePool::TotalElementCount() const
void* ON_FixedSizePool::AllocateDirtyElement()
{
- void* p;
+ void* p;
if ( 0 != m_al_element_stack )
{
@@ -226,7 +226,7 @@ void* ON_FixedSizePool::AllocateDirtyElement()
m_first_block = p;
// If the call to Create() specified a positive element_count_estimate,
// then m_sizeof_block needs to be reset for any future block allocations.
-
+
}
else
{
@@ -279,7 +279,7 @@ bool ON_FixedSizePool::IsValid() const
const bool bBlockIsAlBlock = (block == m_al_block);
capacity = BlockElementCapacity(block);
- count
+ count
= bSkipCcountCheck
? 0xFFFFFFFF :
BlockElementCount(block);
@@ -336,7 +336,7 @@ bool ON_FixedSizePool::IsValid() const
sizeof_block_allocated = sizeof_block_total;
block_element_count = block_element_capacity;
}
-
+
total_element_count += block_element_count;
if (total_element_count > (size_t)m_total_element_count)
{
@@ -382,7 +382,7 @@ void ON_FixedSizePool::ReturnElement(void* p)
if ( m_active_element_count <= 0 )
{
// If you get this error, something is seriously wrong.
- // You may be returning the same element multiple times or
+ // You may be returning the same element multiple times or
// you may be returning pointers that are not from this pool.
// In any case, you're probably going to be crashing sometime soon.
ON_ERROR("ON_FixedSizePool::ReturnElement - no active elements exist.");
@@ -571,7 +571,7 @@ size_t ON_FixedSizePool::BlockElementCount( const void* block ) const
if ( 0 == block || m_sizeof_element <= 0 )
return 0;
- const char* block_end
+ const char* block_end
= (block == m_al_block && m_al_count > 0)
? ((const char*)m_al_element_array)
: *((const char**)(((const char*)block)+sizeof(void*)));
@@ -602,7 +602,7 @@ void* ON_FixedSizePoolIterator::FirstBlock( size_t* block_element_count )
void* ON_FixedSizePoolIterator::NextBlock( size_t* block_element_count )
{
- if ( 0 != m_it_block
+ if ( 0 != m_it_block
&& m_it_block != m_fsp->m_al_block
&& m_it_element == (void*)(((char*)m_it_block)+2*sizeof(void*)) )
{
@@ -970,4 +970,3 @@ unsigned int ON_FixedSizePool::ResetElementId(
return id;
}
-
diff --git a/opennurbs_fsp.h b/opennurbs_fsp.h
index 65cce0f2..a9200681 100644
--- a/opennurbs_fsp.h
+++ b/opennurbs_fsp.h
@@ -16,6 +16,20 @@
#if !defined(OPENNURBS_FSP_INC_)
#define OPENNURBS_FSP_INC_
+class ON_CLASS ON_FixedSizePoolElement
+{
+private:
+ // ON_FixedSizePoolElement is never instantiated
+ ON_FixedSizePoolElement() = delete;
+ ~ON_FixedSizePoolElement() = delete;
+ ON_FixedSizePoolElement(const ON_FixedSizePoolElement&) = delete;
+ ON_FixedSizePoolElement operator=(const ON_FixedSizePoolElement&) = delete;
+
+public:
+ // next element - intentionally not initialized because instantiation is not permitted.
+ ON_FixedSizePoolElement* m_next;
+};
+
class ON_CLASS ON_FixedSizePool
{
public:
@@ -288,7 +302,18 @@ private:
unsigned int m_active_element_count = 0; // number of active elements
unsigned int m_total_element_count = 0; // total number of elements (active + returned)
+
+private:
+ // Used by The ThreadSafe...() functions and for expert users
+ // to use when managing memory controlled by this pool. Best
+ // to ingnore this unless you have a very clear idea of what
+ // you are doing, why you are doing it, and when you are doing it.
+ // Otherwise, you'll find yourself waiting forever on a nested
+ // access request.
+ friend class ON_SleepLockGuard;
ON_SleepLock m_sleep_lock;
+
+private:
unsigned int m_reserved0 = 0;
diff --git a/opennurbs_hatch.cpp b/opennurbs_hatch.cpp
index 145107a0..8b2fcf79 100644
--- a/opennurbs_hatch.cpp
+++ b/opennurbs_hatch.cpp
@@ -605,7 +605,7 @@ ON_HatchPattern::HatchFillType ON_HatchPattern::HatchFillTypeFromUnsigned(
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Solid);
ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Lines);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Gradient);
+ //ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Gradient);
}
ON_ERROR("Invalid hatch_fill_type_as_unsigned value.");
return ON_HatchPattern::HatchFillType::Solid;
@@ -721,9 +721,9 @@ void ON_HatchPattern::Dump( ON_TextLog& dump) const
case ON_HatchPattern::HatchFillType::Lines:
dump.Print( "fill type: Lines");
break;
- case ON_HatchPattern::HatchFillType::Gradient:
- dump.Print( "fill type: Gradient");
- break;
+ //case ON_HatchPattern::HatchFillType::Gradient:
+ // dump.Print( "fill type: Gradient");
+ // break;
}
dump.Print( "\n");
@@ -1484,6 +1484,7 @@ void ON_Hatch::Dump( ON_TextLog& dump) const
dump.Print("Pattern scale: %g\n", PatternScale());
ON_3dPoint p = this->BasePoint();
dump.Print("Base point: %g, %g, %g\n", p.x, p.y, p.z);
+ dump.Print("2d base point: %g, %g\n", m_basepoint.x, m_basepoint.y);
dump.Print("Plane origin: %g, %g, %g\n", m_plane.origin.x, m_plane.origin.y, m_plane.origin.z);
dump.Print("Plane x axis: %g, %g, %g\n", m_plane.xaxis.x, m_plane.xaxis.y, m_plane.xaxis.z);
dump.Print("Plane y axis: %g, %g, %g\n", m_plane.yaxis.x, m_plane.yaxis.y, m_plane.yaxis.z);
@@ -1711,15 +1712,20 @@ double arbaxisRotation(const ON_Plane& plane)
static void UnrotateHatch(ON_Hatch* hatch)
{
double a = arbaxisRotation(hatch->Plane());
+
ON_Plane& plane = *(ON_Plane*)(&hatch->Plane());
if(fabs(a) > ON_ZERO_TOLERANCE)
{
+ ON_2dPoint base2 = hatch->BasePoint2d();
plane.Rotate(-a, plane.zaxis);
for(int i = 0; i < hatch->LoopCount(); i++)
{
ON_Curve* pC = (ON_Curve*)hatch->Loop(i)->Curve();
pC->Rotate(a, ON_3dVector::ZAxis, ON_3dPoint::Origin);
}
+ base2.Rotate(a, ON_2dPoint::Origin);
+ hatch->SetBasePoint(base2);
+
//hatch->SetPatternRotation(hatch->PatternRotation()+a);
}
// Project world origin to hatch plane and set hatch plane origin to the result
@@ -1735,11 +1741,16 @@ static void UnrotateHatch(ON_Hatch* hatch)
ON_Curve* pC = (ON_Curve*)hatch->Loop(i)->Curve();
pC->Translate(V);
}
+
+ ON_2dPoint base2 = hatch->BasePoint2d();
+ base2 = base2 + ON_2dVector(-P.x, -P.y);
+ hatch->SetBasePoint(base2);
+
P = plane.PointAt(P.x, P.y);
plane.origin = P;
}
}
-
+
bool ON_Hatch::Transform( const ON_Xform& xform)
{
if( fabs( fabs( xform.Determinant()) - 1.0) > 1.0e-4)
@@ -1761,7 +1772,11 @@ bool ON_Hatch::Transform( const ON_Xform& xform)
for( int i = 0; i < LoopCount(); i++)
m_loops[i]->m_p2dCurve->Transform( T);
}
- int rc = m_plane.Transform( xform);
+
+ ON_3dPoint base = m_plane.PointAt(m_basepoint.x, m_basepoint.y);
+ base.Transform(xform);
+ int rc = m_plane.Transform(xform);
+ SetBasePoint(base);
//ON_3dVector x = m_plane.xaxis;
//x.Transform(xform);
@@ -2043,3 +2058,357 @@ ON_CurveRegionBoundaryElement& ON_CurveRegionBoundaryElement::operator=(const ON
}
+#if defined(OPENNURBS_GRADIENT_WIP)
+
+
+class ON_CLASS ON_GradientColorData : public ON_UserData
+{
+ ON_OBJECT_DECLARE(ON_GradientColorData);
+public:
+ ON_GradientColorData();
+ ~ON_GradientColorData() = default;
+
+ ON_GradientColorData(const ON_GradientColorData&) = default;
+ ON_GradientColorData& operator=(const ON_GradientColorData&) = default;
+
+ //ON_GradientColorData(const ON_GradientColorData&);
+ //ON_GradientColorData& operator=(const ON_GradientColorData&);
+
+ static ON_GradientColorData* FromObject( const ON_Object* );
+ static ON_GradientColorData* FromObject(ON_Object* obj, bool createAndAttachIfMissing);
+
+ // override virtual ON_Object::Dump function
+ void Dump(ON_TextLog& text_log) const 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::Write function
+ bool Write(ON_BinaryArchive& binary_archive) const override;
+
+ // override virtual ON_Object::Read function
+ bool Read(ON_BinaryArchive& binary_archive) override;
+
+ // override virtual ON_UserData::GetDescription function
+ bool GetDescription(ON_wString& description) 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_UserData::Transform function
+ bool Transform(const ON_Xform&) override;
+
+ ON_GradientType m_gradient_type = ON_GradientType::Linear;
+ ON_3dPoint m_start = ON_3dPoint::UnsetPoint;
+ ON_3dPoint m_end = ON_3dPoint::UnsetPoint;
+ double m_repeat = 0;
+ ON_SimpleArray m_colors;
+
+//private:
+ //void Internal_CopyFrom(const ON_GradientColorData&);
+};
+
+ON_OBJECT_IMPLEMENT(ON_GradientColorData, ON_UserData, "0C1AD613-4EFA-4F47-A147-4D79D77FCB0C");
+
+ON_GradientColorData::ON_GradientColorData()
+{
+ m_userdata_uuid = ON_CLASS_ID(ON_GradientColorData);
+ m_application_uuid = ON_opennurbs6_id;
+ m_userdata_copycount = 1;
+}
+
+//void ON_GradientColorData::Internal_CopyFrom(const ON_GradientColorData & src)
+//{
+// m_gradient_type = src.m_gradient_type;
+// m_start = src.m_start;
+// m_end = src.m_end;
+// m_repeat = src.m_repeat;
+// m_colors = src.m_colors;
+//}
+//
+//ON_GradientColorData::ON_GradientColorData(const ON_GradientColorData &src)
+// : ON_UserData(src)
+//{
+// Internal_CopyFrom(src);
+//}
+
+ON_GradientColorData* ON_GradientColorData::FromObject(const ON_Object* p)
+{
+ return p
+ ? ON_GradientColorData::Cast(p->GetUserData(ON_CLASS_ID(ON_GradientColorData)))
+ : nullptr;
+}
+
+//ON_GradientColorData & ON_GradientColorData::operator=(const ON_GradientColorData & src)
+//{
+// if (this != &src)
+// {
+// ON_UserData::operator=(src);
+// Internal_CopyFrom(src);
+// }
+// return *this;
+//}
+
+ON_GradientColorData* ON_GradientColorData::FromObject(ON_Object* obj, bool createAndAttachIfMissing)
+{
+ if (nullptr == obj)
+ return nullptr;
+ ON_GradientColorData* rc = ON_GradientColorData::Cast(obj->GetUserData(ON_CLASS_ID(ON_GradientColorData)));
+ if (nullptr == rc && createAndAttachIfMissing)
+ {
+ rc = new ON_GradientColorData();
+ if (!obj->AttachUserData(rc))
+ {
+ delete rc;
+ return nullptr;
+ }
+ }
+ return rc;
+}
+
+bool ON_GradientColorData::GetDescription(ON_wString& description)
+{
+ description = L"Color Gradient UserData";
+ return true;
+}
+
+bool ON_GradientColorData::WriteToArchive(const ON_BinaryArchive & archive, const ON_Object * parent_object) const
+{
+ return (archive.Archive3dmVersion() >= 60);
+}
+
+unsigned int ON_GradientColorData::SizeOf() const
+{
+ unsigned int sz = ON_UserData::SizeOf();
+ sz += sizeof(*this) - sizeof(ON_UserData);
+ sz += m_colors.SizeOfArray();
+ return sz;
+}
+
+ON__UINT32 ON_GradientColorData::DataCRC(ON__UINT32 current_remainder) const
+{
+ current_remainder = ON_CRC32(current_remainder, sizeof(m_gradient_type), &m_gradient_type);
+ current_remainder = m_start.DataCRC(current_remainder);
+ current_remainder = m_end.DataCRC(current_remainder);
+ current_remainder = ON_CRC32(current_remainder, sizeof(m_repeat), &m_repeat);
+ current_remainder = m_colors.DataCRC(current_remainder);
+ return current_remainder;
+}
+
+void ON_GradientColorData::Dump(ON_TextLog& text_log) const
+{
+ switch (m_gradient_type)
+ {
+ case ON_GradientType::None:
+ text_log.Print("None gradient\n");
+ break;
+ case ON_GradientType::Linear:
+ text_log.Print("Linear gradient\n");
+ break;
+ case ON_GradientType::Radial:
+ text_log.Print("Radial gradient\n");
+ break;
+ case ON_GradientType::LinearDisabled:
+ text_log.Print("Linear(disabled) gradient\n");
+ break;
+ case ON_GradientType::RadialDisabled:
+ text_log.Print(L"Radial(disabled) gradient\n");
+ break;
+ default:
+ break;
+ }
+
+ text_log.PushIndent();
+ text_log.Print("points ");
+ text_log.Print(m_start);
+ text_log.Print("-");
+ text_log.Print(m_end);
+ text_log.Print("\nrepeat %d\n", m_repeat);
+ text_log.PopIndent();
+}
+
+bool ON_GradientColorData::Write(ON_BinaryArchive& archive) const
+{
+ bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0);
+ if (!rc)
+ return false;
+
+ for (;;)
+ {
+ rc = archive.WriteInt((int)m_gradient_type);
+ if (!rc) break;
+ rc = archive.WritePoint(m_start);
+ if (!rc) break;
+ rc = archive.WritePoint(m_end);
+ if (!rc) break;
+ rc = archive.WriteDouble(m_repeat);
+ if (!rc) break;
+
+ int count = m_colors.Count();
+ rc = archive.WriteInt(count);
+ if (!rc) break;
+
+ for (int i = 0; i < count && rc; i++)
+ {
+ rc = m_colors[i].Write(archive);
+ if (!rc) break;
+ }
+ if (!rc) break;
+
+ break;
+ }
+
+ if (!archive.EndWrite3dmChunk())
+ rc = false;
+
+ return rc;
+}
+
+bool ON_GradientColorData::Read(ON_BinaryArchive& archive)
+{
+ m_colors.SetCount(0);
+
+ 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;
+
+ int gt = (int)ON_GradientType::None;
+ rc = archive.ReadInt(>);
+ if (gt < (int)ON_GradientType::None || gt >(int)ON_GradientType::RadialDisabled)
+ rc = false;
+ if (!rc) break;
+ m_gradient_type = (ON_GradientType)gt;
+
+ rc = archive.ReadPoint(m_start);
+ if (!rc) break;
+ rc = archive.ReadPoint(m_end);
+ if (!rc) break;
+ rc = archive.ReadDouble(&m_repeat);
+ if (!rc) break;
+ int count = 0;
+ rc = archive.ReadInt(&count);
+ if (!rc) break;
+
+ m_colors.Reserve(count);
+ for (int i = 0; i < count && rc; i++)
+ {
+ ON_ColorStop cs;
+ rc = cs.Read(archive);
+ if (!rc) break;
+
+ m_colors.Append(cs);
+ }
+ if (!rc) break;
+
+ break;
+ }
+
+ if (!archive.EndRead3dmChunk())
+ rc = false;
+
+ return rc;
+}
+
+bool ON_GradientColorData::Transform(const ON_Xform& xform)
+{
+ m_start.Transform(xform);
+ m_end.Transform(xform);
+ return ON_UserData::Transform(xform);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+ON_GradientType ON_Hatch::GetGradientType() const
+{
+ ON_GradientColorData* data = ON_GradientColorData::FromObject(this);
+ if (data)
+ return data->m_gradient_type;
+ return ON_GradientType::None;
+}
+
+void ON_Hatch::SetGradientType(ON_GradientType gt)
+{
+ ON_GradientColorData* data = ON_GradientColorData::FromObject(this);
+ if (nullptr == data && ON_GradientType::None == gt)
+ return;
+
+ data = ON_GradientColorData::FromObject(this, true);
+ if (data)
+ data->m_gradient_type = gt;
+}
+
+void ON_Hatch::GetGradientColors(ON_SimpleArray& colors) const
+{
+ colors.Empty();
+ ON_GradientColorData* data = ON_GradientColorData::FromObject(this);
+ if (data)
+ colors = data->m_colors;
+}
+bool ON_Hatch::SetGradientColors(const ON_SimpleArray& colors)
+{
+ ON_GradientColorData* data = ON_GradientColorData::FromObject(this, true);
+ if (nullptr == data)
+ return false;
+ data->m_colors = colors;
+ return true;
+}
+
+double ON_Hatch::GetGradientRepeat() const
+{
+ ON_GradientColorData* data = ON_GradientColorData::FromObject(this);
+ if (data)
+ return data->m_repeat;
+ return 0;
+}
+bool ON_Hatch::SetGradientRepeat(double repeat)
+{
+ ON_GradientColorData* data = ON_GradientColorData::FromObject(this);
+ if (nullptr == data && 0 == repeat)
+ return true;
+
+ data = ON_GradientColorData::FromObject(this, true);
+ if (nullptr == data)
+ return false;
+
+ data->m_repeat = repeat;
+ return true;
+}
+
+void ON_Hatch::GetGradientEndPoints(ON_3dPoint& startPoint, ON_3dPoint& endPoint) const
+{
+ ON_GradientColorData* data = ON_GradientColorData::FromObject(this);
+ if (nullptr == data)
+ {
+ startPoint = ON_3dPoint::UnsetPoint;
+ endPoint = ON_3dPoint::UnsetPoint;
+ return;
+ }
+
+ startPoint = data->m_start;
+ endPoint = data->m_end;
+}
+bool ON_Hatch::SetGradientEndPoints(ON_3dPoint startpoint, ON_3dPoint endpoint)
+{
+ ON_GradientColorData* data = ON_GradientColorData::FromObject(this, true);
+ if (nullptr == data)
+ return false;
+
+ data->m_start = startpoint;
+ data->m_end = endpoint;
+ return true;
+}
+
+#endif
diff --git a/opennurbs_hatch.h b/opennurbs_hatch.h
index 76065fbe..bbf34f59 100644
--- a/opennurbs_hatch.h
+++ b/opennurbs_hatch.h
@@ -397,7 +397,7 @@ public:
{
Solid = 0, // uses entity color
Lines = 1, // pat file definition
- Gradient = 2, // uses a fill color function
+ //Gradient = 2, // uses a fill color function
};
static ON_HatchPattern::HatchFillType HatchFillTypeFromUnsigned(
@@ -883,6 +883,64 @@ public:
*/
bool ReplaceLoops(ON_SimpleArray& loops);
+#if defined(OPENNURBS_GRADIENT_WIP)
+ /*
+ Description:
+ Returns gradient fill type for this hatch
+ */
+ ON_GradientType GetGradientType() const;
+
+ /*
+ Description:
+ Set the gradient fill type for this hatch
+ */
+ void SetGradientType(ON_GradientType gt);
+
+ /*
+ Description:
+ Get list of color stops used for gradient drawing.
+ */
+ void GetGradientColors(ON_SimpleArray& colors) const;
+
+ /*
+ Description:
+ Set list of color stops used for gradient drawing.
+ */
+ bool SetGradientColors(const ON_SimpleArray& colors);
+
+ /*
+ Description:
+ Get gradient repeat factor for gradient drawing.
+ > 1 repeat reflected number of times between start and end point
+ < -1 repeat wrap number of times between start and end point
+ any other value does not affect repeat on a gradient
+ */
+ double GetGradientRepeat() const;
+
+ /*
+ Description:
+ Set gradient repeat factor for gradient drawing
+ > 1 repeat reflected number of times between start and end point
+ < -1 repeat wrap number of times between start and end point
+ any other value does not affect repeat on a gradient
+ Returns:
+ True if the repeat factor was successfully set
+ */
+ bool SetGradientRepeat(double repeat);
+
+ /*
+ Description:
+ Get the start and end points for gradient drawing in 3d
+ */
+ void GetGradientEndPoints(ON_3dPoint& startPoint, ON_3dPoint& endPoint) const;
+
+ /*
+ Description:
+ Set the start and end points for gradient drawing
+ */
+ bool SetGradientEndPoints(ON_3dPoint startpoint, ON_3dPoint endPoint);
+#endif
+
private:
ON_Plane m_plane;
double m_pattern_scale = 1.0;
diff --git a/opennurbs_internal_V2_annotation.cpp b/opennurbs_internal_V2_annotation.cpp
index 22ea36c7..ec309d78 100644
--- a/opennurbs_internal_V2_annotation.cpp
+++ b/opennurbs_internal_V2_annotation.cpp
@@ -5890,7 +5890,8 @@ const wchar_t* ON_TextDot::FontFace() const
void ON_TextDot::SetFontFace( const wchar_t* font_face )
{
- SetDotText(font_face, true, m_font_face);
+ m_font_face = font_face;
+ m_font_face.TrimLeftAndRight();
}
static void SetDisplayBitsFromBool(
diff --git a/opennurbs_internal_V5_annotation.cpp b/opennurbs_internal_V5_annotation.cpp
index 06d68d52..fbb20f37 100644
--- a/opennurbs_internal_V5_annotation.cpp
+++ b/opennurbs_internal_V5_annotation.cpp
@@ -897,7 +897,6 @@ ON_Object* ON_BinaryArchive::Internal_ConvertObject(
return nullptr;
}
-#if defined(OPENNURBS_SUBD_WIP)
if (ON::object_type::mesh_object == archive_object->ObjectType())
{
const ON_Mesh* mesh = ON_Mesh::Cast(archive_object);
@@ -931,7 +930,6 @@ ON_Object* ON_BinaryArchive::Internal_ConvertObject(
return mesh;
}
}
-#endif // defined(OPENNURBS_SUBD_WIP)
// no conversion required.
return nullptr;
diff --git a/opennurbs_internal_Vx_annotation.cpp b/opennurbs_internal_Vx_annotation.cpp
index 2c7a18bd..03a999ee 100644
--- a/opennurbs_internal_Vx_annotation.cpp
+++ b/opennurbs_internal_Vx_annotation.cpp
@@ -814,17 +814,21 @@ ON_Leader* ON_Leader::CreateFromV5Leader(
V6_leader->Internal_SetDimStyleFromV5Annotation(V5_leader,annotation_context);
const ON_DimStyle& parent_dim_style = annotation_context->ParentDimStyle();
- ON_2dVector textdir;
- if (V5_leader.GetTextDirection(textdir))
- {
- ON_3dVector dir;
- dir.Set(textdir.x, textdir.y, 0.0);
- if (0.0 > dir * plane.xaxis)
- {
- // override
- V6_leader->SetLeaderTextHorizontalAlignment(&parent_dim_style, ON::TextHorizontalAlignment::Right);
- }
- }
+ //ON_2dVector textdir;
+ //if (V5_leader.GetTextDirection(textdir))
+ //{
+ // ON_3dVector dir;
+ // dir.Set(textdir.x, textdir.y, 0.0);
+ // if (0.0 > dir * plane.xaxis)
+ // {
+ // // override
+ // V6_leader->SetLeaderTextHorizontalAlignment(&parent_dim_style, ON::TextHorizontalAlignment::Right);
+ // }
+ //}
+
+ // ON::TextHorizontalAlignment::Auto behaves like all V5 leaders did
+ V6_leader->SetLeaderTextHorizontalAlignment(&parent_dim_style, ON::TextHorizontalAlignment::Auto);
+
// updates any m_overrides to be current and updates content hash.
parent_dim_style.ContentHash();
diff --git a/opennurbs_internal_glyph.h b/opennurbs_internal_glyph.h
index e2cc4bb6..107e9911 100644
--- a/opennurbs_internal_glyph.h
+++ b/opennurbs_internal_glyph.h
@@ -57,6 +57,17 @@ public:
static void Internal_GetAppleInstalledCTFonts(ON_SimpleArray& platform_font_list);
#endif
+private:
+ static void Internal_SetFakeWindowsLogfontNames(
+ ON_SimpleArray& device_list
+ );
+ static void Internal_SetFakeWindowsLogfontName(
+ const ON_Font* font,
+ const ON_wString fake_loc_logfont_name,
+ const ON_wString fake_en_logfont_name
+ );
+public:
+
// sorts nulls to end of lists
static int CompareFontPointer(ON_Font const* const* lhs, ON_Font const* const* rhs);
diff --git a/opennurbs_knot.cpp b/opennurbs_knot.cpp
index 039ae202..8f56088a 100644
--- a/opennurbs_knot.cpp
+++ b/opennurbs_knot.cpp
@@ -435,24 +435,19 @@ bool ON_IsKnotVectorUniform(
if (rc)
{
const double delta = knot[order-1] - knot[order-2];
- const double delta_tol = ON_SQRT_EPSILON*delta;
- int i0, i1;
- double d;
- if ( ON_IsKnotVectorClamped(order,cv_count,knot) )
+ rc = (0.0 != delta && delta > ON_UNSET_VALUE && delta < ON_UNSET_POSITIVE_VALUE);
+ if (rc)
{
- i0 = order;
- i1 = cv_count;
- }
- else
- {
- i0 = 1;
- i1 = ON_KnotCount(order,cv_count);
- }
- for (/*empty*/; i0 < i1 && rc; i0++ )
- {
- d = knot[i0] - knot[i0-1];
- if ( fabs(d - delta) > delta_tol )
- rc = false;
+ const int i0 = ON_IsKnotVectorClamped(order, cv_count, knot, 0) ? order : 1;
+ const int i1 = ON_IsKnotVectorClamped(order, cv_count, knot, 1) ? cv_count : ON_KnotCount(order, cv_count);
+ double k0 = knot[i0 - 1];
+ const double delta_tol = fabs(ON_SQRT_EPSILON*delta);
+ for (int i = i0; i < i1 && rc; ++i)
+ {
+ const double d = knot[i] - k0;
+ rc = fabs(d - delta) <= delta_tol;
+ k0 = knot[i];
+ }
}
}
return rc;
@@ -984,32 +979,37 @@ bool ON_GetGrevilleAbcissae( // get Greville abcissa from knots
)
{
// Grevielle abscissae for a given knot vector
- double x, t0;
- int gi, periodic_check;
-
if ( order < 2 || cv_count < order || !knot || !g )
return false;
const int g_count = (bPeriodic) ? cv_count-order+1 : cv_count;
- if (order == 2) {
+ if (order == 2)
+ {
// g[i] = knot[i] in degree 1 case
- memcpy( g, knot, g_count*sizeof(*g) );
+ for (int i = 0; i < g_count; i++)
+ g[i] = knot[i];
}
- else {
+ else
+ {
// g = (knot[i]+...+knot[i+degree-1])/degree
- t0 = knot[order-2];
- gi = 0;
- periodic_check = (bPeriodic) ? order-2 : 0;
- while (gi < g_count) {
- x = ON_GrevilleAbcissa( order, knot++ );
- if ( periodic_check ) {
- periodic_check--;
- if ( x < t0 )
- continue;
+ const double t0 = knot[order-2];
+ if (bPeriodic)
+ {
+ for (int i = 0; i < order - 1; ++i)
+ {
+ g[i] = ON_GrevilleAbcissa(order, knot + i);
+ if (g[i] >= t0)
+ {
+ knot += ((i > 0 && (t0 - g[i - 1]) < (g[i] - t0)) ? (i - 1) : i);
+ break;
+ }
}
- g[gi++] = x;
}
+ for ( int i = 0; i < g_count; ++i)
+ g[i] = ON_GrevilleAbcissa( order, knot+i );
+ if (bPeriodic && g[0] < t0)
+ g[0] = t0;
}
return true;
diff --git a/opennurbs_leader.cpp b/opennurbs_leader.cpp
index 22d74980..3d3074d3 100644
--- a/opennurbs_leader.cpp
+++ b/opennurbs_leader.cpp
@@ -159,7 +159,6 @@ bool ON_Leader::GetBBox( // returns true if successful
//return GetBBox(&ON_DimStyle::Default, 1.0, ON_3dVector::XAxis, ON_3dVector::YAxis, bbox_min, bbox_max, grow);
}
-
bool ON_Leader::GetTextXform(
const ON_Viewport* vp,
const ON_DimStyle* dimstyle,
@@ -185,21 +184,39 @@ bool ON_Leader::GetTextXform(
if (nullptr == text)
return true;
- if ( DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash() )
+ ON_2dVector tail_dir = TailDirection(dimstyle);
+ ON_3dVector view_x = nullptr == vp ? ON_3dVector::XAxis : vp->CameraX();
+ ON_3dVector view_y = nullptr == vp ? ON_3dVector::YAxis : vp->CameraY();
+ ON_Plane objectplane = Plane();
+ ON::TextHorizontalAlignment halign = dimstyle->LeaderTextHorizontalAlignment();
+ ON::TextVerticalAlignment valign = dimstyle->LeaderTextVerticalAlignment();
+ if (ON::TextHorizontalAlignment::Auto == halign)
+ {
+ double xdotx = objectplane.xaxis * view_x;
+ if (tail_dir.x < -0.00001)
+ xdotx = -xdotx;
+ if (xdotx > -0.00001)
+ halign = ON::TextHorizontalAlignment::Left;
+ else
+ halign = ON::TextHorizontalAlignment::Right;
+ }
+ ON::TextHorizontalAlignment last_halign = text->RuntimeHorizontalAlignment();
+ text->SetRuntimeHorizontalAlignment(halign);
+
+ if (last_halign != halign)
+ {
+ const_cast(text)->SetAlignment(halign, valign);
+ }
+ if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash() )
{
ON_wString rtfstr = text->RtfText();
- ON_Plane objectplane = Plane();
const_cast(text)->Create(
rtfstr, ON::AnnotationType::Leader, dimstyle,
text->TextIsWrapped(), text->FormattingRectangleWidth(), text->TextRotationRadians());
- ON::TextHorizontalAlignment halign = dimstyle->LeaderTextHorizontalAlignment();
- ON::TextVerticalAlignment valign = dimstyle->LeaderTextVerticalAlignment();
const_cast(text)->SetAlignment(halign, valign);
}
- ON_2dVector tail_dir = TailDirection(dimstyle);
-
// Find center of scaled text
double textblock_width = 0.0;
double textblock_height = 0.0;
@@ -295,9 +312,6 @@ bool ON_Leader::GetTextXform(
textcenter_xf.m_xform[0][3] = -text_center.x;
textcenter_xf.m_xform[1][3] = -text_center.y + text_shift.y;
- ON_3dVector view_x = nullptr == vp ? ON_3dVector::XAxis : vp->CameraX();
- ON_3dVector view_y = nullptr == vp ? ON_3dVector::YAxis : vp->CameraY();
-
if(ON::TextOrientation::InView == dimstyle->LeaderTextOrientation())
{
const ON_Plane& ldrplane = Plane();
diff --git a/opennurbs_line.cpp b/opennurbs_line.cpp
index dbec7c70..5e3d776b 100644
--- a/opennurbs_line.cpp
+++ b/opennurbs_line.cpp
@@ -133,7 +133,7 @@ bool ON_Line::ClosestPointTo( const ON_3dPoint& point, double *t ) const
const ON_3dVector D = Direction();
const double DoD = D.LengthSquared();
if ( DoD > 0.0 ) {
- if ( point.DistanceTo(from) <= point.DistanceTo(to) ) {
+ if ((point - from).LengthSquared() <= (point - to).LengthSquared()) {
*t = ((point - from)*D)/DoD;
}
else {
@@ -726,6 +726,11 @@ ON_3dPoint ON_Triangle::PointAt(double s1, double s2) const
return (1 - s1 - s2)* m_V[0] + s1*m_V[1] + s2*m_V[2];
}
+ON_3dPoint ON_Triangle::Centroid() const
+{
+ return PointAt(1.0/3.0, 1.0/3.0);
+}
+
bool ON_Triangle::ClosestPointTo(const ON_3dPoint & P, double * s1, double * s2) const
{
bool rc = false;
@@ -876,4 +881,3 @@ bool operator!=(const ON_Triangle & a, const ON_Triangle & b)
a.m_V[2] != b.m_V[2]);
}
-
diff --git a/opennurbs_line.h b/opennurbs_line.h
index f485f1f0..84b0cb77 100644
--- a/opennurbs_line.h
+++ b/opennurbs_line.h
@@ -41,7 +41,7 @@ public:
/*
Returns:
- True if from != to.
+ True if from != to and both from and to are valid.
*/
bool IsValid() const;
@@ -440,7 +440,6 @@ public:
// Ensure !IsDegenerate() to gaurentee meaningful result
ON_PlaneEquation PlaneEquation() const;
-
/*
Description:
Evaluate point on triangle.
@@ -456,6 +455,10 @@ public:
double s1, double s2
) const;
+ // Returns:
+ // Evaluation of PointAt(1/3.0, 1/3.0);
+ ON_3dPoint Centroid() const;
+
/*
Description:
Find the point on the triangle that is
@@ -549,7 +552,4 @@ the result will be false.
ON_DECL
bool operator!=(const ON_Triangle& a, const ON_Triangle& b);
-
-
-
#endif
diff --git a/opennurbs_material.cpp b/opennurbs_material.cpp
index 02d67b98..89cec8d1 100644
--- a/opennurbs_material.cpp
+++ b/opennurbs_material.cpp
@@ -8,7 +8,7 @@
// 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 .
//
////////////////////////////////////////////////////////////////
@@ -19,7 +19,7 @@
#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
+// 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
@@ -90,7 +90,7 @@ void
ON_Material::Dump( ON_TextLog& dump ) const
{
ON_ModelComponent::Dump(dump);
-
+
dump.Print("ambient rgb = "); dump.PrintRGB( m_ambient ); dump.Print("\n");
dump.Print("diffuse rgb = "); dump.PrintRGB( m_diffuse ); dump.Print("\n");
dump.Print("emmisive rgb = "); dump.PrintRGB( m_emission ); dump.Print("\n");
@@ -139,7 +139,7 @@ bool ON_Material::Write( ON_BinaryArchive& archive ) const
const int minor_version = 0;
if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version))
return false;
-
+
bool rc = false;
for (;;)
{
@@ -204,7 +204,7 @@ bool ON_Material::Write( ON_BinaryArchive& archive ) const
break;
if ( !archive.WriteDouble(m_refraction_glossiness))
break;
- if ( !archive.WriteDouble(m_fresnel_index_of_refraction))
+ if ( !archive.WriteDouble(m_fresnel_index_of_refraction))
break;
if ( !archive.WriteUuid(m_rdk_material_instance_id))
break;
@@ -303,14 +303,14 @@ bool ON_Material::Read( ON_BinaryArchive& archive )
break;
if ( !archive.ReadDouble(&m_refraction_glossiness))
break;
- if ( !archive.ReadDouble(&m_fresnel_index_of_refraction))
+ if ( !archive.ReadDouble(&m_fresnel_index_of_refraction))
break;
if ( !archive.ReadUuid(m_rdk_material_instance_id))
break;
if ( !archive.ReadBool(&m_bUseDiffuseTextureAlphaForObjectTransparencyTexture))
break;
- rc = true;
+ rc = true;
break;
}
if (!archive.EndRead3dmChunk())
@@ -326,7 +326,7 @@ bool ON_Material::Internal_WriteV5( ON_BinaryArchive& file ) const
// V2 or V3 file format
rc = Internal_WriteV3(file);
}
- else
+ else
{
// V4 file format
@@ -485,16 +485,16 @@ bool ON_Material::Internal_ReadV5( ON_BinaryArchive& file )
if ( rc ) rc = file.ReadColor( m_reflection );
if ( rc ) rc = file.ReadColor( m_transparent );
- if ( rc
- && file.ArchiveOpenNURBSVersion() < 200912010
- && 128 == m_transparent.Red()
+ if ( rc
+ && file.ArchiveOpenNURBSVersion() < 200912010
+ && 128 == m_transparent.Red()
&& 128 == m_transparent.Green()
&& 128 == m_transparent.Blue()
)
{
// Prior to 1 Dec 2009 the ON_Material::Defaults() set
// m_transparent to 128,128,128. This was the wrong
- // value for the default. This "hack" is here to
+ // value for the default. This "hack" is here to
// make it appear that the default was always white.
m_transparent = m_diffuse;
}
@@ -851,7 +851,7 @@ ON::object_type ON_Material::ObjectType() const
return ON::material_object;
}
-int ON_Material::FindTexture( const wchar_t* filename,
+int ON_Material::FindTexture( const wchar_t* filename,
ON_Texture::TYPE type,
int i0
) const
@@ -859,7 +859,7 @@ int ON_Material::FindTexture( const wchar_t* filename,
int i, count = m_textures.Count();
for (i = ((i0 < 0) ? 0 : (i0+1)); i < count; i++ )
{
- if ( type != m_textures[i].m_type
+ if ( type != m_textures[i].m_type
&& type != ON_Texture::TYPE::no_texture_type )
{
continue;
@@ -912,7 +912,7 @@ double ON_Material::FresnelReflectionCoefficient(
y = x + 1.0; // y = c*(g+c) + 1.0
if ( !(y != 0.0) )
break; // y is NAN or zero
-
+
y = (x - 1.0)/y; // y = (c*(g+c) - 1.0)/(c*(g+c) + 1.0)
x = 0.5*z*z*(1.0 + y*y);
@@ -922,7 +922,7 @@ double ON_Material::FresnelReflectionCoefficient(
if ( !ON_IS_FINITE(x) )
break; // x is infinity
- return x; // x is
+ return x; // x is
}
return 1.0; // error occured
@@ -1186,6 +1186,9 @@ int ON_Texture::Compare(const ON_Texture& a, const ON_Texture& b)
rc = CompareDouble(a.m_blend_RGB3, b.m_blend_RGB3);
if (rc) break;
+ rc = ((int)(a.m_bTreatAsLinear ? 1 : 0)) - ((int)(b.m_bTreatAsLinear ? 1 : 0));
+ if (rc) break;
+
break;
}
@@ -1263,6 +1266,9 @@ int ON_Texture::CompareAppearance(const ON_Texture& a, const ON_Texture& b)
rc = CompareDouble(a.m_blend_RGB3, b.m_blend_RGB3);
if (rc) break;
+ rc = ((int)(a.m_bTreatAsLinear ? 1 : 0)) - ((int)(b.m_bTreatAsLinear ? 1 : 0));
+ if (rc) break;
+
break;
}
@@ -1299,12 +1305,82 @@ int ON_Material::CompareNameAndIds( const ON_Material& a, const ON_Material& b )
rc = ON_UuidCompare(a.m_textures[i].m_texture_id, b.m_textures[i].m_texture_id);
}
- return rc;
+ return rc;
}
int ON_Material::CompareColorAttributes( const ON_Material& a, const ON_Material& b )
{
+ const auto a_pbr = a.PhysicallyBased();
+ const auto b_pbr = b.PhysicallyBased();
+ if (a_pbr.Supported() && !b_pbr.Supported())
+ return -1;
+ if (!a_pbr.Supported() && b_pbr.Supported())
+ return 1;
+
+ if (a_pbr.Supported() && b_pbr.Supported())
+ {
+ int rc = a_pbr.BaseColor().Compare(a_pbr.BaseColor());
+ if (rc) return rc;
+
+ rc = ((int)a_pbr.BRDF()) - ((int)b_pbr.BRDF());
+ if (rc) return rc;
+
+ rc = CompareDouble(a_pbr.Subsurface(), b_pbr.Subsurface());
+ if (0 != rc) return rc;
+
+ rc = a_pbr.SubsurfaceScatteringColor().Compare(b_pbr.SubsurfaceScatteringColor());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.SubsurfaceScatteringRadius(), b_pbr.SubsurfaceScatteringRadius());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.Metallic(), b_pbr.Metallic());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.Specular(), b_pbr.Specular());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.SpecularTint(), b_pbr.SpecularTint());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.Roughness(), b_pbr.Roughness());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.Sheen(), b_pbr.Sheen());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.SheenTint(), b_pbr.SheenTint());
+ if (0 != rc) return rc;
+
+ //rc = CompareDouble(a_pbr.ReflectiveIOR(), b_pbr.ReflectiveIOR());
+ //if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.Anisotropic(), b_pbr.Anisotropic());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.AnisotropicRotation(), b_pbr.AnisotropicRotation());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.Clearcoat(), b_pbr.Clearcoat());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.ClearcoatRoughness(), b_pbr.ClearcoatRoughness());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.Opacity(), b_pbr.Opacity());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.OpacityIOR(), b_pbr.OpacityIOR());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.OpacityRoughness(), b_pbr.OpacityRoughness());
+ if (0 != rc) return rc;
+
+ rc = a_pbr.Emission().Compare(b_pbr.Emission());
+ return rc;
+ }
+
int rc = a.m_ambient.Compare(b.m_ambient);
if (rc) return rc;
@@ -1328,39 +1404,76 @@ int ON_Material::CompareColorAttributes( const ON_Material& a, const ON_Material
rc = ((int)a.m_bDisableLighting) - ((int)b.m_bDisableLighting);
- return rc;
+ return rc;
}
-int ON_Material::CompareReflectionAttributes( const ON_Material& a, const ON_Material& b )
+int ON_Material::CompareReflectionAttributes(const ON_Material& a, const ON_Material& b)
{
- int rc = a.m_reflection.Compare( b.m_reflection );
- if (0 != rc) return rc;
+ const auto a_pbr = a.PhysicallyBased();
+ const auto b_pbr = b.PhysicallyBased();
+ if (a_pbr.Supported() && !b_pbr.Supported())
+ return -1;
+ if (!a_pbr.Supported() && b_pbr.Supported())
+ return 1;
- rc = CompareDouble(a.m_index_of_refraction,b.m_index_of_refraction);
- if (0 != rc) return rc;
-
- rc = CompareDouble(a.m_reflectivity,b.m_reflectivity);
- if (0 != rc) return rc;
-
- rc = CompareDouble(a.m_shine,b.m_shine);
- if (0 != rc) return rc;
-
- rc = (a.m_bFresnelReflections?1:0) - (b.m_bFresnelReflections?1:0);
- if (0 != rc) return rc;
- if ( a.m_bFresnelReflections )
- {
- rc = CompareDouble(a.m_fresnel_index_of_refraction,b.m_fresnel_index_of_refraction);
+ if (a_pbr.Supported() && b_pbr.Supported())
+ {
+ int rc = CompareDouble(a_pbr.Metallic(), b_pbr.Metallic());
if (0 != rc) return rc;
- }
- rc = CompareDouble(a.m_reflection_glossiness,b.m_reflection_glossiness);
- if (0 != rc) return rc;
+ rc = CompareDouble(a_pbr.Specular(), b_pbr.Specular());
+ if (0 != rc) return rc;
- rc = CompareDouble(a.m_refraction_glossiness,b.m_refraction_glossiness);
+ rc = CompareDouble(a_pbr.SpecularTint(), b_pbr.SpecularTint());
+ if (0 != rc) return rc;
+ rc = CompareDouble(a_pbr.Roughness(), b_pbr.Roughness());
+ if (0 != rc) return rc;
- return rc;
+ //rc = CompareDouble(a_pbr.ReflectiveIOR(), b_pbr.ReflectiveIOR());
+ //if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.Anisotropic(), b_pbr.Anisotropic());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.AnisotropicRotation(), b_pbr.AnisotropicRotation());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.Clearcoat(), b_pbr.Clearcoat());
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a_pbr.ClearcoatRoughness(), b_pbr.ClearcoatRoughness());
+
+ return rc;
+ }
+
+ int rc = a.m_reflection.Compare(b.m_reflection);
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a.m_index_of_refraction, b.m_index_of_refraction);
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a.m_reflectivity, b.m_reflectivity);
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a.m_shine, b.m_shine);
+ if (0 != rc) return rc;
+
+ rc = (a.m_bFresnelReflections ? 1 : 0) - (b.m_bFresnelReflections ? 1 : 0);
+ if (0 != rc) return rc;
+ if (a.m_bFresnelReflections)
+ {
+ rc = CompareDouble(a.m_fresnel_index_of_refraction, b.m_fresnel_index_of_refraction);
+ if (0 != rc) return rc;
+ }
+
+ rc = CompareDouble(a.m_reflection_glossiness, b.m_reflection_glossiness);
+ if (0 != rc) return rc;
+
+ rc = CompareDouble(a.m_refraction_glossiness, b.m_refraction_glossiness);
+
+ return rc;
}
int ON_Material::CompareTextureAttributes(const ON_Material& a, const ON_Material& b)
@@ -1473,11 +1586,11 @@ const ON_UUID ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_class_id_ctor
const ON_UUID ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_userdata_uuid_ctor =
{ 0xb63ed079, 0xcf67, 0x416c, { 0x80, 0xd, 0x22, 0x2, 0x3a, 0xe1, 0xbe, 0x21 } };
-// CRhRdkUserData::m_application_uuid value
+// CRhRdkUserData::m_application_uuid value
// = CRhRdkRhinoPlugIn::RhinoPlugInUuid()
// = CRhRdkRhinoPlugIn::m_uuidRhinoPlugIn
// = 16592D58-4A2F-401D-BF5E-3B87741C1B1B
-// From
+// From
// file: http://subversion.mcneel.com/rhino/6/trunk/src4/rhino4/Plug-ins/RDK/RDK/RhRcmRhinoPlugIn.cpp
// revision: 102101
//
@@ -1561,7 +1674,7 @@ bool ON_RdkMaterialInstanceIdObsoleteUserData::DeleteAfterRead(
ON_Material* mat = ON_Material::Cast(parent_object);
if (mat && mat->RdkMaterialInstanceIdIsNil())
mat->SetRdkMaterialInstanceId(m_rdk_material_instance_id);
-
+
// Returning true will cause the ON_BinaryArchive::ReadObject()
// plumbing to delete this user data.
return true;
@@ -1606,11 +1719,11 @@ static const char* ParsePast(const char* token, const char* s)
{
if (0 == token || token[0] <= 32)
return 0;
-
+
s = ParsePastWhiteSpace(s);
if (0 == s || s[0] <= 32)
return 0;
-
+
for (/*empty init*/; ToUpper(*s) == ToUpper(*token); s++, token++)
{
if (0 == *s)
@@ -1644,7 +1757,7 @@ bool ON_RdkMaterialInstanceIdObsoleteUserData::Read(
rc = true;
break;
}
-
+
ON_String str((char)0, (int)s_length);
if (str.Length() < s_length)
break;
@@ -1696,7 +1809,7 @@ bool ON_RdkMaterialInstanceIdObsoleteUserData::Write(
"\n"
"\n"
"\n"
"";
@@ -1820,6 +1933,10 @@ ON_Color ON_Material::PreviewColor(void) const
bool ON_Material::UseDiffuseTextureAlphaForObjectTransparencyTexture() const
{
+ //Physically based materials do not support alpha transparency (at the moment).
+ if (PhysicallyBased().Supported())
+ return false;
+
return m_bUseDiffuseTextureAlphaForObjectTransparencyTexture;
}
@@ -1833,7 +1950,7 @@ void ON_Material::SetUseDiffuseTextureAlphaForObjectTransparencyTexture(
IncrementContentVersionNumber();
}
}
-
+
bool ON_Material::FresnelReflections() const
{
return m_bFresnelReflections;
@@ -1892,9 +2009,9 @@ bool ON_Texture::Write(
) const
{
const int major_version = 1;
- const int minor_version
+ const int minor_version
= binary_archive.Archive3dmVersion() >= 60
- ? 1
+ ? (binary_archive.Archive3dmVersion() >= 70 ? 2 : 1)
: 0;
bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version);
@@ -1964,11 +2081,18 @@ bool ON_Texture::Write(
if ( minor_version <= 0 )
break;
-
+
// version 1.1 added m_image_file_reference
rc = m_image_file_reference.Write(true,binary_archive);
if (!rc) break;
+ if ( minor_version <= 1 )
+ break;
+
+ // version 1.2 added m_bTreatAsLinear
+ rc = binary_archive.WriteBool(m_bTreatAsLinear);
+ if (!rc) break;
+
break;
}
@@ -2050,6 +2174,7 @@ const ON_SHA1_Hash ON_Texture::ContentHash() const
sha1.AccumulateDouble(m_blend_RGB2);
sha1.AccumulateDouble(m_blend_RGB3);
sha1.AccumulateInteger32(m_blend_order);
+ sha1.AccumulateBool(m_bTreatAsLinear);
return sha1.Hash();
}
@@ -2076,15 +2201,34 @@ void ON_Texture::SetMappingChannel(
m_mapping_channel_id = mapping_channel_id;
}
-ON_Texture::TYPE ON_Texture::TypeFromUnsigned( unsigned int type_as_unsigned )
+ON_Texture::TYPE ON_Texture::TypeFromUnsigned(unsigned int type_as_unsigned)
{
- switch(type_as_unsigned)
+ switch (type_as_unsigned)
{
- ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::no_texture_type );
- ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::bitmap_texture);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::bump_texture);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::emap_texture);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::transparency_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::no_texture_type);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::diffuse_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::bump_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::emap_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::opacity_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_subsurface_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_subsurface_scattering_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_subsurface_scattering_radius_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_metallic_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_specular_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_specular_tint_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_roughness_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_anisotropic_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_anisotropic_rotation_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_sheen_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_sheen_tint_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_roughness_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_bump_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_opacity_ior_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_opacity_roughness_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_emission_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_ambient_occlusion_texture);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_displacement_texture);
}
ON_ERROR("Invalid type_as_unsigned value.");
@@ -2242,6 +2386,12 @@ bool ON_Texture::Read(
rc = m_image_file_reference.Read(binary_archive);
if (!rc) break;
+ if (minor_version <= 1)
+ break;
+
+ rc = binary_archive.ReadBool(&m_bTreatAsLinear);
+ if (!rc) break;
+
break;
}
}
@@ -2343,7 +2493,7 @@ ON_TextureMapping::TEXTURE_SPACE ON_TextureMapping::TextureSpaceFromUnsigned(
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TEXTURE_SPACE::single);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TEXTURE_SPACE::divided);
}
-
+
ON_ERROR("Invalid texture_space_as_unsigned value.");
return ON_TextureMapping::TEXTURE_SPACE::single;
}
@@ -2355,7 +2505,7 @@ const ON_wString ON_TextureMapping::SpaceToString(ON_TextureMapping::TEXTURE_SPA
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TEXTURE_SPACE::single);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TEXTURE_SPACE::divided);
}
-
+
ON_ERROR("Invalid texture_mapping_space value.");
return ON_wString::EmptyString;
}
@@ -2484,11 +2634,11 @@ ON_Xform ON_Texture::GetPictureShrinkSurfaceTransformation(
const ON_Xform* error_return
)
{
- const class ON_Surface* original_srf
+ const class ON_Surface* original_srf
= (nullptr != original && 1 == original->m_F.Count())
? original->m_F[0].SurfaceOf()
: nullptr;
- const class ON_Surface* shrunk_srf
+ const class ON_Surface* shrunk_srf
= (nullptr != shrunk && 1 == shrunk->m_F.Count())
? shrunk->m_F[0].SurfaceOf()
: nullptr;
@@ -2506,7 +2656,7 @@ ON_Xform ON_Texture::GetPictureShrinkSurfaceTransformation(
)
{
ON_Interval original_udomain;
- ON_Interval original_vdomain;
+ ON_Interval original_vdomain;
ON_Interval shrunk_udomain;
ON_Interval shrunk_vdomain;
if (nullptr != original)
@@ -2750,7 +2900,7 @@ int IntersectBoxRayHelper(const ON_3dPoint& rst, const ON_3dVector& n, int dir,
|| Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON)
)
{
- // The ray's intersection with the plane missed the
+ // The ray's intersection with the plane missed the
// (-1,+1)x(-1,+1) square that is the side of the box.
t0 = ON_UNSET_VALUE;
}
@@ -2768,7 +2918,7 @@ int IntersectBoxRayHelper(const ON_3dPoint& rst, const ON_3dVector& n, int dir,
|| Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON)
)
{
- // The ray's intersection with the plane missed the
+ // The ray's intersection with the plane missed the
// (-1,+1)x(-1,+1) square that is the side of the box.
t1 = ON_UNSET_VALUE;
if ( ON_UNSET_VALUE == t0 )
@@ -2793,7 +2943,7 @@ int IntersectBoxRayHelper(const ON_3dPoint& rst, const ON_3dVector& n, int dir,
}
-int ON_TextureMapping::EvaluatePlaneMapping(
+int ON_TextureMapping::EvaluatePlaneMapping(
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T
@@ -2821,7 +2971,7 @@ int ON_TextureMapping::EvaluatePlaneMapping(
rst.x = 0.5*rst.x + 0.5;
rst.y = 0.5*rst.y + 0.5;
- // Apply texture coordinate transformation
+ // Apply texture coordinate transformation
*T = m_uvw*rst;
//See docs - if m_bCapped is false, then planar is truely flat.
@@ -2831,7 +2981,7 @@ int ON_TextureMapping::EvaluatePlaneMapping(
return 1;
}
-int ON_TextureMapping::EvaluateSphereMapping(
+int ON_TextureMapping::EvaluateSphereMapping(
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T
@@ -2844,15 +2994,15 @@ int ON_TextureMapping::EvaluateSphereMapping(
ON_3dPoint rst(m_Pxyz*P);
const double r = ((const ON_3dVector*)(&rst.x))->Length();
double t0, t1;
-
+
if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection )
{
ON_3dVector n(m_Nxyz*N);
- // Shoot a ray from P in the direction N and see if it
+ // Shoot a ray from P in the direction N and see if it
// hits the sphere.
- int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y+n.z*n.z),
- 2.0*(rst.x*n.x+rst.y*n.y+rst.z*n.z),
- (rst.x*rst.x+rst.y*rst.y+rst.z*rst.z) - 1.0,
+ int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y+n.z*n.z),
+ 2.0*(rst.x*n.x+rst.y*n.y+rst.z*n.z),
+ (rst.x*rst.x+rst.y*rst.y+rst.z*rst.z) - 1.0,
&t0, &t1 );
if (rc >= 0 )
{
@@ -2863,17 +3013,17 @@ int ON_TextureMapping::EvaluateSphereMapping(
rst = rst + t0*n;
}
}
-
+
// convert sphere 3d location to longitude, latitude, radius
- double longitude = (0.0 != rst.y || 0.0 != rst.x)
- ? atan2(rst.y,rst.x)
+ double longitude = (0.0 != rst.y || 0.0 != rst.x)
+ ? atan2(rst.y,rst.x)
: 0.0;
- double latitude = (0.0 != rst.z)
- ? atan2(rst.z,((const ON_2dVector*)(&rst.x))->Length())
+ double latitude = (0.0 != rst.z)
+ ? atan2(rst.z,((const ON_2dVector*)(&rst.x))->Length())
: 0.0;
if ( latitude > ON_PI )
latitude -= 2.0*ON_PI;
-
+
// convert longitude to normalized texture coordinate
rst.x = 0.5*longitude/ON_PI;
if ( rst.x < -ON_EPSILON )
@@ -2889,17 +3039,17 @@ int ON_TextureMapping::EvaluateSphereMapping(
rst.y = 0.0;
else if ( rst.y > 1.0 )
rst.y = 1.0;
-
+
// radius is already normalized
rst.z = r;
-
+
// apply texture coordinate transformation
*T = m_uvw*rst;
return 1;
}
-int ON_TextureMapping::EvaluateCylinderMapping(
+int ON_TextureMapping::EvaluateCylinderMapping(
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T
@@ -2917,17 +3067,17 @@ int ON_TextureMapping::EvaluateCylinderMapping(
double t, t0, t1;
int side0, side1;
PROJECTION mapping_proj = m_projection;
-
+
side0 = 0;
if ( ON_TextureMapping::PROJECTION::ray_projection == mapping_proj )
{
ON_3dVector n(m_Nxyz*N);
t = 0.0;
-
+
if ( m_bCapped )
{
// shoot at caps
- // The < t check prevents overflow when the
+ // The < t check prevents overflow when the
// ray is nearly parallel to the cap.
t = fabs(n.z)*on__overflow_tol;
if ( fabs(1.0+rst.z) < t && fabs(1.0-rst.z) < t )
@@ -2940,7 +3090,7 @@ int ON_TextureMapping::EvaluateCylinderMapping(
if ( fabs(1.0+Q.z) > ON_SQRT_EPSILON
|| (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON )
{
- // The ray's intersection with the bottom plane missed the
+ // The ray's intersection with the bottom plane missed the
// radius 1 disk that is the bottom of the cylinder.
side0 = 0;
}
@@ -2950,7 +3100,7 @@ int ON_TextureMapping::EvaluateCylinderMapping(
if ( fabs(1.0-Q.z) > ON_SQRT_EPSILON
|| (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON )
{
- // The ray's intersection with the top plane missed the
+ // The ray's intersection with the top plane missed the
// radius 1 disk that is the top of the cylinder.
side1 = 0;
}
@@ -2965,11 +3115,11 @@ int ON_TextureMapping::EvaluateCylinderMapping(
}
}
}
-
+
// shoot ray at the cylinder wall
- int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y),
- 2.0*(rst.x*n.x+rst.y*n.y),
- (rst.x*rst.x+rst.y*rst.y) - 1.0,
+ int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y),
+ 2.0*(rst.x*n.x+rst.y*n.y),
+ (rst.x*rst.x+rst.y*rst.y) - 1.0,
&t0, &t1 );
if (rc >= 0 )
{
@@ -2986,9 +3136,9 @@ int ON_TextureMapping::EvaluateCylinderMapping(
}
else if ( 1 != BestHitHelper(t0,t) )
{
- // The cylinder is capped and the ray hit the cap,
- // hit the infinite cylinder wall, and the wall
- // hit is "first". If the ray hits the finite
+ // The cylinder is capped and the ray hit the cap,
+ // hit the infinite cylinder wall, and the wall
+ // hit is "first". If the ray hits the finite
// cylinder wall, the I will use the wall hit.
t1 = rst.z + t0*n.z;
if ( t1 >= -(1.0+ON_SQRT_EPSILON) && t1 <= 1.0+ON_SQRT_EPSILON )
@@ -3001,14 +3151,14 @@ int ON_TextureMapping::EvaluateCylinderMapping(
}
}
}
-
+
if ( side0 > 1 )
{
// best hit is on a cap
rst = rst + t*n;
}
}
-
+
if ( m_bCapped && 0 == side0 )
{
if ( fabs(rst.z) > 1.0+ON_SQRT_EPSILON )
@@ -3030,19 +3180,19 @@ int ON_TextureMapping::EvaluateCylinderMapping(
}
}
}
-
+
if ( 2 == side0 || 3 == side0 )
{
- // The cylinder is capped and P maps to
+ // The cylinder is capped and P maps to
// the top (1 == side0) or bottom (2 == side0)
if ( 2 == side0 )
{
// This is the same convention as box mapping.
- // Put another way, if you change the mapping
+ // Put another way, if you change the mapping
// between box and cylinder, you get the same
// picture on the top and bottom.
- rst.x = -rst.x;
+ rst.x = -rst.x;
}
if ( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space )
@@ -3059,7 +3209,7 @@ int ON_TextureMapping::EvaluateCylinderMapping(
rst.y /= r;
}
-
+
// convert to normalized texture coordinates
rst.x = 0.5*rst.x + 0.5;
if ( rst.x < 0.0) rst.x = 0.0; else if (rst.x > 1.0) rst.x = 1.0;
@@ -3071,7 +3221,7 @@ int ON_TextureMapping::EvaluateCylinderMapping(
// bottom uses 4/6 <= x <= 5/6 region of the texture map.
// top uses 5/6 <= x <= 1 region of the texture map.
rst.x = (2.0 + side0 + rst.x)/6.0;
- }
+ }
}
else
{
@@ -3103,14 +3253,14 @@ int ON_TextureMapping::EvaluateCylinderMapping(
}
side0 = 1;
}
- rst.z = r;
-
+ rst.z = r;
+
*T = m_uvw*rst;
return side0;
}
-int ON_TextureMapping::EvaluateBoxMapping(
+int ON_TextureMapping::EvaluateBoxMapping(
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T
@@ -3128,7 +3278,7 @@ int ON_TextureMapping::EvaluateBoxMapping(
int side0, side1;
double t0, t1;
-
+
side0 = 0;
t0 = 0.0;
@@ -3139,10 +3289,10 @@ int ON_TextureMapping::EvaluateBoxMapping(
// 4 = front side (y=+1)
// 5 = bottom side (z=-1)
// 6 = top side (z=+1)
-
+
if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection )
{
-
+
if ( m_bCapped )
{
// intersect ray with top and bottom
@@ -3167,7 +3317,7 @@ int ON_TextureMapping::EvaluateBoxMapping(
// ray hit the box
rst = rst + t0*n;
}
- }
+ }
if ( 0 == side0 )
{
@@ -3180,14 +3330,14 @@ int ON_TextureMapping::EvaluateBoxMapping(
{
side0 = 2*side1 + 1;
}
- else
+ else
{
side0 = 2*side1 + 2;
}
-
+
//if ( fabs(t1) <= 1.0+ON_SQRT_EPSILON )...
//// The point is inside the box. If the normal
- //// is not zero, then use it to choose the side
+ //// is not zero, then use it to choose the side
//// used for the closest point projection.
side1 = ( fabs(n.x) >= fabs(n.y) ) ? 0 : 1;
@@ -3206,7 +3356,7 @@ int ON_TextureMapping::EvaluateBoxMapping(
}
double shift = 0.0;
-
+
// side flag
// 1 = left side (x=-1)
// 2 = right side (x=+1)
@@ -3217,27 +3367,27 @@ int ON_TextureMapping::EvaluateBoxMapping(
switch(side0)
{
- case 1: // x = -1
- rst.x = -rst.y;
- rst.y = rst.z;
+ case 1: // x = -1
+ rst.x = -rst.y;
+ rst.y = rst.z;
shift = 3.0;
break;
case 2: // x = +1
- rst.x = rst.y;
- rst.y = rst.z;
+ rst.x = rst.y;
+ rst.y = rst.z;
shift = 1.0;
break;
case 3: // y = -1
- rst.y = rst.z;
+ rst.y = rst.z;
shift = 0.0;
break;
case 4: // y = +1
- rst.x = -rst.x;
- rst.y = rst.z;
+ rst.x = -rst.x;
+ rst.y = rst.z;
shift = 2.0;
break;
case 5: // z = -1
- rst.x = -rst.x;
+ rst.x = -rst.x;
shift = 4.0;
break;
case 6: // z = +1
@@ -3249,14 +3399,14 @@ int ON_TextureMapping::EvaluateBoxMapping(
rst.x = 0.5*rst.x + 0.5;
rst.y = 0.5*rst.y + 0.5;
rst.z = 0.0;
-
+
if( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space)
{
rst.x = (shift + rst.x)/(m_bCapped ? 6.0 : 4.0);
}
*T = m_uvw*rst;
-
+
return side0;
}
@@ -3325,7 +3475,7 @@ int ON_TextureMapping::Evaluate(
default:
rc = EvaluatePlaneMapping(P,N,T);
break;
- }
+ }
return rc;
}
@@ -3407,8 +3557,8 @@ ON__UINT32 ON_TextureMapping::MappingCRC() const
// Should brep's render meshes be included in the crc?
// The texture that is being mapped is actually
// being applied to the brep by the render mesh's
- // m_T[] values and some users will want to see
- // the "picture" on the brep mapped to the
+ // m_T[] values and some users will want to see
+ // the "picture" on the brep mapped to the
// "picture" on the
// target.
}
@@ -3445,12 +3595,12 @@ bool ON_TextureMapping::RequiresVertexNormals() const
if ( ON_TextureMapping::TYPE::srfp_mapping == m_type )
return false;
- if(m_projection == ON_TextureMapping::PROJECTION::ray_projection)
+ if(m_projection == ON_TextureMapping::PROJECTION::ray_projection)
return true;
- if(m_type == ON_TextureMapping::TYPE::box_mapping)
+ if(m_type == ON_TextureMapping::TYPE::box_mapping)
return true;
- if(m_type == ON_TextureMapping::TYPE::cylinder_mapping && m_bCapped)
+ if(m_type == ON_TextureMapping::TYPE::cylinder_mapping && m_bCapped)
return true;
return false;
@@ -3461,7 +3611,7 @@ bool ON_TextureMapping::IsPeriodic(void) const
return (m_type == ON_TextureMapping::TYPE::sphere_mapping || m_type == ON_TextureMapping::TYPE::cylinder_mapping);
}
-bool ON_TextureMapping::HasMatchingTextureCoordinates(
+bool ON_TextureMapping::HasMatchingTextureCoordinates(
const ON_Mesh& mesh,
const ON_Xform* mesh_xform
) const
@@ -3473,7 +3623,7 @@ bool ON_TextureMapping::HasMatchingTextureCoordinates(
return rc;
}
-bool ON_TextureMapping::HasMatchingTextureCoordinates(
+bool ON_TextureMapping::HasMatchingTextureCoordinates(
const ON_MappingTag& tag,
const ON_Xform* mesh_xform
) const
@@ -3495,9 +3645,9 @@ bool ON_TextureMapping::HasMatchingTextureCoordinates(
// alwasy independent of 3d location but
// the transformations are often set.
if ( ON_TextureMapping::TYPE::srfp_mapping != m_type
- && mesh_xform
+ && mesh_xform
&& mesh_xform->IsValid()
- && !mesh_xform->IsZero()
+ && !mesh_xform->IsZero()
&& !tag.m_mesh_xform.IsZero()
)
{
@@ -3550,26 +3700,26 @@ bool GetSPTCHelper(
for ( i = 1; i < vcnt; i++ )
{
u = S[i].x;
- if (u < srf_udom.m_t[0]) srf_udom.m_t[0] = u;
- else if (u > srf_udom.m_t[1]) srf_udom.m_t[1] = u;
+ if (u < srf_udom.m_t[0]) srf_udom.m_t[0] = u;
+ else if (u > srf_udom.m_t[1]) srf_udom.m_t[1] = u;
v = S[i].y;
- if (v < srf_vdom.m_t[0]) srf_vdom.m_t[0] = v;
- else if (v > srf_vdom.m_t[1]) srf_vdom.m_t[1] = v;
+ if (v < srf_vdom.m_t[0]) srf_vdom.m_t[0] = v;
+ else if (v > srf_vdom.m_t[1]) srf_vdom.m_t[1] = v;
}
- if ( !srf_udom.IsIncreasing()
+ if ( !srf_udom.IsIncreasing()
|| !srf_vdom.IsIncreasing() )
{
return false;
}
}
- bool bHaveUVWXform = mapping.m_uvw.IsValid()
- && !mapping.m_uvw.IsIdentity()
+ bool bHaveUVWXform = mapping.m_uvw.IsValid()
+ && !mapping.m_uvw.IsIdentity()
&& !mapping.m_uvw.IsZero();
if ( mesh.HasPackedTextureRegion() )
{
- // Packed textures are not compatible with the use
+ // Packed textures are not compatible with the use
// of m_uvw. m_uvw is ignored in this block
// of code on purpose. //SEE BELOW
const ON_Interval tex_udom = mesh.m_packed_tex_domain[0];
@@ -3593,13 +3743,13 @@ bool GetSPTCHelper(
}
// (u, v) = known surface parameter
- if ( mesh.m_packed_tex_rotate )
+ if ( mesh.m_packed_tex_rotate )
{
// verify this by checking with mesher
a = 1.0 - srf_vdom.NormalizedParameterAt( v );
b = srf_udom.NormalizedParameterAt( u );
}
- else
+ else
{
a = srf_udom.NormalizedParameterAt( u );
b = srf_vdom.NormalizedParameterAt( v );
@@ -3655,7 +3805,7 @@ bool GetSPTCHelper(
bool ON_TextureMapping::GetTextureCoordinates(
- const ON_Mesh& mesh,
+ const ON_Mesh& mesh,
ON_SimpleArray& T,
const ON_Xform* mesh_xform,
bool bLazy,
@@ -3758,15 +3908,15 @@ bool ON_TextureMapping::GetTextureCoordinates(
double w;
int sd;
- if (ON_TextureMapping::PROJECTION::clspt_projection == m_projection
+ if (ON_TextureMapping::PROJECTION::clspt_projection == m_projection
&& (ON_TextureMapping::TYPE::mesh_mapping_primitive == m_type || ON_TextureMapping::TYPE::brep_mapping_primitive == m_type)
&& nullptr != m_mapping_primitive)
{
rc = false;
}
else if ( mesh_N &&
- ( ON_TextureMapping::PROJECTION::ray_projection == m_projection
- || ON_TextureMapping::TYPE::box_mapping == m_type
+ ( ON_TextureMapping::PROJECTION::ray_projection == m_projection
+ || ON_TextureMapping::TYPE::box_mapping == m_type
|| ON_TextureMapping::TYPE::cylinder_mapping == m_type
|| ON_TextureMapping::TYPE::mesh_mapping_primitive == m_type
)
@@ -3845,8 +3995,8 @@ bool ON_TextureMapping::GetTextureCoordinates(
return rc;
}
-static
-void ThreeToTwoHelper(
+static
+void ThreeToTwoHelper(
const ON_SimpleArray& T3,
ON_SimpleArray& T2
)
@@ -3867,8 +4017,8 @@ void ThreeToTwoHelper(
}
bool ON_TextureMapping::GetTextureCoordinates(
- const ON_Mesh& mesh,
- ON_SimpleArray& T,
+ const ON_Mesh& mesh,
+ ON_SimpleArray& T,
const ON_Xform* mesh_xform,
bool bLazy,
ON_SimpleArray* Tside
@@ -3938,8 +4088,8 @@ bool ON_TextureMapping::GetTextureCoordinates(
}
-//bool ON_Mesh::GetSurfaceParameterTextureXform(
-// class ON_Xform& StoT
+//bool ON_Mesh::GetSurfaceParameterTextureXform(
+// class ON_Xform& StoT
// ) const
//
//{
@@ -3952,7 +4102,7 @@ bool ON_TextureMapping::GetTextureCoordinates(
// const ON_Interval texture_u_domain(m_tex_domain[0]);
// const ON_Interval texture_v_domain(m_tex_domain[1]);
// bool bRotateTexture = m_srf_tex_rotate;
-// if ( surface_u_domain.IsInterval()
+// if ( surface_u_domain.IsInterval()
// && surface_v_domain.IsInterval()
// && texture_u_domain.IsInterval()
// && texture_v_domain.IsInterval()
@@ -4012,11 +4162,11 @@ public:
bool m_bHasPrincipalCurvatures;
bool m_bHasHiddenVertices;
- bool m_bHasCachedTextures;
+ bool m_bHasCachedTextures;
ON_SimpleArray< ON_TextureCoordinates* > m_TC;
- // m_vuse[] is an array of length = original number of
- // vertices in m_mesh and m_vuse[vi] = number of faces
+ // m_vuse[] is an array of length = original number of
+ // vertices in m_mesh and m_vuse[vi] = number of faces
// that reference vertex vi. If this vertex needs to be
// split, vuse[vi] is decremented. The ultimate goal
// is to split a few times as needed so we don't
@@ -4057,10 +4207,10 @@ void ON__CChangeTextureCoordinateHelper::ChangeTextureCoordinate(int* Fvi, int f
}
-ON__CChangeTextureCoordinateHelper::ON__CChangeTextureCoordinateHelper(
+ON__CChangeTextureCoordinateHelper::ON__CChangeTextureCoordinateHelper(
ON_Mesh& mesh,
int newvcnt,
- float*& mesh_T )
+ float*& mesh_T )
: m_mesh(mesh)
, m_mesh_dV(0)
, m_vuse_count(0)
@@ -4093,7 +4243,7 @@ ON__CChangeTextureCoordinateHelper::ON__CChangeTextureCoordinateHelper(
}
m_bHasVertexNormals = m_mesh.HasVertexNormals();
- if ( m_bHasVertexNormals )
+ if ( m_bHasVertexNormals )
m_mesh.m_N.Reserve(vcnt+newvcnt);
m_bHasVertexTextures = m_mesh.HasTextureCoordinates();
@@ -4154,8 +4304,8 @@ int ON__CChangeTextureCoordinateHelper::DupVertex(int vi)
{
if ( 0 == m_vuse_count )
{
- // m_vuse[] is an array of length = original number of
- // vertices in m_mesh and m_vuse[vi] = number of faces
+ // m_vuse[] is an array of length = original number of
+ // vertices in m_mesh and m_vuse[vi] = number of faces
// that reference vertex vi. If this vertex needs to be
// split, vuse[vi] is decremented. The ultimate goal
// is to split a few times as needed so we don't
@@ -4343,7 +4493,7 @@ int IntersectBoxSideRayHelper(int side, const ON_3dPoint& rst, const ON_3dVector
}
static
-bool EvBoxSideTextureCoordinateHelper2(
+bool EvBoxSideTextureCoordinateHelper2(
int side,
const ON_TextureMapping& box_mapping,
const ON_3dPoint& P,
@@ -4375,7 +4525,7 @@ bool EvBoxSideTextureCoordinateHelper2(
// 4 = front side (y=+1)
// 5 = bottom side (z=-1)
// 6 = top side (z=+1)
-
+
if ( ON_TextureMapping::PROJECTION::ray_projection == box_mapping.m_projection )
{
double s;
@@ -4384,10 +4534,10 @@ bool EvBoxSideTextureCoordinateHelper2(
// ray hit the box side
rst = rst + s*n;
}
- }
+ }
double shift = 0.0;
-
+
// side flag
// 1 = left side (x=-1)
// 2 = right side (x=+1)
@@ -4398,27 +4548,27 @@ bool EvBoxSideTextureCoordinateHelper2(
switch(side)
{
- case 1: // x = -1
- rst.x = -rst.y;
- rst.y = rst.z;
+ case 1: // x = -1
+ rst.x = -rst.y;
+ rst.y = rst.z;
shift = 3.0;
break;
case 2: // x = +1
- rst.x = rst.y;
- rst.y = rst.z;
+ rst.x = rst.y;
+ rst.y = rst.z;
shift = 1.0;
break;
case 3: // y = -1
- rst.y = rst.z;
+ rst.y = rst.z;
shift = 0.0;
break;
case 4: // y = +1
- rst.x = -rst.x;
- rst.y = rst.z;
+ rst.x = -rst.x;
+ rst.y = rst.z;
shift = 2.0;
break;
case 5: // z = -1
- rst.x = -rst.x;
+ rst.x = -rst.x;
shift = 4.0;
break;
case 6: // z = +1
@@ -4433,20 +4583,20 @@ bool EvBoxSideTextureCoordinateHelper2(
rst.x = 0.5*rst.x + 0.5;
rst.y = 0.5*rst.y + 0.5;
rst.z = 0.0;
-
+
if( ON_TextureMapping::TEXTURE_SPACE::divided == box_mapping.m_texture_space)
{
rst.x = (shift + rst.x)/(box_mapping.m_bCapped ? 6.0 : 4.0);
}
*T = box_mapping.m_uvw*rst;
-
+
return true;
}
static
bool EvBoxSideTextureCoordinateHelper1(
- const ON_Mesh& mesh,
+ const ON_Mesh& mesh,
const ON_Xform* mesh_xform,
int vi,
int side,
@@ -4560,17 +4710,17 @@ static
float TcDistanceHelper(const ON_2fPoint& tc)
{
float dx = (tc.x > 0.5f) ? (1.0f-tc.x) : tc.x;
- if ( dx < 0.0f)
+ if ( dx < 0.0f)
return 0.0f;
float dy = (tc.y > 0.5f) ? (1.0f-tc.y) : tc.y;
- if ( dy < 0.0f)
+ if ( dy < 0.0f)
return 0.0f;
return (dx < dy) ? dx : dy;
}
static
-void AdjustSingleBoxTextureCoordinatesHelper(
- ON_Mesh& mesh,
+void AdjustSingleBoxTextureCoordinatesHelper(
+ ON_Mesh& mesh,
const ON_Xform* mesh_xform,
float* mesh_T,
int mesh_T_stride,
@@ -4664,7 +4814,7 @@ void AdjustSingleBoxTextureCoordinatesHelper(
return;
ON__CChangeTextureCoordinateHelper helper(mesh,vcnt+newvcnt,mesh_T);
-
+
const int mflist_count = mflist.Count();
for ( k = 0; k < mflist_count; k++ )
@@ -4678,13 +4828,13 @@ void AdjustSingleBoxTextureCoordinatesHelper(
{
helper.ChangeTextureCoordinate(fvi,j,mf.tc[j].x,mf.tc[j].y,mesh_T,mesh_T_stride);
}
- }
+ }
}
}
-static
-void AdjustMeshPeriodicTextureCoordinatesHelper(
- ON_Mesh& mesh,
+static
+void AdjustMeshPeriodicTextureCoordinatesHelper(
+ ON_Mesh& mesh,
const ON_Xform* mesh_xform,
float* mesh_T,
int mesh_T_stride,
@@ -4759,7 +4909,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper(
t = Tx[vi]; // t = "u" texture coordinate
if ( t < ang0 )
- {
+ {
quad[vi] = 1; q |= 1; // longitude < pi/2
ftc_count++;
}
@@ -4804,7 +4954,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper(
}
// ftc.fi will be set to fi if a texture coordinate needs to be adjusted
- ftc.fi = -1;
+ ftc.fi = -1;
ftc.Tx[0] = Tx[Fvi[0]];
ftc.Tx[1] = Tx[Fvi[1]];
@@ -4814,7 +4964,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper(
if ( 0 != (8&q) )
{
// see if check for north/south sphere mapping poles and fix them
- if ( 8 == ftc.quad[0] )
+ if ( 8 == ftc.quad[0] )
{
t0 = (8 == ftc.quad[3]) ? ON_UNSET_FLOAT : ftc.Tx[3];
t1 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1];
@@ -4826,7 +4976,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper(
ftc.fi = fi;
}
}
- if ( 8 == ftc.quad[1] )
+ if ( 8 == ftc.quad[1] )
{
t0 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0];
t1 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2];
@@ -4838,7 +4988,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper(
ftc.fi = fi;
}
}
- if ( 8 == ftc.quad[2] )
+ if ( 8 == ftc.quad[2] )
{
int k = (Fvi[2] == Fvi[3]) ? 0 : 3;
t0 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1];
@@ -4856,7 +5006,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper(
ftc.fi = fi;
}
}
- if ( 8 == ftc.quad[3] && Fvi[2] != Fvi[3] )
+ if ( 8 == ftc.quad[3] && Fvi[2] != Fvi[3] )
{
t0 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2];
t1 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0];
@@ -4900,7 +5050,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper(
}
if (f0 >= f1 )
{
- // "most" of the face is on the left side of the texture
+ // "most" of the face is on the left side of the texture
// If a vertex is on the right side, clamp its tc to 0.
if ( 4 == ftc.quad[0] ) {ftc.Tx[0] = 0.0f; ftc.fi = fi;}
if ( 4 == ftc.quad[1] ) {ftc.Tx[1] = 0.0f; ftc.fi = fi;}
@@ -4909,7 +5059,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper(
}
else
{
- // "most" of the face is on the right side of the texture
+ // "most" of the face is on the right side of the texture
// If a vertex is on the left side, clamp its tc to two_pi_tc.
if ( 1 == ftc.quad[0] ) {ftc.Tx[0] = twopitc; ftc.fi = fi;}
if ( 1 == ftc.quad[1] ) {ftc.Tx[1] = twopitc; ftc.fi = fi;}
@@ -4922,7 +5072,7 @@ void AdjustMeshPeriodicTextureCoordinatesHelper(
if ( ftc.fi >= 0 )
{
// face will require special handling
- ftc_list.Append(ftc);
+ ftc_list.Append(ftc);
}
}
@@ -4998,8 +5148,8 @@ void AdjustMeshPeriodicTextureCoordinatesHelper(
}
static
-bool SeamCheckHelper( const ON_TextureMapping& mp,
- double& two_pi_tc,
+bool SeamCheckHelper( const ON_TextureMapping& mp,
+ double& two_pi_tc,
ON_SimpleArray& Tside,
ON_SimpleArray*& Tsd )
{
@@ -5049,7 +5199,7 @@ static inline bool HasSharedVertices(const ON_Mesh& mesh)
}
-const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates(
+const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates(
const class ON_TextureMapping& mapping,
const class ON_Xform* mesh_xform,
bool bLazy
@@ -5097,7 +5247,7 @@ const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates(
TC->m_tag.Set(mapping);
if ( mesh_xform && mesh_xform->IsValid()
- && !mesh_xform->IsIdentity()
+ && !mesh_xform->IsIdentity()
&& !mesh_xform->IsZero()
)
{
@@ -5134,7 +5284,7 @@ const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates(
}
bool ON_Mesh::SetTextureCoordinates(
- const class ON_TextureMapping& mapping,
+ const class ON_TextureMapping& mapping,
const class ON_Xform* mesh_xform,
bool bLazy
)
@@ -5147,7 +5297,7 @@ bool ON_Mesh::SetTextureCoordinates(
ON_SimpleArray Tside;
ON_SimpleArray* Tsd = 0;
ON_TextureMapping mp = mapping;
-
+
double two_pi_tc = 1.0;
bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this);
@@ -5165,15 +5315,15 @@ bool ON_Mesh::SetTextureCoordinates(
// were already once seam checked. So seam check can be skipped now.
bSeamCheck = false;
}
-
+
if (rc)
{
// update the texture coordinate tag
m_Ttag.Set(mapping);
- if ( mesh_xform
- && mesh_xform->IsValid()
- && !mesh_xform->IsIdentity()
- && !mesh_xform->IsZero()
+ if ( mesh_xform
+ && mesh_xform->IsValid()
+ && !mesh_xform->IsIdentity()
+ && !mesh_xform->IsZero()
)
{
m_Ttag.m_mesh_xform = *mesh_xform;
@@ -5264,8 +5414,8 @@ bool ON_MappingChannel::Read( ON_BinaryArchive& archive )
{
// 1.1 field added 6 June 2006
if (rc) rc = archive.ReadXform(m_object_xform);
- if (rc
- && archive.ArchiveOpenNURBSVersion() < 200610030
+ if (rc
+ && archive.ArchiveOpenNURBSVersion() < 200610030
&& m_object_xform.IsZero()
)
{
@@ -5357,7 +5507,7 @@ bool ON_MaterialRef::Write( ON_BinaryArchive& archive ) const
//if (rc) rc = archive.WriteArray( m_mapping_channels );
if (rc) rc = archive.WriteInt(0);
- // 23 May 2006 added
+ // 23 May 2006 added
if (rc) rc = archive.WriteUuid( m_material_backface_id );
if (rc) rc = archive.WriteInt( m_material_source );
@@ -5421,7 +5571,7 @@ bool ON_MappingRef::Write( ON_BinaryArchive& archive ) const
{
if (rc) rc = archive.WriteUuid( m_plugin_id );
if (rc) rc = archive.WriteArray( m_mapping_channels );
-
+
if ( !archive.EndWrite3dmChunk() )
rc = false;
}
@@ -5491,7 +5641,7 @@ void ON_ObjectRenderingAttributes::EnableAdvancedTexturePreview(bool b)
{
if ( b )
m_bits |= 1; // set bit 1
- else
+ else
m_bits &= 0xFE; // clear bit 1
}
@@ -5523,7 +5673,7 @@ bool ON_RenderingAttributes::IsValid( ON_TextLog* text_log ) const
return false;
}
}
- }
+ }
}
return true;
}
@@ -5554,7 +5704,7 @@ bool ON_ObjectRenderingAttributes::IsValid( ON_TextLog* text_log ) const
return false;
}
}
- }
+ }
}
return true;
@@ -5638,12 +5788,12 @@ const ON_MappingRef* ON_ObjectRenderingAttributes::MappingRef(
{
if ( plugin_id == mr->m_plugin_id )
return mr;
- }
+ }
}
//ALB 2013.12.03
//Fixes http://mcneel.myjetbrains.com/youtrack/issue/RH-5730
- //I'm sick of this bug being considered irrelavent, and since I've decided to go out of my way to
+ //I'm sick of this bug being considered irrelavent, and since I've decided to go out of my way to
//Sort out as many mapping problems as I can, I'm fixing this one like this.
if (m_mappings.Count() > 0)
{
@@ -5653,8 +5803,8 @@ const ON_MappingRef* ON_ObjectRenderingAttributes::MappingRef(
return 0;
}
-ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef(
- const ON_UUID& plugin_id
+ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef(
+ const ON_UUID& plugin_id
)
{
ON_MappingRef* mr = 0;
@@ -5665,7 +5815,7 @@ ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef(
{
if ( plugin_id == mr->m_plugin_id )
break;
- }
+ }
}
if ( !mr )
@@ -5677,18 +5827,18 @@ ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef(
return mr;
}
-bool ON_ObjectRenderingAttributes::DeleteMappingRef(
- const ON_UUID& plugin_id
+bool ON_ObjectRenderingAttributes::DeleteMappingRef(
+ const ON_UUID& plugin_id
)
{
const ON_MappingRef* mr = MappingRef(plugin_id);
- if ( mr )
+ if ( mr )
m_mappings.Remove( (int)(mr - m_mappings.Array()) ); // safe ptr to in conversion
- return (0 != mr);
+ return (0 != mr);
}
-const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel(
- const ON_UUID& plugin_id,
+const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel(
+ const ON_UUID& plugin_id,
const ON_UUID& mapping_id
) const
{
@@ -5708,8 +5858,8 @@ const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel(
return 0;
}
-const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel(
- const ON_UUID& plugin_id,
+const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel(
+ const ON_UUID& plugin_id,
int mapping_channel_id
) const
{
@@ -5732,7 +5882,7 @@ const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel(
bool ON_ObjectRenderingAttributes::AddMappingChannel(
- const ON_UUID& plugin_id,
+ const ON_UUID& plugin_id,
int mapping_channel_id,
const ON_UUID& mapping_id
)
@@ -5754,7 +5904,7 @@ bool ON_ObjectRenderingAttributes::AddMappingChannel(
}
bool ON_ObjectRenderingAttributes::DeleteMappingChannel(
- const ON_UUID& plugin_id,
+ const ON_UUID& plugin_id,
int mapping_channel_id
)
{
@@ -5763,7 +5913,7 @@ bool ON_ObjectRenderingAttributes::DeleteMappingChannel(
}
bool ON_ObjectRenderingAttributes::DeleteMappingChannel(
- const ON_UUID& plugin_id,
+ const ON_UUID& plugin_id,
const ON_UUID& mapping_id
)
{
@@ -5772,7 +5922,7 @@ bool ON_ObjectRenderingAttributes::DeleteMappingChannel(
}
bool ON_ObjectRenderingAttributes::ChangeMappingChannel(
- const ON_UUID& plugin_id,
+ const ON_UUID& plugin_id,
int old_mapping_channel_id,
int new_mapping_channel_id
)
@@ -5781,7 +5931,7 @@ bool ON_ObjectRenderingAttributes::ChangeMappingChannel(
return mr ? mr->ChangeMappingChannel(old_mapping_channel_id,new_mapping_channel_id) : false;
}
-const ON_MappingChannel* ON_MappingRef::MappingChannel(
+const ON_MappingChannel* ON_MappingRef::MappingChannel(
const ON_UUID& mapping_id
) const
{
@@ -5797,7 +5947,7 @@ const ON_MappingChannel* ON_MappingRef::MappingChannel(
return 0;
}
-const ON_MappingChannel* ON_MappingRef::MappingChannel(
+const ON_MappingChannel* ON_MappingRef::MappingChannel(
int mapping_channel_id
) const
{
@@ -5899,7 +6049,7 @@ bool ON_RenderingAttributes::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)
+ if (!rc)
return false;
for(;;)
{
@@ -5922,7 +6072,7 @@ bool ON_ObjectRenderingAttributes::Write( ON_BinaryArchive& archive ) const
return false;
for(;;)
{
- // DO NOT CALL ON_RenderingAttributes::Write
+ // DO NOT CALL ON_RenderingAttributes::Write
rc = archive.WriteArray(m_materials);
if ( !rc ) break;
rc = archive.WriteArray(m_mappings);
@@ -5952,14 +6102,14 @@ bool ON_ObjectRenderingAttributes::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)
+ if (!rc)
return false;
for(;;)
{
rc = ( 1 == major_version && minor_version >= 1 );
if (!rc) break;
- // DO NOT CALL ON_RenderingAttributes::Read
+ // DO NOT CALL ON_RenderingAttributes::Read
if (rc) rc = archive.ReadArray(m_materials);
if (!rc) break;
if (rc) rc = archive.ReadArray(m_mappings);
@@ -6035,9 +6185,9 @@ bool ON_TextureMapping::SetPlaneMapping(
ON_3dVector yaxis = plane.yaxis;
ON_3dVector zaxis = plane.zaxis;
- // Any "cleanup" needs to be done here
+ // Any "cleanup" needs to be done here
// to xaxis, yaxis, zaxis.
-
+
double sx,sy,sz;
if ( 0.0 == (sx = dx.Length())) sx = 2.0;
if ( 0.0 == (sy = dy.Length())) sy = 2.0;
@@ -6045,9 +6195,9 @@ bool ON_TextureMapping::SetPlaneMapping(
// The plane mapping matrix m_Pxyz transforms the
// world coordinate rectangle to a (-1<=r<=1,
- // on plane to a
+ // on plane to a
// 1 X 1 square in the xy plane centered at the
- // origin.
+ // origin.
// m_Pxyz = surface point transformation
ON_3dVector X = (2.0/sx)*xaxis;
@@ -6139,7 +6289,7 @@ bool ON_TextureMapping::SetBoxMapping(const ON_Plane& plane,
ON_Interval dx,
ON_Interval dy,
ON_Interval dz,
- bool bCapped
+ bool bCapped
)
{
bool rc = SetPlaneMapping(plane,dx,dy,dz);
@@ -6221,10 +6371,10 @@ bool ON_TextureMapping::GetMappingPlane(ON_Plane& plane,
S.z = 1.0/S.z;
xform.m_xform[0][0] *= S.x; xform.m_xform[0][1] *= S.x; xform.m_xform[0][2] *= S.x;
- xform.m_xform[0][3] *= S.x;
-
+ xform.m_xform[0][3] *= S.x;
+
xform.m_xform[1][0] *= S.y; xform.m_xform[1][1] *= S.y; xform.m_xform[1][2] *= S.y;
- xform.m_xform[1][3] *= S.y;
+ xform.m_xform[1][3] *= S.y;
xform.m_xform[2][0] *= S.z; xform.m_xform[2][1] *= S.z; xform.m_xform[2][2] *= S.z;
xform.m_xform[2][3] *= S.z;
@@ -6239,7 +6389,7 @@ bool ON_TextureMapping::GetMappingPlane(ON_Plane& plane,
return false;
plane.origin.Set(inv.m_xform[0][3],inv.m_xform[1][3],inv.m_xform[2][3]);
- xform.m_xform[0][3] = 0.0;
+ xform.m_xform[0][3] = 0.0;
xform.m_xform[1][3] = 0.0;
xform.m_xform[2][3] = 0.0;
plane.xaxis = &xform.m_xform[0][0];
@@ -6297,6 +6447,616 @@ bool ON_TextureMapping::GetMappingSphere(ON_Sphere& sphere) const
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Class ON_PBRMaterial
+//
+
+#define SUPPORT_PBR_USERDATA_SERIALIZATION
+
+class ON_PhysicallyBasedMaterialUserData : public ON_UserData
+{
+private:
+ ON_OBJECT_DECLARE(ON_PhysicallyBasedMaterialUserData);
+
+
+ bool GetDescription(ON_wString & description)
+ {
+ description = L"ON_PhysicallyBasedMaterialUserData";
+ return true;
+ }
+
+public:
+ ON_PhysicallyBasedMaterialUserData()
+ {
+ m_userdata_uuid = ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData);
+ m_application_uuid = ON_opennurbs6_id;
+ m_userdata_copycount = 1;
+ }
+
+ ~ON_PhysicallyBasedMaterialUserData() {}
+
+
+ ON_PhysicallyBasedMaterialUserData(const ON_PhysicallyBasedMaterialUserData& src)
+ : ON_UserData(src)
+ {
+ m_userdata_copycount = src.m_userdata_copycount;
+ m_parameters = src.m_parameters;
+ }
+
+ ON_PhysicallyBasedMaterialUserData& operator=(const ON_PhysicallyBasedMaterialUserData& src)
+ {
+ if (this != &src)
+ {
+ ON_UserData::operator = (src);
+ m_parameters = src.m_parameters;
+ }
+ return *this;
+ }
+
+private:
+ // ON_Object overrides
+#if defined SUPPORT_PBR_USERDATA_SERIALIZATION
+ bool Write(ON_BinaryArchive& archive) const override
+ {
+ ON_ASSERT(IsValid());
+
+ const int chunk_version = 1;
+ if (false == archive.BeginWrite3dmAnonymousChunk(chunk_version))
+ return false;
+
+ bool rc = m_parameters.Write(archive);
+
+ if (!archive.EndWrite3dmChunk())
+ {
+ rc = false;
+ }
+
+ return rc;
+ }
+
+ bool Read(ON_BinaryArchive& archive) override
+ {
+ int chunk_version = 0;
+ if (false == archive.BeginRead3dmAnonymousChunk(&chunk_version))
+ return false;
+
+ bool rc = false;
+
+ if (chunk_version == 1)
+ {
+ rc = m_parameters.Read(archive);
+ }
+
+ if (!archive.EndRead3dmChunk())
+ {
+ rc = false;
+ }
+
+ ON_ASSERT(IsValid());
+ return rc;
+ }
+
+ bool Archive() const override
+ {
+ return true;
+ }
+#else
+ bool Archive() const override
+ {
+ return false;
+ }
+#endif
+
+ bool IsValid(class ON_TextLog* text_log = nullptr) const override
+ {
+ if (!m_parameters.IsValid(text_log))
+ return false;
+
+ return ON_UserData::IsValid(text_log);
+ }
+
+public:
+ struct Parameters
+ {
+#if defined SUPPORT_PBR_USERDATA_SERIALIZATION
+ bool Write(ON_BinaryArchive& binary_archive) const
+ {
+ if (!binary_archive.WriteColor(base_color)) return false;
+ if (!binary_archive.WriteInt((int)brdf)) return false;
+ if (!binary_archive.WriteDouble(subsurface)) return false;
+ if (!binary_archive.WriteColor(subsurface_scattering_color)) return false;
+ if (!binary_archive.WriteDouble(subsurface_scattering_radius)) return false;
+ if (!binary_archive.WriteDouble(metallic)) return false;
+ if (!binary_archive.WriteDouble(specular)) return false;
+ if (!binary_archive.WriteDouble(specular_tint)) return false;
+ if (!binary_archive.WriteDouble(roughness)) return false;
+ if (!binary_archive.WriteDouble(anisotropic)) return false;
+ if (!binary_archive.WriteDouble(anisotropic_rotation)) return false;
+ if (!binary_archive.WriteDouble(sheen)) return false;
+ if (!binary_archive.WriteDouble(sheen_tint)) return false;
+ if (!binary_archive.WriteDouble(clearcoat)) return false;
+ if (!binary_archive.WriteDouble(clearcoat_roughness)) return false;
+ if (!binary_archive.WriteDouble(opacity_IOR)) return false;
+ if (!binary_archive.WriteDouble(opacity)) return false;
+ if (!binary_archive.WriteDouble(opacity_roughness)) return false;
+ if (!binary_archive.WriteColor(emission)) return false;
+
+ return true;
+ }
+
+ bool Read(ON_BinaryArchive& binary_archive)
+ {
+ if (!binary_archive.ReadColor(base_color)) return false;
+ if (!binary_archive.ReadInt((int*)&brdf)) return false;
+ if (!binary_archive.ReadDouble(&subsurface)) return false;
+ if (!binary_archive.ReadColor(subsurface_scattering_color)) return false;
+ if (!binary_archive.ReadDouble(&subsurface_scattering_radius)) return false;
+ if (!binary_archive.ReadDouble(&metallic)) return false;
+ if (!binary_archive.ReadDouble(&specular)) return false;
+ if (!binary_archive.ReadDouble(&specular_tint)) return false;
+ if (!binary_archive.ReadDouble(&roughness)) return false;
+ if (!binary_archive.ReadDouble(&anisotropic)) return false;
+ if (!binary_archive.ReadDouble(&anisotropic_rotation)) return false;
+ if (!binary_archive.ReadDouble(&sheen)) return false;
+ if (!binary_archive.ReadDouble(&sheen_tint)) return false;
+ if (!binary_archive.ReadDouble(&clearcoat)) return false;
+ if (!binary_archive.ReadDouble(&clearcoat_roughness)) return false;
+ if (!binary_archive.ReadDouble(&opacity_IOR)) return false;
+ if (!binary_archive.ReadDouble(&opacity)) return false;
+ if (!binary_archive.ReadDouble(&opacity_roughness)) return false;
+ if (!binary_archive.ReadColor(emission)) return false;
+
+ return true;
+ }
+#endif
+
+ bool IsValid(class ON_TextLog* text_log = nullptr) const
+ {
+ //!base_color.IsValid() means that PBR is not supported.
+ //if (!base_color.IsValid()) return false;
+ if (ON_UNSET_VALUE == subsurface) return false;
+ if (!subsurface_scattering_color.IsValid()) return false;
+ if (ON_IS_UNSET_DOUBLE(subsurface_scattering_radius)) return false;
+ if (ON_IS_UNSET_DOUBLE(metallic)) return false;
+ if (ON_IS_UNSET_DOUBLE(specular)) return false;
+ if (ON_IS_UNSET_DOUBLE(specular_tint)) return false;
+ if (ON_IS_UNSET_DOUBLE(roughness)) return false;
+ if (ON_IS_UNSET_DOUBLE(anisotropic)) return false;
+ if (ON_IS_UNSET_DOUBLE(anisotropic_rotation)) return false;
+ if (ON_IS_UNSET_DOUBLE(sheen)) return false;
+ if (ON_IS_UNSET_DOUBLE(sheen_tint)) return false;
+ if (ON_IS_UNSET_DOUBLE(clearcoat)) return false;
+ if (ON_IS_UNSET_DOUBLE(clearcoat_roughness)) return false;
+ if (ON_IS_UNSET_DOUBLE(opacity_IOR)) return false;
+ if (ON_IS_UNSET_DOUBLE(opacity)) return false;
+ if (ON_IS_UNSET_DOUBLE(opacity_roughness)) return false;
+ if (!emission.IsValid()) return false;
+
+ return true;
+ }
+
+ ON_4fColor base_color = ON_4fColor::Unset;
+ double subsurface = 0.0;
+ ON_4fColor subsurface_scattering_color = ON_Color::White;
+ double subsurface_scattering_radius = 0.0;
+ double metallic = 0.0;
+ double specular = 0.5;
+ double specular_tint = 0.0;
+ double roughness = 1.0;
+ double anisotropic = 0.0;
+ double anisotropic_rotation = 0.0;
+ double sheen = 0.0;
+ double sheen_tint = 0.0;
+ double clearcoat = 0.0;
+ double clearcoat_roughness = 0.0;
+ double opacity_IOR = 1.52;
+ double opacity = 1.0;
+ double opacity_roughness = 0.0;
+ ON_4fColor emission = ON_Color::Black;
+ ON_PhysicallyBasedMaterial::BRDFs brdf = ON_PhysicallyBasedMaterial::BRDFs::GGX;
+ } m_parameters;
+};
+
+ON_UUID ON_Material::PhysicallyBasedUserdataId(void)
+{
+ // {5694E1AC-40E6-44F4-9CA9-3B6D0E8C4440}
+ static const ON_UUID id = { 0x5694e1ac, 0x40e6, 0x44f4,{ 0x9c, 0xa9, 0x3b, 0x6d, 0xe, 0x8c, 0x44, 0x40 } };
+
+ return id;
+}
+
+ON_OBJECT_IMPLEMENT(ON_PhysicallyBasedMaterialUserData, ON_UserData, "5694E1AC-40E6-44F4-9CA9-3B6D0E8C4440");
+
+class ON_PhysicallyBasedMaterial::Impl
+{
+public:
+ Impl(ON_Material& mat)
+ : material(&mat),
+ m_pDummy(nullptr)
+ {
+ }
+
+ ~Impl()
+ {
+ delete m_pDummy;
+ }
+
+ const ON_PhysicallyBasedMaterialUserData& UserData() const
+ {
+ const auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData));
+ if (pUD)
+ {
+ return static_cast(*pUD);
+ }
+
+ if (m_pDummy)
+ {
+ return *m_pDummy;
+ }
+
+ //This is the const version
+ m_pDummy = new ON_PhysicallyBasedMaterialUserData();
+ return *m_pDummy;
+ }
+
+
+ ON_PhysicallyBasedMaterialUserData& UserData()
+ {
+ auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData));
+ if (pUD)
+ {
+ return static_cast(*pUD);
+ }
+
+ ON_PhysicallyBasedMaterialUserData* p = nullptr;
+
+ if (nullptr == m_pDummy)
+ {
+ p = new ON_PhysicallyBasedMaterialUserData();
+ }
+ else
+ {
+ p = m_pDummy;
+ m_pDummy = nullptr;
+ }
+
+ material->AttachUserData(p);
+
+ return *p;
+ }
+
+ ON_Material* material;
+
+ mutable ON_PhysicallyBasedMaterialUserData* m_pDummy;
+};
+
+
+
+
+
+ON_4fColor ON_PhysicallyBasedMaterial::BaseColor(void) const
+{
+ return Implementation().UserData().m_parameters.base_color;
+}
+
+void ON_PhysicallyBasedMaterial::SetBaseColor(const ON_4fColor& c)
+{
+ Implementation().UserData().m_parameters.base_color = c;
+}
+
+ON_PhysicallyBasedMaterial::BRDFs ON_PhysicallyBasedMaterial::BRDF(void) const
+{
+ return Implementation().UserData().m_parameters.brdf;
+}
+
+void ON_PhysicallyBasedMaterial::SetBRDF(const BRDFs& b)
+{
+ Implementation().UserData().m_parameters.brdf = b;
+}
+
+double ON_PhysicallyBasedMaterial::Subsurface(void) const
+{
+ return Implementation().UserData().m_parameters.subsurface;
+}
+
+void ON_PhysicallyBasedMaterial::SetSubsurface(double d)
+{
+ Implementation().UserData().m_parameters.subsurface = d;
+}
+
+ON_4fColor ON_PhysicallyBasedMaterial::SubsurfaceScatteringColor(void) const
+{
+ return Implementation().UserData().m_parameters.subsurface_scattering_color;
+}
+
+void ON_PhysicallyBasedMaterial::SetSubsurfaceScatteringColor(const ON_4fColor& c)
+{
+ Implementation().UserData().m_parameters.subsurface_scattering_color = c;
+}
+
+double ON_PhysicallyBasedMaterial::SubsurfaceScatteringRadius(void) const
+{
+ return Implementation().UserData().m_parameters.subsurface_scattering_radius;
+}
+
+void ON_PhysicallyBasedMaterial::SetSubsurfaceScatteringRadius(double d)
+{
+ Implementation().UserData().m_parameters.subsurface_scattering_radius = d;
+}
+
+double ON_PhysicallyBasedMaterial::Metallic(void) const
+{
+ return Implementation().UserData().m_parameters.metallic;
+}
+
+void ON_PhysicallyBasedMaterial::SetMetallic(double d)
+{
+ Implementation().UserData().m_parameters.metallic = d;
+}
+
+double ON_PhysicallyBasedMaterial::Specular(void) const
+{
+ return Implementation().UserData().m_parameters.specular;
+}
+
+double ON_PhysicallyBasedMaterial::ReflectiveIOR(void) const
+{
+ const double d2 = Specular() * 0.08;
+ return sqrt(d2);
+}
+
+void ON_PhysicallyBasedMaterial::SetReflectiveIOR(double ior)
+{
+ const double d = (ior - 1.0) / (ior + 1.0);
+ SetSpecular(d*d / 0.08);
+}
+
+void ON_PhysicallyBasedMaterial::SetSpecular(double d)
+{
+ Implementation().UserData().m_parameters.specular = d;
+}
+
+double ON_PhysicallyBasedMaterial::SpecularTint(void) const
+{
+ return Implementation().UserData().m_parameters.specular_tint;
+}
+
+void ON_PhysicallyBasedMaterial::SetSpecularTint(double d)
+{
+ Implementation().UserData().m_parameters.specular_tint = d;
+}
+
+double ON_PhysicallyBasedMaterial::Roughness(void) const
+{
+ return Implementation().UserData().m_parameters.roughness;
+}
+
+void ON_PhysicallyBasedMaterial::SetRoughness(double d)
+{
+ Implementation().UserData().m_parameters.roughness = d;
+}
+
+double ON_PhysicallyBasedMaterial::Anisotropic(void) const
+{
+ return Implementation().UserData().m_parameters.anisotropic;
+}
+
+void ON_PhysicallyBasedMaterial::SetAnisotropic(double d)
+{
+ Implementation().UserData().m_parameters.anisotropic = d;
+}
+
+double ON_PhysicallyBasedMaterial::AnisotropicRotation(void) const
+{
+ return Implementation().UserData().m_parameters.anisotropic_rotation;
+}
+
+void ON_PhysicallyBasedMaterial::SetAnisotropicRotation(double d)
+{
+ Implementation().UserData().m_parameters.anisotropic_rotation = d;
+}
+
+double ON_PhysicallyBasedMaterial::Sheen(void) const
+{
+ return Implementation().UserData().m_parameters.sheen;
+}
+
+void ON_PhysicallyBasedMaterial::SetSheen(double d)
+{
+ Implementation().UserData().m_parameters.sheen = d;
+}
+
+double ON_PhysicallyBasedMaterial::SheenTint(void) const
+{
+ return Implementation().UserData().m_parameters.sheen_tint;
+}
+
+void ON_PhysicallyBasedMaterial::SetSheenTint(double d)
+{
+ Implementation().UserData().m_parameters.sheen_tint = d;
+}
+
+double ON_PhysicallyBasedMaterial::Clearcoat(void) const
+{
+ return Implementation().UserData().m_parameters.clearcoat;
+}
+
+void ON_PhysicallyBasedMaterial::SetClearcoat(double d)
+{
+ Implementation().UserData().m_parameters.clearcoat = d;
+}
+
+double ON_PhysicallyBasedMaterial::ClearcoatRoughness(void) const
+{
+ return Implementation().UserData().m_parameters.clearcoat_roughness;
+}
+
+void ON_PhysicallyBasedMaterial::SetClearcoatRoughness(double d)
+{
+ Implementation().UserData().m_parameters.clearcoat_roughness = d;
+}
+
+double ON_PhysicallyBasedMaterial::OpacityIOR(void) const
+{
+ return Implementation().UserData().m_parameters.opacity_IOR;
+}
+
+void ON_PhysicallyBasedMaterial::SetOpacityIOR(double d)
+{
+ Implementation().UserData().m_parameters.opacity_IOR = d;
+}
+
+double ON_PhysicallyBasedMaterial::Opacity(void) const
+{
+ return Implementation().UserData().m_parameters.opacity;
+}
+
+void ON_PhysicallyBasedMaterial::SetOpacity(double d)
+{
+ Implementation().UserData().m_parameters.opacity = d;
+}
+
+double ON_PhysicallyBasedMaterial::OpacityRoughness(void) const
+{
+ return Implementation().UserData().m_parameters.opacity_roughness;
+}
+
+void ON_PhysicallyBasedMaterial::SetOpacityRoughness(double d)
+{
+ Implementation().UserData().m_parameters.opacity_roughness = d;
+}
+
+bool ON_PhysicallyBasedMaterial::IsValid(class ON_TextLog* text_log) const
+{
+ return Implementation().UserData().m_parameters.IsValid(text_log);
+}
+
+ON_4fColor ON_PhysicallyBasedMaterial::Emission(void) const
+{
+ return Implementation().UserData().m_parameters.emission;
+}
+
+void ON_PhysicallyBasedMaterial::SetEmission(ON_4fColor d)
+{
+ Implementation().UserData().m_parameters.emission = d;
+}
+
+ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_Material& src)
+ : _pImpl(new Impl(const_cast(src)))
+{
+}
+
+ON_PhysicallyBasedMaterial::~ON_PhysicallyBasedMaterial()
+{
+ delete _pImpl;
+}
+
+
+ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_PhysicallyBasedMaterial& src)
+ : _pImpl(new Impl(*src._pImpl->material))
+{
+}
+
+ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void)
+{
+ return ON_PhysicallyBasedMaterial(*this);
+}
+
+const ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void) const
+{
+ return ON_PhysicallyBasedMaterial(*this);
+}
+
+
+const ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(void) const
+{
+ return *_pImpl;
+}
+
+ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(void)
+{
+ return *_pImpl;
+}
+
+
+bool ON_PhysicallyBasedMaterial::Supported(void) const
+{
+ return BaseColor().IsValid();
+}
+
+void ON_PhysicallyBasedMaterial::SynchronizeLegacyMaterial(void)
+{
+ auto& mat = *Implementation().material;
+
+ const bool bIsMetal = Metallic() > 0.5;
+
+ mat.SetDiffuse(bIsMetal ? ON_Color::Black : (ON_Color)BaseColor());
+
+ const double reflectivity = bIsMetal ? 1.0 : 1.0 - Roughness();
+
+ mat.SetFresnelReflections(!bIsMetal);
+
+ mat.SetReflectivity(reflectivity);
+ mat.SetTransparency(1.0 - Opacity());
+ mat.SetAmbient(ON_Color::Black);
+
+ //Gloss
+ mat.SetShine(ON_Material::MaxShine * reflectivity);
+
+ if (bIsMetal)
+ {
+ mat.SetSpecular(BaseColor());
+ mat.m_reflection = BaseColor();
+ }
+ else
+ {
+ const int bf = (int)(255.0 * reflectivity);
+ mat.SetSpecular(ON_Color(bf, bf, bf));
+ mat.m_reflection = ON_Color::White;
+ }
+
+ mat.m_reflection_glossiness = Roughness();
+ mat.m_refraction_glossiness = OpacityRoughness();
+
+ mat.SetEmission(Emission());
+
+ mat.m_index_of_refraction = OpacityIOR();
+
+ //No need to do the textures, because the ones that are supported are in the right channels already.
+}
+
+int ON_PhysicallyBasedMaterial::FindTexture(const wchar_t* filename, ON_Texture::TYPE type, int i0) const
+{
+ return Material().FindTexture(filename, type, i0);
+}
+
+int ON_PhysicallyBasedMaterial::AddTexture(const ON_Texture& tx)
+{
+ return Material().AddTexture(tx);
+}
+
+int ON_PhysicallyBasedMaterial::AddTexture(const wchar_t* filename, ON_Texture::TYPE type)
+{
+ return Material().AddTexture(filename, type);
+}
+
+int ON_PhysicallyBasedMaterial::DeleteTexture(const wchar_t* filename, ON_Texture::TYPE type)
+{
+ return Material().DeleteTexture(filename, type);
+}
+
+ON_Material& ON_PhysicallyBasedMaterial::Material(void)
+{
+ return *Implementation().material;
+}
+
+const ON_Material& ON_PhysicallyBasedMaterial::Material(void) const
+{
+ return *Implementation().material;
+}
+
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::BaseColor(void) { return L"pbr-base-color"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::BRDF(void) { return L"pbr-brdf"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Subsurface(void) { return L"pbr-subsurface"; }
@@ -6312,13 +7072,8 @@ ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Sheen(void) { return L"p
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::SheenTint(void) { return L"pbr-sheen-tint"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Clearcoat(void) { return L"pbr-clearcoat"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::ClearcoatRoughness(void) { return L"pbr-clearcoat-roughness"; }
+ON_wString ON_PhysicallyBasedMaterial::ParametersNames::ClearcoatBump(void) { return L"pbr-clearcoat-bump"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::OpacityIor(void) { return L"pbr-opacity-ior"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Opacity(void) { return L"pbr-opacity"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::OpacityRoughness(void) { return L"pbr-opacity-roughness"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Emission(void) { return L"pbr-emission"; }
-ON_wString ON_PhysicallyBasedMaterial::ParametersNames::AmbientOcclusion(void) { return L"pbr-ambient-occlusion"; }
-ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Smudge(void) { return L"pbr-smudge"; }
-ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Displacement(void) { return L"pbr-displacement"; }
-ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Normal(void) { return L"pbr-normal"; }
-ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Bump(void) { return L"pbr-bump"; }
-
diff --git a/opennurbs_material.h b/opennurbs_material.h
index 78013d7a..8ad7e28c 100644
--- a/opennurbs_material.h
+++ b/opennurbs_material.h
@@ -8,7 +8,7 @@
// 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 .
//
////////////////////////////////////////////////////////////////
@@ -17,11 +17,12 @@
#if !defined(OPENNURBS_MATERIAL_INC_)
#define OPENNURBS_MATERIAL_INC_
+class ON_PhysicallyBasedMaterial;
///////////////////////////////////////////////////////////////////////////////
//
// Class ON_Material
-//
+//
class ON_CLASS ON_Material : public ON_ModelComponent
{
ON_OBJECT_DECLARE(ON_Material);
@@ -45,7 +46,7 @@ public:
is nullptr
Returns:
If ON_Material::Cast(model_component_ref.ModelComponent()) is not nullptr,
- that pointer is returned. Otherwise, none_return_value is returned.
+ that pointer is returned. Otherwise, none_return_value is returned.
*/
static const ON_Material* FromModelComponentRef(
const class ON_ModelComponentReference& model_component_reference,
@@ -53,31 +54,31 @@ public:
);
// compare everything except Index() value.
- static int Compare(
+ static int Compare(
const ON_Material& a,
const ON_Material& b
);
// compare Id(), Name(), m_rdk_material_instance_id
- static int CompareNameAndIds(
+ static int CompareNameAndIds(
const ON_Material& a,
const ON_Material& b
);
- // Compare all settings (color, reflection, texture, plug-in id)
- // that effect the appearance.
+ // Compare all settings (color, reflection, texture, plug-in id)
+ // that affect the appearance.
// Ignore Index(), Id(), Name(), m_rdk_material_instance_id.
- static int CompareAppearance(
+ static int CompareAppearance(
const ON_Material& a,
const ON_Material& b
);
- static int CompareColorAttributes(
+ static int CompareColorAttributes(
const ON_Material& a,
const ON_Material& b
);
- static int CompareReflectionAttributes(
+ static int CompareReflectionAttributes(
const ON_Material& a,
const ON_Material& b
);
@@ -97,15 +98,15 @@ public:
/*
Parameters:
fresnel_index_of_refraction - [in]
- ON_Material::Material::Default.m_fresnel_index_of_refraction
+ ON_Material::Material::Default.m_fresnel_index_of_refraction
is a good default
N - [in]
3d surface normal
R - [in]
3d reflection direction
Returns:
- 1.0:
- The input values were not valid or the calculation failed due to
+ 1.0:
+ The input values were not valid or the calculation failed due to
a divide by zero or some other numerical arithmetic failure.
fresnel reflection coefficient
1/2 * ((g-c)/(g+c))^2 * (1 + ( (c*(g+c) -1)/(c*(g+c) + 1) )^2)
@@ -149,9 +150,9 @@ public:
) override;
ON::object_type ObjectType() const override;
-
+
/////////////////////////////////////////////////////////////////
- // Interface
+ // Interface
ON_Color Ambient() const;
ON_Color Diffuse() const;
@@ -177,7 +178,7 @@ public:
// ID of the last plug-in to modify this material
ON_UUID MaterialPlugInId() const;
- void SetMaterialPlugInId(
+ void SetMaterialPlugInId(
ON_UUID plugin_id
);
@@ -242,7 +243,7 @@ public:
void SetDisableLighting(
bool bDisableLighting
);
-
+
//If m_bUseDiffuseTextureAlphaForObjectTransparencyTexture is true, the alpha channel
//of the texture in m_textures with m_type=bitmap_texture is used in addition to any
//textures with m_type=transparency_texture.
@@ -250,7 +251,7 @@ public:
void SetUseDiffuseTextureAlphaForObjectTransparencyTexture(
bool bUseDiffuseTextureAlphaForObjectTransparencyTexture
);
-
+
//////////////////////////////////////////////////////////////
//
// Reflection and Refraction settings
@@ -272,9 +273,20 @@ public:
//the function that the layer manager uses to color the little material swatch, for example.
ON_Color PreviewColor() const;
+
+ //Physically based material interface. Use this interface to set and get PBR parameters
+ //and to check if this material supports PBR.
+ //NOTE WELL - ON_PhysicallyBasedMaterial contains a pointer to this - the scope of the ON_PhysicallyBasedMaterial
+ //object must not exceed the scope of the material that it originally came from. Ideal usage is material.PhysicallyBased().Function()
+ const ON_PhysicallyBasedMaterial PhysicallyBased(void) const;
+ ON_PhysicallyBasedMaterial PhysicallyBased(void);
+
+ //Internal use only
+ static ON_UUID PhysicallyBasedUserdataId(void);
+
private:
// The value of m_rdk_material_id idetifies an RDK (rendering development kit)
- // material. Multiple materials in a Rhino model can refer to the same
+ // material. Multiple materials in a Rhino model can refer to the same
// RDK material id. In V5 this value is stored in user data. In V6 it is
// saved in the m_rdk_material_id field.
ON_UUID m_rdk_material_instance_id = ON_nil_uuid;
@@ -309,7 +321,7 @@ public:
/*
m_reflection_glossiness:
- Default is 0.0.
+ Default is 0.0.
Values from 0.0 to 1.0 make sense.
- 0.0 reflections are perfectly specular.
- t > 0.0 permits reflection ray direction to vary
@@ -319,7 +331,7 @@ public:
/*
m_refraction_glossiness:
- Default is 0.0.
+ Default is 0.0.
Values from 0.0 to 1.0 make sense.
- 0.0 refractions are perfectly specular.
- t > 0.0 permits refraction ray direction to vary
@@ -330,7 +342,7 @@ public:
/*
m_index_of_refraction:
Default is 1.0.
- Physically, the index of refraction is >= 1.0 and is
+ Physically, the index of refraction is >= 1.0 and is
the value (speed of light in vacum)/(speed of light in material).
Some rendering algorithms set m_index_of_refraction to zero or
values < 1.0 to generate desirable effects.
@@ -340,15 +352,15 @@ public:
/*
m_fresnel_index_of_refraction:
Default is 1.56.
- This is the value ON:Material::FresnelReflectionCoefficient() passes
+ This is the value ON:Material::FresnelReflectionCoefficient() passes
as the first parameter to ON_FresnelReflectionCoefficient().
- - Glass material types can be simulated with
+ - Glass material types can be simulated with
m_index_of_refraction ~ 1.56
m_fresnel_index_of_refraction ~ 1.56
- - Thin glass can be simulated with
+ - Thin glass can be simulated with
m_fresnel_index_of_refraction = 1.56
m_index_of_refraction = 0.0
- - Porcelain type materials can be simulated with
+ - Porcelain type materials can be simulated with
m_fresnel_index_of_refraction = 1.56
m_index_of_refraction = 1.0
m_transparency = 0.0
@@ -363,18 +375,18 @@ public:
3d reflection direction
Returns:
If m_bFresnelReflections is false, then 1.0 is returned.
- If m_bFresnelReflections is true, then the value of the fresnel
+ If m_bFresnelReflections is true, then the value of the fresnel
reflection coefficient is returned. In typical rendering applications,
the reflection term is multiplied by the fresnel reflection coefficient
before it is added to the diffuse color.
If any input is not valid or the calculation fails, then 1.0 is returned.
Remarks:
- When m_bFresnelReflections is true, the calculation is performed by
+ When m_bFresnelReflections is true, the calculation is performed by
calling ON_FresnelReflectionCoefficient() with m_fresnel_index_of_refraction
as the fresnel index of refraction.
*/
double FresnelReflectionCoefficient(
- ON_3dVector N,
+ ON_3dVector N,
ON_3dVector R
) const;
@@ -402,12 +414,12 @@ public:
filename - [in] If nullptr, then any filename matches.
type - [in] If ON_Texture::no_texture_type, then
any texture type matches.
- i0 - [in] If i0 is < 0, the search begins at
+ i0 - [in] If i0 is < 0, the search begins at
m_textures[0], if i0 >= m_textures.Count(),
-1 is returnd, otherwise, the search begins
at m_textures[i0+1].
Example:
- Iterate through all the the bitmap textures on
+ Iterate through all the the bitmap textures on
a material.
ON_Material& mat = ...;
@@ -415,9 +427,9 @@ public:
int bitmap_texture_count = 0;
for(;;)
{
- ti = mat.FindTexture(
- nullptr,
- ON_Texture::TYPE::bitmap_texture,
+ ti = mat.FindTexture(
+ nullptr,
+ ON_Texture::TYPE::bitmap_texture,
ti );
if ( ti < 0 )
@@ -457,14 +469,14 @@ public:
textures to the material. If you need to do something
different, then just work on the m_textures[] array.
*/
- int AddTexture(
+ int AddTexture(
const ON_Texture& tx
);
/*
Description:
If there is a texture with a matching type, that texture's
- filename is modified, otherwise a new texture is added.
+ filename is modified, otherwise a new texture is added.
Parameters:
filename - [in] new filename
type - [in]
@@ -477,7 +489,7 @@ public:
*/
int AddTexture(
const wchar_t* filename,
- ON_Texture::TYPE type
+ ON_Texture::TYPE type
);
/*
@@ -492,15 +504,15 @@ public:
*/
int DeleteTexture(
const wchar_t* filename,
- ON_Texture::TYPE type
+ ON_Texture::TYPE type
);
ON_ObjectArray m_textures;
/*
Description:
- Used to provide per face material support.
- The parent object reference a basic material.
+ Used to provide per face material support.
+ The parent object reference a basic material.
When a brep face or mesh facet wants to use
a material besides the base material, it specifies
a channelSupports material channel. The default
@@ -508,17 +520,17 @@ public:
material. A channel of n > 0 means that face
used the material with id m_material_channel[n-1].
If (n-1) >= m_material_channel.Count(), then the base
- material is used. The value of
+ material is used. The value of
m_material_channel[n].m_id is persistent. The
value of m_material_channel[n].m_i is a runtime
- index in the CRhinoDoc::m_material_table[]. If
+ index in the CRhinoDoc::m_material_table[]. If
CRhinoDoc::m_material_table[m_i].m_uuid != m_id,
then m_id is assumed to be correct.
*/
ON_SimpleArray m_material_channel;
-
+
private:
- ON_UUID m_plugin_id = ON_nil_uuid;
+ ON_UUID m_plugin_id = ON_nil_uuid;
private:
bool Internal_ReadV3( ON_BinaryArchive& archive, int minor_version );
@@ -539,42 +551,179 @@ ON_DLL_TEMPLATE template class ON_CLASS ON_SimpleArray;
ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray;
// NO! // ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray;
-// It is a serious error to have an ON_ClassArray and crashes
+// It is a serious error to have an ON_ClassArray and crashes
// will occur when user data back pointers are not updated.
#endif
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Class ON_PBRMaterial
+//
class ON_CLASS ON_PhysicallyBasedMaterial
{
public:
- class ParametersNames
+ ON_PhysicallyBasedMaterial(const ON_Material& src);
+ ON_PhysicallyBasedMaterial(const ON_PhysicallyBasedMaterial& src);
+ virtual ~ON_PhysicallyBasedMaterial();
+
+ virtual bool IsValid(class ON_TextLog* text_log = nullptr) const;
+
+ /////////////////////////////////////////////////////////////////
+ // Interface
+public:
+ //Call this function to determine if the material supports a PBR definition.
+ //A material will support PBR if the base color is set. All other values are set
+ //to defaults that will produce a simple plaster-like surface.
+ virtual bool Supported(void) const;
+
+ //Reflectance model to use. Default is GGX. Renderers do not need to support a specific
+ //model, but certain material definitions may specify in the hope that a renderer will support.
+ //GGX support is built into Rhino (Cycles, display)
+ enum class BRDFs : int
{
- public:
- static ON_wString BaseColor(void);
- static ON_wString BRDF(void);
- static ON_wString Subsurface(void);
- static ON_wString SubsurfaceScatteringColor(void);
- static ON_wString SubsurfaceScatteringRadius(void);
- static ON_wString Specular(void);
- static ON_wString SpecularTint(void);
- static ON_wString Metallic(void);
- static ON_wString Roughness(void);
- static ON_wString Anisotropic(void);
- static ON_wString AnisotropicRotation(void);
- static ON_wString Sheen(void);
- static ON_wString SheenTint(void);
- static ON_wString Clearcoat(void);
- static ON_wString ClearcoatRoughness(void);
- static ON_wString OpacityIor(void);
- static ON_wString Opacity(void);
- static ON_wString OpacityRoughness(void);
- static ON_wString Emission(void);
- static ON_wString AmbientOcclusion(void);
- static ON_wString Smudge(void);
- static ON_wString Displacement(void);
- static ON_wString Normal(void);
- static ON_wString Bump(void);
+ GGX = 0, //http://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
+ Ward = 1, //https://pdfs.semanticscholar.org/330e/59117d7da6c794750730a15f9a178391b9fe.pdf
};
+
+ virtual BRDFs BRDF(void) const;
+ virtual void SetBRDF(const BRDFs&);
+
+ virtual ON_4fColor BaseColor(void) const;
+ virtual void SetBaseColor(const ON_4fColor&);
+
+ //Controls diffuse shape using a subsurface approximation. If full subsurface transport is
+ //implemented, acts as a mix between diffuse and SSS
+ virtual double Subsurface(void) const;
+ virtual void SetSubsurface(double);
+
+ //Color for full subsurface transport if implemented
+ virtual ON_4fColor SubsurfaceScatteringColor(void) const;
+ virtual void SetSubsurfaceScatteringColor(const ON_4fColor&);
+
+ //Radius for full subsurface transport if implemented
+ virtual double SubsurfaceScatteringRadius(void) const;
+ virtual void SetSubsurfaceScatteringRadius(double);
+
+ //The metallic-ness (0 = dielectric, 1 = metallic). This is a linear blend between two
+ //different models.The metallic model has no diffuse component and also has a tinted incident
+ //specular, equal to the base color
+ virtual double Metallic(void) const;
+ virtual void SetMetallic(double);
+
+ //Incident specular amount. Linked to Ior property below.
+ //specular=((ior−1)/(ior+1))2/0.08
+ virtual double Specular(void) const;
+ virtual void SetSpecular(double);
+
+ //Reflective Ior - see specular amount above. Linked to Specular property
+ virtual double ReflectiveIOR(void) const;
+ virtual void SetReflectiveIOR(double);
+
+ //A concession for artistic control that tints incident specular towards the base color.
+ //Grazing specular is still achromatic.
+ //Tints the facing specular reflection using the base color, while glancing reflection remains white.
+ //Normal dielectrics have colorless reflection, so this parameter is not technically physically correct and is provided for faking the appearance of materials with complex surface structure.
+ virtual double SpecularTint(void) const;
+ virtual void SetSpecularTint(double);
+
+ //Surface roughness, controls both diffuse and specular response
+ virtual double Roughness(void) const;
+ virtual void SetRoughness(double);
+
+ //Degree of anisotropy. This controls the aspect ratio of the specular highlight. (0 = isotropic, 1 = maximally anisotropic)
+ virtual double Anisotropic(void) const;
+ virtual void SetAnisotropic(double);
+
+ //Rotates the direction of anisotropy, with 1.0 going full circle.
+ virtual double AnisotropicRotation(void) const;
+ virtual void SetAnisotropicRotation(double);
+
+ //An additional grazing component, primarily intended for cloth.
+ //Amount of soft velvet like reflection near edges, for simulating materials such as cloth.
+ virtual double Sheen(void) const;
+ virtual void SetSheen(double);
+
+ //Amount to tint sheen towards base color
+ virtual double SheenTint(void) const;
+ virtual void SetSheenTint(double);
+
+ //A second, special-purpose specular lobe - Extra white specular layer on top of others. This is useful for materials like car paint and the like.
+ virtual double Clearcoat(void) const;
+ virtual void SetClearcoat(double);
+
+ //Controls clearcoat glossiness (0 = a “satin” appearance, 1 = a “gloss” appearance)
+ virtual double ClearcoatRoughness(void) const;
+ virtual void SetClearcoatRoughness(double);
+
+ //Index of refraction for transmission.
+ virtual double OpacityIOR(void) const;
+ virtual void SetOpacityIOR(double);
+
+ //Mix between fully opaque surface at zero and fully glass like transmission at one.
+ virtual double Opacity(void) const;
+ virtual void SetOpacity(double);
+
+ //Controls roughness used for transmitted light.
+ virtual double OpacityRoughness(void) const;
+ virtual void SetOpacityRoughness(double);
+
+ //Controls emission - uses base color.
+ virtual ON_4fColor Emission(void) const;
+ 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);
+
+ //Access the referenced ON_Material.
+ ON_Material& Material(void);
+ 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);
+
+public:
+ ON_DEPRECATED 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);
+ };
+
+private:
+ class Impl;
+ Impl* _pImpl;
+ const Impl& Implementation(void) const;
+ Impl& Implementation(void);
+
+ //Ban copying - usage should be material.PhysicallyBased().Function()
+ ON_PhysicallyBasedMaterial& operator=(const ON_Material& src) = delete;
+ ON_PhysicallyBasedMaterial& operator=(const ON_PhysicallyBasedMaterial& src) = delete;
+ friend ON_Material;
};
+
+
#endif
diff --git a/opennurbs_math.cpp b/opennurbs_math.cpp
index 3a6e5b4f..d53b31e2 100644
--- a/opennurbs_math.cpp
+++ b/opennurbs_math.cpp
@@ -3407,6 +3407,14 @@ int ON_Compare2dex( const ON_2dex* a, const ON_2dex* b)
}
+int ON_Compare2udex(const ON_2udex* a, const ON_2udex* b)
+{
+ if (a->i < b->i) return -1;
+ if (b->i < a->i) return 1;
+ return (b->j < a->j) - (a->j < b->j);
+}
+
+
int ON_Compare3dex( const ON_3dex* a, const ON_3dex* b)
{
int d;
diff --git a/opennurbs_math.h b/opennurbs_math.h
index 61516720..f34dd232 100644
--- a/opennurbs_math.h
+++ b/opennurbs_math.h
@@ -450,7 +450,7 @@ private:
#define ON_IS_VALID_FLOAT(x) ((x) != ON_UNSET_FLOAT && (x) != ON_UNSET_POSITIVE_FLOAT && ON_IS_FINITE_FLOAT(x))
#define ON_IS_UNSET_DOUBLE(x) (ON_UNSET_VALUE == (x) || ON_UNSET_POSITIVE_VALUE == (x))
#define ON_IS_UNSET_FLOAT(x) (ON_UNSET_FLOAT == (x) || ON_UNSET_POSITIVE_FLOAT == (x))
-#define ON_IS_NAN(x) (!((x)==(x))
+#define ON_IS_NAN(x) (!((x)==(x)))
ON_DECL
float ON_ArrayDotProduct( // returns AoB
@@ -1631,6 +1631,9 @@ bool ON_TuneupEvaluationParameter(
ON_DECL
int ON_Compare2dex( const ON_2dex* a, const ON_2dex* b);
+ON_DECL
+int ON_Compare2udex(const ON_2udex* a, const ON_2udex* b);
+
ON_DECL
int ON_Compare3dex( const ON_3dex* a, const ON_3dex* b);
@@ -2281,4 +2284,46 @@ Returns
*/
ON_DECL float ON_FloatCeil(double x);
+/*
+Description:
+ Determine if a polyline is convex.
+Parameters:
+ point_dim - [in]
+ 2 or 3
+ point_count - [in]
+ points - [in]
+ If point_count >= 4 and the first and last points are equal,
+ then the zero length segment between those points is ignored.
+ point_stride - [in]
+ number of doubles between points (>=point_dim)
+ bStrictlyConvex - [in]
+ If false, colinear segments are considered convex.
+Returns
+ True if the polyline is convex.
+*/
+ON_DECL bool ON_IsConvexPolyline(
+ size_t point_dim,
+ size_t point_count,
+ const double* points,
+ size_t point_stride,
+ bool bStrictlyConvex
+);
+
+/*
+Description:
+ Determine if a polyline is convex.
+Parameters:
+ points - [in]
+ If points.Count() >= 4 and the first and last points are equal,
+ then the zero length segment between those points is ignored.
+ bStrictlyConvex - [in]
+ If false, colinear segments are considered convex.
+Returns
+ True if the polyline is convex.
+*/
+ON_DECL bool ON_IsConvexPolyline(
+ const ON_SimpleArray& points,
+ bool bStrictlyConvex
+);
+
#endif
diff --git a/opennurbs_mesh.cpp b/opennurbs_mesh.cpp
index 866f3650..10b80ed7 100644
--- a/opennurbs_mesh.cpp
+++ b/opennurbs_mesh.cpp
@@ -282,10 +282,10 @@ bool ON_MeshFace::Repair(
int fvi_count = 0;
f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
- if ( vi[0] >= 0 && vi[0] < mesh_vertex_count )
+ if ( vi[0] >= 0 && vi[0] < mesh_vertex_count && V[vi[0]].IsValid() )
f.vi[fvi_count++] = vi[0];
- if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] )
+ if (vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] && V[vi[1]].IsValid())
{
if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] )
f.vi[fvi_count++] = vi[1];
@@ -294,7 +294,7 @@ bool ON_MeshFace::Repair(
if ( fvi_count < 1 )
return false;
- if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[f.vi[0]] != V[vi[2]] )
+ if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[vi[2]].IsValid() && V[f.vi[0]] != V[vi[2]] )
{
if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] )
f.vi[fvi_count++] = vi[2];
@@ -303,7 +303,7 @@ bool ON_MeshFace::Repair(
if ( fvi_count < 2 )
return false;
- if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] )
+ if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[vi[3]].IsValid() && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] )
{
if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] )
f.vi[fvi_count++] = vi[3];
@@ -336,10 +336,10 @@ bool ON_MeshFace::Repair(
int fvi_count = 0;
f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
- if ( vi[0] >= 0 && vi[0] < mesh_vertex_count )
+ if ( vi[0] >= 0 && vi[0] < mesh_vertex_count && V[vi[0]].IsValid() )
f.vi[fvi_count++] = vi[0];
- if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] )
+ if (vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] && V[vi[1]].IsValid())
{
if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] )
f.vi[fvi_count++] = vi[1];
@@ -348,7 +348,7 @@ bool ON_MeshFace::Repair(
if ( fvi_count < 1 )
return false;
- if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[f.vi[0]] != V[vi[2]] )
+ if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[vi[2]].IsValid() && V[f.vi[0]] != V[vi[2]] )
{
if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] )
f.vi[fvi_count++] = vi[2];
@@ -357,7 +357,7 @@ bool ON_MeshFace::Repair(
if ( fvi_count < 2 )
return false;
- if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] )
+ if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[vi[3]].IsValid() && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] )
{
if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] )
f.vi[fvi_count++] = vi[3];
@@ -3741,8 +3741,16 @@ bool ON_Mesh::IsEmpty() const
{
// Deep sigh.
// The "this == &ON_Mesh::Empty" check is to handle the case when
- // confused developers const cast ON_Mesh::Empty and modify it.
- return (0 == VertexUnsignedCount() && 0 == FaceUnsignedCount());
+ // confused developers const cast ON_Mesh::Empty and then modify it.
+ return (0 == VertexUnsignedCount() && 0 == FaceUnsignedCount()) || (this == &ON_Mesh::Empty);
+}
+
+bool ON_Mesh::IsNotEmpty() const
+{
+ // Deep sigh.
+ // The "this != &ON_Mesh::Empty" check is to handle the case when
+ // confused developers const cast ON_Mesh::Empty and then modify it.
+ return ( VertexUnsignedCount() > 0 && FaceUnsignedCount() > 0 && this != &ON_Mesh::Empty);
}
bool ON_Mesh::SetVertex(
@@ -4886,6 +4894,17 @@ static int CompareMeshPoint(const void* a,const void* b,void* ptr)
return 0;
}
+unsigned int ON_Mesh::RemoveAllCreases()
+{
+ unsigned int vertex_count0 = this->VertexUnsignedCount();
+ bool bChanged = this->CombineIdenticalVertices(true, true);
+ const unsigned int vertex_count1 = this->VertexUnsignedCount();
+
+ if (vertex_count0 == vertex_count1 && bChanged)
+ vertex_count0 = vertex_count1 + 1;
+ return (vertex_count0 - vertex_count1);
+}
+
bool ON_Mesh::CombineIdenticalVertices(
bool bIgnoreVertexNormals,
bool bIgnoreTextureCoordinates
@@ -5121,6 +5140,7 @@ bool ON_Mesh::CombineIdenticalVertices(
mesh.DestroyPartition();
mesh.DestroyTopology();
+ mesh.m_S.Destroy();
if ( mesh.m_V.Capacity() > 4*mesh.m_V.Count() && mesh.m_V.Capacity() > 50 )
{
@@ -5701,7 +5721,17 @@ void ON_MeshParameters::SetSimplePlanes(
Internal_SetBoolHelper(bSimplePlanes, &m_bSimplePlanes);
}
-
+void ON_MeshParameters::SetSubDDisplayParameters(
+ const ON_SubDDisplayParameters& subd_parameters
+)
+{
+ m_subd_mesh_parameters = subd_parameters.EncodeAsUnsignedChar();
+}
+
+const ON_SubDDisplayParameters ON_MeshParameters::SubDDisplayParameters() const
+{
+ return ON_SubDDisplayParameters::DecodeFromUnsignedChar(m_subd_mesh_parameters);
+}
const bool ON_MeshParameters::Refine() const
{
@@ -5822,23 +5852,6 @@ void ON_MeshParameters::SetTolerance(
Internal_SetDoubleHelper(tolerance, 0.0, ON_UNSET_VALUE, &m_tolerance);
}
-unsigned int ON_MeshParameters::SubDDisplayMeshDensity() const
-{
- // If this was set using the "slider",
- const int mesh_density_percentage = GeometrySettingsDensityPercentage(-1);
-
- const double relative_density
- = (mesh_density_percentage >= 0)
- ? (((double)mesh_density_percentage) / 100.0)
- : RelativeTolerance();
-
- if (false == (relative_density >= 0.0 && relative_density <= 1.0))
- return ON_SubDLimitMesh::DefaultDisplayDensity;
-
- unsigned int subd_mesh_density = (unsigned int)floor(relative_density*ON_SubDLimitMesh::MaximumDisplayDensity);
- return subd_mesh_density;
-}
-
const double ON_MeshParameters::RelativeTolerance() const
{
return m_relative_tolerance;
@@ -6023,6 +6036,26 @@ ON_MeshParameters::ON_MeshParameters(
SetRelativeTolerance(density);
SetRefine((density < 0.65));
SetSimplePlanes((0.0 == density));
+
+
+ unsigned int subd_display_density = ON_SubDDisplayParameters::Default.DisplayDensity();
+
+ if (density <= ON_ZERO_TOLERANCE)
+ subd_display_density = 1;
+ else if (density < 1.0/6.0)
+ subd_display_density = ON_SubDDisplayParameters::CourseDensity;
+ else if (density < 1.0/3.0)
+ subd_display_density = (ON_SubDDisplayParameters::DefaultDensity+ON_SubDDisplayParameters::CourseDensity)/2;
+ else if (density <= 0.75)
+ subd_display_density = ON_SubDDisplayParameters::DefaultDensity;
+ else if (density <= 1.0-ON_ZERO_TOLERANCE)
+ subd_display_density = (ON_SubDDisplayParameters::DefaultDensity+ON_SubDDisplayParameters::MaximumDensity)/2;
+ else if (density >= 1.0 - ON_ZERO_TOLERANCE)
+ subd_display_density = ON_SubDDisplayParameters::MaximumDensity;
+
+ ON_SubDDisplayParameters subd_parameters(ON_SubDDisplayParameters::Default);
+ subd_parameters.SetDisplayDensity(subd_display_density);
+ SetSubDDisplayParameters(subd_parameters);
}
}
@@ -7838,8 +7871,19 @@ void ON_Mesh::DeleteMeshParameters()
}
}
+static bool isValid3fPoint(const ON_3fPoint* a)
+{
+ return (ON_IS_VALID_FLOAT(a->x) && ON_IS_VALID_FLOAT(a->y) && ON_IS_VALID_FLOAT(a->z)) ? true : false;
+}
+
static int compare3fPoint( const ON_3fPoint* a, const ON_3fPoint* b )
{
+ const bool aValid = isValid3fPoint(a);
+ const bool bValid = isValid3fPoint(b);
+ if (aValid != bValid)
+ return (aValid ? -1 : 1); // invalid points sort to end.
+ if (false == aValid)
+ return 0; // all invalid points are "equal"
if ( a->x < b->x ) return -1;
if ( a->x > b->x ) return 1;
if ( a->y < b->y ) return -1;
@@ -7849,8 +7893,20 @@ static int compare3fPoint( const ON_3fPoint* a, const ON_3fPoint* b )
return 0;
}
+
+static bool isValid3dPoint(const ON_3dPoint* a)
+{
+ return (ON_IS_VALID(a->x) && ON_IS_VALID(a->y) && ON_IS_VALID(a->z)) ? true : false;
+}
+
static int compare3dPoint( const ON_3dPoint* a, const ON_3dPoint* b )
{
+ const bool aValid = isValid3dPoint(a);
+ const bool bValid = isValid3dPoint(b);
+ if (aValid != bValid)
+ return (aValid ? -1 : 1); // invalid points sort to end.
+ if (false == aValid)
+ return 0; // all invalid points are "equal"
if ( a->x < b->x ) return -1;
if ( a->x > b->x ) return 1;
if ( a->y < b->y ) return -1;
@@ -7863,7 +7919,12 @@ static int compare3dPoint( const ON_3dPoint* a, const ON_3dPoint* b )
typedef int (*ON_COMPAR_LPVOID_LPVOID)(const void*,const void*);
static
-unsigned int GetPointMap(unsigned int pt_count, const ON_3fPoint* fV, const ON_3dPoint* dV, ON_SimpleArray& pt_map)
+unsigned int GetRemoveDegenerateFacesPointMap(
+ unsigned int pt_count,
+ const ON_3fPoint* fV,
+ const ON_3dPoint* dV,
+ ON_SimpleArray& pt_map
+)
{
// Faster than ON_Mesh::GetVertexLocationIds()
// This static is used only in CullDegenerateFaces().
@@ -7904,6 +7965,15 @@ unsigned int GetPointMap(unsigned int pt_count, const ON_3fPoint* fV, const ON_3
map[index[vt0++]] = max_pt_index;
}
}
+
+ // invalid points are sorted to the end
+ vt1 = pt_count;
+ while (vt1-- > 0)
+ {
+ if (isValid3dPoint(dV + index[vt1]))
+ break;
+ map[index[vt1]] = ON_UNSET_UINT_INDEX; // invalid point get invalid index
+ }
}
else
{
@@ -7917,6 +7987,15 @@ unsigned int GetPointMap(unsigned int pt_count, const ON_3fPoint* fV, const ON_3
map[index[vt0++]] = max_pt_index;
}
}
+
+ // invalid points are sorted to the end
+ vt1 = pt_count;
+ while (vt1-- > 0)
+ {
+ if (isValid3fPoint(fV + index[vt1]))
+ break;
+ map[index[vt1]] = ON_UNSET_UINT_INDEX; // invalid point get invalid index
+ }
}
onfree(index);
}
@@ -7944,6 +8023,49 @@ unsigned int ON_Mesh::CullDegenerateFaces()
return (face_count0 > face_count1) ? face_count0 - face_count1 : 0;
}
+unsigned int ON_Mesh::CullDegenerates()
+{
+ const int mesh_vertex_count0 = VertexCount();
+ const int mesh_face_count0 = FaceCount();
+ const int mesh_quad_count0 = QuadCount();
+ const int mesh_tri_count0 = TriangleCount();
+
+ // now cull bad faces, invalid vertices, and unreferenced vertices
+ DeleteComponents(
+ nullptr,
+ 0,
+ true,
+ true,
+ true,
+ true
+ );
+
+ const int mesh_vertex_count1 = VertexUnsignedCount();
+ const int mesh_face_count1 = FaceUnsignedCount();
+ const int mesh_quad_count1 = QuadCount();
+ const int mesh_tri_count1 = TriangleCount();
+ if (
+ mesh_vertex_count0 == mesh_vertex_count1
+ && mesh_face_count0 == mesh_face_count1
+ && mesh_quad_count0 == mesh_quad_count1
+ && mesh_tri_count0 == mesh_tri_count1
+ )
+ return 0;
+
+ int rc = abs(mesh_vertex_count1 - mesh_vertex_count0) + abs(mesh_face_count1 - mesh_face_count0);
+ if (0 == rc)
+ {
+ // need to return nonzero if a degenerate quad got changed into a triangle
+ rc = abs(mesh_quad_count1 - mesh_quad_count0);
+ if (0 == rc)
+ {
+ rc = abs(mesh_tri_count1 - mesh_tri_count0);
+ }
+ }
+
+ return (unsigned int)rc;
+}
+
int ON_Mesh::CullUnusedVertices()
{
const unsigned int vcount0 = m_V.UnsignedCount();
@@ -8553,6 +8675,82 @@ int* ON_MeshTopology::GetIntArray(int length)
return a;
}
+bool ON_MeshTopology::IsWeldedEdge(int top_ei) const
+{
+ if (top_ei < 0 || top_ei >= m_tope.Count() || nullptr == m_mesh)
+ return false;
+
+ const ON_MeshTopologyEdge& e = m_tope[top_ei];
+ if (e.m_topf_count <= 1 || e.m_topvi[0] < 0 || e.m_topvi[1] < 0)
+ return false;
+
+ const int face_count = m_topf.Count();
+ if (face_count < 2 || face_count != m_mesh->FaceCount())
+ return false;
+
+ if (e.m_topvi[0] < 0 || e.m_topvi[1] < 0 || e.m_topvi[0] == e.m_topvi[1] )
+ return false;
+ const int topv_count = m_topv.Count();
+ if (e.m_topvi[0] >= topv_count || e.m_topvi[1] >= topv_count || topv_count < 3)
+ return false;
+
+ const int meshv_count = m_mesh->VertexCount();
+ if (meshv_count < topv_count || meshv_count != m_topv_map.Count())
+ return false;
+ if (1 == m_topv[e.m_topvi[0]].m_v_count && 1 == m_topv[e.m_topvi[0]].m_v_count)
+ return true;
+
+ // need to examine faces
+ int mesh_vi[2] = { -1,-1 };
+ for (int efi = 0; efi < e.m_topf_count; ++efi)
+ {
+ int top_fi = e.m_topfi[efi];
+ if (top_fi < 0 || top_fi >= face_count)
+ return false;
+ const ON_MeshTopologyFace& f = m_topf[top_fi];
+ const int fe_count = f.IsTriangle() ? 3 : 4;
+ int fvi[2] = { -1, -1 };
+ for (int fei = 0; fei < fe_count; ++fei)
+ {
+ if (top_ei != f.m_topei[fei])
+ continue;
+ const ON_MeshFace& mf = m_mesh->m_F[top_fi];
+ const bool bRev = f.m_reve[fei];
+ fvi[bRev?1:0] = mf.vi[(fei + fe_count - 1) % fe_count];
+ fvi[bRev?0:1] = mf.vi[fei];
+ if (fvi[0] < 0 || fvi[0] >= meshv_count)
+ return false;
+ if (fvi[1] < 0 || fvi[1] >= meshv_count)
+ return false;
+ if (m_topv_map[fvi[0]] != e.m_topvi[0] || m_topv_map[fvi[1]] != e.m_topvi[1])
+ {
+ ON_ERROR("Bug in this loop or bad mesh topology.");
+ fvi[0] = -1;
+ fvi[1] = -1;
+ continue;
+ }
+ break;
+ }
+ if (0 == efi)
+ {
+ if (fvi[0] < 0 || fvi[1] < 0 || fvi[0] == fvi[1])
+ return false;
+ mesh_vi[0] = fvi[0];
+ mesh_vi[1] = fvi[1];
+ }
+ else
+ {
+ if (mesh_vi[0] != fvi[0])
+ return false;
+ if (mesh_vi[1] != fvi[1])
+ return false;
+ }
+ }
+
+ return (mesh_vi[0] >= 0 && mesh_vi[1]);
+}
+
+
bool ON_MeshTopologyFace::IsTriangle() const
{
return ( m_topei[2] == m_topei[3] && m_topei[0] != m_topei[1] )
@@ -10489,7 +10687,7 @@ bool ON_Mesh::IsValidMeshComponentIndex(
break;
case ON_COMPONENT_INDEX::mesh_face:
- if (ci.m_type >= m_F.Count())
+ if (ci.m_index >= m_F.Count())
rc = false;
break;
@@ -10768,6 +10966,16 @@ ON_MappingTag::ON_MappingTag()
Default();
}
+ON_MappingTag::ON_MappingTag(const ON_TextureMapping & mapping, const ON_Xform * xform)
+{
+ Default();
+ Set(mapping);
+ if (
+ ON_TextureMapping::TYPE::no_mapping != mapping.m_type && ON_TextureMapping::TYPE::srfp_mapping != mapping.m_type
+ && nullptr != xform && xform->IsValid() && false == xform->IsIdentity(ON_ZERO_TOLERANCE) && false == xform->IsZero())
+ m_mesh_xform = *xform;
+}
+
void ON_MappingTag::Dump( ON_TextLog& text_log ) const
{
text_log.Print("Texture/color coordinates tag:\n");
@@ -11575,43 +11783,43 @@ static int compare2dPoint(const void* a, const void* b)
return 0;
}
-static int compare3fPoint(const void* a, const void* b)
-{
- const float* af = (const float*)a;
- const float* bf = (const float*)b;
- if (af[0] < bf[0])
- return -1;
- if (af[0] > bf[0])
- return 1;
- if (af[1] < bf[1])
- return -1;
- if (af[1] > bf[1])
- return 1;
- if (af[2] < bf[2])
- return -1;
- if (af[2] > bf[2])
- return 1;
- return 0;
-}
-
-static int compare3dPoint(const void* a, const void* b)
-{
- const double* af = (const double*)a;
- const double* bf = (const double*)b;
- if (af[0] < bf[0])
- return -1;
- if (af[0] > bf[0])
- return 1;
- if (af[1] < bf[1])
- return -1;
- if (af[1] > bf[1])
- return 1;
- if (af[2] < bf[2])
- return -1;
- if (af[2] > bf[2])
- return 1;
- return 0;
-}
+//static int compare3fPoint(const void* a, const void* b)
+//{
+// const float* af = (const float*)a;
+// const float* bf = (const float*)b;
+// if (af[0] < bf[0])
+// return -1;
+// if (af[0] > bf[0])
+// return 1;
+// if (af[1] < bf[1])
+// return -1;
+// if (af[1] > bf[1])
+// return 1;
+// if (af[2] < bf[2])
+// return -1;
+// if (af[2] > bf[2])
+// return 1;
+// return 0;
+//}
+//
+//static int compare3dPoint(const void* a, const void* b)
+//{
+// const double* af = (const double*)a;
+// const double* bf = (const double*)b;
+// if (af[0] < bf[0])
+// return -1;
+// if (af[0] > bf[0])
+// return 1;
+// if (af[1] < bf[1])
+// return -1;
+// if (af[1] > bf[1])
+// return 1;
+// if (af[2] < bf[2])
+// return -1;
+// if (af[2] > bf[2])
+// return 1;
+// return 0;
+//}
static int comparedUnsignedPair(const void* a, const void* b)
@@ -11672,9 +11880,9 @@ static unsigned int* ON_GetPointLocationIdsHelper(
{
// Dictionary sort the 3d points (sort on x, then y, then z).
if (nullptr != dPoints)
- ON_Sort(ON::sort_algorithm::quick_sort, Vid, dPoints, Vcount, point_stride*sizeof(dPoints[0]), compare3dPoint);
+ ON_Sort(ON::sort_algorithm::quick_sort, Vid, dPoints, Vcount, point_stride*sizeof(dPoints[0]), (ON_COMPAR_LPVOID_LPVOID)compare3dPoint);
else
- ON_Sort(ON::sort_algorithm::quick_sort, Vid, fPoints, Vcount, point_stride*sizeof(fPoints[0]), compare3fPoint);
+ ON_Sort(ON::sort_algorithm::quick_sort, Vid, fPoints, Vcount, point_stride*sizeof(fPoints[0]), (ON_COMPAR_LPVOID_LPVOID)compare3fPoint);
}
// Assign a unique temporary id to each group of coincident points.
@@ -12143,17 +12351,112 @@ bool ON_Mesh::DeleteComponents(
}
bool ON_Mesh::DeleteComponents(
- const ON_COMPONENT_INDEX* ci_list,
- size_t ci_count,
- bool bIgnoreInvalidComponents,
- bool bRemoveDegenerateFaces,
- bool bRemoveUnusedVertices,
- bool bRemoveEmptyNgons
- )
+ const ON_COMPONENT_INDEX* ci_list,
+ size_t ci_count,
+ bool bIgnoreInvalidComponents,
+ bool bRemoveDegenerateFaces,
+ bool bRemoveUnusedVertices,
+ bool bRemoveEmptyNgons
+)
{
- if ( ci_count <= 0 && false == bRemoveUnusedVertices && false == bRemoveEmptyNgons )
+ return DeleteComponents(ci_list, ci_count,
+ bIgnoreInvalidComponents, bRemoveDegenerateFaces, bRemoveUnusedVertices, bRemoveEmptyNgons,
+ nullptr);
+}
+
+static int Internal_FaceDegenerateAreaCheck(
+ ON_MeshFace& f,
+ int vertex_count,
+ const ON_3fPoint* fV,
+ const ON_3dPoint* dV
+)
+{
+ // returns:
+ // 0: f is degenerate
+ // 1: f is not degenerate
+ // 2: f was a degenerate quad and fixed to be a good triangle
+ ON_3dPoint V[4];
+ double a[2];
+ const double atol = 1.0e-36; // a hair bigger than the smallest positive normalized float.
+ if (nullptr != dV)
+ {
+ V[0] = dV[f.vi[0]];
+ V[1] = dV[f.vi[1]];
+ V[2] = dV[f.vi[2]];
+ V[3] = dV[f.vi[3]];
+ }
+ else
+ {
+ V[0] = fV[f.vi[0]];
+ V[1] = fV[f.vi[1]];
+ V[2] = fV[f.vi[2]];
+ V[3] = fV[f.vi[3]];
+ }
+
+ if (f.IsTriangle())
+ {
+ a[0] = ON_CrossProduct(V[1]-V[0],V[2]-V[0]).Length();
+ return (a[0] > atol) ? 1 : 0;
+ }
+
+ a[0] = V[0].DistanceTo(V[2]);
+ a[1] = V[1].DistanceTo(V[3]);
+
+ // discard L shaped quads
+ if (false == (a[0] > atol && a[1] > atol))
+ return 0;
+
+ if (a[0] <= a[1] * 1e-8)
+ return 0; // quad is L shaped at floating point precision
+ if (a[1] <= a[0] * 1e-8)
+ return 0; // quad is L shaped at floating point precision
+
+ if (a[0] <= a[1])
+ {
+ a[0] = ON_CrossProduct(V[1] - V[0], V[2] - V[0]).Length();
+ a[1] = ON_CrossProduct(V[2] - V[0], V[3] - V[0]).Length();
+ if (a[0] > atol)
+ {
+ if (a[1] > atol)
+ return 1;
+ f.vi[3] = -1;
+ }
+ else if (a[1] > atol)
+ f.vi[1] = -1;
+ }
+ else if (a[1] < a[0] )
+ {
+ a[0] = ON_CrossProduct(V[2] - V[1], V[3] - V[1]).Length();
+ a[1] = ON_CrossProduct(V[3] - V[1], V[2] - V[1]).Length();
+ if (a[0] > atol)
+ {
+ if (a[1] > atol)
+ return 1;
+ f.vi[0] = -1;
+ }
+ else if (a[1] > atol)
+ f.vi[2] = -1;
+ }
+ else
+ return 0; // nan snuck through
+
+ const bool bRepaired = (nullptr != dV) ? f.Repair(vertex_count, dV) : f.Repair(vertex_count, fV);
+ return bRepaired ? 2 : 0;
+}
+
+bool ON_Mesh::DeleteComponents(
+ const ON_COMPONENT_INDEX* ci_list,
+ size_t ci_count,
+ bool bIgnoreInvalidComponents,
+ bool bRemoveDegenerateFaces,
+ bool bRemoveUnusedVertices,
+ bool bRemoveEmptyNgons,
+ unsigned int* faceMap
+)
+{
+ if (ci_count <= 0 && false == bRemoveUnusedVertices && false == bRemoveEmptyNgons && false == bRemoveDegenerateFaces)
return true;
- if ( 0 == ci_list && ci_count > 0 )
+ if (0 == ci_list && ci_count > 0)
return false;
const ON_MeshTopology* top = m_top.IsValid() ? &m_top : 0;
@@ -12168,38 +12471,38 @@ bool ON_Mesh::DeleteComponents(
unsigned int* point_id_map_fvi = (unsigned int*)point_id_map_f.vi;
- if ( false == bIgnoreInvalidComponents )
+ if (false == bIgnoreInvalidComponents)
{
- for ( size_t i = 0; i < ci_count; i++ )
+ for (size_t i = 0; i < ci_count; i++)
{
cdex = (unsigned int)(ci_list[i].m_index);
- switch(ci_list[i].m_type)
+ switch (ci_list[i].m_type)
{
case ON_COMPONENT_INDEX::mesh_vertex:
- if ( cdex >= vertex_count0 )
+ if (cdex >= vertex_count0)
return false;
break;
case ON_COMPONENT_INDEX::meshtop_vertex:
- if ( cdex >= topvertex_count0 )
+ if (cdex >= topvertex_count0)
return false;
break;
case ON_COMPONENT_INDEX::meshtop_edge:
- if ( cdex >= topedge_count0 )
+ if (cdex >= topedge_count0)
return false;
break;
case ON_COMPONENT_INDEX::mesh_face:
- if ( cdex >= face_count0 )
+ if (cdex >= face_count0)
return false;
break;
case ON_COMPONENT_INDEX::mesh_ngon:
- if ( cdex >= ngon_count0 )
+ if (cdex >= ngon_count0)
return false;
break;
}
}
}
- if ( vertex_count0 <= 0 )
+ if (vertex_count0 <= 0)
return (face_count0 <= 0);
ON_SimpleArray vertex_status_buffer(vertex_count0);
@@ -12207,10 +12510,19 @@ bool ON_Mesh::DeleteComponents(
vertex_status_buffer.Zero();
unsigned int* vertex_status = vertex_status_buffer.Array();
- ON_SimpleArray face_status_buffer(face_count0);
- face_status_buffer.SetCount(face_count0);
- face_status_buffer.Zero();
- unsigned int* face_status = face_status_buffer.Array();
+ ON_SimpleArray face_status_buffer;
+ unsigned int* face_status;
+ if (faceMap == nullptr)
+ {
+ face_status_buffer.SetCapacity(face_count0);
+ face_status_buffer.SetCount(face_count0);
+ face_status = face_status_buffer.Array();
+ }
+ else
+ {
+ face_status = faceMap;
+ }
+ memset(face_status, 0, m_F.UnsignedCount() * sizeof(unsigned int));
bool bDoomedFaces = false;
bool bDoomedVertices = false;
@@ -12219,13 +12531,13 @@ bool ON_Mesh::DeleteComponents(
const unsigned int* fvi;
- for ( size_t i = 0; i < ci_count; i++ )
+ for (size_t i = 0; i < ci_count; i++)
{
cdex = (unsigned int)(ci_list[i].m_index);
- switch(ci_list[i].m_type)
+ switch (ci_list[i].m_type)
{
case ON_COMPONENT_INDEX::mesh_vertex:
- if ( cdex < vertex_count0 )
+ if (cdex < vertex_count0)
{
bDoomedVertices = true;
vertex_status[cdex] = ON_UNSET_UINT_INDEX;
@@ -12233,42 +12545,42 @@ bool ON_Mesh::DeleteComponents(
break;
case ON_COMPONENT_INDEX::meshtop_vertex:
- if ( cdex < topvertex_count0 )
+ if (cdex < topvertex_count0)
{
const ON_MeshTopologyVertex& tv = top->m_topv[cdex];
- for ( int tvi = 0; tvi < tv.m_v_count; tvi++ )
+ for (int tvi = 0; tvi < tv.m_v_count; tvi++)
{
- const unsigned int vi =(unsigned int)(tv.m_vi[tvi]);
- if ( vi < vertex_count0 )
+ const unsigned int vi = (unsigned int)(tv.m_vi[tvi]);
+ if (vi < vertex_count0)
{
bDoomedVertices = true;
vertex_status[vi] = ON_UNSET_UINT_INDEX;
}
- }
+ }
}
break;
case ON_COMPONENT_INDEX::meshtop_edge:
- if ( cdex < topedge_count0 )
+ if (cdex < topedge_count0)
{
const ON_MeshTopologyEdge& te = top->m_tope[cdex];
- if ( 0 != te.m_topfi )
+ if (0 != te.m_topfi)
{
- for ( int j = 0; j < te.m_topf_count; j++ )
+ for (int j = 0; j < te.m_topf_count; j++)
{
- fi =(unsigned int)(te.m_topfi[j]);
- if ( fi < face_count0 )
+ fi = (unsigned int)(te.m_topfi[j]);
+ if (fi < face_count0)
{
bDoomedFaces = true;
face_status[fi] = ON_UNSET_UINT_INDEX;
}
- }
+ }
}
}
break;
case ON_COMPONENT_INDEX::mesh_face:
- if ( cdex < face_count0 )
+ if (cdex < face_count0)
{
bDoomedFaces = true;
face_status[cdex] = ON_UNSET_UINT_INDEX;
@@ -12276,25 +12588,25 @@ bool ON_Mesh::DeleteComponents(
break;
case ON_COMPONENT_INDEX::mesh_ngon:
- if ( cdex < ngon_count0 )
+ if (cdex < ngon_count0)
{
const ON_MeshNgon* ngon = Ngon(cdex);
- if ( 0 != ngon )
+ if (0 != ngon)
{
const int ngon_index = cdex;
bDoomedNgons = true;
m_NgonMap.SetCount(0);
- if ( 0 != ngon->m_fi )
+ if (0 != ngon->m_fi)
{
- for ( unsigned int j = 0; j < ngon->m_Fcount; j++ )
+ for (unsigned int j = 0; j < ngon->m_Fcount; j++)
{
fi = ngon->m_fi[j];
- if ( fi < face_count0 )
+ if (fi < face_count0)
{
bDoomedFaces = true;
face_status[fi] = ON_UNSET_UINT_INDEX;
}
- }
+ }
}
RemoveNgon(ngon_index);
}
@@ -12308,12 +12620,25 @@ bool ON_Mesh::DeleteComponents(
const unsigned int* point_id_map = nullptr;
if (bRemoveDegenerateFaces)
{
+ // point_id_map[vi0] == point_id_map[vi1] if and only if m_V[vi0] == m_V[vi1].
+ const unsigned int point_count = m_V.UnsignedCount();
ON_3fPoint* fV = m_V.Array();
ON_3dPoint* dV = HasDoublePrecisionVertices() ? DoublePrecisionVertices().Array() : 0;
- point_id_count = GetPointMap(m_V.UnsignedCount(), fV, dV, point_id_map_buffer);
+ if (nullptr != dV)
+ {
+ ON_3dPoint* p1 = dV + point_count;
+ const ON_3dPoint badp(ON_DBL_MAX, ON_DBL_MAX, ON_DBL_MAX);
+ for (ON_3dPoint* p = dV; p < p1; ++p)
+ {
+ if (p->IsValid())
+ continue;
+ *p = badp; // so bad points will sort las
+ }
+ }
+ point_id_count = GetRemoveDegenerateFacesPointMap(point_count, fV, dV, point_id_map_buffer);
if (point_id_count > 0
&& point_id_count <= point_id_map_buffer.UnsignedCount()
- && m_V.UnsignedCount() == point_id_map_buffer.UnsignedCount()
+ && point_count == point_id_map_buffer.UnsignedCount()
)
{
point_id_map = point_id_map_buffer.Array();
@@ -12325,103 +12650,89 @@ bool ON_Mesh::DeleteComponents(
}
unsigned int face_count1 = 0;
- for ( fi = 0; fi < face_count0; fi++ )
{
- if ( 0 == face_status[fi] )
+ const ON_3fPoint* fV = m_V.Array();
+ const ON_3dPoint* dV = HasDoublePrecisionVertices() ? DoublePrecisionVertices().Array() : 0;
+ for (fi = 0; fi < face_count0; fi++)
{
- fvi = (const unsigned int*)m_F[fi].vi;
- for ( unsigned int j = 0; j < 4; j++ )
+ if (0 != face_status[fi])
+ continue; // this face status is already known
+
+ ON_MeshFace& f0 = m_F[fi];
+ fvi = (const unsigned int*)f0.vi;
+ for (unsigned int j = 0; j < 4; j++)
{
- if ( fvi[j] >= vertex_count0 || ON_UNSET_UINT_INDEX == vertex_status[fvi[j]] )
+ if (fvi[j] >= vertex_count0 || ON_UNSET_UINT_INDEX == vertex_status[fvi[j]])
{
bDoomedFaces = true;
face_status[fi] = ON_UNSET_UINT_INDEX;
break;
}
}
- if ( 0 == face_status[fi] )
+ if (ON_UNSET_UINT_INDEX == face_status[fi])
+ continue; // f0 has invalid vertex indices
+
+ if (point_id_count > 0)
{
- if (point_id_count > 0)
+ // if point_id_count > 0, then bRemoveDegenerateFaces = true.
+ // set point_id_map_f.vi[] to values of "topological indices"
+ point_id_map_fvi[0] = point_id_map[f0.vi[0]];
+ point_id_map_fvi[1] = point_id_map[f0.vi[1]];
+ point_id_map_fvi[2] = point_id_map[f0.vi[2]];
+ point_id_map_fvi[3] = point_id_map[f0.vi[3]];
+ if (false == point_id_map_f.IsValid(point_id_count))
{
- ON_MeshFace& f0 = m_F[fi];
- // set f.vi[] to values of topological indices
+ // point_id_map_f invalid means we have a degenerate quad or worse.
+
+ if (f0.IsQuad() && (point_id_map_f.vi[0] == point_id_map_f.vi[2] || point_id_map_f.vi[1] == point_id_map_f.vi[3]))
{
- unsigned int f0vi = (unsigned int)f0.vi[0];
- point_id_map_fvi[0] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi];
-
- f0vi = (unsigned int)f0.vi[1];
- point_id_map_fvi[1] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi];
-
- f0vi = (unsigned int)f0.vi[2];
- point_id_map_fvi[2] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi];
-
- f0vi = (unsigned int)f0.vi[3];
- point_id_map_fvi[3] = (f0vi >= vertex_count0) ? ON_UNSET_UINT_INDEX : point_id_map[f0vi];
+ // "L" quads just get deleted.
+ bDoomedFaces = true;
+ face_status[fi] = ON_UNSET_UINT_INDEX;
+ continue;
}
- if (!point_id_map_f.IsValid(point_id_count))
+ // the corners of face f0 are not perfect. We either have to convert a quad to a triangle
+ // or we have a degenerate face.
+ if ((nullptr != dV) ? f0.Repair(vertex_count0, dV) : f0.Repair(vertex_count0, fV))
{
- // determine if a degenerate quad can be made into a valid triangle.
- if (point_id_map_fvi[0] == point_id_map_fvi[1] || point_id_map_fvi[0] >= point_id_count)
- {
- f0.vi[0] = f0.vi[1];
- f0.vi[1] = f0.vi[2];
- f0.vi[2] = f0.vi[3];
- point_id_map_fvi[0] = point_id_map_fvi[1];
- point_id_map_fvi[1] = point_id_map_fvi[2];
- point_id_map_fvi[2] = point_id_map_fvi[3];
- }
-
- if (point_id_map_fvi[1] == point_id_map_fvi[2] || point_id_map_fvi[1] >= point_id_count)
- {
- f0.vi[1] = f0.vi[2];
- f0.vi[2] = f0.vi[3];
- point_id_map_fvi[1] = point_id_map_fvi[2];
- point_id_map_fvi[2] = point_id_map_fvi[3];
- }
-
- if (point_id_map_fvi[2] >= point_id_count)
- {
- f0.vi[2] = f0.vi[3];
- point_id_map_fvi[2] = point_id_map_fvi[3];
- }
-
- if (point_id_map_fvi[3] >= point_id_count)
- {
- f0.vi[3] = f0.vi[2];
- point_id_map_fvi[3] = point_id_map_fvi[2];
- }
- else if (point_id_map_fvi[0] == point_id_map_fvi[3] && point_id_map_fvi[2] != point_id_map_fvi[3])
- {
- f0.vi[0] = f0.vi[1];
- f0.vi[1] = f0.vi[2];
- f0.vi[2] = f0.vi[3];
- point_id_map_fvi[0] = point_id_map_fvi[1];
- point_id_map_fvi[1] = point_id_map_fvi[2];
- point_id_map_fvi[2] = point_id_map_fvi[3];
- }
-
- if (!f0.IsValid(vertex_count0) || !point_id_map_f.IsValid(point_id_count))
- {
- // face cannot be repaired by juggling vertex indices
- bDoomedFaces = true;
- face_status[fi] = ON_UNSET_UINT_INDEX;
- continue;
- }
- else
- {
- bModifiedFaces = true;
- }
-
+ // f0 what an invalid quad fixed to be a valid triangle
+ bModifiedFaces = true;
+ }
+ else
+ {
+ // face cannot be repaired by juggling vertex indices
+ bDoomedFaces = true;
+ face_status[fi] = ON_UNSET_UINT_INDEX;
+ continue;
}
}
-
- face_status[fi] = face_count1++;
- for ( unsigned int j = 0; j < 4; j++ )
+ switch (Internal_FaceDegenerateAreaCheck(f0, (int)vertex_count0, fV, dV))
{
- vertex_status[fvi[j]] = 1; // vertex is referenced by a face
+ case 0:
+ bDoomedFaces = true;
+ face_status[fi] = ON_UNSET_UINT_INDEX;
+ break;
+
+ case 1:
+ // f0 is totally valid
+ break;
+
+ case 2:
+ // f0 what an invalid quad fixed to be a valid triangle
+ bModifiedFaces = true;
+ break;
}
+
+ if (ON_UNSET_UINT_INDEX == face_status[fi])
+ continue;
+ }
+
+ face_status[fi] = face_count1++;
+ for (unsigned int j = 0; j < 4; j++)
+ {
+ vertex_status[fvi[j]] = 1; // vertex is referenced by a face
}
}
}
@@ -12448,7 +12759,7 @@ bool ON_Mesh::DeleteComponents(
}
}
- if ( 0 == vertex_count1 && 0 == face_count1 )
+ if ( 0 == vertex_count1 || 0 == face_count1 )
{
Destroy();
return true;
@@ -12634,6 +12945,7 @@ bool ON_Mesh::DeleteComponents(
m_invalid_count = 0;
m_quad_count = 0;
m_triangle_count = 0;
+ SetClosed(-1);
}
if ( bRemoveEmptyNgons )
@@ -13721,3 +14033,1160 @@ bool ON_MeshCache::Transform(
return rc;
}
+//////////////////////////////////////////////////////////////////////////
+//
+// ON_MeshRef
+//
+
+ON_MeshRef::ON_MeshRef() ON_NOEXCEPT
+{}
+
+ON_MeshRef::~ON_MeshRef()
+{
+ m_mesh_sp.reset();
+}
+
+ON_MeshRef::ON_MeshRef(const ON_MeshRef& src) ON_NOEXCEPT
+ : m_mesh_sp(src.m_mesh_sp)
+{}
+
+ON_MeshRef& ON_MeshRef::operator=(const ON_MeshRef& src)
+{
+ if ( this != &src )
+ m_mesh_sp = src.m_mesh_sp;
+ return *this;
+}
+
+#if defined(ON_HAS_RVALUEREF)
+// rvalue copy constructor
+ON_MeshRef::ON_MeshRef( ON_MeshRef&& src) ON_NOEXCEPT
+ : m_mesh_sp(std::move(src.m_mesh_sp))
+{}
+
+// rvalue assignment operator
+ON_MeshRef& ON_MeshRef::operator=(ON_MeshRef&& src)
+{
+ m_mesh_sp.reset();
+ m_mesh_sp = std::move(src.m_mesh_sp);
+ return *this;
+}
+#endif
+
+const class ON_Mesh& ON_MeshRef::Mesh() const
+{
+ const ON_Mesh* mesh = m_mesh_sp.get();
+ if ( nullptr == mesh )
+ mesh = &ON_Mesh::Empty;
+ return *mesh;
+}
+
+unsigned int ON_MeshRef::ReferenceCount() const
+{
+ return (unsigned int)m_mesh_sp.use_count();
+}
+
+void ON_MeshRef::Clear()
+{
+ m_mesh_sp.reset();
+}
+
+class ON_Mesh& ON_MeshRef::NewMesh()
+{
+ ON_Mesh* mesh = new ON_Mesh();
+ ON_Mesh* managed_mesh = SetMeshForExperts(mesh);
+ return *managed_mesh;
+}
+
+class ON_Mesh& ON_MeshRef::CopyMesh(
+ const ON_MeshRef& src
+ )
+{
+ return CopyMesh(src.Mesh());
+}
+
+class ON_Mesh& ON_MeshRef::CopyMesh(
+ const ON_Mesh& src
+ )
+{
+ ON_Mesh* mesh_copy = new ON_Mesh(src);
+ ON_Mesh* managed_mesh = SetMeshForExperts(mesh_copy);
+ return *managed_mesh;
+}
+
+class ON_Mesh& ON_MeshRef::UniqueMesh()
+{
+ const ON_Mesh& mesh = Mesh();
+ if (m_mesh_sp.use_count() > 1 )
+ return CopyMesh(mesh);
+ return const_cast< ON_Mesh& >(mesh);
+}
+
+class ON_Mesh* ON_MeshRef::SetMeshForExperts(
+ class ON_Mesh*& mesh
+ )
+{
+ Clear();
+ ON_Mesh* managed_mesh = ( mesh == &ON_Mesh::Empty ) ? nullptr : mesh;
+ mesh = nullptr;
+ if (nullptr != managed_mesh )
+ m_mesh_sp = std::shared_ptr(managed_mesh);
+ return managed_mesh;
+}
+
+
+
+unsigned int ON_Mesh::DissolveOrDelete(
+ const ON_SimpleArray& ci_list
+)
+{
+ // Dissolve edges and vertices, delete faces
+ const unsigned int bailout_rc = ON_UNSET_UINT_INDEX;
+
+ const int ci_list_count = ci_list.UnsignedCount();
+ if (ci_list_count <= 0)
+ return bailout_rc;
+
+
+ const int mesh0_vertex_count = this->VertexCount();
+ if (mesh0_vertex_count < 3)
+ return bailout_rc;
+ const int mesh0_face_count = this->FaceCount();
+ if (mesh0_face_count < 1)
+ return bailout_rc;
+
+ const ON_MeshTopology& top0 = Topology();
+ const int top0_vertex_count = top0.m_topv.Count();
+
+
+ ON_SimpleArray faces(ci_list_count);
+ ON_SimpleArray edges_and_vertices(ci_list_count);
+ ON_SimpleArray edges(ci_list_count);
+
+ for (int i = 0; i < ci_list_count; ++i)
+ {
+ ON_COMPONENT_INDEX ci = ci_list[i];
+ if (ci.m_index < 0)
+ continue;
+ switch (ci.m_type)
+ {
+ case ON_COMPONENT_INDEX::TYPE::mesh_vertex:
+ if ( ci.m_index < mesh0_vertex_count)
+ edges_and_vertices.Append(ci);
+ break;
+
+ case ON_COMPONENT_INDEX::TYPE::meshtop_vertex:
+ // must save input top vertex as an ordinary vertex index
+ // because input topology indices will be changed if faces are deleted.
+ if (ci.m_index < top0_vertex_count)
+ {
+ const ON_MeshTopologyVertex& v = top0.m_topv[ci.m_index];
+ if (v.m_v_count > 0 && nullptr != v.m_vi)
+ {
+ ON_COMPONENT_INDEX vci(ON_COMPONENT_INDEX::TYPE::mesh_vertex, v.m_vi[0]);
+ if ( vci.m_index >= 0 && vci.m_index < mesh0_vertex_count)
+ edges_and_vertices.Append(vci);
+ }
+ }
+ break;
+
+ case ON_COMPONENT_INDEX::TYPE::meshtop_edge:
+ // must save input edge as a pair of ordinary vertex indices
+ // because input topology indices will be changed if faces are deleted.
+ if ( ci.m_index < top0.m_tope.Count())
+ {
+ const ON_MeshTopologyEdge& e = top0.m_tope[ci.m_index];
+ ON_2dex evdex(-1, -1);
+ int* evi = &evdex.i;
+ for (int j = 0; j < 2; j++)
+ {
+ if (e.m_topvi[j] < 0 || e.m_topvi[j] >= top0_vertex_count)
+ break;
+ const ON_MeshTopologyVertex& v = top0.m_topv[e.m_topvi[j]];
+ if (v.m_v_count > 0 && nullptr != v.m_vi)
+ evi[j] = v.m_vi[0];
+ }
+ if (evdex.i > evdex.j)
+ {
+ const int x = evdex.i;
+ evdex.i = evdex.j;
+ evdex.j = x;
+ }
+ if (0 <= evdex.i && evdex.i < evdex.j && evdex.j < mesh0_vertex_count)
+ edges.Append(evdex);
+ }
+ break;
+
+ case ON_COMPONENT_INDEX::TYPE::mesh_face:
+ faces.Append(ci);
+ break;
+
+ case ON_COMPONENT_INDEX::TYPE::mesh_ngon:
+ {
+ // must convert ngon to a bucnh of face indices
+ const ON_MeshNgon* ngon = this->Ngon(ci.m_index);
+ if (nullptr == ngon || nullptr == ngon->m_fi)
+ break;
+ ON_COMPONENT_INDEX fci(ON_COMPONENT_INDEX::TYPE::mesh_face, -1);
+ for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi)
+ {
+ fci.m_index = ngon->m_fi[nfi];
+ if ( fci.m_index < mesh0_face_count)
+ faces.Append(fci);
+ }
+ }
+ break;
+ };
+ }
+
+
+ // delete the faces in a way that does not change ordinary vertex indices
+ DeleteComponents(
+ faces.Array(),
+ faces.UnsignedCount(),
+ true, // bIgnoreInvalidComponents,
+ true, // bRemoveDegenerateFaces,
+ false, // bRemoveUnusedVertices CRITICAL to preserve vertex index values
+ true, // bRemoveEmptyNgons,
+ nullptr // unsigned int* faceMap
+ );
+
+ if (FaceCount() <= 0)
+ {
+ Destroy();
+ return bailout_rc;
+ }
+
+ if (mesh0_vertex_count != VertexCount())
+ return bailout_rc; // vertex array was modified
+
+ if (edges.Count() > 0 || edges_and_vertices.Count() > 0)
+ {
+ // convert ordinary vertex indices into top1 indices
+ const ON_MeshTopology& top1 = Topology();
+ const int top1_vertex_count = top1.m_topv.Count();
+ const int top1_edge_count = top1.m_tope.Count();
+ if (mesh0_vertex_count == top1.m_topv_map.Count())
+ {
+ // get a clean list of top1.m_topv[] indices
+ int vertex_count = 0;
+ for (int i = 0; i < edges_and_vertices.Count(); ++i)
+ {
+ ON_COMPONENT_INDEX ci = edges_and_vertices[i];
+ ci.m_type = ON_COMPONENT_INDEX::TYPE::meshtop_vertex;
+ ci.m_index = top1.m_topv_map[ci.m_index];
+ if (ci.m_index >= 0 && ci.m_index < top1_vertex_count)
+ edges_and_vertices[vertex_count++] = ci;
+ }
+ edges_and_vertices.SetCount(vertex_count);
+ edges_and_vertices.QuickSortAndRemoveDuplicates(ON_COMPONENT_INDEX::Compare);
+ vertex_count = edges_and_vertices.Count();
+
+ // now add edges to edges_and_vertices[]
+ ON_COMPONENT_INDEX eci(ON_COMPONENT_INDEX::TYPE::meshtop_edge, -1);
+ for (int i = 0; i < edges.Count(); i++)
+ {
+ ON_2dex evi = edges[i];
+ const int topv[2] = { top1.m_topv_map[evi.i],top1.m_topv_map[evi.j] };
+ if (
+ topv[0] != topv[1]
+ && topv[0] >= 0 && topv[0] < top1_vertex_count
+ && topv[1] >= 0 && topv[1] < top1_vertex_count
+ )
+ {
+ const ON_MeshTopologyVertex& v0 = top1.m_topv[topv[0]];
+ if ( v0.m_tope_count > 0 && nullptr != v0.m_topei )
+ {
+ // find the edge connecting the two vertices
+ for (int k = 0; k < v0.m_tope_count; ++k)
+ {
+ eci.m_index = v0.m_topei[k];
+ if (eci.m_index >= 0 && eci.m_index < top1_edge_count)
+ {
+ const ON_MeshTopologyEdge& e = top1.m_tope[eci.m_index];
+ if (
+ (e.m_topvi[0] == topv[0] && e.m_topvi[1] == topv[1])
+ || (e.m_topvi[0] == topv[1] && e.m_topvi[1] == topv[0]))
+ {
+ edges_and_vertices.Append(eci);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // dissolve the edges and vertices by merging faces
+ unsigned int rc
+ = (edges_and_vertices.Count() > 0)
+ ? MergeFaceSets(edges_and_vertices)
+ : bailout_rc;
+
+ // Finally, we can remove unused vertices.
+ DeleteComponents(
+ nullptr,
+ 0,
+ true, // bIgnoreInvalidComponents,
+ true, // bRemoveDegenerateFaces,
+ true, // bRemoveUnusedVertices,
+ true, // bRemoveEmptyNgons,
+ nullptr // unsigned int* faceMap
+ );
+
+ return rc;
+}
+
+static void Internal_WeldNgonCandiate(
+ const ON_Mesh& mesh,
+ const ON_MeshTopology& top,
+ unsigned char* fmarks,
+ const unsigned char xmark,
+ const ON_SimpleArray& ngon_fi
+)
+{
+ const unsigned ngon_face_count = ngon_fi.UnsignedCount();
+ if (ngon_face_count < 2)
+ return;
+
+ // Assumption fmarks[] has no xmark.
+ // Set xmark for all faces referenced in ngon_fi[] and
+ // use that mark to find interior vertices that need
+ // to be welded before the ngon can be created.
+ for (unsigned nfi = 0; nfi < ngon_face_count; ++nfi)
+ fmarks[ngon_fi[nfi]] |= xmark;
+
+ for (unsigned nfi = 0; nfi < ngon_face_count; ++nfi)
+ {
+ const int fi = ngon_fi[nfi];
+ const ON_MeshTopologyFace& f = top.m_topf[fi];
+ const int fv_count = f.IsTriangle() ? 3 : 4;
+ int* fvi = const_cast( mesh.m_F[fi].vi );
+ for (int fei = 0; fei < fv_count; ++fei)
+ {
+ const int evi = (0 == f.m_reve[fei]) ? 1 : 0;
+ const ON_MeshTopologyEdge& fe = top.m_tope[f.m_topei[fei]];
+ const int topvi = fe.m_topvi[evi];
+ const ON_MeshTopologyVertex& v = top.m_topv[topvi];
+ if (v.m_v_count <= 1 || nullptr == v.m_vi)
+ continue;
+ int mesh_vi[2] = { fvi[fei],fvi[fei] };
+ for (int pass = 0; pass < 2; ++pass)
+ {
+ for (int vei = 0; vei < v.m_v_count; ++vei)
+ {
+ const ON_MeshTopologyEdge& ve = top.m_tope[f.m_topei[fei]];
+ for (int efi = 0; efi < ve.m_topf_count; ++efi)
+ {
+ const int f1i = ve.m_topfi[efi];
+ if (fi == f1i)
+ continue;
+ if (0 == (fmarks[f1i] |= xmark))
+ continue; // this face is not in the ngon
+ const ON_MeshTopologyFace& f1 = top.m_topf[f1i];
+ int* f1vi = const_cast( mesh.m_F[f1i].vi );
+ const int f1v_count = f1.IsTriangle() ? 3 : 4;
+ for (int f1ei = 0; f1ei < f1v_count; ++f1ei)
+ {
+ const int k = (0 == f1.m_reve[f1ei]) ? 1 : 0;
+ const ON_MeshTopologyEdge& f1e = top.m_tope[f1.m_topei[f1ei]];
+ if (topvi == f1e.m_topvi[k])
+ {
+ if (0 == pass)
+ {
+ if (f1vi[f1ei] < mesh_vi[0])
+ mesh_vi[0] = f1vi[f1ei];
+ else if (f1vi[f1ei] > mesh_vi[1])
+ mesh_vi[1] = f1vi[f1ei];
+ }
+ else
+ {
+ // weld all faces in ngon to use vertex mesh_vi[0]
+ if (2 == f1ei && f1vi[2] == f1vi[3])
+ {
+ f1vi[2] = mesh_vi[0];
+ f1vi[3] = mesh_vi[0];
+ }
+ else
+ {
+ f1vi[f1ei] = mesh_vi[0];
+ }
+ }
+ }
+ }
+ if (3 == f1v_count)
+ f1vi[3] = f1vi[2];
+ }
+ }
+ if (0 == pass)
+ {
+ if (mesh_vi[0] == mesh_vi[1])
+ break;
+ }
+ else
+ {
+ // weld all faces in ngon to use vertex mesh_vi[0]
+ if (2 == fei && fvi[2] == fvi[3])
+ {
+ fvi[2] = mesh_vi[0];
+ fvi[3] = mesh_vi[0];
+ }
+ else
+ {
+ fvi[fei] = mesh_vi[0];
+ }
+ }
+ }
+ }
+ }
+
+ // clear the xmarks set above
+ const unsigned char mask = ~xmark;
+ for (unsigned nfi = 0; nfi < ngon_face_count; ++nfi)
+ fmarks[ngon_fi[nfi]] &= mask;
+}
+
+static void Internal_GrowNgon(
+ const ON_MeshTopology& top,
+ unsigned char* emarks,
+ unsigned char* fmarks,
+ unsigned char etest_mask,
+ unsigned char etest_result,
+ unsigned char ftest_mask,
+ unsigned char ftest_result,
+ const unsigned char merged_mark,
+ ON_SimpleArray& ngon_fi
+)
+{
+ if (ngon_fi.Count() <= 0)
+ return;
+
+ const int face_count = top.m_topf.Count();
+
+ for (int nfi = 0; nfi < ngon_fi.Count(); ++nfi)
+ fmarks[ngon_fi[nfi]] |= merged_mark;
+
+ etest_mask |= merged_mark;
+ etest_result &= ~merged_mark;
+
+ ftest_mask |= merged_mark;
+ ftest_result &= ~merged_mark;
+
+ for (int nfi = 0; nfi < ngon_fi.Count(); ++nfi)
+ {
+ const int f0i = ngon_fi[nfi];
+ const ON_MeshTopologyFace& f0 = top.m_topf[f0i];
+ const int f0_ecount = f0.IsTriangle() ? 3 : 4;
+ for (int fei = 0; fei < f0_ecount; ++fei)
+ {
+ const int ei = f0.m_topei[fei];
+ if (ei < 0 || ei > top.m_tope.Count())
+ continue;
+ if (etest_result != (emarks[ei] & etest_mask))
+ continue;
+ emarks[ei] |= merged_mark;
+ const ON_MeshTopologyEdge& e = top.m_tope[ei];
+ if (2 != e.m_topf_count || nullptr == e.m_topfi)
+ continue;
+ const int efi = (f0i != e.m_topfi[0]) ? 0 : 1;
+ const int f1i = e.m_topfi[efi];
+ if (f1i < 0 || f1i >= face_count)
+ continue;
+ if (ftest_result != (fmarks[f1i] & ftest_mask))
+ continue;
+ fmarks[f1i] |= merged_mark;
+ ngon_fi.Append(f1i);
+ }
+ }
+}
+
+static void Internal_AddMarkToFaceAndEdgesAndVertices(
+ const ON_Mesh& mesh,
+ const ON_MeshTopology& top,
+ const unsigned int* ngon0_map,
+ int face_index,
+ const unsigned char mark,
+ unsigned char* fmarks,
+ unsigned char* emarks,
+ unsigned char* vmarks
+)
+{
+ if (0 == mark || face_index < 0 || face_index > top.m_topf.Count())
+ return;
+ if (nullptr == fmarks && nullptr == emarks && nullptr == vmarks)
+ return;
+
+ unsigned fi_count = 1;
+ const int* fi_list = &face_index;
+ if (nullptr != ngon0_map)
+ {
+ const unsigned ni = ngon0_map[face_index];
+ if (ni < mesh.NgonUnsignedCount())
+ {
+ const ON_MeshNgon* ngon = mesh.Ngon(ni);
+ if (ngon->m_Fcount > 1 && nullptr != ngon->m_fi)
+ {
+ for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi)
+ {
+ if (face_index == (int)(ngon->m_fi[nfi]))
+ {
+ fi_count = ngon->m_Fcount;
+ fi_list = (const int*)ngon->m_fi;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for (unsigned nfi = 0; nfi < fi_count; ++nfi)
+ {
+ int fi = fi_list[nfi];
+ if (fi < 0 || fi >= top.m_topf.Count())
+ continue;
+ if (nullptr != fmarks)
+ fmarks[fi] |= mark;
+ if (nullptr != emarks || nullptr != vmarks)
+ {
+ const ON_MeshTopologyFace& f = top.m_topf[fi];
+ //const int fecount = f.IsTriangle() ? 3 : 4;
+ const int tope_count = top.m_tope.Count();
+ const int topv_count = top.m_topv.Count();
+ for (int fei = 0; fei < 4; fei++)
+ {
+ const int ei = f.m_topei[fei];
+ if (ei < 0 || ei >= tope_count)
+ continue;
+ if (nullptr != emarks)
+ emarks[ei] |= mark;
+ if (nullptr != vmarks)
+ {
+ const ON_MeshTopologyEdge& e = top.m_tope[ei];
+ for (int evi = 0; evi < 2; ++evi)
+ {
+ const int topvi = e.m_topvi[evi];
+ if (topvi < 0 || topvi >= topv_count)
+ continue;
+ vmarks[topvi] |= mark;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void Internal_AddMarkToNgonInteriorEdges(
+ const ON_Mesh& mesh,
+ const ON_MeshTopology& top,
+ const unsigned int* ngon0_map,
+ int face_index,
+ const unsigned char interior_edge_mark,
+ unsigned char* emarks
+)
+{
+ if (nullptr == ngon0_map)
+ return;
+ const int face_count = top.m_topf.Count();
+ const int edge_count = top.m_tope.Count();
+ if (face_index < 0 || face_index >= face_count)
+ return;
+ const unsigned ni = ngon0_map[face_index];
+ if (ni >= mesh.NgonUnsignedCount())
+ return;
+ const ON_MeshNgon* ngon = mesh.Ngon(ni);
+ if (nullptr == ngon || ngon->m_Fcount < 2 || nullptr == ngon->m_fi)
+ return;
+
+ for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi)
+ {
+ const int fi = (int)ngon->m_fi[nfi];
+ if (fi < 0 || fi >= face_count)
+ continue;
+ const ON_MeshTopologyFace& f = top.m_topf[fi];
+ const int f_ecount = f.IsTriangle() ? 3 : 4;
+ for (int fei = 0; fei < f_ecount; ++fei)
+ {
+ const int ei = f.m_topei[fei];
+ if (ei < 0 || ei >= edge_count)
+ continue;
+ const ON_MeshTopologyEdge& e = top.m_tope[ei];
+ if (2 != e.m_topf_count || nullptr == e.m_topfi)
+ continue;
+ const int f1i = e.m_topfi[(fi != e.m_topfi[0]) ? 0 : 1];
+ if (ni == ngon0_map[f1i])
+ emarks[ei] |= interior_edge_mark;
+ }
+ }
+}
+
+static void Internal_WeldAndAddNgon(
+ ON_Mesh& mesh,
+ const ON_MeshTopology& top,
+ const unsigned char xmark,
+ unsigned char* fmarks,
+ ON_SimpleArray& ngon_fi
+)
+{
+ if (ngon_fi.Count() < 2)
+ return;
+
+ // ngons must be welded and MergeFaces supports making an ngon across an unwelded edge.
+ Internal_WeldNgonCandiate(mesh,top,fmarks,xmark,ngon_fi);
+
+ if (
+ 2 == ngon_fi.Count()
+ &&ngon_fi[0] != ngon_fi[1]
+ && mesh.m_F[ngon_fi[0]].IsTriangle()
+ && mesh.m_F[ngon_fi[1]].IsTriangle()
+ )
+ {
+ // make a single quad face
+ ON_MeshFace f[2] = {mesh.m_F[ngon_fi[0]],mesh.m_F[ngon_fi[1]]};
+ for (int f0ei = 0; f0ei < 3; ++f0ei)
+ {
+ const int f0vi[2] = { f[0].vi[f0ei],f[0].vi[(f0ei + 1) % 3] };
+ for (int f1ei = 0; f1ei < 3; ++f1ei)
+ {
+ const int f1vi[2] = { f[1].vi[f1ei],f[1].vi[(f1ei + 1) % 3] };
+ if (f0vi[0] != f1vi[1] || f0vi[1] != f1vi[0])
+ continue;
+ // merge triangles into a quad
+ ON_MeshFace q;
+ q.vi[f0ei] = f[0].vi[f0ei];
+ q.vi[(f0ei+1)%4] = f[1].vi[(f1ei+2)%3];
+ q.vi[(f0ei+2)%4] = f[0].vi[(f0ei+1)%3];
+ q.vi[(f0ei+3)%4] = f[0].vi[(f0ei+2)%3];
+ if (q.IsQuad() && q.IsValid(mesh.m_V.Count()))
+ {
+ mesh.m_F[ngon_fi[0]] = q;
+ q.vi[0] = -1;
+ q.vi[1] = -1;
+ q.vi[2] = -1;
+ q.vi[3] = -1;
+ mesh.m_F[ngon_fi[1]] = q;
+ ngon_fi.SetCount(1);
+ mesh.AddNgon(1, ngon_fi.Array());
+ return;
+ }
+ }
+ }
+ }
+
+ mesh.AddNgon(ngon_fi.UnsignedCount(), ngon_fi.Array());
+}
+
+unsigned int ON_Mesh::MergeFaceSets(
+ const ON_SimpleArray& ci_list
+)
+{
+ const unsigned int bailout_rc = ON_UNSET_UINT_INDEX;
+
+ const int ci_list_count = ci_list.UnsignedCount();
+ if (ci_list_count <= 0)
+ return bailout_rc;
+
+ const int vertex_count = VertexCount();
+ const int face_count = FaceCount();
+ if (face_count < 1 || vertex_count < 3)
+ return bailout_rc;
+
+
+ const ON_MeshTopology& top = Topology();
+ if (top.m_topf.Count() != face_count)
+ return bailout_rc;
+
+ const int topv_count = top.m_topv.Count();
+ const int tope_count = top.m_tope.Count();
+
+ if (topv_count < 3 || topv_count > vertex_count || tope_count < 3)
+ return bailout_rc;
+
+ ON_SimpleArray vmarks_buffer(topv_count);
+ vmarks_buffer.SetCount(topv_count);
+ vmarks_buffer.Zero();
+ unsigned char* vmarks = vmarks_buffer.Array();
+
+ ON_SimpleArray emarks_buffer(tope_count);
+ emarks_buffer.SetCount(tope_count);
+ emarks_buffer.Zero();
+ unsigned char* emarks = emarks_buffer.Array();
+
+ ON_SimpleArray fmarks_buffer(face_count);
+ fmarks_buffer.SetCount(face_count);
+ fmarks_buffer.Zero();
+ unsigned char* fmarks = fmarks_buffer.Array();
+
+
+ const unsigned char in_ci_list_mark = 1;
+ const unsigned char vmark = 2;
+ const unsigned char emark = 4;
+ const unsigned char fmark = 8;
+ const unsigned char xmark = 0x10;
+ const unsigned char v_list_mark = vmark | in_ci_list_mark;
+ const unsigned char e_list_mark = emark | in_ci_list_mark;
+ const unsigned char f_list_mark = fmark | in_ci_list_mark;
+ const unsigned char v_x_mark = vmark | xmark;
+ const unsigned char e_x_mark = emark | xmark;
+ //const unsigned char f_x_mark = fmark | xmark;
+ const unsigned char merged_mark = 0x80; // face has been merged
+
+ ON_MeshNgonBuffer ngon_buffer;
+
+ unsigned ngon0_count = this->NgonCount();
+ const unsigned int* ngon0_map = ( ngon0_count > 0) ? this->NgonMap(true) : nullptr;
+
+ // Mark faces, edges, and vertices referenced in ci_list[]
+ // by setting bits in fmarks[], emarks[], and vmarks[]
+ for (int i = 0; i < ci_list_count; ++i)
+ {
+ ON_COMPONENT_INDEX ci = ci_list[i];
+ if (ci.m_index < 0)
+ continue;
+ switch (ci.m_type)
+ {
+ case ON_COMPONENT_INDEX::mesh_vertex:
+ // convert ci.m_index from a ON_COMPONENT_INDEX::mesh_vertex
+ // to a ON_COMPONENT_INDEX::meshtop_vertex index.
+ if (ci.m_index >= vertex_count)
+ break;
+ ci.m_index = top.m_topv_map[ci.m_index];
+ if (ci.m_index < 0)
+ break;
+ // no break here
+ case ON_COMPONENT_INDEX::meshtop_vertex:
+ if (ci.m_index < topv_count)
+ {
+ const ON_MeshTopologyVertex& v = top.m_topv[ci.m_index];
+ if (nullptr == v.m_topei || v.m_tope_count < 2)
+ break;
+
+ // validate the vertex
+ unsigned ni[2] = { ON_UNSET_UINT_INDEX,ON_UNSET_UINT_INDEX };
+ bool bValid = (v.m_tope_count >= 2 && nullptr != v.m_topei);
+ for (int vei = 0; vei < v.m_tope_count && bValid; ++vei)
+ {
+ int topei = v.m_topei[vei];
+ bValid = (topei >= 0 && topei < tope_count);
+ if (bValid)
+ {
+ const ON_MeshTopologyEdge& e = top.m_tope[topei];
+ bValid = (nullptr != e.m_topfi && e.m_topf_count >= 1 && e.m_topf_count <= 2);
+ for (int efi = 0; efi < e.m_topf_count && bValid; ++efi)
+ {
+ int fi = e.m_topfi[efi];
+ bValid = (fi >= 0 && fi < face_count);
+ if (nullptr != ngon0_map && ni[0] == ni[1])
+ {
+ const unsigned k = ngon0_map[fi];
+ const ON_MeshNgon* ngon = (k < ngon0_count) ? Ngon(k) : nullptr;
+ if (nullptr != ngon && ngon->m_Fcount >= 2 && nullptr != ngon->m_fi)
+ {
+ if (ON_UNSET_UINT_INDEX == ni[0])
+ ni[0] = k;
+ ni[1] = k;
+ }
+ else
+ {
+ // this face is not part of an ngon - stop checking
+ ni[0] = ON_UNSET_UINT_INDEX;
+ ni[1] = 0;
+ }
+ }
+ }
+ }
+ }
+ if (false == bValid)
+ break;
+ if (ON_UNSET_UINT_INDEX != ni[0] && ni[0] == ni[1])
+ break; // v is inside an ngon
+
+ vmarks[ci.m_index] |= v_list_mark;
+
+ // add a vmark to every face touching this vertex
+ for (int vei = 0; vei < v.m_tope_count; ++vei)
+ {
+ const ON_MeshTopologyEdge& e = top.m_tope[v.m_topei[vei]];
+ for (int efi = 0; efi < e.m_topf_count; ++efi)
+ Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, e.m_topfi[efi], vmark, fmarks, nullptr, nullptr);
+ }
+ }
+ break;
+
+ case ON_COMPONENT_INDEX::meshtop_edge:
+ if (ci.m_index < tope_count)
+ {
+ const ON_MeshTopologyEdge& e = top.m_tope[ci.m_index];
+ if (nullptr == e.m_topfi || 2 != e.m_topf_count)
+ continue;
+ if (e.m_topvi[0] < 0 || e.m_topvi[0] >= topv_count)
+ continue;
+ if (e.m_topvi[1] < 0 || e.m_topvi[1] >= topv_count)
+ continue;
+ if (e.m_topfi[0] < 0 || e.m_topfi[0] >= face_count)
+ continue;
+ if (e.m_topfi[1] < 0 || e.m_topfi[1] >= face_count)
+ continue;
+
+ if (nullptr != ngon0_map)
+ {
+ const unsigned ni[2] = { ngon0_map[e.m_topfi[0]],ngon0_map[e.m_topfi[1]] };
+ if (ni[0] < ngon0_count && ni[0] == ni[1])
+ continue; // edge is inside an ngon
+ }
+
+ emarks[ci.m_index] |= e_list_mark;
+
+ // add an emark to face tounching this edge
+ vmarks[e.m_topvi[0]] |= emark;
+ vmarks[e.m_topvi[1]] |= emark;
+ for (int efi = 0; efi < e.m_topf_count; ++efi)
+ {
+ const int fi = e.m_topfi[efi];
+ Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, emark, fmarks, nullptr, nullptr);
+ Internal_AddMarkToNgonInteriorEdges(*this, top, ngon0_map, fi, e_list_mark, emarks);
+ }
+ }
+ break;
+
+ case ON_COMPONENT_INDEX::mesh_face:
+ if (ci.m_index >= face_count)
+ break;
+ if (ngon0_count > 0)
+ {
+ const unsigned ni = ngon0_map[ci.m_index];
+ if (ni < ngon0_count )
+ {
+ const ON_MeshNgon* ngon = Ngon(ni);
+ if (nullptr != ngon && ngon->m_Fcount > 1 && nullptr != ngon->m_fi)
+ {
+ ci.m_type = ON_COMPONENT_INDEX::mesh_ngon;
+ ci.m_index = (int)ni;
+ }
+ }
+ }
+ // no break here;
+ case ON_COMPONENT_INDEX::mesh_ngon:
+ if (ON_COMPONENT_INDEX::mesh_face == ci.m_type || ci.m_index < NgonCount())
+ {
+ const ON_MeshNgon* ngon =
+ (ON_COMPONENT_INDEX::mesh_face == ci.m_type)
+ ? ngon_buffer.CreateFromMeshFaceIndex(this,ci.m_index)
+ : Ngon(ci.m_index);
+ if (nullptr == ngon || nullptr == ngon->m_fi || ngon->m_Fcount < 1)
+ break;
+ for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi)
+ {
+ int fi = ngon->m_fi[nfi];
+ if (fi >= 0 && fi < face_count)
+ {
+ const ON_MeshTopologyFace& f = top.m_topf[fi];
+ bool bValidFace = true;
+ for (int fei = 0; fei < 4 && bValidFace; fei++)
+ {
+ const int ei = f.m_topei[fei];
+ bValidFace = (ei >= 0 && ei < tope_count);
+ if (bValidFace)
+ {
+ const ON_MeshTopologyEdge& e = top.m_tope[ei];
+ for (int evi = 0; evi < 2 && bValidFace; ++evi)
+ {
+ int topvi = e.m_topvi[evi];
+ bValidFace = (topvi >= 0 && topvi < topv_count);
+ }
+ }
+ }
+
+ if (bValidFace)
+ {
+ fmarks[fi] |= f_list_mark;
+ Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, fmark, fmarks, emarks, vmarks);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Resolve multi-marked faces and remove all marked faces from ngons.
+ unsigned int fmark_face_count = 0;
+ unsigned int vmark_face_count = 0;
+ unsigned int emark_face_count = 0;
+
+ for (int fi = 0; fi < face_count; ++fi)
+ {
+ if (f_list_mark == ( fmarks[fi] & f_list_mark ))
+ {
+ fmarks[fi] = (f_list_mark | xmark);
+ ++fmark_face_count;
+ }
+ else if (emark == (fmarks[fi] & e_list_mark))
+ {
+ fmarks[fi] = emark;
+ ++emark_face_count;
+ }
+ else if (vmark == (fmarks[fi] & v_list_mark))
+ {
+ fmarks[fi] = vmark;
+ ++vmark_face_count;
+ }
+ else
+ {
+ fmarks[fi] = 0;
+ }
+ }
+
+ unsigned int emark_count = 0;
+ if (emark_face_count >= 2)
+ {
+ // ignore edges that are part of a boundary of a face in ci_list[]
+ emark_face_count = 0;
+ for (int ei = 0; ei < tope_count; ++ei)
+ {
+ unsigned char m = emarks[ei];
+ emarks[ei] = 0;
+ if (e_list_mark != m)
+ continue;
+ const ON_MeshTopologyEdge& e = top.m_tope[ei];
+ for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi)
+ {
+ const int fi = e.m_topfi[efi];
+ if (emark != fmarks[fi] && e_x_mark != fmarks[fi])
+ m = 0;
+ }
+ if (0 != m)
+ {
+ emarks[ei] = e_list_mark;
+ ++emark_count;
+ for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi)
+ {
+ const int fi = e.m_topfi[efi];
+ Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, emark, fmarks, emarks, vmarks);
+ if (emark == fmarks[fi])
+ {
+ fmarks[fi] |= xmark;
+ ++emark_face_count;
+ }
+ }
+ }
+ }
+ }
+ if (0 == emark_count)
+ emark_face_count = 0;
+
+ unsigned int vmark_count = 0;
+ if (vmark_face_count >= 2)
+ {
+ // ignore vertices that belong to faces or edges in ci_list[]
+ vmark_face_count = 0;
+ for (int topvi = 0; topvi < topv_count; ++topvi)
+ {
+ unsigned char m = vmarks[topvi];
+ vmarks[topvi] = 0;
+ if (v_list_mark != m)
+ continue;
+ const ON_MeshTopologyVertex& v = top.m_topv[topvi];
+ for (int vei = 0; vei < v.m_tope_count && 0 != m; ++vei)
+ {
+ const int ei = v.m_topei[vei];
+ if (0 != (emarks[ei] & (fmark|emark)) )
+ m = 0;
+ else
+ {
+ const ON_MeshTopologyEdge& e = top.m_tope[ei];
+ for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi)
+ {
+ const int fi = e.m_topfi[efi];
+ if (vmark != fmarks[fi] && v_x_mark != fmarks[fi])
+ m = 0;
+ }
+ }
+ }
+ if (0 != m)
+ {
+ vmarks[topvi] = v_list_mark;
+ ++vmark_count;
+ for (int vei = 0; vei < v.m_tope_count && 0 != m; ++vei)
+ {
+ const int ei = v.m_topei[vei];
+ emarks[ei] |= vmark;
+ const ON_MeshTopologyEdge& e = top.m_tope[ei];
+ for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi)
+ {
+ const int fi = e.m_topfi[efi];
+ Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, vmark, fmarks, nullptr, vmarks);
+ Internal_AddMarkToNgonInteriorEdges(*this, top, ngon0_map, fi, vmark, emarks);
+ if (vmark == fmarks[fi])
+ {
+ fmarks[fi] |= xmark;
+ ++vmark_face_count;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (0 == vmark_count)
+ vmark_face_count = 0;
+
+ unsigned max_ngon_face_count = fmark_face_count;
+ if (max_ngon_face_count < emark_face_count)
+ max_ngon_face_count = emark_face_count;
+ if (max_ngon_face_count < vmark_face_count)
+ max_ngon_face_count = vmark_face_count;
+
+ if (max_ngon_face_count < 2 )
+ return bailout_rc;
+
+ ON_SimpleArray ngon_fi(max_ngon_face_count);
+
+ if (ngon0_count > 0)
+ {
+ // remove ngons that include marked faces
+ const unsigned int* ngon_map = ( ngon0_count > 0) ? this->NgonMap(true) : nullptr;
+ for (int fi = 0; fi < face_count; ++fi)
+ {
+ if (0 != (fmarks[fi] & xmark))
+ {
+ unsigned int ngon_index = (nullptr != ngon_map) ? ngon_map[fi] : ON_UNSET_UINT_INDEX;
+ if (ngon_index < ngon0_count)
+ ngon_fi.Append(ngon_index);
+ }
+ }
+ ngon_fi.QuickSortAndRemoveDuplicates(ON_CompareDecreasing< unsigned>);
+ for (int i = 0; i < ngon_fi.Count(); ++i)
+ this->RemoveNgon((unsigned)ngon_fi[i]);
+ this->RemoveEmptyNgons();
+
+ // Any ngons with indices >= ngon0_count are ngons added by the code below.
+ ngon0_count = this->NgonCount();
+
+
+ ngon_fi.SetCount(0);
+ }
+
+ // zero x mark in fmarks[] because it is used in Internal_WeldAndAddNgon()
+ for (int fi = 0; fi < face_count; ++fi)
+ fmarks[fi] &= ~xmark;
+
+ for (int fi = 0; fi < face_count; ++fi)
+ {
+ if (f_list_mark != (fmarks[fi] & (f_list_mark | merged_mark)))
+ continue; // face not in ci_list[] or already merged
+
+ // seed ngon with this face
+ ngon_fi.SetCount(0);
+ fmarks[fi] |= merged_mark;
+ ngon_fi.Append((unsigned)fi);
+
+ // grow ngon by adding other faces in ci_list[] that share and edge with a face in the ngon.
+ Internal_GrowNgon(
+ top, emarks, fmarks,
+ 0, // etest_mask,
+ 0, // etest_result,
+ f_list_mark, // ftest_mask,
+ f_list_mark, // ftest_result,
+ merged_mark,
+ ngon_fi
+ );
+
+ // weld ngon vertices and then add ngon
+ Internal_WeldAndAddNgon(*this, top, xmark, fmarks, ngon_fi);
+ }
+
+ for (int ei = 0; ei < tope_count; ++ei)
+ {
+ if ( e_list_mark != (emarks[ei] & (e_list_mark | merged_mark)) )
+ continue; // edge not in ci_list[] or already merged
+ emarks[ei] |= merged_mark;
+ const ON_MeshTopologyEdge& e = top.m_tope[ei];
+
+ // seed ngon with faces attached to this edge
+ ngon_fi.SetCount(0);
+ for (int efi = 0; efi < e.m_topf_count; ++efi)
+ {
+ const int fi = e.m_topfi[efi];
+ if (fi < 0 || fi >= face_count)
+ break;
+ if (emark != (fmarks[fi] & (emark|fmark|in_ci_list_mark|merged_mark)))
+ break;
+ fmarks[fi] |= merged_mark;
+ ngon_fi.Append(fi);
+ }
+
+ // grow ngon by jumping accross edges in ci_list[]
+ Internal_GrowNgon(
+ top, emarks, fmarks,
+ e_list_mark | fmark, // etest_mask,
+ e_list_mark, // etest_result,
+ emark | fmark, // ftest_mask,
+ emark, // ftest_result,
+ merged_mark,
+ ngon_fi
+ );
+
+ // weld ngon vertices and then add ngon
+ Internal_WeldAndAddNgon(*this, top, xmark, fmarks, ngon_fi);
+ }
+
+
+ unsigned int mark_mask = v_list_mark | merged_mark;
+ for (int vi = 0; vi < topv_count; ++vi)
+ {
+ if (v_list_mark != (vmarks[vi] & mark_mask))
+ continue; // edge not in ci_list[] or already merged
+ vmarks[vi] |= merged_mark;
+ const ON_MeshTopologyVertex& v = top.m_topv[vi];
+
+ // seed ngon with faces attached to this vertex
+ ngon_fi.SetCount(0);
+ for (int vei = 0; vei < v.m_tope_count; ++vei)
+ {
+ const ON_MeshTopologyEdge& e = top.m_tope[v.m_topei[vei]];
+ for (int efi = 0; efi < e.m_topf_count; ++efi)
+ {
+ int fi = e.m_topfi[efi];
+ if (fi < 0 || fi >= face_count)
+ continue;
+ if (vmark != (fmarks[fi] & (vmark|emark|fmark|in_ci_list_mark|merged_mark)))
+ continue;
+ fmarks[fi] |= merged_mark;
+ ngon_fi.Append(fi);
+ }
+ }
+
+ // grow ngon by jumping across edges with a vmark
+ Internal_GrowNgon(
+ top, emarks, fmarks,
+ vmark, // etest_mask,
+ vmark, // etest_result,
+ vmark | emark | fmark, // ftest_mask,
+ vmark, // ftest_result,
+ merged_mark,
+ ngon_fi
+ );
+
+ // weld ngon vertices and then add ngon
+ Internal_WeldAndAddNgon(*this, top, xmark, fmarks, ngon_fi);
+ }
+
+ // clean up
+ DeleteComponents(
+ nullptr,
+ 0,
+ true, // bIgnoreInvalidComponents
+ true, // bRemoveDegenerateFaces
+ true, // bRemoveUnusedVertices
+ true // bRemoveEmptyNgons
+ );
+
+ const unsigned ngon1_count = this->NgonCount();
+
+ // return index where new ngons begin
+ return (ngon1_count > ngon0_count) ? ngon0_count : bailout_rc;
+}
+
diff --git a/opennurbs_mesh.h b/opennurbs_mesh.h
index 3aab0f5e..eeababfb 100644
--- a/opennurbs_mesh.h
+++ b/opennurbs_mesh.h
@@ -17,6 +17,214 @@
#if !defined(OPENNURBS_MESH_INC_)
#define OPENNURBS_MESH_INC_
+#pragma region RH_C_SHARED_ENUM [ON_SubDComponentLocation] [Rhino.Geometry.SubDComponentLocation] [byte]
+ ///
+ /// The ON_SubDComponentLocation enum is used when an ON_SubD component
+ /// is referenced and it is important to distinguish between the
+ /// component's location in the SubD control net and its location
+ /// in the SubD limit surface.
+ ///
+enum class ON_SubDComponentLocation : unsigned char
+{
+ ///
+ /// Not a valid component location and used to indicate the value is not initialized.
+ ///
+ Unset = 0,
+
+ ///
+ /// The component's location in the SubD control net.
+ ///
+ ControlNet = 1,
+
+ ///
+ /// The component's location in the SubD limit surface.
+ ///
+ Surface = 2
+};
+#pragma endregion
+
+//////////////////////////////////////////////////////////////////////////
+//
+// ON_SubDDisplayParameters
+//
+// A collection of parameters that are passed to functions that
+// calculate a various representations of ON_SubD objects.
+//
+
+class ON_CLASS ON_SubDDisplayParameters
+{
+public:
+ ON_SubDDisplayParameters() = default;
+ ~ON_SubDDisplayParameters() = default;
+ ON_SubDDisplayParameters(const ON_SubDDisplayParameters&) = default;
+ ON_SubDDisplayParameters& operator=(const ON_SubDDisplayParameters&) = default;
+
+public:
+ enum : unsigned int
+ {
+ CourseDensity = 2, // (2^2 = 4, 4x4 = 16 quads per SubD quad)
+ DefaultDensity = 4, // (2^4 = 16, 16x16 = 256 quads per SubD quad)
+ MaximumDensity = 6 // (2^6 = 64, 64x64 = 4096 quads per SubD quad)
+ };
+
+public:
+ static const ON_SubDDisplayParameters Empty;
+
+ // Parameters for the default limit surface display mesh.
+ // m_display_density = ON_SubDMesh::DefaultDisplayDensity
+ static const ON_SubDDisplayParameters Course;
+
+ // Parameters for the default limit surface display mesh.
+ // m_display_density = ON_SubDMesh::DefaultDisplayDensity
+ static const ON_SubDDisplayParameters Default;
+
+ /*
+ Description:
+ In most applications, the caller sets the mesh_density
+ and leaves the other parameters set to the default
+ values.
+ */
+ static const ON_SubDDisplayParameters CreateFromDisplayDensity(
+ unsigned int display_density
+ );
+
+ // TODO - add functional interface and hide implementation
+
+public:
+ // 0 <= m_display_density <= ON_SubDDisplayParameters::MaximumDensity
+ // If n = m_display_density, then each SubD quad face will have
+ // a grid of 2^n x 2^n mesh quads for a total of 2^(2n) mesh quads.
+ // n grid size total number of mesh faces per SubD quad
+ // 0 1 x 1 1
+ // 1 2 x 2 4
+ // 2 4 x 4 16
+ // 3 8 x 8 64
+ // 4 16 x 16 128
+ // 5 32 x 32 1,024
+ // 6 64 x 64 4,096
+ unsigned int DisplayDensity() const;
+
+ void SetDisplayDensity(
+ unsigned int display_density
+ );
+
+
+ /*
+ Description:
+ The MeshLocation() property determines if the mesh is
+ on the SubD's control net or the SubD's surface.
+ */
+ ON_SubDComponentLocation MeshLocation() const;
+
+ /*
+ Description:
+ The MeshLocation() property determines if the mesh is
+ on the SubD's control net or the SubD's surface.
+ Parameters:
+ mesh_location - [in]
+ */
+ void SetMeshLocation(ON_SubDComponentLocation mesh_location);
+
+ // By default, limit surface mesh quads for each subd face are grouped into
+ // a single ON_Mesh n-gon.
+ // If you don't want n-gons, then set m_bSkipMeshNgons = true;
+ bool AddNgons() const;
+ void SetAddNgons(
+ bool bAddNgons
+ );
+
+ // By default, limit surface mesh quads for each subd face are grouped into
+ // a single ON_Mesh n-gon.
+ // If you don't want n-gons, then set m_bSkipMeshNgons = true;
+ bool AddFakeEvaluationParameters() const;
+ void SetAddFakeEvaluationParameters(
+ bool bAddFakeEvaluationParameters
+ );
+
+ unsigned char EncodeAsUnsignedChar() const;
+ static const ON_SubDDisplayParameters DecodeFromUnsignedChar(
+ unsigned char encoded_parameters
+ );
+
+private:
+ enum : unsigned char
+ {
+ // do not change these values - they control how m_subd_mesh_parameters is set
+ // and the value of m_subd_mesh_parameters is saved in 3dm archives.
+ subd_mesh_density_mask = 0x07,
+ subd_mesh_location_bit = 0x08,
+ subd_mesh_skip_ngons_bit = 0x10,
+ subd_mesh_skip_fakeevalparams_bit = 0x20,
+ subd_mesh_nondefault_bit = 0x80
+ };
+
+private:
+ // 0 <= m_display_density <= ON_SubDDisplayParameters::MaximumDensity
+ // If n = m_display_density, then each SubD quad face will have
+ // a grid of 2^n x 2^n mesh quads for a total of 2^(2n) mesh quads.
+ // n grid size total number of mesh faces per SubD quad
+ // 0 1 x 1 1
+ // 1 2 x 2 4
+ // 2 4 x 4 16
+ // 3 8 x 8 64
+ // 4 16 x 16 128
+ // 5 32 x 32 1,024
+ // 6 64 x 64 4,096
+ unsigned int m_display_density = 0;
+
+ // If m_bControlNetMesh is false, a mesh of the limit surface is produced.
+ // If m_bControlNetMesh is true, a mesh of the subdivided control net is produced.
+ bool m_bControlNetMesh = false;
+
+ // By default, limit surface mesh quads for each subd face are grouped into
+ // a single ON_Mesh n-gon.
+ // If you don't want n-gons, then set m_bSkipMeshNgons = true;
+ bool m_bSkipMeshNgons = false;
+
+ // By default, fake evaluation parameters are assigned to mesh
+ // vertex locations and used to set normalized texture coordinates.
+ // If you don't want fake evaluation parameters, then set m_bSkipFakeEvaluationParameters = true;
+ bool m_bSkipFakeEvaluationParameters = false;
+
+private:
+ unsigned char m_reserved2 = 0;
+ unsigned int m_reserved3 = 0;
+ unsigned int m_reserved4 = 0;
+ ON__UINT_PTR m_reserved5 = 0;
+ double m_reserved6 = 0.0;
+
+
+ // TODO - split this class into two - what's above and one derived from that with what's below.
+public:
+ bool UseMultipleThreads() const;
+ void SetUseMultipleThreads(
+ bool bUseMultipleThreads
+ );
+
+ ON_Terminator* Terminator() const;
+ void SetTerminator(
+ ON_Terminator* terminator
+ );
+
+ ON_ProgressReporter* ProgressReporter() const;
+ const ON_Interval ProgressReporterInterval() const;
+ void SetProgressReporter(
+ ON_ProgressReporter* progress_reporter,
+ ON_Interval progress_reporter_interval
+ );
+
+private:
+ bool m_bUseMultipleThreads = false;
+
+private:
+ ON_Terminator* m_terminator = nullptr;
+
+private:
+ // optional progress reporting
+ ON_ProgressReporter* m_progress_reporter = nullptr;
+ ON_Interval m_progress_reporter_interval = ON_Interval::ZeroToOne;
+};
+
///////////////////////////////////////////////////////////////////////////////
//
// Class ON_Mesh
@@ -341,6 +549,14 @@ public:
bool bSimplePlanes
);
+public:
+ void SetSubDDisplayParameters(
+ const class ON_SubDDisplayParameters& subd_mesh_parameters
+ );
+
+ const ON_SubDDisplayParameters SubDDisplayParameters() const;
+
+
public:
// false - skip stage 2 mesh refinement step
// true - (default) do stage 2 mesh refinement step
@@ -452,19 +668,6 @@ public:
double minimum_edge_length
);
- /*
- Returns:
- SubD display mesh density.
- Example:
- Use ON_MeshParameters to control the density of a SubD limit mesh.
- ON_MeshParameters mp = ...;
- ON_Mesh* mesh = subd->GetLimitSurfaceMesh(
- ON_SubDDisplayParameters::CreateFromDisplayDensity( mp.SubDDisplayMeshDensity() ),
- nullptr
- );
- */
- unsigned int SubDDisplayMeshDensity() const;
-
public:
// edges longer than MaximumEdgeLength() will
// be split even when they meet all other
@@ -584,7 +787,10 @@ private:
unsigned char m_texture_range = 2;
unsigned char m_face_type = 0;
- unsigned char m_reserved1 = 0;
+ // Uses ON_SubDDisplayParameters::EncodeAsUnsignedChar() / ON_SubDDisplayParameters::DecodeFromUnsignedChar()
+ // to save ON_SubDDisplayParameters settings in this class.
+ // (Done this way to avoid breaking the C++ public SDK.)
+ unsigned char m_subd_mesh_parameters = 0;
int m_grid_min_count = 0;
int m_grid_max_count = 0;
@@ -638,9 +844,11 @@ private:
// END Pangolin parameters
//
//////////////////////////////////////////////////////////
+private:
+ unsigned int m_subd_stuff_reserved5 = 0;
private:
- ON__UINT_PTR m_reserved5 = 0;
+ ON__UINT_PTR m_reserved6 = 0;
};
ON_DECL
@@ -944,7 +1152,6 @@ public:
const class ON_3dPointListRef& vertex_list,
ON_PlaneEquation& face_plane_equation
) const;
-
};
@@ -1504,6 +1711,36 @@ public:
ON_SimpleArray& ngon_vi
);
+ /*
+Description:
+ Get a list of vertices that form any boundary of a set of faces.
+ This includes inner boundaries.
+Parameters:
+ mesh_vertex_list - [in]
+ mesh_face_list - [in]
+ vertex_face_map - [in]
+ null or a vertex map made from the information in
+ mesh_vertex_list and mesh_face_list.
+ ngon_fi_count - [in]
+ length of ngon_fi[] array
+ ngon_fi - [in]
+ An array of length ngon_fi_count that contains the indices
+ of the faces that form the ngon.
+ ngon_vi - [out]
+ An array of vertex indices that make the ngon boundary.
+Returns:
+ Number of vertices in the ngon outer boundary or 0 if the input is
+ not valid.
+*/
+ static unsigned int FindNgonBoundary(
+ const class ON_3dPointListRef& mesh_vertex_list,
+ const class ON_MeshFaceList& mesh_face_list,
+ const unsigned int *const* vertex_face_map,
+ size_t ngon_fi_count,
+ const unsigned int* ngon_fi,
+ ON_SimpleArray& ngon_vi
+ );
+
/*
Description:
Create an ngon pointer that contains a triangle (3-gon)
@@ -2095,6 +2332,18 @@ public:
*/
bool TopFaceIsHidden( int topfi ) const;
+ /*
+ Parameters:
+ topei - [in]
+ m_tope[] index
+ Returns:
+ true if the edge
+ has 2 distinct vertices,
+ 2 or more attached faces,
+ and all attached faces reference the same ON_Mesh vertices along this edge.
+ */
+ bool IsWeldedEdge( int topei ) const;
+
//////////
// m_topv_map[] has length m_mesh.VertexCount() and
// m_topv[m_topv_map[vi]] is the topological mesh vertex that is assocated
@@ -2189,6 +2438,11 @@ class ON_CLASS ON_MappingTag
{
public:
ON_MappingTag();
+ ON_MappingTag(const ON_TextureMapping& mapping,const ON_Xform* xform);
+
+ static const ON_MappingTag Unset;
+ static const ON_MappingTag SurfaceParameterMapping;
+
void Default();
bool Write(ON_BinaryArchive&) const;
bool Read(ON_BinaryArchive&);
@@ -2231,10 +2485,10 @@ public:
// m_mesh_xform to zero so that compares will work right.
//
//
- ON_UUID m_mapping_id; // ON_TextureMapping::m_mapping_id
- ON_TextureMapping::TYPE m_mapping_type; // ON_TextureMapping::m_type
- ON__UINT32 m_mapping_crc; // ON_TextureMapping::MappingCRC()
- ON_Xform m_mesh_xform;
+ ON_UUID m_mapping_id = ON_nil_uuid; // ON_TextureMapping::m_mapping_id
+ ON_TextureMapping::TYPE m_mapping_type = ON_TextureMapping::TYPE::no_mapping; // ON_TextureMapping::m_type
+ ON__UINT32 m_mapping_crc = 0; // ON_TextureMapping::MappingCRC()
+ ON_Xform m_mesh_xform = ON_Xform::IdentityTransformation;
};
class ON_CLASS ON_TextureCoordinates
@@ -2269,6 +2523,10 @@ public:
ON_Mesh& operator=( const ON_Mesh& );
~ON_Mesh();
+public:
+ static const ON_Mesh Empty;
+
+
// Override of virtual ON_Object::MemoryRelocate
void MemoryRelocate() override;
@@ -2377,6 +2635,14 @@ public:
*/
bool IsEmpty() const;
+
+ /*
+ Returns
+ true if there are vertices and faces.
+ */
+
+ bool IsNotEmpty() const;
+
// creation
bool SetVertex(
int, // vertex index
@@ -2509,6 +2775,49 @@ public:
bool bRemoveEmptyNgons
);
+ /*
+Description:
+ Delete the portions of the mesh identified in ci_list[].
+Parameters:
+ ci_list - [in]
+ List of components to delete.
+ ci_list_count - [in]
+ Number of elements in the ci_list[] array.
+ Can be zero if you are using this function to remove
+ unused vertices or empty ngons.
+ bIgnoreInvalidComponents - [in]
+ If true, invalid elements in ci_list[] are ignored.
+ If false and ci_list[] contains an invalid element,
+ then no changes are made and false is returned.
+ bRemoveDegenerateFaces - [in]
+ If true, remove degenerate faces.
+ bCullUnusedVertices - [in]
+ Remove vertices that are not referenced by a face.
+ Pass true unless you have a good reason for keeping
+ unreferenced vertices.
+ bRemoveEmptyNgons - [in]
+ Remove ngons that are empty.
+ Pass true unless you have a good reason for keeping
+ empty ngons.
+ faceMap - [i]
+ If anything other than nullptr is passed in, then
+ faceMap[fi] is the index of the new face after the
+ removal of vertices, faces, etc.
+ This needs to be allocated to be at least m_F.Count() long.
+Returns:
+ True: succesful
+ False: failure - no changes.
+*/
+ bool DeleteComponents(
+ const ON_COMPONENT_INDEX* ci_list,
+ size_t ci_count,
+ bool bIgnoreInvalidComponents,
+ bool bRemoveDegenerateFaces,
+ bool bRemoveUnusedVertices,
+ bool bRemoveEmptyNgons,
+ unsigned int* faceMap
+ );
+
/*
Description:
Calls the detailed version of DeleteComponents() with
@@ -2546,6 +2855,35 @@ public:
ON_COMPONENT_INDEX ci
);
+ /*
+ Description:
+ Merge faces like ON_SubD::MergeFaces() does.
+ Parameters:
+ ci_list - [in]
+ vertices, edges, and faces that trigger merging.
+ Returns:
+ If ngons were added, then the index of the first added ngon is returned.
+ Otherwise ON_UNSET_UINT_INDEX is returned.
+ */
+ unsigned int MergeFaceSets(
+ const ON_SimpleArray& ci_list
+ );
+
+ /*
+ Description:
+ Merge faces like ON_SubD::MergeFaces() does.
+ Parameters:
+ ci_list - [in]
+ vertices, edges, and faces that trigger merging.
+ Returns:
+ If ngons were added, then the index of the first added ngon is returned.
+ Otherwise ON_UNSET_UINT_INDEX is returned.
+ */
+ unsigned int DissolveOrDelete(
+ const ON_SimpleArray& ci_list
+ );
+
+
/*
Description:
Copy the subset of the mesh idenfied in the component list.
@@ -2785,6 +3123,14 @@ public:
int CullUnusedVertices(); // returns number of culled vertices
+ /*
+ Description:
+ Removes degenerate and unused mesh components.
+ Returns:
+ Number of removed components;
+ */
+ unsigned int CullDegenerates();
+
// Description:
// Removes any unreferenced objects from arrays, reindexes as needed,
// and shrinks arrays to minimum required size.
@@ -2913,6 +3259,8 @@ public:
bool bIgnoreTextureCoordinates = false
);
+ unsigned int RemoveAllCreases();
+
void Append( const ON_Mesh& ); // appends a copy of mesh to this and updates
// indices of appended mesh parts
@@ -3507,7 +3855,7 @@ public:
in m_V[], then you must call UpdateDoublePrecisionVertices().
Remarks:
If double precision vertices are not present, this function
- does nothing.
+ creates them.
*/
void UpdateDoublePrecisionVertices();
@@ -3653,6 +4001,85 @@ public:
ON_COMPONENT_INDEX ci
) const;
+ unsigned int AddNgons(
+ const ON_SimpleArray& ci_list
+ );
+
+ /*
+ Description:
+ Add a new ngon to the mesh.
+ Does not allow the creation of inner boundaries.
+ Parameters:
+ ngon_fi[]
+ An array of distinct ON_Mesh.m_F[] face indices referencing
+ a set of connected faces.
+ Returns:
+ index of the new n-gon.
+ -1: If input information is not valid.
+ */
+ int AddNgon(
+ const ON_SimpleArray& ngon_fi
+ );
+
+ /*
+ Description:
+ Add a new ngon to the mesh.
+ Parameters:
+ ngon_fi[]
+ An array of distinct ON_Mesh.m_F[] face indices referencing
+ a set of connected faces.
+ bPermitHoles
+ If true, also ngons that contain inner boundaries are allowed.
+ Returns:
+ index of the new n-gon.
+ -1: If input information is not valid.
+ */
+ int AddNgon(
+ const ON_SimpleArray& ngon_fi,
+ bool bPermitHoles
+ );
+
+ /*
+ Description:
+ Add a new ngon to the mesh.
+ Does not allow the creation of inner boundaries.
+ Parameters:
+ Fcount - [in]
+ Number of face that make up the ngon.
+ ngon_fi[]
+ An array of N distinct ON_Mesh.m_F[] face indices referencing
+ a set of connected faces.
+ Returns:
+ index of the new n-gon.
+ -1: If input information is not valid.
+ */
+ int AddNgon(
+ unsigned int Fcount,
+ const unsigned int* ngon_fi
+ );
+
+ /*
+ Description:
+ Add a new ngon to the mesh.
+ Parameters:
+ Fcount - [in]
+ Number of face that make up the ngon.
+ ngon_fi[]
+ An array of N distinct ON_Mesh.m_F[] face indices referencing
+ a set of connected faces.
+ bPermitHoles
+ If true, also ngons that contain inner boundaries are allowed.
+ Returns:
+ index of the new n-gon.
+ -1: If input information is not valid.
+*/
+ int AddNgon(
+ unsigned int Fcount,
+ const unsigned int* ngon_fi,
+ bool bPermitHoles
+ );
+
+
/*
Description:
Add a new ngon to the mesh.
@@ -4105,7 +4532,38 @@ public:
- Otherwise, ngon_map[fi] = -1.
*/
const unsigned int* CreateNgonMap();
-
+
+ /*
+ Description:
+ Expert user function to construct n-gon map even on const objects
+ after the expert user did something that made the current one invalid.
+ map [in-out]:
+ The map must have at least m_F elements
+ It is modified to match the new ngon map, if true is returned.
+ Returns:
+ A value indicating if the mesh has ngon information.
+ The map is an array of length m_F.Count(), ngon_map[]
+ - If ngon_map[fi] >= 0, then ON_MeshFace.m_F[fi] belongs
+ to ON_Mesh.Ngon(ngon_map[fi]).
+ - Otherwise, ngon_map[fi] = -1.
+ */
+ bool CreateNgonMap(unsigned int* ngon_map) const;
+
+
+ /*
+Description:
+ Expert user function to construct n-gon map even on const objects
+ after the expert user did something that made the current one invalid.
+map [out]:
+ The map is modified to match the new ngon map, if true is returned.
+Returns:
+A value indicating if the mesh has ngon information.
+The map is an array of length m_F.Count(), ngon_map[]
+ - If ngon_map[fi] >= 0, then ON_MeshFace.m_F[fi] belongs
+ to ON_Mesh.Ngon(ngon_map[fi]).
+ - Otherwise, ngon_map[fi] = -1.
+*/
+ bool CreateNgonMap(ON_SimpleArray& map) const;
/*
Description:
@@ -4353,6 +4811,115 @@ private:
bool SwapEdge_Helper( int, bool );
};
+//////////////////////////////////////////////////////////////////////////
+//
+// ON_MeshRef
+//
+class ON_CLASS ON_MeshRef
+{
+public:
+ static const ON_MeshRef Empty;
+
+ ON_MeshRef() ON_NOEXCEPT;
+ ~ON_MeshRef();
+ ON_MeshRef(const ON_MeshRef& src) ON_NOEXCEPT;
+ ON_MeshRef& operator=(const ON_MeshRef& src);
+
+#if defined(ON_HAS_RVALUEREF)
+ // rvalue copy constructor
+ ON_MeshRef( ON_MeshRef&& ) ON_NOEXCEPT;
+ // rvalue assignment operator
+ ON_MeshRef& operator=( ON_MeshRef&& );
+#endif
+
+ const class ON_Mesh& Mesh() const;
+
+ /*
+ Returns:
+ Number of references to the managed ON_Mesh, including the one by this ON_MeshRef.
+ */
+ unsigned int ReferenceCount() const;
+
+ /*
+ Description:
+ Allocates a new empty ON_Mesh and has this ON_MeshRef reference it.
+ */
+ class ON_Mesh& NewMesh();
+
+ /*
+ Description:
+ Allocates a new ON_Mesh and has this ON_MeshRef reference it.
+ Parameters:
+ src - [in]
+ The new ON_Mesh managed by this ON_MeshRef will be a copy of src.Mesh().
+ Returns:
+ A reference to the new ON_Mesh managed by this ON_MeshRef.
+ */
+ class ON_Mesh& CopyMesh(
+ const ON_MeshRef& src
+ );
+
+ /*
+ Description:
+ Allocates a new ON_Mesh and has this ON_MeshRef reference it.
+ Parameters:
+ src - [in]
+ The new ON_Mesh managed by this ON_MeshRef will be a copy of src.
+ Returns:
+ A reference to the new ON_Mesh managed by this ON_MeshRef.
+ */
+ class ON_Mesh& CopyMesh(
+ const ON_Mesh& src
+ );
+
+ /*
+ Description:
+ If ReferenceCount() > 1, then have this ON_MeshRef reference a
+ new copy. Otherwise do nothing. The result being that this will
+ be the unique reference to the ON_Mesh managed by this ON_MeshRef.
+ Returns:
+ A reference to the ON_Mesh uniquely managed by this ON_MeshRef.
+ */
+ class ON_Mesh& UniqueMesh();
+
+ /*
+ Description:
+ Remove this reference to the managed ON_Mesh.
+ If this is the last reference, then the managed ON_Mesh is deleted.
+ */
+ void Clear();
+
+public:
+ /*
+ Description:
+ Expert user function to have this ON_MeshRef manage mesh.
+ Parameters:
+ mesh - [in/out]
+ mesh must point to an ON_Mesh that was constructed on the heap using
+ an operator new call with a public ON_Mesh constructor.
+ Returns:
+ a pointer to the managed mesh
+ Example:
+ ON_Mesh* mesh = new ON_Mesh(...);
+ ON_MeshRef mesh_ref;
+ ON_Mesh* managed_mesh = mesh_ref.SetMesh(mesh);
+ // mesh = nullptr
+ // managed_mesh = pointer you can use
+ */
+ class ON_Mesh* SetMeshForExperts(
+ class ON_Mesh*& mesh
+ );
+
+private:
+#pragma ON_PRAGMA_WARNING_PUSH
+#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4251 )
+ // C4251: ... needs to have dll-interface to be used by clients of class ...
+ // m_mesh_sp is private and all code that manages m_mesh_sp is explicitly implemented in the DLL.
+private:
+ std::shared_ptr m_mesh_sp;
+#pragma ON_PRAGMA_WARNING_POP
+};
+
//////////////////////////////////////////////////////////////////////////
//
// ON_MeshCache
diff --git a/opennurbs_mesh_ngon.cpp b/opennurbs_mesh_ngon.cpp
index 5df77950..bf397b2d 100644
--- a/opennurbs_mesh_ngon.cpp
+++ b/opennurbs_mesh_ngon.cpp
@@ -26,6 +26,8 @@ const class ON_MeshNgon* ON_MeshNgonBuffer::CreateFromMeshFaceIndex(
const class ON_MeshFace* mesh_face = ( mesh && face_index < mesh->m_F.UnsignedCount() )
? &mesh->m_F[face_index]
: 0;
+ if (nullptr == mesh_face || false == mesh_face->IsValid(mesh->m_V.Count()))
+ return nullptr;
return CreateFromMeshFace(mesh_face,face_index);
}
@@ -762,7 +764,7 @@ const unsigned int* ON_Mesh::NgonMap(
return fdex_to_ndex_map;
}
-const unsigned int* ON_Mesh::CreateNgonMap()
+bool ON_Mesh::CreateNgonMap(ON_SimpleArray& map) const
{
unsigned int* ngon_map = 0;
const ON_MeshNgon* ngon;
@@ -770,17 +772,18 @@ const unsigned int* ON_Mesh::CreateNgonMap()
unsigned int ngon_index;
unsigned int j;
- m_NgonMap.SetCount(0);
-
const unsigned int ngon_count = m_Ngon.UnsignedCount();
const unsigned int meshFcount = m_F.UnsignedCount();
- if ( meshFcount <= 0 )
- return 0;
+ if (meshFcount <= 0)
+ {
+ map.SetCount(0);
+ return false;
+ }
- m_NgonMap.Reserve(meshFcount);
- m_NgonMap.SetCount(meshFcount);
- ngon_map = m_NgonMap.Array();
+ map.Reserve(meshFcount);
+ map.SetCount(meshFcount);
+ ngon_map = map.Array();
for ( fi = 0; fi < meshFcount; fi++ )
ngon_map[fi] = ON_UNSET_UINT_INDEX;
@@ -806,9 +809,28 @@ const unsigned int* ON_Mesh::CreateNgonMap()
}
}
- return ngon_map;
+ return true;
}
+bool ON_Mesh::CreateNgonMap(unsigned int* map) const
+{
+ ON_SimpleArray new_map {};
+ new_map.SetArray(map, m_F.Count(), m_F.Count());
+
+ if (!CreateNgonMap(new_map)) return false;
+ new_map.KeepArray();
+ return true;
+}
+
+const unsigned int* ON_Mesh::CreateNgonMap()
+{
+ if (!CreateNgonMap(m_NgonMap)) return nullptr;
+
+ return m_NgonMap.Array();
+}
+
+
+
bool ON_Mesh::ModifyNgon(
unsigned int ngon_index,
unsigned int Vcount,
@@ -894,6 +916,335 @@ bool ON_Mesh::ModifyNgon(
return false;
}
+static bool Internal_ListContainNgon(
+ unsigned int count,
+ const int* fi,
+ ON_SimpleArray< const ON_MeshNgon* >& tested_ngons,
+ const ON_MeshNgon* ngon
+)
+{
+ if (nullptr == ngon || ngon->m_Fcount <= 0)
+ return true; // nothing to test.
+
+ if (ngon->m_Fcount > count)
+ return false;
+
+ if (tested_ngons.Search(ngon) >= 0)
+ return true; // already tested this ngon
+
+ // fi[] is a sorted list
+ for (unsigned int k = 0; k < ngon->m_Fcount; ++k)
+ {
+ if (nullptr == ON_BinarySearchIntArray((int)ngon->m_fi[k], fi, count))
+ return false; // ngon->m_fi[k] is not in fi[]
+ }
+
+ if (ngon->m_Fcount > 1)
+ tested_ngons.Append(ngon); // no need to test this one again
+
+ // Every index in ngon->m_fi[] is also in fi[]
+ return true;
+}
+
+unsigned int ON_Mesh::AddNgons(
+ const ON_SimpleArray& ci_list
+)
+{
+ const int ci_count = ci_list.UnsignedCount();
+ if (ci_count < 2)
+ return 0;
+
+ const int face_count = FaceCount();
+ if (face_count < 2)
+ return 0;
+ const int ngon_count = NgonCount();
+ const unsigned int ngon_ucount = NgonUnsignedCount();
+
+ /////////////////////
+ //
+ // fi_list[] = list of faces to be merged into ngons
+ ON_SimpleArray fi_list(ci_count);
+ ON_COMPONENT_INDEX ci;
+ for (int i = 0; i < ci_count; ++i)
+ {
+ ci = ci_list[i];
+ if (ci.m_index < 0)
+ continue;
+ switch (ci.m_type)
+ {
+ case ON_COMPONENT_INDEX::TYPE::mesh_face:
+ if (ci.m_index < face_count)
+ fi_list.Append(ci.m_index);
+ break;
+ case ON_COMPONENT_INDEX::TYPE::mesh_ngon:
+ if (ci.m_index < ngon_count)
+ {
+ const ON_MeshNgon* ngon = Ngon(ci.m_index);
+ if (nullptr == ngon)
+ break;
+ for (unsigned j = 0; j < ngon->m_Fcount; ++j)
+ {
+ const int fi = ngon->m_fi[j];
+ if (fi >= 0 && fi < face_count)
+ fi_list.Append(fi);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ fi_list.QuickSortAndRemoveDuplicates(ON_CompareIncreasing);
+ const int fi_count = fi_list.Count();
+ if (fi_count < 2)
+ return 0;
+
+ /////////////////////
+ //
+ // Partition fi_list[] into subsets where each subset will be a new ngon.
+ const ON_MeshTopology& top = Topology();
+ if (face_count != top.m_topf.Count())
+ return 0;
+
+ const int edge_count = top.TopEdgeCount();
+
+ ON_SimpleArray face_available_buffer(face_count);
+ face_available_buffer.SetCount(face_count);
+ face_available_buffer.Zero();
+ bool* face_available = face_available_buffer.Array();
+
+ for (int i = 0; i < fi_count; ++i)
+ face_available[fi_list[i]] = true;
+
+ /////////////////////
+ //
+ // ngon_faces[] is the same set of indiced that are in fi_list[], but with indices for
+ // each new ngon grouped together.
+ // ngon_partition[] is used to find the new ngon subsets.
+ // If i0 = ngon_partition[j] && i1 = ngon_partition[j+1], then
+ // (ngon_faces[i0],...,ngon_faces[i1-1]) are the face indices for a new ngon.
+ //
+ ON_SimpleArray ngon_faces(fi_count);
+ ON_SimpleArray ngon_partition(64);
+
+ const unsigned int* ngon_map = (ngon_count > 0) ? NgonMap(true) : nullptr;
+
+ ON_SimpleArray< const ON_MeshNgon* > old_ngons_inside_new_ngon(32);
+
+ for (int i = 0; i < fi_count; ++i)
+ {
+ const int face_index = fi_list[i];
+ if (false == face_available[face_index])
+ continue; // face is no longer available.
+
+ // find the new ngon contain face_index.
+ face_available[face_index] = false;
+ const unsigned int count0 = ngon_faces.UnsignedCount();
+ unsigned int i0 = 0;
+ unsigned int i1 = count0;
+ ngon_partition.Append(count0);
+ ngon_faces.Append(face_index);
+ for (;;)
+ {
+ i0 = i1;
+ i1 = ngon_faces.Count();
+ if (i0 >= i1)
+ break;
+ for (/*empty init*/; i0 < i1; ++i0)
+ {
+ const int fi = ngon_faces[i0];
+ const ON_MeshTopologyFace& topf = top.m_topf[fi];
+ const int fe_count = topf.IsQuad() ? 4 : 3;
+ for (int fei = 0; fei < fe_count; ++fei)
+ {
+ int ei = topf.m_topei[fei];
+ if (ei < 0 || ei >= edge_count)
+ continue;
+ const ON_MeshTopologyEdge& tope = top.m_tope[ei];
+ if (2 != tope.m_topf_count)
+ continue;
+ if (2 != tope.m_topf_count || false == top.IsWeldedEdge(ei))
+ continue;
+ int neighbor_fi = tope.m_topfi[(fi == tope.m_topfi[0]) ? 1 : 0];
+ if (neighbor_fi < 0 || neighbor_fi >= face_count || false == face_available[neighbor_fi])
+ continue;
+ face_available[neighbor_fi] = false;
+ ngon_faces.Append(neighbor_fi);
+ }
+ }
+ }
+
+ const unsigned new_ngon_F_count = ngon_faces.UnsignedCount() - count0;
+ int* new_ngon_fi = ngon_faces.Array() + count0;
+ ON_SortIntArray(ON::sort_algorithm::quick_sort, new_ngon_fi, new_ngon_F_count);
+ bool bSkipNewNgon = (new_ngon_F_count < 2);
+ if (false == bSkipNewNgon && nullptr != ngon_map)
+ {
+ old_ngons_inside_new_ngon.SetCount(0);
+
+ // Make sure the new ngon doesn't contain proper subsets of existing ngons.
+ for (unsigned j = 0; j < new_ngon_F_count; ++j)
+ {
+ int fi = new_ngon_fi[j];
+ const unsigned int ngon_dex = ngon_map[fi];
+ if (ngon_dex >= ngon_ucount)
+ continue;
+ const ON_MeshNgon* existing_ngon = Ngon(ngon_dex);
+ if (nullptr == existing_ngon)
+ continue;
+ if (false == Internal_ListContainNgon(new_ngon_F_count, new_ngon_fi, old_ngons_inside_new_ngon, existing_ngon))
+ {
+ // existing ngon has faces that are not in new ngon.
+ bSkipNewNgon = true;
+ break;
+ }
+ if (existing_ngon->m_Fcount == new_ngon_F_count)
+ {
+ // new ngon and existing ngon are the same.
+ bSkipNewNgon = true;
+ break;
+ }
+ }
+ }
+ if (bSkipNewNgon)
+ {
+ ngon_faces.SetCount(count0);
+ ngon_partition.Remove();
+ }
+ }
+
+ if (ngon_partition.UnsignedCount() < 1 || ngon_faces.UnsignedCount() < 2)
+ return 0;
+
+ ngon_partition.Append(ngon_faces.UnsignedCount());
+
+ bool bRemoveddNgons = false;
+ if (nullptr != ngon_map)
+ {
+ // Delete ngons that are being merged into bigger ngons
+ ON_SimpleArray delete_old_ngon(ngon_count);
+ delete_old_ngon.SetCount(ngon_count);
+ for (int j = 0; j < ngon_faces.Count(); ++j)
+ {
+ unsigned ngon_dex = ngon_map[ngon_faces[j]];
+ if (ngon_dex < ngon_ucount)
+ delete_old_ngon[ngon_dex] = true;
+ }
+ for (int j = ngon_count; j > 0; --j)
+ {
+ unsigned int ngon_dex = (unsigned int)(j - 1);
+ if (delete_old_ngon[ngon_dex])
+ {
+ RemoveNgon(ngon_dex);
+ bRemoveddNgons = true;
+ }
+ }
+ }
+
+ // Add new ngons
+ unsigned int new_ngon_count = 0;
+ unsigned int i1 = ngon_partition[0];
+ for (unsigned int i = 1; i < ngon_partition.UnsignedCount(); ++i)
+ {
+ const unsigned int i0 = i1;
+ i1 = ngon_partition[i];
+ if (i0 + 2 <= i1)
+ {
+ if (AddNgon(i1 - i0, (const unsigned int*)(ngon_faces.Array() + i0)) >= 0)
+ ++new_ngon_count;
+ }
+ }
+
+ if (bRemoveddNgons || new_ngon_count > 0)
+ {
+ // clean up ngon storage
+ m_NgonMap.SetCount(0);
+ int count = 0;
+ for (int i = 0; i < m_Ngon.Count(); ++i)
+ {
+ ON_MeshNgon* n = m_Ngon[i];
+ if (nullptr == n)
+ continue;
+ m_Ngon[count++] = n;
+ }
+ m_Ngon.SetCount(count);
+ NgonMap(true);
+ }
+
+ return new_ngon_count;
+}
+
+int ON_Mesh::AddNgon(const ON_SimpleArray& ngon_fi)
+{
+ return AddNgon(ngon_fi.UnsignedCount(), ngon_fi.Array());
+}
+
+int ON_Mesh::AddNgon(const ON_SimpleArray& ngon_fi, bool bPermitHoles)
+{
+ return AddNgon(ngon_fi.UnsignedCount(), ngon_fi.Array(), bPermitHoles);
+}
+
+int ON_Mesh::AddNgon(
+ unsigned int Fcount,
+ const unsigned int* ngon_fi
+)
+{
+ return AddNgon(Fcount, ngon_fi, false);
+}
+
+int ON_Mesh::AddNgon(
+ unsigned int Fcount,
+ const unsigned int* ngon_fi,
+ bool bPermitHoles
+)
+{
+ unsigned int ngon_index = ON_UNSET_UINT_INDEX;
+ if (Fcount < 1 || nullptr == ngon_fi)
+ return ngon_index;
+
+ ON_SimpleArray ngon_vi;
+ const ON_3dPointListRef mesh_vertex_list(this);
+ const ON_MeshFaceList& mesh_face_list(this);
+ const unsigned int *const* vertex_face_map = nullptr;
+
+ const int face_count = m_F.Count();
+ const unsigned int ngon_count0 = HasNgons() ? NgonUnsignedCount() : 0U;
+ bool bCheckNgonMap = (ngon_count0 > 0 && face_count == m_NgonMap.Count());
+ for (unsigned int i = 0; i < Fcount; ++i)
+ {
+ unsigned int fi = ngon_fi[i];
+ if (fi >= (unsigned int)face_count)
+ return ngon_index;
+ if (bCheckNgonMap && m_NgonMap[fi] < ngon_count0)
+ return ngon_index; // input faces are already in an ngon
+ }
+
+ unsigned int vi_count;
+ if (bPermitHoles)
+ vi_count = ON_MeshNgon::FindNgonBoundary(
+ mesh_vertex_list,
+ mesh_face_list,
+ vertex_face_map,
+ Fcount,
+ ngon_fi,
+ ngon_vi
+ );
+ else
+ vi_count = ON_MeshNgon::FindNgonOuterBoundary(
+ mesh_vertex_list,
+ mesh_face_list,
+ vertex_face_map,
+ Fcount,
+ ngon_fi,
+ ngon_vi
+ );
+
+ if (vi_count < 3 || ngon_vi.Count() < 3)
+ return ngon_index; //
+
+ return AddNgon(ngon_vi.UnsignedCount(), ngon_vi.Array(), Fcount, ngon_fi);
+}
+
int ON_Mesh::AddNgon(
unsigned int Vcount,
const unsigned int* ngon_vi,
@@ -3660,26 +4011,27 @@ const ON_MeshNgon* ON_Mesh::NgonFromComponentIndex(
return ngon;
}
-unsigned int ON_MeshNgon::FindNgonOuterBoundary(
+static unsigned int FindNgonBoundary_Helper(
const ON_3dPointListRef& mesh_vertex_list,
const ON_MeshFaceList& mesh_face_list,
const unsigned int *const* vertex_face_map,
size_t ngon_fi_count,
const unsigned int* ngon_fi,
- ON_SimpleArray& ngon_vi
- )
+ ON_SimpleArray& ngon_vi,
+ bool permitOnlyOneBoundary
+)
{
const unsigned int mesh_vertex_count = mesh_vertex_list.PointCount();
const unsigned int ngon_boundary_index = 1;
unsigned int boundary_edge_count;
- for(;;)
+ for (;;)
{
- if ( mesh_vertex_count <= 0 || ON_UNSET_UINT_INDEX == mesh_vertex_count )
+ if (mesh_vertex_count <= 0 || ON_UNSET_UINT_INDEX == mesh_vertex_count)
break;
- if ( ngon_fi_count <= 0 || 0 == ngon_fi )
+ if (ngon_fi_count <= 0 || 0 == ngon_fi)
break;
ON_SimpleArray ngon_nbr_map;
@@ -3692,14 +4044,14 @@ unsigned int ON_MeshNgon::FindNgonOuterBoundary(
(unsigned int)ngon_fi_count,
ngon_fi,
ngon_nbr_map.Array()
- );
+ );
- if ( boundary_edge_count <= 0 )
+ if (boundary_edge_count <= 0)
break;
ngon_vi.SetCount(0);
ngon_vi.Reserve(boundary_edge_count);
- if ( !GetNgonBoundarySegments(
+ if (!GetNgonBoundarySegments(
mesh_face_list,
(unsigned int)ngon_fi_count,
ngon_fi,
@@ -3707,10 +4059,10 @@ unsigned int ON_MeshNgon::FindNgonOuterBoundary(
ngon_nbr_map.Array(),
&ngon_vi,
0
- ))
+ ))
break;
- if ( boundary_edge_count != ngon_vi.UnsignedCount() )
+ if (permitOnlyOneBoundary && boundary_edge_count != ngon_vi.UnsignedCount())
break; // inner boundaries exist - ngon has holes
return ngon_vi.UnsignedCount();
@@ -3721,6 +4073,47 @@ unsigned int ON_MeshNgon::FindNgonOuterBoundary(
return 0;
}
+
+unsigned int ON_MeshNgon::FindNgonOuterBoundary(
+ const ON_3dPointListRef& mesh_vertex_list,
+ const ON_MeshFaceList& mesh_face_list,
+ const unsigned int *const* vertex_face_map,
+ size_t ngon_fi_count,
+ const unsigned int* ngon_fi,
+ ON_SimpleArray& ngon_vi
+ )
+{
+ return FindNgonBoundary_Helper(
+ mesh_vertex_list,
+ mesh_face_list,
+ vertex_face_map,
+ ngon_fi_count,
+ ngon_fi,
+ ngon_vi,
+ true
+ );
+}
+
+unsigned int ON_MeshNgon::FindNgonBoundary(
+ const ON_3dPointListRef& mesh_vertex_list,
+ const ON_MeshFaceList& mesh_face_list,
+ const unsigned int *const* vertex_face_map,
+ size_t ngon_fi_count,
+ const unsigned int* ngon_fi,
+ ON_SimpleArray& ngon_vi
+)
+{
+ return FindNgonBoundary_Helper(
+ mesh_vertex_list,
+ mesh_face_list,
+ vertex_face_map,
+ ngon_fi_count,
+ ngon_fi,
+ ngon_vi,
+ false
+ );
+}
+
unsigned int ON_MeshNgon::GetBoundarySides(
const class ON_MeshFaceList& mesh_face_list,
ON_SimpleArray& ngon_boundary_sides
@@ -4660,7 +5053,7 @@ ON_Plane ON_Plane::FromPointList(
//
// Find a plane to project the 3d points to.
//
- ON_3dVector N(ON_3dVector::UnsetVector);
+ ON_3dVector N(ON_3dVector::ZeroVector);
ON_3dVector X(ON_3dVector::UnsetVector);
if ( point_index_count <= 4 )
{
@@ -4680,7 +5073,8 @@ ON_Plane ON_Plane::FromPointList(
if ( X.Length() < Pi1.DistanceTo(Pi0) )
X = Pi1-Pi0;
}
- else
+
+ if (N == ON_3dVector::ZeroVector)
{
// find far apart points
unsigned int i0 = 0;
@@ -4774,7 +5168,7 @@ ON_Plane ON_Plane::FromPointList(
-const ON_MeshNgonIterator ON_MeshNgonIterator::EmptyMeshNgonIterator;
+const ON_MeshNgonIterator ON_MeshNgonIterator::EmptyMeshNgonIterator ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_MeshNgonIterator);
ON_MeshNgonIterator::ON_MeshNgonIterator(
const class ON_Mesh* mesh
diff --git a/opennurbs_nurbscurve.cpp b/opennurbs_nurbscurve.cpp
index 0f96f59d..3332e792 100644
--- a/opennurbs_nurbscurve.cpp
+++ b/opennurbs_nurbscurve.cpp
@@ -30,44 +30,80 @@ ON_OBJECT_IMPLEMENT(ON_NurbsCurve,ON_Curve,"4ED7D4DD-E947-11d3-BFE5-0010830122F0
Description:
Helper to make a deep copy (duplicate memory) from src to dest
*/
-static void ON_NurbsCurve_copy_member_data( const ON_NurbsCurve& src, ON_NurbsCurve& dest )
+void ON_NurbsCurve::Internal_DeepCopyFrom( const ON_NurbsCurve& src )
{
- int i;
- dest.m_dim = src.m_dim;
- dest.m_is_rat = src.m_is_rat;
- dest.m_order = src.m_order;
- dest.m_cv_count = src.m_cv_count;
- dest.m_cv_stride = dest.m_is_rat ? dest.m_dim+1 : dest.m_dim;
- if ( src.m_knot )
+ if (this == &src)
{
- // copy knot array
- i = dest.KnotCount();
- dest.ReserveKnotCapacity( i );
- memcpy( dest.m_knot, src.m_knot, i*sizeof(*dest.m_knot) );
+ ON_ERROR("this and &src must be different.");
+ return;
}
- if ( src.m_cv )
+
+ // erase tags
+ m_knot_capacity_and_tags &= ON_NurbsCurve::masks::knot_capacity;
+
+ const int knot_capacity = (nullptr != src.m_knot) ? src.KnotCount() : 0;
+ if (knot_capacity > 0)
{
- // copy cv array
- dest.ReserveCVCapacity( dest.m_cv_stride*dest.m_cv_count );
- const int dst_cv_size = dest.CVSize()*sizeof(*dest.m_cv);
- const int src_stride = src.m_cv_stride;
- const int dst_stride = dest.m_cv_stride;
- const double *src_cv = src.CV(0);
- double *dst_cv = dest.m_cv;
- if ( src_stride == dst_stride )
+ ReserveKnotCapacity(knot_capacity);
+ if ( nullptr != m_knot )
+ memcpy( m_knot, src.m_knot, knot_capacity*sizeof(m_knot[0]) );
+ }
+ else if (nullptr != m_knot && KnotCapacity() > 0)
+ {
+ onfree(m_knot);
+ m_knot = nullptr;
+ m_knot_capacity_and_tags = 0;
+ }
+
+ int cv_count = (src.m_cv_count>0) ? src.m_cv_count : 0;
+ int cv_stride = (src.m_dim > 0) ? ((src.m_is_rat) ? (src.m_dim + 1) : src.m_dim) : 0;
+ const int cv_capacity = (nullptr != src.m_cv) ? (cv_count*cv_stride) : 0;
+ if (cv_capacity > 0)
+ {
+ ReserveCVCapacity(cv_capacity);
+ if ( nullptr != m_cv )
{
- memcpy( dst_cv, src_cv, dest.m_cv_count*dst_stride*sizeof(*dst_cv) );
- }
- else
- {
- for ( i = 0; i < dest.m_cv_count; i++ )
+ // copy cv array
+ const int src_stride = src.m_cv_stride;
+ const double *src_cv = src.m_cv;
+ double *dst_cv = m_cv;
+ if (src_stride == cv_stride)
{
- memcpy( dst_cv, src_cv, dst_cv_size );
- dst_cv += dst_stride;
- src_cv += src_stride;
+ memcpy(dst_cv, src_cv, cv_capacity * sizeof(dst_cv[0]));
+ }
+ else
+ {
+ const size_t sizeof_cv = (size_t)(cv_stride * sizeof(dst_cv[0]));
+ for (int i = 0; i < cv_count; i++)
+ {
+ memcpy(dst_cv, src_cv, sizeof_cv);
+ dst_cv += cv_stride;
+ src_cv += src_stride;
+ }
}
}
}
+ else
+ {
+ if (nullptr != m_cv && m_cv_capacity > 0)
+ {
+ onfree(m_cv);
+ m_cv = nullptr;
+ }
+ m_cv_capacity = 0;
+ cv_count = 0;
+ cv_stride = 0;
+ }
+
+ m_dim = src.m_dim;
+ m_is_rat = src.m_is_rat;
+ m_order = src.m_order;
+ m_cv_count = cv_count;
+ m_cv_stride = cv_stride;
+
+ // copy tags
+ const unsigned int src_tags = (src.m_knot_capacity_and_tags & ON_NurbsCurve::masks::all_tags);
+ m_knot_capacity_and_tags |= src_tags;
}
/*
@@ -75,17 +111,22 @@ Description:
Helper to move (shallow copy) from src to dest
*/
#if defined(ON_HAS_RVALUEREF)
-static void ON_NurbsCurve_move_member_data( const ON_NurbsCurve& src, ON_NurbsCurve& dest )
+void ON_NurbsCurve::Internal_ShallowCopyFrom( const ON_NurbsCurve& src )
{
- dest.m_dim = src.m_dim;
- dest.m_is_rat = src.m_is_rat;
- dest.m_order = src.m_order;
- dest.m_cv_count = src.m_cv_count;
- dest.m_knot_capacity = src.m_knot_capacity;
- dest.m_knot = src.m_knot;
- dest.m_cv_stride = src.m_cv_stride;
- dest.m_cv_capacity = src.m_cv_capacity;
- dest.m_cv = src.m_cv;
+ if (this == &src)
+ {
+ ON_ERROR("this and &src must be different.");
+ return;
+ }
+ m_dim = src.m_dim;
+ m_is_rat = src.m_is_rat;
+ m_order = src.m_order;
+ m_cv_count = src.m_cv_count;
+ m_knot_capacity_and_tags = src.m_knot_capacity_and_tags;
+ m_knot = src.m_knot;
+ m_cv_stride = src.m_cv_stride;
+ m_cv_capacity = src.m_cv_capacity;
+ m_cv = src.m_cv;
}
#endif
@@ -93,28 +134,28 @@ static void ON_NurbsCurve_move_member_data( const ON_NurbsCurve& src, ON_NurbsCu
Description:
Helper to clear (set all member vars to zero)
*/
-static void ON_NurbsCurve_zero_member_data( ON_NurbsCurve& dest )
+void ON_NurbsCurve::Internal_InitializeToZero()
{
- dest.m_dim = 0;
- dest.m_is_rat = 0;
- dest.m_order = 0;
- dest.m_cv_count = 0;
- dest.m_knot_capacity = 0;
- dest.m_knot = 0;
- dest.m_cv_stride = 0;
- dest.m_cv_capacity = 0;
- dest.m_cv = 0;
+ m_dim = 0;
+ m_is_rat = 0;
+ m_order = 0;
+ m_cv_count = 0;
+ m_knot_capacity_and_tags = 0;
+ m_knot = 0;
+ m_cv_stride = 0;
+ m_cv_capacity = 0;
+ m_cv = 0;
}
/*
Description:
Helper to make a deep copy (duplicate memory) from src to dest
*/
-static void ON_NurbsCurve_delete_member_data( ON_NurbsCurve& dest )
+void ON_NurbsCurve::Internal_Destroy()
{
- double* cv = ( dest.m_cv && dest.m_cv_capacity ) ? dest.m_cv : 0;
- double* knot = ( dest.m_knot && dest.m_knot_capacity ) ? dest.m_knot : 0;
- ON_NurbsCurve_zero_member_data(dest);
+ double* cv = (nullptr != m_cv && CVCapacity() > 0 ) ? m_cv : nullptr;
+ double* knot = (nullptr != m_knot && KnotCapacity() > 0 ) ? m_knot : nullptr;
+ Internal_InitializeToZero();
if ( cv )
onfree(cv);
if ( knot )
@@ -124,23 +165,24 @@ static void ON_NurbsCurve_delete_member_data( ON_NurbsCurve& dest )
ON_NurbsCurve::ON_NurbsCurve() ON_NOEXCEPT
{
ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr);
- ON_NurbsCurve_zero_member_data(*this);
+ Internal_InitializeToZero();
}
ON_NurbsCurve::ON_NurbsCurve( const ON_NurbsCurve& src )
: ON_Curve(src)
{
ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr);
- ON_NurbsCurve_zero_member_data(*this);
- ON_NurbsCurve_copy_member_data(src,*this);
+ Internal_InitializeToZero();
+ Internal_DeepCopyFrom(src);
}
ON_NurbsCurve& ON_NurbsCurve::operator=( const ON_NurbsCurve& src )
{
if ( this != &src )
{
+ Internal_Destroy(); // consider removing this so "expert" knots and cv can be used.
ON_Curve::operator=(src);
- ON_NurbsCurve_copy_member_data(src,*this);
+ Internal_DeepCopyFrom(src);
}
return *this;
}
@@ -151,18 +193,18 @@ ON_NurbsCurve::ON_NurbsCurve( ON_NurbsCurve&& src) ON_NOEXCEPT
: ON_Curve(std::move(src))
{
ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr);
- ON_NurbsCurve_move_member_data(src,*this);
- ON_NurbsCurve_zero_member_data(src);
+ Internal_ShallowCopyFrom(src);
+ src.Internal_InitializeToZero();
}
ON_NurbsCurve& ON_NurbsCurve::operator=( ON_NurbsCurve&& src)
{
if ( this != &src )
{
- ON_NurbsCurve_delete_member_data(*this);
+ Internal_Destroy();
ON_Curve::operator=(std::move(src));
- ON_NurbsCurve_move_member_data(src,*this);
- ON_NurbsCurve_zero_member_data(src);
+ Internal_ShallowCopyFrom(src);
+ src.Internal_InitializeToZero();
}
return *this;
}
@@ -221,8 +263,8 @@ unsigned int ON_NurbsCurve::SizeOf() const
{
unsigned int sz = ON_Curve::SizeOf();
sz += (sizeof(*this) - sizeof(ON_Curve));
- sz += m_knot_capacity*sizeof(*m_knot);
- sz += m_cv_capacity*sizeof(*m_cv);
+ sz += KnotCapacity()*sizeof(*m_knot);
+ sz += CVCapacity()*sizeof(*m_cv);
return sz;
}
@@ -404,18 +446,18 @@ bool ON_NurbsCurve::Create(
void ON_NurbsCurve::Destroy()
{
DestroyCurveTree();
- ON_NurbsCurve_delete_member_data(*this);
+ Internal_Destroy();
}
void ON_NurbsCurve::EmergencyDestroy()
{
- ON_NurbsCurve_zero_member_data(*this);
+ Internal_InitializeToZero();
}
void ON_NurbsCurve::Initialize()
{
- ON_NurbsCurve_zero_member_data(*this);
+ Internal_InitializeToZero();
}
ON_NurbsCurve& ON_NurbsCurve::operator=(const ON_BezierCurve& src)
@@ -724,7 +766,8 @@ bool ON_NurbsCurve::Write(
) const
{
// NOTE - check legacy I/O code if changed
- bool rc = file.Write3dmChunkVersion(1,0);
+ const int minor_version = (file.Archive3dmVersion() >= 60);
+ bool rc = file.Write3dmChunkVersion(1,minor_version);
if (rc)
{
if (rc) rc = file.WriteInt( m_dim );
@@ -763,6 +806,12 @@ bool ON_NurbsCurve::Write(
}
}
+ if (rc && minor_version >= 1)
+ {
+ // April 2019 Chunk version 1.1 - added SubDFriendlyTag
+ const bool bSubDFriendlyTag = SubDFriendlyTag();
+ rc = file.WriteBool(bSubDFriendlyTag);
+ }
}
return rc;
}
@@ -825,6 +874,15 @@ bool ON_NurbsCurve::Read(
rc = file.ReadDouble( cv_size, CV(i) );
}
}
+
+ if (rc && minor_version >= 1)
+ {
+ // April 2019 Chunk version 1.1 - added SubDFriendlyTag
+ bool bSubDFriendlyTag = false;
+ rc = file.ReadBool(&bSubDFriendlyTag);
+ if (bSubDFriendlyTag)
+ SetSubDFriendlyTag(bSubDFriendlyTag);
+ }
}
if ( !rc )
Destroy();
@@ -870,6 +928,109 @@ bool ON_NurbsCurve::SetDomain( double t0, double t1 )
return rc;
}
+
+static ON_NurbsCurve* MoveSeamPeriodicKnot(const ON_NurbsCurve& crv, int knot_index)
+
+{
+ int dg = crv.Degree();
+ if (dg < 2)
+ return 0;
+ if (!crv.IsPeriodic())
+ return 0;
+ if (knot_index < dg || knot_index >= crv.KnotCount()-dg)
+ return 0;
+ int sc = crv.SpanCount();
+ if (sc < crv.KnotCount()-2*dg+1)//Not all knots are simple
+ return 0;
+ const double* knots = &crv.m_knot[dg-1];
+ int distinct_cvc = crv.CVCount()-dg;
+ double dom_len = crv.Domain().Length();
+ ON_NurbsCurve* pNC = ON_NurbsCurve::New(crv);
+ int curr = dg-1;
+ for (int i=knot_index; iSetKnot(curr, pNC->Knot(i));
+ curr++;
+ }
+
+ for (int i=0; i<=knot_index-dg+1; i++){
+ pNC->SetKnot(curr, knots[i] + dom_len);
+ curr++;
+ }
+
+ for (int i=0; iSetKnot(curr+i, pNC->Knot(curr+i-1)+pNC->Knot(dg+i) - pNC->Knot(dg+i-1));
+ pNC->SetKnot(dg-2-i, pNC->Knot(dg-i-1) - pNC->Knot(curr-1-i) + pNC->Knot(curr-2-i));
+ }
+
+ int cv_id = knot_index-dg+1;
+ for (int i=0; iCVCount(); i++){
+ ON_4dPoint cv;
+ crv.GetCV(cv_id%distinct_cvc, cv);
+ pNC->SetCV(i, cv);
+ cv_id++;
+ }
+
+ return pNC;
+}
+
+
+static ON_NurbsCurve* MoveSeamPeriodic(const ON_Curve& crv, double t)
+
+{
+ ON_NurbsCurve nc;
+ double s = t;
+ const ON_NurbsCurve*pNC = ON_NurbsCurve::Cast(&crv);
+ if (pNC)
+ nc = *pNC;
+ else {
+ if (!crv.GetNurbFormParameterFromCurveParameter(t, &s))
+ return 0;
+ if (!crv.GetNurbForm(nc))
+ return 0;
+ }
+ if (!nc.IsPeriodic())
+ return 0;
+ //Make sure all interior knots are simple
+
+ int sc = nc.SpanCount();
+ if (sc < nc.KnotCount()-2*nc.Degree()+1)//Not all knots are simple
+ return 0;
+
+ int knot_index = -1;
+ for (int i=0; i s){
+ knot_index = i;
+ break;
+ }
+ }
+ if (knot_index < nc.Degree() || knot_index > nc.KnotCount()-nc.Degree())
+ return 0;
+
+ double k0 = nc.Knot(knot_index-1);
+ double d0 = s-k0;
+ double k1 = nc.Knot(knot_index);
+ double d1 = k1-s;
+ bool bInsert = true;
+ if (d0<=d1){
+ if (d0 < ON_ZERO_TOLERANCE*k0){
+ knot_index--;
+ bInsert = false;
+ }
+ }
+ else {
+ if (d1 < ON_ZERO_TOLERANCE*k1)
+ bInsert = false;
+ }
+
+ if (bInsert && !nc.InsertKnot(s, 1))
+ return 0;
+
+ return MoveSeamPeriodicKnot(nc, knot_index);
+
+}
+
+
+
bool ON_NurbsCurve::ChangeClosedCurveSeam( double t )
{
bool rc = IsClosed();
@@ -890,8 +1051,16 @@ bool ON_NurbsCurve::ChangeClosedCurveSeam( double t )
if ( old_dom.Includes(k,true) )
{
ON_NurbsCurve left, right;
- // TODO - if periodic - dont' put multi knot in middle
- //if ( IsPeriodic() ){} else
+ // TODO - if periodic - dont' put multi knot in middle.
+ bool bGotIt = false;
+ if ( IsPeriodic() ){//This only works if the curve has only simple knots
+ ON_NurbsCurve* pMovedNC = MoveSeamPeriodic(*this, t);
+ if (pMovedNC){
+ *this = *pMovedNC;
+ delete pMovedNC;
+ }
+ }
+ if (!bGotIt)
{
ON_Curve* pleft = &left;
ON_Curve* pright = &right;
@@ -2034,9 +2203,11 @@ bool ON_NurbsCurve::ZeroCVs()
{
bool rc = false;
int i;
- if ( m_cv ) {
- if ( m_cv_capacity > 0 ) {
- memset( m_cv, 0, m_cv_capacity*sizeof(*m_cv) );
+ if ( m_cv )
+ {
+ if ( CVCapacity() > 0 )
+ {
+ memset( m_cv, 0, CVCapacity()*sizeof(*m_cv) );
if ( m_is_rat ) {
for ( i = 0; i < m_cv_count; i++ ) {
SetWeight( i, 1.0 );
@@ -2067,78 +2238,124 @@ bool ON_NurbsCurve::IsClamped( // determine if knot vector is clamped
return ON_IsKnotVectorClamped( m_order, m_cv_count, m_knot, end );
}
+bool ON_NurbsCurve::IsNatural(int end) const
+{
+ bool rc = false;
+ ON_3dPoint CV0, CV2, P;
+ ON_3dVector D1, D2;
+ const ON_Interval domain(Domain());
+ for (int pass = ((0==end||2==end) ? 0 : 1); pass < ((1==end||2==end)?2 : 1); ++pass)
+ {
+ ON_BezierCurve span;
+ if (false == ConvertSpanToBezier(((0 == pass) ? 0 : (m_cv_count - m_order)), span) || span.m_order < 2)
+ return false;
+ int i0 = (0 == pass) ? 0 : span.m_order - 1;
+ int di = (0 == pass) ? 1 : -1;
+ if (span.m_order > 2)
+ di *= 2;
+ if (false == span.GetCV(i0, CV0))
+ return false;
+ if (false == span.GetCV(i0 + di, CV2))
+ return false;
+ Ev2Der(domain[pass], P, D1, D2, (0 == pass) ? 1 : -1);
+ const double d2 = D2.Length();
+ const double tol = CV0.DistanceTo(CV2)*1.0e-8;
+ if (false == (d2 <= tol))
+ return false;
+ rc = true;
+ }
+ return rc;
+}
+
+int ON_NurbsCurve::CVCapacity() const
+{
+ // If m_cv_capacity > 0, then m_cv[] was allocated by onmalloc(sz)/onrealloc(...,sz)
+ // where sz = m_cv_capacity*sizeof(double).
+ return m_cv_capacity; // number of doubles in m_cv
+}
bool ON_NurbsCurve::ReserveCVCapacity(int desired_capacity)
{
- // If m_cv_capacity == 0 and m_cv != nullptr, then the user
- // has hand built the ON_NurbsCurve.m_cv array and is responsible
- // for making sure it's always big enough.
- bool rc = true;
- if ( desired_capacity > m_cv_capacity ) {
- if ( !m_cv ) {
- // no cv array - allocate one
- m_cv = (double*)onmalloc(desired_capacity*sizeof(*m_cv));
- if ( !m_cv ) {
- rc = false;
- }
- else {
- m_cv_capacity = desired_capacity;
- }
- }
- else if ( m_cv_capacity > 0 ) {
- // existing m_cv[] is too small and the fact that
- // m_cv_capacity > 0 indicates that ON_NurbsCurve is
- // managing the m_cv[] memory, so we need to grow
- // the m_cv[] array.
- m_cv = (double*)onrealloc(m_cv,desired_capacity*sizeof(*m_cv));
- if ( !m_cv ) {
- rc = false;
- m_cv_capacity = 0;
- }
- else {
- m_cv_capacity = desired_capacity;
- }
- }
+ if (nullptr != m_cv && 0 == m_cv_capacity)
+ {
+ // If m_cv_capacity == 0 and m_cv != nullptr, then the "expert" user
+ // has hand built the ON_NurbsCurve.m_cv array and is responsible
+ // for making sure it's always big enough.
+ return true;
}
+
+ const int current_capacity = (nullptr != m_cv && m_cv_capacity > 0) ? m_cv_capacity : 0;
+ if ( desired_capacity > current_capacity )
+ {
+ m_cv = (0 == current_capacity)
+ ? (double*)onmalloc(desired_capacity*sizeof(*m_cv))
+ : (double*)onrealloc(m_cv,desired_capacity*sizeof(*m_cv));
+ m_cv_capacity = ( nullptr != m_cv ) ? desired_capacity : 0;
+ }
+
+ const bool rc = (m_cv_capacity >= desired_capacity);
return rc;
}
+int ON_NurbsCurve::KnotCapacity() const
+{
+ const int knot_capacity
+ = (nullptr == m_knot)
+ ? 0
+ : ((int)(m_knot_capacity_and_tags & ON_NurbsCurve::masks::knot_capacity));
+ return knot_capacity;
+}
+
+void ON_NurbsCurve::UnmanageKnotForExperts(
+ int& knot_capacity,
+ double*& knot
+)
+{
+ knot_capacity = KnotCapacity();
+ knot = m_knot;
+ m_knot_capacity_and_tags &= ON_NurbsCurve::masks::all_tags; // knot_capacity = 0;
+ m_knot = nullptr;
+}
+
+void ON_NurbsCurve::ManageKnotForExperts(
+ int knot_capacity,
+ double* knot
+)
+{
+ // Unconditionally set m_knot and KnotCapacity().
+ // (Do not free preexisting m_knot.)
+ const unsigned int tags = (m_knot_capacity_and_tags & ON_NurbsCurve::masks::all_tags);
+ const unsigned int new_knot_capacity = (knot_capacity > 0) ? (((unsigned int)knot_capacity) & ON_NurbsCurve::masks::knot_capacity) : 0U;
+ m_knot_capacity_and_tags = (tags | new_knot_capacity);
+ m_knot = knot;
+}
+
bool ON_NurbsCurve::ReserveKnotCapacity(int desired_capacity)
{
- // If m_knot_capacity == 0 and m_knot != nullptr, then the user
- // has hand built the ON_NurbsCurve.m_knot array and is responsible
- // for making sure it's always big enough.
- bool rc = true;
- if ( desired_capacity > m_knot_capacity ) {
- if ( !m_knot ) {
- // no knot array - allocate one
- m_knot = (double*)onmalloc(desired_capacity*sizeof(*m_knot));
- if ( !m_knot ) {
- m_knot_capacity = 0;
- rc = false;
- }
- else {
- m_knot_capacity = desired_capacity;
- }
- }
- else if ( m_knot_capacity > 0 ) {
- // existing m_knot[] is too small and the fact that
- // m_knot_capacity > 0 indicates that ON_NurbsCurve is
- // managing the m_knot[] memory, so we need to grow
- // the m_knot[] array.
- m_knot = (double*)onrealloc(m_knot,desired_capacity*sizeof(*m_knot));
- if ( !m_knot ) {
- rc = false;
- m_knot_capacity = 0;
- }
- else {
- m_knot_capacity = desired_capacity;
- }
- }
+ const int current_knot_capacity = KnotCapacity();
+
+ if (nullptr != m_knot && 0 == current_knot_capacity)
+ {
+ // If knot_capacity == 0 and m_knot != nullptr, then the "expert" user
+ // has hand built the ON_NurbsCurve.m_knot array and is responsible
+ // for making sure it's always big enough.
+ return true;
}
+
+ if ( desired_capacity > current_knot_capacity )
+ {
+ double* knot
+ = ( 0 == current_knot_capacity )
+ ? (double*)onmalloc(desired_capacity*sizeof(knot[0]))
+ : (double*)onrealloc(m_knot,desired_capacity*sizeof(knot[0]));
+ this->ManageKnotForExperts((nullptr != knot) ? desired_capacity : 0, knot);
+ }
+
+ bool rc = (nullptr != m_knot && KnotCapacity() >= desired_capacity);
return rc;
}
+
bool ON_NurbsCurve::ConvertSpanToBezier( int span_index, ON_BezierCurve& bez ) const
{
bool rc = false;
@@ -2506,7 +2723,7 @@ static bool IncrementNurbDegree(ON_NurbsCurve& N)
i+=mult;
}
//zero out N's cvs
- memset(N.m_cv, 0, N.m_cv_capacity*sizeof(double));
+ memset(N.m_cv, 0, N.CVCapacity()*sizeof(double));
int cvdim = N.CVSize();
const double* cvM;
double* cvN;
@@ -2595,9 +2812,12 @@ bool ON_NurbsCurve::ChangeDimension( int desired_dimension )
//const int cv_size0 = CVSize();
const int cv_size1 = m_is_rat ? desired_dimension + 1 : desired_dimension;
const int cv_stride1 = (m_cv_stride < cv_size1) ? cv_size1 : m_cv_stride;
- if ( m_cv_stride < cv_stride1 && m_cv_capacity > 0 ) {
- m_cv_capacity = cv_stride1*m_cv_count;
- m_cv = (double*)onrealloc( m_cv, m_cv_capacity*sizeof(*m_cv) );
+ if ( m_cv_stride < cv_stride1 && CVCapacity() > 0 )
+ {
+ const int new_cv_capacity = cv_stride1*m_cv_count;
+ m_cv = (double*)onrealloc( m_cv, new_cv_capacity*sizeof(*m_cv) );
+ if (nullptr != m_cv)
+ m_cv_capacity = new_cv_capacity;
}
for ( i = CVCount()-1; i >= 0; i-- ) {
cv0 = CV(i);
@@ -3058,7 +3278,7 @@ int ON_NurbsCurve::GetNurbForm( ON_NurbsCurve& curve,
// 4 May 2007 Dale Lear
// I'm replacing the call to operator= with a call to
- // ON_NurbsCurveCopyHelper(). The operator= call
+ // Internal_DeepCopyFrom(). The operator= call
// was copying userdata and that does not happen for
// any other GetNurbForm overrides. Copying userdata
// in GetNurbForm is causing trouble in Make2D and
@@ -3068,8 +3288,10 @@ int ON_NurbsCurve::GetNurbForm( ON_NurbsCurve& curve,
// curve = *this; // copied user data
curve.DestroyRuntimeCache(true);
- if ( this != &curve )
- ON_NurbsCurve_copy_member_data(*this,curve); // does not copy user data
+ if (this != &curve)
+ {
+ curve.Internal_DeepCopyFrom(*this); // does not copy user data
+ }
if ( subdomain )
{
@@ -4140,3 +4362,236 @@ int ON_NurbsCurve::RemoveSingularSpans()
return singular_span_count;
}
+
+bool ON_NurbsCurve::SubDFriendlyTag() const
+{
+ if (0 != (m_knot_capacity_and_tags & ON_NurbsCurve::masks::subdfriendly_tag))
+ {
+ if (IsSubDFriendly(true))
+ return true;
+ // Something modified NURBS properties after the tag was set.
+ const_cast(this)->SetSubDFriendlyTag(false);
+ }
+ return false;
+}
+
+void ON_NurbsCurve::SetSubDFriendlyTag(
+ bool bSubDFriendlyTag
+)
+{
+ if (bSubDFriendlyTag && IsSubDFriendly(true))
+ m_knot_capacity_and_tags |= ON_NurbsCurve::masks::subdfriendly_tag;
+ else
+ m_knot_capacity_and_tags &= ~ON_NurbsCurve::masks::subdfriendly_tag;
+}
+
+static bool Internal_IsSubDFriendlyEnd(
+ int endex,
+ const double* cubic_cpan_knots,
+ const ON_3dPoint& cv0,
+ const ON_3dPoint& cv1,
+ const ON_3dPoint& cv2
+)
+{
+ ON_3dPoint Q;
+ if (0 == endex)
+ {
+ Q = (cubic_cpan_knots[0] == cubic_cpan_knots[2] && cubic_cpan_knots[3] < cubic_cpan_knots[5])
+ ? ((2.0*cv0 + cv2) / 3.0)
+ : (0.5*(cv0 + cv2))
+ ;
+ }
+ else
+ {
+ Q = (cubic_cpan_knots[0] < cubic_cpan_knots[2] && cubic_cpan_knots[3] == cubic_cpan_knots[5])
+ ? ((cv0 + 2.0*cv2) / 3.0)
+ : (0.5*(cv0 + cv2))
+ ;
+ }
+ const double d = cv0.DistanceTo(cv2);
+ const double tol = 1.0e-6*d;
+ const double e = cv1.DistanceTo(Q);
+ return (e <= tol);
+}
+
+bool ON_NurbsCurve::IsSubDFriendly(
+ bool bPermitCreases
+) const
+{
+ for (;;)
+ {
+ if (m_dim <= 0)
+ break;
+ if (false != m_is_rat)
+ break;
+ if (4 != m_order)
+ break;
+ if (m_cv_count < 4)
+ break;
+ if (nullptr == m_knot)
+ break;
+ if (m_cv_stride < m_dim)
+ break;
+ if (nullptr == m_cv)
+ break;
+
+ // knots must be piecewise uniform
+ const ON_SubD::SubDFriendlyKnotType knot_type = ON_SubD::NurbsKnotType(m_order, m_cv_count, m_knot);
+ if (ON_SubD::SubDFriendlyKnotType::Unset == knot_type || ON_SubD::SubDFriendlyKnotType::Unfriendly == knot_type)
+ return false;
+ if (ON_SubD::SubDFriendlyKnotType::ClampedPiecewiseUniform == knot_type && false == bPermitCreases)
+ return false;
+
+ ON_3dPoint CV[2] = { ON_3dPoint::NanPoint,ON_3dPoint::NanPoint };
+ if (false == GetCV(0, CV[0]))
+ break;
+ if (false == GetCV(1, CV[1]))
+ break;
+ bool bAllEqual = CV[0] == CV[1];
+ if (bAllEqual)
+ {
+ // All CVs must be equal - curve is a "point" used in lofting to a point.
+ for (int i = 2; bAllEqual && i < m_cv_count; ++i)
+ {
+ bAllEqual = GetCV(i, CV[1]) && CV[0] == CV[1];
+ }
+ if (false == bAllEqual)
+ break;
+ }
+
+ // curve must be periodic or natural
+ bool bNaturalEnds = (m_cv_count >= 6 && IsPeriodic());
+ if ( false == bNaturalEnds)
+ {
+ // test ends for natural end conditions
+ ON_3dPoint P[3];
+ bNaturalEnds = true;
+ for (int endex = 0; endex < 2 && bNaturalEnds; ++endex)
+ {
+ const int cv_dex = (0 == endex) ? 0 : ( CVCount() - 3);
+ for (int j = 0; j < 3 && bNaturalEnds; ++j)
+ bNaturalEnds = GetCV(cv_dex+j, P[j]);
+ if (false == bNaturalEnds)
+ break;
+ const double* span_knots = m_knot + ((0 == endex) ? 0 : (m_cv_count - m_order));
+ bNaturalEnds = Internal_IsSubDFriendlyEnd(endex, span_knots, P[0], P[1], P[2]);
+ }
+ }
+
+ if ( bNaturalEnds && ON_SubD::SubDFriendlyKnotType::ClampedPiecewiseUniform == knot_type )
+ {
+ // Interior triple knots must be natural on either side of the triple knot.
+ // Interior triple knots are used to place kinks inside the curve.
+ ON_3dPoint P[5];
+ const unsigned int knot_count = (unsigned int)KnotCount();
+ const double* knot = m_knot;
+ for (unsigned int knot_dex = 3; knot_dex < knot_count - 4U && bNaturalEnds; ++knot_dex)
+ {
+ if (knot[knot_dex] == knot[knot_dex + 1])
+ {
+ // This tests that the 2nd derivative is zero when evaluated from both below and above knot[knot_dex].
+ bNaturalEnds =
+ knot[knot_dex - 1] < knot[knot_dex]
+ // tested above to enter this scope // && knot[knot_dex] == knot[knot_dex+1]
+ && knot[knot_dex + 1] == knot[knot_dex + 2]
+ && knot[knot_dex + 2] < knot[knot_dex + 3]
+ && GetCV(knot_dex - 2, P[0])
+ && GetCV(knot_dex - 1, P[1])
+ && GetCV(knot_dex, P[2])
+ && GetCV(knot_dex + 1, P[3])
+ && GetCV(knot_dex + 2, P[4])
+ && Internal_IsSubDFriendlyEnd(1,knot+knot_dex-3, P[0], P[1], P[2])
+ && Internal_IsSubDFriendlyEnd(0,knot+knot_dex, P[2], P[3], P[4]);
+ ++knot_dex;
+ }
+ }
+ }
+
+ if (false == bNaturalEnds)
+ break;
+
+ return true;
+ }
+
+ return false;
+}
+
+ON_SubD::SubDFriendlyKnotType ON_SubD::NurbsKnotType(
+ int order,
+ int cv_count,
+ const double* knots
+)
+{
+ return ON_SubD::NurbsKnotType(order, cv_count, knots, nullptr);
+}
+
+ON_SubD::SubDFriendlyKnotType ON_SubD::NurbsKnotType(
+ int order,
+ int cv_count,
+ const double* knots,
+ ON_SimpleArray* triple_knots
+ )
+{
+ if (nullptr != triple_knots)
+ triple_knots->SetCount(0);
+ for (;;)
+ {
+ if (4 != order || cv_count < order || nullptr == knots)
+ break;
+
+ const double delta = knots[3] - knots[2];
+ if (false == (delta > 0.0))
+ break;
+
+ const double deltatol = ON_SQRT_EPSILON * delta;
+
+ const bool bClamped0 = knots[0] == knots[1] && knots[1] == knots[2];
+ const bool bClamped1 = knots[cv_count - 1] == knots[cv_count] && knots[cv_count] == knots[cv_count + 1];
+ if (bClamped0 && nullptr != triple_knots)
+ triple_knots->Append(knots[2]);
+
+ const bool bClamped = bClamped0 && bClamped1;
+ const bool bUnclamped =
+ (false == bClamped)
+ && fabs(knots[1] - knots[0] - delta) <= deltatol
+ && fabs(knots[2] - knots[1] - delta) <= deltatol
+ && fabs(knots[cv_count] - knots[cv_count - 1] - delta) <= deltatol
+ && fabs(knots[cv_count + 1] - knots[cv_count] - delta) <= deltatol
+ ;
+ if ( bClamped == bUnclamped)
+ break;
+
+ bool bPiecewiseUniform = false;
+ for (int knot_dex = 3; knot_dex < cv_count - 1; ++knot_dex)
+ {
+ const double d = knots[knot_dex + 1] - knots[knot_dex];
+ if (0.0 == d)
+ {
+ if (bClamped && knots[knot_dex + 2] == knots[knot_dex])
+ {
+ bPiecewiseUniform = true;
+ ++knot_dex;
+ if (bClamped1 && nullptr != triple_knots)
+ triple_knots->Append(knots[knot_dex]);
+ continue;
+ }
+ }
+ else
+ {
+ if (fabs(d - delta) <= deltatol)
+ continue;
+ }
+ return ON_SubD::SubDFriendlyKnotType::Unfriendly;
+ }
+
+ if (bClamped1 && nullptr != triple_knots)
+ triple_knots->Append(knots[cv_count - 1]);
+
+ return
+ bClamped
+ ? (bPiecewiseUniform ? ON_SubD::SubDFriendlyKnotType::ClampedPiecewiseUniform : ON_SubD::SubDFriendlyKnotType::ClampedUniform)
+ : ON_SubD::SubDFriendlyKnotType::UnclampedUniform;
+ }
+
+ return ON_SubD::SubDFriendlyKnotType::Unfriendly;
+}
diff --git a/opennurbs_nurbscurve.h b/opennurbs_nurbscurve.h
index 926447de..6952f343 100644
--- a/opennurbs_nurbscurve.h
+++ b/opennurbs_nurbscurve.h
@@ -941,9 +941,31 @@ public:
double delta = 1.0
);
- bool IsClamped( // determine if knot vector is clamped
- int = 2 // end to check: 0 = start, 1 = end, 2 = start and end
+ /*
+ Description:
+ Test the knot vector to see if it is clamped.
+ Parameters:
+ end:
+ 0: test start
+ 1: test end
+ 2: test start and end.
+ */
+ bool IsClamped(
+ int end = 2
) const;
+
+ /*
+ Description:
+ Test the start or end of a curve to see if it's natural (Zero 2nd derivative).
+ Parameters:
+ end:
+ 0: test start
+ 1: test end
+ 2: test start and end.
+ */
+ bool IsNatural(
+ int end = 2
+ ) const;
double SuperfluousKnot(
int // 0 = start, 1 = end
@@ -1002,10 +1024,58 @@ public:
bool ReserveCVCapacity(
int // number of doubles to reserve
);
+
+ /*
+ Returns:
+ If this class is managing m_cv, then CVCapacity() is the number of doubles
+ m_cv[] can accomodate. Otherwise, CVCapacity() is 0.
+ */
+ int CVCapacity() const;
+
bool ReserveKnotCapacity(
int // number of doubles to reserve
);
+ /*
+ Returns:
+ If this class is managing m_knot, then KnotCapacity() is the number of doubles
+ m_knot[] can accomodate. Otherwise, KnotCapacity() is 0.
+ */
+ int KnotCapacity() const;
+
+ /*
+ Description:
+ Unconditionally transfer knot managment to caller and zero m_knot and KnotCapacity().
+ Parameters:
+ knot_capacity - [out]
+ knot_capacity is set to input value of KnotCapacity() and then KnotCapacity() is set to 0.
+ knot - [out]
+ knot is set to input value of m_knot and then this->m_knot is set to nullptr.
+ Remarks:
+ If knot_capacity > 0, then knot points to memory allocated by onmalloc()/onrealloc().
+ */
+ void UnmanageKnotForExperts(
+ int& knot_capacity,
+ double*& knot
+ );
+
+ /*
+ Description:
+ Unconditionally transfer knot management to this NURBS curve.
+ Sets KnotCapacity() to knot_capacity and m_knot to knot.
+ If knot_capacity > 0, then knot must point to memory allocated by onmalloc()/onrealloc().
+ Parameters:
+ knot_capacity - [in]
+ KnotCapacity() is set to knot_capacity.
+ knot - [in]
+ m_knot is set to knot.
+ */
+ void ManageKnotForExperts(
+ int knot_capacity,
+ double* knot
+ );
+
+
//////////
// returns the length of the control polygon
double ControlPolygonLength() const;
@@ -1139,6 +1209,42 @@ public:
bool Reparameterize( double c );
+ public:
+ /*
+ Returns:
+ True if this curve was explicitly tagged as SubDFriendly and is currently SubDFriendly.
+ */
+ bool SubDFriendlyTag() const;
+
+ public:
+ /*
+ Returns:
+ True if this NURBS curve is cubic, non-rational, uniform, and is either periodic or has clamped end knots.
+ Parameters:
+ bPermitCreases - [in]
+ If true, then a curve with clamped end knots may have interior triple knots.
+ Remarks:
+ The value of SubDFriendlyTag() is ignored.
+ See Also:
+ SubDFriendlyTag().
+ */
+ bool IsSubDFriendly(
+ bool bPermitCreases
+ ) const;
+
+ /*
+ Description:
+ Set the curve's SubDFriendlyTag() property.
+ Parameters:
+ bSubDFriendlyTag - [in]
+ If bSubDFriendlyTag is true and IsSubDFriendly(true) is true,
+ then the SubDFriendlyTag() property is set to true.
+ Otherwise the SubDFriendlyTag property is set to false.
+ */
+ void SetSubDFriendlyTag(
+ bool bSubDFriendlyTag
+ );
+
/////////////////////////////////////////////////////////////////
// Implementation
@@ -1167,17 +1273,24 @@ public:
// knot vector memory
- int m_knot_capacity; // If m_knot_capacity > 0, then m_knot[]
- // is an array of at least m_knot_capacity
- // doubles whose memory is managed by the
- // ON_NurbsCurve class using onmalloc(),
- // onrealloc(), and onfree().
- // If m_knot_capacity is 0 and m_knot is
- // not nullptr, then m_knot[] is assumed to
- // be big enough for any requested operation
- // and m_knot[] is not deleted by the
- // destructor.
+private:
+ enum masks : unsigned int
+ {
+ knot_capacity = 0x0FFFFFFFU,
+ subdfriendly_tag = 0x80000000U,
+ all_tags = 0xF0000000U,
+ };
+ unsigned int m_knot_capacity_and_tags;
+ // unsigned int tags = (m_knot_capacity_and_tags & ON_NurbsCurve::masks::tags);
+ // unsigned intknot_capacity = (m_knot_capacity_and_tags & ON_NurbsCurve::masks::knot_capacity_mask);
+ // If knot_capacity > 0, then m_knot[] is an array of at least knot_capacity
+ // doubles whose memory is managed by the ON_NurbsCurve class using
+ // onmalloc(), onrealloc(), and onfree().
+ // If knot_capacity is 0 and m_knot is not nullptr, then m_knot[] is assumed to
+ // be big enough for any requested operation and m_knot[] is not deleted by the
+ // destructor.
+public:
double* m_knot; // Knot vector. ( The knot vector has length
// m_order+m_cv_count-2. )
@@ -1186,6 +1299,7 @@ public:
int m_cv_stride; // The pointer to start of "CV[i]" is
// m_cv + i*m_cv_stride.
+public:
int m_cv_capacity; // If m_cv_capacity > 0, then m_cv[] is an array
// of at least m_cv_capacity doubles whose
// memory is managed by the ON_NurbsCurve
@@ -1195,6 +1309,7 @@ public:
// for any requested operation and m_cv[] is not
// deleted by the destructor.
+public:
double* m_cv; // Control points.
// - The i-th control point begins at
// CV(i) = m_cv + (i*m_cv_stride).
@@ -1203,6 +1318,17 @@ public:
// - If m_is_rat is true, then the i-th control
// point is stored in HOMOGENEOUS form and is
// [ CV(i)[0], ..., CV(i)[m_dim] ].
+
+private:
+ #if defined(ON_HAS_RVALUEREF)
+ void Internal_ShallowCopyFrom(const ON_NurbsCurve& src);
+ #endif
+private:
+ void Internal_DeepCopyFrom(const ON_NurbsCurve& src);
+private:
+ void Internal_InitializeToZero();
+private:
+ void Internal_Destroy();
};
/* Adjust the second point to be within the domains, when the first point is
diff --git a/opennurbs_nurbssurface.cpp b/opennurbs_nurbssurface.cpp
index e1f4b395..74fbd870 100644
--- a/opennurbs_nurbssurface.cpp
+++ b/opennurbs_nurbssurface.cpp
@@ -1017,7 +1017,7 @@ static bool FromCurve( ON_NurbsCurve& crv,
{
onfree( srf.m_cv );
}
- srf.m_cv_capacity = crv.m_cv_capacity;
+ srf.m_cv_capacity = crv.CVCapacity();
srf.m_cv = crv.m_cv;
crv.m_cv_capacity = 0;
crv.m_cv = 0;
@@ -1027,10 +1027,12 @@ static bool FromCurve( ON_NurbsCurve& crv,
}
srf.m_order[dir] = crv.m_order;
srf.m_cv_count[dir] = crv.m_cv_count;
- srf.m_knot_capacity[dir] = crv.m_knot_capacity;
- srf.m_knot[dir] = crv.m_knot;
- crv.m_knot_capacity = 0;
- crv.m_knot = 0;
+ // transfer knot vector from crv to srf.m_knot[dir]
+ crv.UnmanageKnotForExperts(
+ srf.m_knot_capacity[dir],
+ srf.m_knot[dir]
+ );
+
srf.m_cv_stride[dir] = crv.m_cv_stride;
srf.m_cv_stride[1-dir] = srf_cv_size;
return true;
@@ -2209,6 +2211,165 @@ bool ON_NurbsSurface::IsClamped( // determine if knot vector is clamped
return rc;
}
+bool ON_NurbsSurface::IsNatural(int dir, int end) const
+{
+ if (dir < 0 || dir > 1 || end < 0 || end > 2)
+ return false;
+
+ const ON_Interval domain = Domain(dir);
+ size_t parameter_count = 0;
+ double parameter_list[2] = {ON_DBL_QNAN,ON_DBL_QNAN};
+ if (0 == end || 2 == end)
+ parameter_list[parameter_count++] = domain[0];
+ if (1 == end || 2 == end)
+ parameter_list[parameter_count++] = domain[1];
+ return ON_NurbsSurface::IsNatural(dir, parameter_count, parameter_list);
+}
+
+bool ON_NurbsSurface::IsNatural(
+ int dir,
+ size_t parameter_count,
+ const double* parameter_list
+) const
+{
+ if (dir < 0 || dir > 1 || parameter_count < 1 || nullptr == parameter_list)
+ return false;
+
+ const int degree_dir = Degree(dir);
+ if (degree_dir < 1)
+ return false;
+
+ const ON_Interval domain[2] = { Domain(0), Domain(1) };
+ ON_SimpleArray g(m_cv_count[1 - dir]);
+ const int g_count = m_cv_count[1 - dir];
+ g.SetCount(g_count);
+ if (false == ON_GetGrevilleAbcissae( // get Greville abcissae from knots
+ m_order[1 - dir],
+ m_cv_count[1 - dir],
+ m_knot[1 - dir],
+ false,
+ g.Array()
+ ))
+ return false;
+
+ int gdex0 = 0;
+ int gdex1 = g_count;
+ if (0 == dir)
+ {
+ // 0 = south, 1 = east, 2 = north, 3 = west
+ if (IsSingular(0))
+ ++gdex0; // skip south side singular check - 2nd der is tiny and noisy
+ if (IsSingular(2))
+ --gdex1; // skip north side singular check - 2nd der is tiny and noisy
+ }
+ else
+ {
+ // 0 = south, 1 = east, 2 = north, 3 = west
+ if (IsSingular(3))
+ ++gdex0; // skip west side singular check - 2nd der is tiny and noisy
+ if (IsSingular(1))
+ --gdex1; // skip east side singular check - 2nd der is tiny and noisy
+ }
+ while (gdex0 < gdex1 && false == domain[1 - dir].Includes(g[gdex0]))
+ ++gdex0;
+ while (gdex0 < gdex1 && false == domain[1 - dir].Includes(g[gdex1-1]))
+ --gdex1;
+
+ if (gdex0 >= gdex1)
+ return false;
+
+ const int knot_count_dir = KnotCount(dir);
+ int quadrant;
+ int cv0dex[2] = {};
+ int cv2dex[2] = {};
+ int hint[2] = { 0,0 };
+ double st[2] = { ON_DBL_QNAN, ON_DBL_QNAN };
+ ON_3dVector D1[2], D2[2], Duv;
+ ON_3dPoint CV0, CV2, P;
+
+ bool bIsNatural = false;
+ for (size_t tdex = 0; tdex < parameter_count; ++tdex)
+ {
+ const double t = parameter_list[tdex];
+ if (false == domain[dir].Includes(t))
+ return false;
+ for (int side = -1; side <= 1; side += 2)
+ {
+ const int span_index = ON_NurbsSpanIndex(m_order[dir], m_cv_count[dir], m_knot[dir], t, side, hint[dir]);
+ if (span_index < 0 || span_index + 2*degree_dir > knot_count_dir)
+ return false;
+ const ON_Interval span_domain(m_knot[dir][span_index + degree_dir-1], m_knot[dir][span_index + degree_dir]);
+ if (false == span_domain.Includes(t))
+ return false;
+ cv0dex[dir] = span_index;
+ if (t == span_domain[0])
+ {
+ quadrant = 1;
+ cv2dex[dir] = cv0dex[dir] + 2;
+ }
+ else if (t == span_domain[1])
+ {
+ quadrant = (0 == dir) ? 2 : 4;
+ cv0dex[dir] += degree_dir;
+ cv2dex[dir] = cv0dex[dir] - 2;
+ }
+ else
+ {
+ cv2dex[dir] = cv0dex[dir] + 3;
+ quadrant = 0;
+ }
+
+ if ( cv0dex[0] < 0 || cv0dex[0] >= m_cv_count[0] || cv0dex[1] < 0 || cv0dex[1] >= m_cv_count[1] )
+ {
+ // bug in this code or invalid surface
+ ON_ERROR("cv0dex out of bounds");
+ return false;
+ }
+ if ( cv2dex[0] < 0 || cv2dex[0] >= m_cv_count[0] || cv2dex[1] < 0 || cv2dex[1] >= m_cv_count[1] )
+ {
+ // bug in this code or invalid surface
+ ON_ERROR("cv1dex out of bounds");
+ return false;
+ }
+
+ st[dir] = t;
+ hint[dir] = span_index;
+ hint[1 - dir] = 0;
+ for (int gi = gdex0; gi < gdex1; gi++)
+ {
+ st[1 - dir] = g[gi];
+ if (false == domain[1 - dir].Includes(st[1 - dir]))
+ continue;
+ if (false == Ev2Der(st[0], st[1], P, D1[0], D1[1], D2[0], Duv, D2[1], quadrant, hint))
+ return false;
+ cv0dex[1 - dir] = gi;
+ cv2dex[1 - dir] = gi;
+ if (false == GetCV(cv0dex[0], cv0dex[1], CV0))
+ return false;
+ if (false == GetCV(cv2dex[0], cv2dex[1], CV2))
+ return false;
+ const double d2 = D2[dir].Length();
+ const double tol = CV0.DistanceTo(CV2)*1.0e-8;
+ if (false == (d2 <= tol))
+ return false;
+ bIsNatural = true;
+ }
+ if (false == bIsNatural)
+ return false;
+
+ if (-1 == side && t == span_domain[1] && t < domain[dir][1])
+ {
+ if (degree_dir >= 3 && m_knot[dir][span_index + degree_dir] < m_knot[dir][span_index + degree_dir + 2])
+ break; // other side evaluations are equal
+ continue;
+ }
+ break;
+ }
+ }
+
+ return bIsNatural;
+}
+
static void ConvertToCurve( const ON_NurbsSurface& srf, int dir, ON_NurbsCurve& crv )
{
// DO NOT MAKE THIS FUNCTION PUBLIC - IT IS DELICATE AND DEDICATED TO USE IN THIS FILE
@@ -2315,10 +2476,9 @@ static void ConvertFromCurve( ON_NurbsCurve& crv, int dir, ON_NurbsSurface& srf
srf.m_knot[dir] = 0;
srf.m_knot_capacity[dir] = 0;
}
- srf.m_knot[dir] = crv.m_knot;
- srf.m_knot_capacity[dir] = crv.m_knot_capacity;
- crv.m_knot = 0;
- crv.m_knot_capacity = 0;
+
+ // transfer crv.m_knot to srf.m_knot[dir]
+ crv.UnmanageKnotForExperts(srf.m_knot_capacity[dir], srf.m_knot[dir]);
}
}
@@ -2359,10 +2519,12 @@ bool ON_NurbsSurface::InsertKnot(
else
{
ON_NurbsCurve crv;
- crv.m_knot = m_knot[dir];
- crv.m_knot_capacity = m_knot_capacity[dir];
- m_knot[dir] = 0;
+
+ // transfer knot vector from srf.m_knot[dir] to crv
+ crv.ManageKnotForExperts(m_knot_capacity[dir], m_knot[dir]);
+ m_knot[dir] = nullptr;
m_knot_capacity[dir] = 0;
+
crv.ReserveKnotCapacity(CVCount(dir)+knot_multiplicity);
ConvertToCurve(*this,dir,crv);
rc = crv.InsertKnot(knot_value,knot_multiplicity);
@@ -2512,10 +2674,12 @@ bool ON_NurbsSurface::IncreaseDegree(
else
{
ON_NurbsCurve crv;
- crv.m_knot = m_knot[dir];
- crv.m_knot_capacity = m_knot_capacity[dir];
+
+ // transfer knot vector from srf.m_knot[dir] to crv
+ crv.ManageKnotForExperts(m_knot_capacity[dir], m_knot[dir]);
m_knot[dir] = 0;
m_knot_capacity[dir] = 0;
+
ConvertToCurve(*this,dir,crv);
rc = crv.IncreaseDegree(desired_degree);
ConvertFromCurve(crv,dir,*this);
@@ -2857,9 +3021,9 @@ int ON_NurbsSurface::CreateRuledSurface(
if ( m_knot[0] && m_knot_capacity[0] > 0 )
onfree(m_knot[0]);
- m_knot[0] = nurbs_curveA.m_knot;
- m_knot_capacity[0] = nurbs_curveA.m_knot_capacity;
- nurbs_curveA.m_knot_capacity = 0;
+
+ // transfer knot vector from nurbs_curveA to srf.m_knot[0]
+ nurbs_curveA.UnmanageKnotForExperts(m_knot_capacity[0], m_knot[0]);
// Fill in linear knots
ReserveKnotCapacity( 1, 2 );
@@ -3008,9 +3172,9 @@ int ON_NurbsSurface::CreateConeSurface(
if ( m_knot[0] && m_knot_capacity[0] > 0 )
onfree(m_knot[0]);
- m_knot[0] = nurbs_curve.m_knot;
- m_knot_capacity[0] = nurbs_curve.m_knot_capacity;
- nurbs_curve.m_knot_capacity = 0;
+
+ // Transfer knot vector from nurbs_curve to srf.m_knot[0].
+ nurbs_curve.UnmanageKnotForExperts(m_knot_capacity[0], m_knot[0]);
// Fill in linear knots
ReserveKnotCapacity( 1, 2 );
diff --git a/opennurbs_nurbssurface.h b/opennurbs_nurbssurface.h
index d8ec6968..a13f734b 100644
--- a/opennurbs_nurbssurface.h
+++ b/opennurbs_nurbssurface.h
@@ -886,11 +886,56 @@ public:
);
- bool IsClamped( // determine if knot vector is clamped
+ /*
+ Description:
+ Test the knot vector to see if it is clamped.
+ Parameters:
+ dir:
+ 0: first parameter
+ 1: second parameter
+ end:
+ 0: test start
+ 1: test end
+ 2: test start and end.
+ */ bool IsClamped(
+ int dir,
+ int end = 2
+ ) const;
+
+ /*
+ Description:
+ Test the side of a surface to see if it's natural (Zero 2nd derivative).
+ Parameters:
+ dir:
+ 0: first parameter
+ 1: second parameter
+ end:
+ 0: test start
+ 1: test end
+ 2: test start and end.
+ */
+ bool IsNatural( // determine if knot vector is clamped
int dir, // dir 0 = "s", 1 = "t"
int end = 2 // end to check: 0 = start, 1 = end, 2 = start and end
) const;
-
+
+ /*
+ Description:
+ Test a surface to see if it's natural (Zero 2nd derivative) on an iso curve.
+ Parameters:
+ dir:
+ 0: first parameter
+ 1: second parameter
+ t_count - [in]
+ t - [in]
+ t[] is a list of parameters to check.
+ */
+ bool IsNatural( // determine if knot vector is clamped
+ int dir, // dir 0 = "s", 1 = "t"
+ size_t t_count,
+ const double* t
+ ) const;
+
double SuperfluousKnot(
int dir, // dir 0 = "s", 1 = "t"
int end // 0 = start, 1 = end
diff --git a/opennurbs_nurbsvolume.cpp b/opennurbs_nurbsvolume.cpp
index f6171dc5..eade09b8 100644
--- a/opennurbs_nurbsvolume.cpp
+++ b/opennurbs_nurbsvolume.cpp
@@ -1361,8 +1361,8 @@ ON_NurbsSurface* ON_NurbsCage::IsoSurface(
nurbs_curve.m_cv_count = nurbs_curve.m_order;
nurbs_curve.ReserveCVCapacity(nurbs_curve.m_dim*nurbs_curve.m_cv_count);
nurbs_curve.m_cv_stride = nurbs_curve.m_dim;
- nurbs_curve.m_knot = m_knot[dir] + span_index;
- nurbs_curve.m_knot_capacity = 0;
+ // nurbs_curve.m_knot[] shares memory with m_knot[dir] + span_index. nurbs_curve destructor does not free nurbs_curve.m_knot[]
+ nurbs_curve.ManageKnotForExperts(0, m_knot[dir] + span_index);
int ii,jj,kk;
/*
diff --git a/opennurbs_object.cpp b/opennurbs_object.cpp
index 8d4056d8..2cfb7247 100644
--- a/opennurbs_object.cpp
+++ b/opennurbs_object.cpp
@@ -46,6 +46,8 @@ unsigned int ON_IsRhinoApplicationId(
return 5;
if (ON_rhino6_id == id)
return 6;
+ if (ON_rhino7_id == id)
+ return 7;
return 0;
}
@@ -59,6 +61,8 @@ unsigned int ON_IsOpennurbsApplicationId(
return 5;
if (ON_opennurbs6_id == id)
return 6;
+ if (ON_opennurbs7_id == id)
+ return 7;
return 0;
}
diff --git a/opennurbs_objref.cpp b/opennurbs_objref.cpp
index 5290d20f..ca348b4f 100644
--- a/opennurbs_objref.cpp
+++ b/opennurbs_objref.cpp
@@ -70,11 +70,19 @@ ON_COMPONENT_INDEX::TYPE ON_COMPONENT_INDEX::Type(int i)
case ON_COMPONENT_INDEX::extrusion_cap_surface: t = ON_COMPONENT_INDEX::extrusion_cap_surface; break;
case ON_COMPONENT_INDEX::extrusion_path: t = ON_COMPONENT_INDEX::extrusion_path; break;
+ case ON_COMPONENT_INDEX::subd_vertex: t = ON_COMPONENT_INDEX::subd_vertex; break;
+ case ON_COMPONENT_INDEX::subd_edge: t = ON_COMPONENT_INDEX::subd_edge; break;
+ case ON_COMPONENT_INDEX::subd_face: t = ON_COMPONENT_INDEX::subd_face; break;
+
+ case ON_COMPONENT_INDEX::hatch_loop: t = ON_COMPONENT_INDEX::hatch_loop; break;
+
case ON_COMPONENT_INDEX::dim_linear_point: t = ON_COMPONENT_INDEX::dim_linear_point; break;
case ON_COMPONENT_INDEX::dim_radial_point: t = ON_COMPONENT_INDEX::dim_radial_point; break;
case ON_COMPONENT_INDEX::dim_angular_point: t = ON_COMPONENT_INDEX::dim_angular_point; break;
case ON_COMPONENT_INDEX::dim_ordinate_point: t = ON_COMPONENT_INDEX::dim_ordinate_point; break;
case ON_COMPONENT_INDEX::dim_text_point: t = ON_COMPONENT_INDEX::dim_text_point; break;
+ case ON_COMPONENT_INDEX::dim_centermark_point: t = ON_COMPONENT_INDEX::dim_centermark_point; break;
+ case ON_COMPONENT_INDEX::dim_leader_point: t = ON_COMPONENT_INDEX::dim_leader_point; break;
}
return t;
}
@@ -267,6 +275,11 @@ bool ON_COMPONENT_INDEX::IsPointCloudComponentIndex() const
return ( ON_COMPONENT_INDEX::pointcloud_point == m_type && m_index >= 0 );
}
+bool ON_COMPONENT_INDEX::IsHatchLoopComponentIndex() const
+{
+ return (ON_COMPONENT_INDEX::hatch_loop == m_type && m_index >= 0);
+}
+
static void ToStringHelper( ON_COMPONENT_INDEX ci, char* buffer, size_t sizeof_buffer )
{
char* str = buffer;
@@ -323,11 +336,19 @@ static void ToStringHelper( ON_COMPONENT_INDEX ci, char* buffer, size_t sizeof_b
case ON_COMPONENT_INDEX::extrusion_cap_surface: s = "ON_COMPONENT_INDEX::extrusion_cap_surface"; break;
case ON_COMPONENT_INDEX::extrusion_path: s = "ON_COMPONENT_INDEX::extrusion_path"; break;
+ case ON_COMPONENT_INDEX::subd_vertex: s = "ON_COMPONENT_INDEX::subd_vertex"; break;
+ case ON_COMPONENT_INDEX::subd_edge: s = "ON_COMPONENT_INDEX::subd_edge"; break;
+ case ON_COMPONENT_INDEX::subd_face: s = "ON_COMPONENT_INDEX::subd_face"; break;
+
+ case ON_COMPONENT_INDEX::hatch_loop: s = "ON_COMPONENT_INDEX::hatch_loop"; break;
+
case ON_COMPONENT_INDEX::dim_linear_point: s = "ON_COMPONENT_INDEX::dim_linear_point"; break;
case ON_COMPONENT_INDEX::dim_radial_point: s = "ON_COMPONENT_INDEX::dim_radial_point"; break;
case ON_COMPONENT_INDEX::dim_angular_point: s = "ON_COMPONENT_INDEX::dim_angular_point"; break;
case ON_COMPONENT_INDEX::dim_ordinate_point: s = "ON_COMPONENT_INDEX::dim_ordinate_point"; break;
case ON_COMPONENT_INDEX::dim_text_point: s = "ON_COMPONENT_INDEX::dim_text_point"; break;
+ case ON_COMPONENT_INDEX::dim_centermark_point: s = "ON_COMPONENT_INDEX::dim_centermark_point"; break;
+ case ON_COMPONENT_INDEX::dim_leader_point: s = "ON_COMPONENT_INDEX::dim_leader_point"; break;
default: s = 0; break;
}
@@ -458,6 +479,16 @@ bool ON_COMPONENT_INDEX::IsSet() const
case ON_COMPONENT_INDEX::subd_edge:
case ON_COMPONENT_INDEX::subd_face:
+ case ON_COMPONENT_INDEX::hatch_loop:
+
+ case ON_COMPONENT_INDEX::dim_linear_point:
+ case ON_COMPONENT_INDEX::dim_radial_point:
+ case ON_COMPONENT_INDEX::dim_angular_point:
+ case ON_COMPONENT_INDEX::dim_ordinate_point:
+ case ON_COMPONENT_INDEX::dim_text_point:
+ case ON_COMPONENT_INDEX::dim_centermark_point:
+ case ON_COMPONENT_INDEX::dim_leader_point:
+
rc = (m_index != -1);
break;
@@ -469,14 +500,32 @@ bool ON_COMPONENT_INDEX::IsSet() const
}
-int ON_COMPONENT_INDEX::Compare( const ON_COMPONENT_INDEX* a, const ON_COMPONENT_INDEX* b )
+int ON_COMPONENT_INDEX::CompareType( const ON_COMPONENT_INDEX* lhs, const ON_COMPONENT_INDEX* rhs )
{
- int i = ((int)a->m_type) - ((int)b->m_type);
- if ( 0 == i )
- {
- i = a->m_index - b->m_index;
- }
- return i;
+ const int lhs_i = (int)lhs->m_type;
+ const int rhs_i = (int)rhs->m_type;
+ if (lhs_i < rhs_i)
+ return -1;
+ if (lhs_i > rhs_i)
+ return 1;
+ return 0;
+}
+
+
+int ON_COMPONENT_INDEX::Compare( const ON_COMPONENT_INDEX* lhs, const ON_COMPONENT_INDEX* rhs )
+{
+ const int lhs_i = (int)lhs->m_type;
+ const int rhs_i = (int)rhs->m_type;
+ if (lhs_i < rhs_i)
+ return -1;
+ if (lhs_i > rhs_i)
+ return 1;
+
+ if (lhs->m_index < rhs->m_index)
+ return -1;
+ if (lhs->m_index > rhs->m_index)
+ return 1;
+ return 0;
}
bool ON_COMPONENT_INDEX::operator==(const ON_COMPONENT_INDEX& other) const
@@ -775,7 +824,7 @@ bool ON_ObjRef_IRefID::Read( ON_BinaryArchive& archive )
bool ON_ObjRef::Write( ON_BinaryArchive& archive ) const
{
- bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 2 );
+ bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 3 );
if ( !rc )
return false;
@@ -824,6 +873,10 @@ bool ON_ObjRef::Write( ON_BinaryArchive& archive ) const
rc = archive.WriteInterval(m_evp.m_s[2]);
if (!rc) break;
+ // 1.3 IO fields
+ rc = archive.WriteInt((int)m_osnap_mode);
+ if (!rc) break;
+
break;
}
@@ -884,6 +937,13 @@ bool ON_ObjRef::Read( ON_BinaryArchive& archive )
{
rc = archive.ReadInterval(m_evp.m_s[2]);
if (!rc) break;
+ if (minor_version >= 3)
+ {
+ int osmode = 0;
+ rc = archive.ReadInt(&osmode);
+ if (!rc) break;
+ m_osnap_mode = ON::OSnapMode(osmode);
+ }
}
}
diff --git a/opennurbs_parse_settings.cpp b/opennurbs_parse_settings.cpp
index 0a79eb82..d4ad01d7 100644
--- a/opennurbs_parse_settings.cpp
+++ b/opennurbs_parse_settings.cpp
@@ -1181,15 +1181,15 @@ bool ON_ParseSettings::IsDigitSeparator(ON__UINT32 c) const
switch(c)
{
case 0x002C: // comma
- return ParseCommaAsDigitSeparator();
+ return ParseCommaAsDigitSeparator(); // default = true
break;
case 0x002E: // full stop (period)
- return ParseFullStopAsDigitSeparator();
+ return ParseFullStopAsDigitSeparator(); // default = false
break;
case 0x0020: // space
- return ParseSpaceAsDigitSeparator();
+ return ParseSpaceAsDigitSeparator(); // default = true
break;
case 0x00A0: // no-break space
@@ -1205,6 +1205,13 @@ bool ON_ParseSettings::IsDigitSeparator(ON__UINT32 c) const
case 0x202F: // narrow no-break
return ParseNoBreakThinSpaceAsDigitSeparator();
break;
+
+ case 0x066C: // UNICODE ARABIC THOUSANDS SEPARATOR
+ // Intended to be used with eastern arabic numerials,
+ // but its lexical function is always a thousands separator.
+ return false;
+ break;
+
}
return false;
@@ -1221,6 +1228,18 @@ bool ON_ParseSettings::IsDecimalPoint(ON__UINT32 c) const
case 0x002E: // full stop (period)
return ParseFullStopAsDecimalPoint();
break;
+
+ case 0x2396: // UNICODE DECIMAL SEPARATOR KEY SYMBOL
+ // Intended to be used as a symbol on keyboards,
+ // but its lexical function is always a decimal point.
+ return true;
+ break;
+
+ case 0x066B: // UNICODE ARABIC DECIMAL SEPARATOR
+ // Intended to be used with eastern arabic numerials,
+ // but its lexical function is always a decimal point.
+ return true;
+ break;
}
return false;
}
diff --git a/opennurbs_point.cpp b/opennurbs_point.cpp
index a7320070..4c3ebfbf 100644
--- a/opennurbs_point.cpp
+++ b/opennurbs_point.cpp
@@ -176,31 +176,97 @@ static bool Internal_IsUnsetDouble(
return false;
}
+static bool Internal_IsNanDouble(
+ size_t count,
+ const double* a
+)
+{
+ const double * e = a + count;
+ while (a < e)
+ {
+ double x = *a++;
+ if (x == x)
+ continue;
+ return true;
+ }
+ return false;
+}
+
bool ON_2dPoint::IsUnset() const
{
return Internal_IsUnsetDouble(2, &x);
}
+bool ON_2dPoint::IsNan() const
+{
+ return Internal_IsNanDouble(2, &x);
+}
+
+bool ON_2dPoint::IsUnsetOrNan() const
+{
+ return Internal_IsUnsetDouble(2, &x) || Internal_IsNanDouble(2, &x);
+}
+
bool ON_3dPoint::IsUnset() const
{
return Internal_IsUnsetDouble(3, &x);
}
+bool ON_3dPoint::IsNan() const
+{
+ return Internal_IsNanDouble(3, &x);
+}
+
+bool ON_3dPoint::IsUnsetOrNan() const
+{
+ return Internal_IsUnsetDouble(3, &x) || Internal_IsNanDouble(3, &x);
+}
+
bool ON_4dPoint::IsUnset() const
{
return Internal_IsUnsetDouble(4, &x);
}
+bool ON_4dPoint::IsNan() const
+{
+ return Internal_IsNanDouble(4, &x);
+}
+
+bool ON_4dPoint::IsUnsetOrNan() const
+{
+ return Internal_IsUnsetDouble(4, &x) || Internal_IsNanDouble(4, &x);
+}
+
bool ON_2dVector::IsUnset() const
{
return Internal_IsUnsetDouble(2, &x);
}
+bool ON_2dVector::IsNan() const
+{
+ return Internal_IsNanDouble(2, &x);
+}
+
+bool ON_2dVector::IsUnsetOrNan() const
+{
+ return Internal_IsUnsetDouble(2, &x) || Internal_IsNanDouble(2, &x);
+}
+
bool ON_3dVector::IsUnset() const
{
return Internal_IsUnsetDouble(3, &x);
}
+bool ON_3dVector::IsNan() const
+{
+ return Internal_IsNanDouble(3, &x);
+}
+
+bool ON_3dVector::IsUnsetOrNan() const
+{
+ return Internal_IsUnsetDouble(3, &x) || Internal_IsNanDouble(3, &x);
+}
+
int ON_2dVector::Compare(
const ON_2dVector& lhs,
const ON_2dVector& rhs
@@ -517,26 +583,26 @@ ON_Interval::Length() const
bool
ON_Interval::IsIncreasing() const
{
- return ( m_t[0] < m_t[1] && ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) ? true : false;
+ return ( ON_UNSET_VALUE < m_t[0] && m_t[0] < m_t[1] && m_t[1] < ON_UNSET_POSITIVE_VALUE ) ? true : false;
}
bool
ON_Interval::IsDecreasing() const
{
- return ( m_t[0] > m_t[1] && ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) ? true : false;
+ return (ON_UNSET_POSITIVE_VALUE > m_t[0] && m_t[0] > m_t[1] && m_t[1] > ON_UNSET_VALUE) ? true : false;
}
bool
ON_Interval::IsInterval() const
{
- return ( m_t[0] != m_t[1] && ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) ) ? true : false;
+ return ( IsIncreasing() || IsDecreasing() ) ? true : false;
}
bool
ON_Interval::IsSingleton() const
{
- return ( m_t[0] == m_t[1] && ON_IS_VALID(m_t[1]) ) ? true : false;
+ return (ON_UNSET_VALUE < m_t[0] && m_t[0] == m_t[1] && m_t[1] < ON_UNSET_POSITIVE_VALUE) ? true : false;
}
bool
@@ -554,8 +620,7 @@ ON_Interval::IsEmptySet() const
bool
ON_Interval::IsValid() const
{
- // 05/29/2007 TimH. Changed 0 to 1.
- return ( ON_IS_VALID(m_t[0]) && ON_IS_VALID(m_t[1]) );
+ return (ON_UNSET_VALUE < m_t[0] && m_t[0] < ON_UNSET_POSITIVE_VALUE && ON_UNSET_VALUE < m_t[1] && m_t[1] < ON_UNSET_POSITIVE_VALUE);
}
bool
@@ -568,8 +633,6 @@ ON_Interval::MakeIncreasing() // returns true if resulting interval IsIncreasin
return IsIncreasing();
}
-
-
int ON_Interval::Compare(const ON_Interval& lhs, const ON_Interval& rhs)
{
return Internal_DoubleArrayCompare(2, lhs.m_t, rhs.m_t);
@@ -2159,8 +2222,10 @@ bool ON_2fPoint::IsZero() const
bool ON_2fPoint::IsNotZero() const
{
- // the && (x == x && y == y) insures no coordinate is a Nan.
- return (x != 0.0f || y != 0.0f) && (x == x && y == y);
+ return
+ (x != 0.0f || y != 0.0f)
+ && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT)
+ && (x != ON_UNSET_POSITIVE_FLOAT && y != ON_UNSET_POSITIVE_FLOAT);
}
ON_2fPoint operator*(int d, const ON_2fPoint& p)
@@ -2596,8 +2661,11 @@ bool ON_3fPoint::IsZero() const
bool ON_3fPoint::IsNotZero() const
{
- // the && (x == x && y == y) insures no coordinate is a Nan.
- return (x != 0.0f || y != 0.0f || z != 0.0f) && (x == x && y == y && z == z);
+ // the && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT && z != ON_UNSET_FLOAT) insures no coordinate is a Nan.
+ return
+ (x != 0.0f || y != 0.0f || z != 0.0f)
+ && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT && z != ON_UNSET_FLOAT)
+ && (x != ON_UNSET_POSITIVE_FLOAT && y != ON_UNSET_POSITIVE_FLOAT && z != ON_UNSET_POSITIVE_FLOAT);
}
ON_3fPoint operator*(int d, const ON_3fPoint& p)
@@ -3398,8 +3466,11 @@ bool ON_2fVector::IsZero() const
bool ON_2fVector::IsNotZero() const
{
- // the && (x == x && y == y) insures no coordinate is a Nan.
- return (x != 0.0f || y != 0.0f) && (x == x && y == y);
+ // the && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT) insures no coordinate is a Nan.
+ return
+ (x != 0.0f || y != 0.0f)
+ && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT)
+ && (x != ON_UNSET_POSITIVE_FLOAT && y != ON_UNSET_POSITIVE_FLOAT);
}
// set this vector to be perpendicular to another vector
@@ -3905,8 +3976,11 @@ bool ON_3fVector::IsZero() const
bool ON_3fVector::IsNotZero() const
{
- // the && (x == x && y == y && z == z) insures no coordinate is a Nan.
- return (x != 0.0f || y != 0.0f || z != 0.0f) && (x == x && y == y && z == z);
+ // the && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT && z != ON_UNSET_FLOAT) insures no coordinate is a Nan.
+ return
+ (x != 0.0f || y != 0.0f || z != 0.0f)
+ && (x != ON_UNSET_FLOAT && y != ON_UNSET_FLOAT && z != ON_UNSET_FLOAT)
+ && (x != ON_UNSET_POSITIVE_FLOAT && y != ON_UNSET_POSITIVE_FLOAT && z != ON_UNSET_POSITIVE_FLOAT);
}
ON_3fVector operator*(int d, const ON_3fVector& v)
@@ -4371,8 +4445,12 @@ bool ON_2dPoint::IsZero() const
bool ON_2dPoint::IsNotZero() const
{
- // the && (x == x && y == y) insures no coordinate is a Nan.
- return (x != 0.0 || y != 0.0) && (x == x && y == y);
+ // the && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE) insures no coordinate is a Nan.
+ return
+ (x != 0.0 || y != 0.0)
+ && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE)
+ && (x != ON_UNSET_POSITIVE_VALUE && y != ON_UNSET_POSITIVE_VALUE)
+ ;
}
ON_2dPoint operator*(int i, const ON_2dPoint& p)
@@ -4824,8 +4902,11 @@ bool ON_3dPoint::IsZero() const
bool ON_3dPoint::IsNotZero() const
{
- // the && (x == x && y == y && z == z) insures no coordinate is a Nan.
- return (x != 0.0 || y != 0.0 || z != 0.0) && (x == x && y == y && z == z);
+ // the && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE) insures no coordinate is a Nan.
+ return
+ (x != 0.0 || y != 0.0 || z != 0.0)
+ && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE)
+ && (x != ON_UNSET_POSITIVE_VALUE && y != ON_UNSET_POSITIVE_VALUE && z != ON_UNSET_POSITIVE_VALUE);
}
ON_3dPoint operator*(int i, const ON_3dPoint& p)
@@ -5718,8 +5799,13 @@ bool ON_2dVector::IsZero() const
bool ON_2dVector::IsNotZero() const
{
- // the && (x == x && y == y) insures no coordinate is a Nan.
- return (x != 0.0 || y != 0.0) && (x == x && y == y);
+{
+ // the && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE) insures no coordinate is a Nan.
+ return
+ (x != 0.0 || y != 0.0)
+ && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE)
+ && (x != ON_UNSET_POSITIVE_VALUE && y != ON_UNSET_POSITIVE_VALUE);
+}
}
bool ON_2dVector::IsUnitVector() const
@@ -6371,8 +6457,11 @@ bool ON_3dVector::IsZero() const
bool ON_3dVector::IsNotZero() const
{
- // the && (x == x && y == y && z == z) insures no coordinate is a Nan.
- return (x != 0.0 || y != 0.0 || z != 0.0) && (x == x && y == y && z == z);
+ // the && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE) insures no coordinate is a Nan.
+ return
+ (x != 0.0 || y != 0.0 || z != 0.0)
+ && (x != ON_UNSET_VALUE && y != ON_UNSET_VALUE && z != ON_UNSET_VALUE)
+ && (x != ON_UNSET_POSITIVE_VALUE && y != ON_UNSET_POSITIVE_VALUE && z != ON_UNSET_POSITIVE_VALUE);
}
bool ON_3dVector::IsUnitVector() const
@@ -7509,7 +7598,30 @@ bool ON_PlaneEquation::operator!=( const ON_PlaneEquation& eq ) const
return (x!=eq.x || y!=eq.y || z!=eq.z || d!=eq.d)?true:false;
}
-
+// Find the maximum absolute value of a array of (possibly homogeneous) points
+double ON_MaximumCoordinate(const double* data, int dim, bool is_rat, int count)
+{
+ double norm = 0;
+ if (is_rat)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ double w = fabs(data[i*(dim+1) + dim ]);
+ double norm_i = 0;
+ for (int j = 0; j < dim; j++)
+ norm_i = ON_Max(norm_i, fabs(data[i*(dim+1) + j]));
+ if (norm_i > norm * w)
+ norm = norm_i / w;
+ }
+ }
+ else
+ {
+ int n = dim * count;
+ for (int i = 0; i < n; i++)
+ norm = ON_Max(norm, fabs(data[i]));
+ }
+ return norm;
+}
int ON_Get3dConvexHull(
const ON_SimpleArray& points,
diff --git a/opennurbs_point.h b/opennurbs_point.h
index 733ca47f..2c8b7abb 100644
--- a/opennurbs_point.h
+++ b/opennurbs_point.h
@@ -417,6 +417,18 @@ public:
*/
bool IsUnset() const;
+ /*
+ Returns:
+ True if any coordinate is a nan.
+ */
+ bool IsNan() const;
+
+ /*
+ Returns:
+ True if any coordinate is ON_UNSET_VALUE, ON_UNSET_POSITIVE_VALUE, or a nan
+ */
+ bool IsUnsetOrNan() const;
+
// set 2d point value
void Set(double x,double y);
@@ -440,7 +452,7 @@ public:
/*
Returns:
- true if at lease one coordinate is not zero and no coordinates are nans.
+ true if at lease one coordinate is not zero and no coordinates are unset or nans.
*/
bool IsNotZero() const;
@@ -603,6 +615,18 @@ public:
*/
bool IsUnset() const;
+ /*
+ Returns:
+ True if any coordinate is a nan.
+*/
+ bool IsNan() const;
+
+ /*
+ Returns:
+ True if any coordinate is ON_UNSET_VALUE, ON_UNSET_POSITIVE_VALUE, or a nan
+ */
+ bool IsUnsetOrNan() const;
+
// set 3d point value
void Set(double x,double y,double z);
@@ -631,7 +655,7 @@ public:
/*
Returns:
- true if at lease one coordinate is not zero and no coordinates are nans.
+ true if at lease one coordinate is not zero and no coordinates are unset or nans.
*/
bool IsNotZero() const;
@@ -838,6 +862,18 @@ public:
*/
bool IsUnset() const;
+ /*
+ Returns:
+ True if any coordinate is a nan.
+*/
+ bool IsNan() const;
+
+ /*
+ Returns:
+ True if any coordinate is ON_UNSET_VALUE, ON_UNSET_POSITIVE_VALUE, or a nan
+ */
+ bool IsUnsetOrNan() const;
+
// set 4d point value
void Set(double x,double y,double z,double w);
@@ -999,6 +1035,18 @@ public:
*/
bool IsUnset() const;
+ /*
+ Returns:
+ True if any coordinate is a nan.
+*/
+ bool IsNan() const;
+
+ /*
+ Returns:
+ True if any coordinate is ON_UNSET_VALUE, ON_UNSET_POSITIVE_VALUE, or a nan
+ */
+ bool IsUnsetOrNan() const;
+
// set 2d vector value
void Set(double x,double y);
@@ -1081,7 +1129,7 @@ public:
/*
Returns:
- true if at lease one coordinate is not zero and no coordinates are nans.
+ true if at lease one coordinate is not zero and no coordinates are unset or nans.
*/
bool IsNotZero() const;
@@ -1318,6 +1366,18 @@ public:
*/
bool IsUnset() const;
+ /*
+ Returns:
+ True if any coordinate is a nan.
+ */
+ bool IsNan() const;
+
+ /*
+ Returns:
+ True if any coordinate is ON_UNSET_VALUE, ON_UNSET_POSITIVE_VALUE, or a nan
+ */
+ bool IsUnsetOrNan() const;
+
// set 3d vector value
void Set(double x,double y,double z);
@@ -1406,7 +1466,7 @@ public:
/*
Returns:
- true if at lease one coordinate is not zero and no coordinates are nans.
+ true if at lease one coordinate is not zero and no coordinates are unset or nans.
*/
bool IsNotZero() const;
@@ -1948,6 +2008,10 @@ ON_IsRightHandFrame( // true if X, Y, Z are orthonormal and right handed
const ON_3dVector& // Z
);
+// Find the largest absolute value of coordinates from an array of points (possibly homogeneous).
+ON_DECL
+double ON_MaximumCoordinate(const double* data, int dim, bool is_rat, int count);
+
///////////////////////////////////////////////////////////////
//
// common points and vectors
diff --git a/opennurbs_pointcloud.cpp b/opennurbs_pointcloud.cpp
index 6f7773ff..bb418f05 100644
--- a/opennurbs_pointcloud.cpp
+++ b/opennurbs_pointcloud.cpp
@@ -78,6 +78,16 @@ ON_PointCloud& ON_PointCloud::operator=( const ON_PointCloud& src )
return *this;
}
+ON_PointCloud::ON_PointCloud(const ON_3dPoint* P0, int count) : m_P(count)
+{
+ m_P.Create(3, false, count, 3, P0[0]);
+}
+
+ON_PointCloud::ON_PointCloud(const double* P0, int dim, bool is_rat, int count) : m_P(count)
+{
+ m_P.Create(dim, is_rat, count, dim + is_rat, P0);
+}
+
ON_PointCloud::~ON_PointCloud()
{
Destroy();
diff --git a/opennurbs_pointcloud.h b/opennurbs_pointcloud.h
index 6b326021..50251c43 100644
--- a/opennurbs_pointcloud.h
+++ b/opennurbs_pointcloud.h
@@ -34,6 +34,11 @@ public:
);
ON_PointCloud( const ON_PointCloud& );
~ON_PointCloud();
+
+ ON_PointCloud(const ON_3dPoint* P0, int count);
+
+ // 0PointAtStart();
// Note: The point compare test should be the same
// as the one used in ON_Curve::IsClosed().
+ //
+ // June 2019 - sometime in the past decade ON_PolyCurve::HasGap()
+ // changed and the test here is different from ON_Curve::IsClosed().
+ // The initial "Note" no longer applies becaue it's no longer
+ // clear why the current ON_PolyCurve::HasGap() elimiated the "b c"
+ // test that remained in ON_Curve::IsClosed().
+
if ( false == ON_PointsAreCoincident( 3, false, &P0.x, &P1.x ) )
{
// To fix RR 13325 I allow a little more leeway for arcs.
diff --git a/opennurbs_polyline.cpp b/opennurbs_polyline.cpp
index 87cc3adc..87e9eff4 100644
--- a/opennurbs_polyline.cpp
+++ b/opennurbs_polyline.cpp
@@ -171,6 +171,21 @@ double ON_Polyline::Length() const
return d;
}
+ON_Line ON_Polyline::Segment(int segment_index) const
+{
+ ON_Line line;
+ if (segment_index >= 0 && segment_index < m_count - 1)
+ {
+ line.from = m_a[segment_index];
+ line.to = m_a[segment_index + 1];
+ }
+ else
+ {
+ line = ON_Line::ZeroLine;
+ }
+ return line;
+}
+
ON_3dVector ON_Polyline::SegmentDirection( int segment_index ) const
{
ON_3dVector v;
@@ -376,3 +391,100 @@ bool ON_Polyline::CreateStarPolygon(
return rc;
}
+bool ON_IsConvexPolyline(
+ size_t point_dim,
+ size_t point_count,
+ const double* points,
+ size_t point_stride,
+ bool bStrictlyConvex
+)
+{
+ if (point_dim < 2 || point_dim > 3 || point_count < 3 || nullptr == points || point_stride < point_dim)
+ return false;
+
+ const double* p;
+ ON_3dPoint P[2];
+
+ p = points + (point_stride*(point_count - 1));
+ P[0] = ON_3dPoint(p[0], p[1], (3 == point_dim) ? p[2] : 0.0);
+
+ p = points;
+ P[1] = ON_3dPoint(points[0], p[1], (3 == point_dim) ? p[2] : 0.0);
+
+ if (P[0] == P[1])
+ {
+ --point_count;
+ if (point_count < 3)
+ return false;
+ p = points + (point_stride*(point_count - 1));
+ P[0] = ON_3dPoint(p[0], p[1], (3 == point_dim) ? p[2] : 0.0);
+ }
+
+ ON_3dVector D[2] = { ON_3dVector::NanVector, P[1]-P[0]};
+ if (false == D[1].IsNotZero())
+ return false;
+ ON_SimpleArray C(point_count);
+ ON_3dVector maxN = ON_3dVector::ZeroVector;
+ double maxNlen = 0.0;
+ for (size_t i = 0; i < point_count; ++i)
+ {
+ p = points + (point_stride*((i+1)%point_count));
+ P[0] = P[1];
+ P[1] = ON_3dPoint(p[0], p[1], (3 == point_dim) ? p[2] : 0.0);
+ D[0] = D[1];
+ D[1] = P[1] - P[0];
+ if (false == D[1].IsNotZero())
+ return false;
+ const ON_3dVector N = ON_CrossProduct(D[0], D[1]);
+ const double Nlen = N.Length();
+ if (Nlen > maxNlen)
+ {
+ maxNlen = Nlen;
+ maxN = N;
+ }
+ else if (false == (Nlen > 0.0))
+ {
+ if ( bStrictlyConvex || false == (D[0]*D[1] > 0.0) )
+ return false;
+ }
+ C.Append(N.UnitVector());
+ }
+
+ maxN = maxN.UnitVector();
+ for (size_t i = 0; i < point_count; ++i)
+ {
+#if defined(ON_RUNTIME_ANDROID) || defined(ON_RUNTIME_LINUX)
+ double d = maxN * C[(unsigned int)i];
+#else
+ double d = maxN * C[i];
+#endif
+ if ( false == ((bStrictlyConvex) ? (d > 0.0) : (d >= 0.0)) )
+ return false;
+ }
+
+ return true;
+}
+
+bool ON_IsConvexPolyline(
+ const ON_SimpleArray& points,
+ bool bStrictlyConvex
+)
+{
+ return ON_IsConvexPolyline(
+ 3,
+ points.UnsignedCount(),
+ (const double*)(points.Array()),
+ 3,
+ bStrictlyConvex
+ );
+}
+
+
+bool ON_Polyline::IsConvexLoop(bool bStrictlyConvex) const
+{
+ if (false == IsClosed())
+ return false;
+ const ON_SimpleArray& points = *this;
+ return ON_IsConvexPolyline(points, bStrictlyConvex);
+}
+
diff --git a/opennurbs_polyline.h b/opennurbs_polyline.h
index 9ebf7706..3a403636 100644
--- a/opennurbs_polyline.h
+++ b/opennurbs_polyline.h
@@ -122,11 +122,34 @@ public:
double tolerance = 0.0
) const;
+ /*
+ Description:
+ Determine if a polyline is convex.
+ Parameters:
+ bStrictlyConvex - [in]
+ If false, colinear segments are considered convex.
+ Returns
+ True if the polyline is a closed, convex loop.
+ */
+ bool IsConvexLoop(
+ bool bStrictlyConvex
+ ) const;
+
// Returns:
// Length of the polyline.
double Length() const;
+
+ // Parameters:
+ // segment_index - [in] zero based segment index
+ // Returns:
+ // line = point[segment_index] -> point[segment_index+1]
+ ON_Line Segment(
+ int segment_index
+ ) const;
+
+
// Parameters:
// segment_index - [in] zero based segment index
// Returns:
@@ -220,7 +243,8 @@ Parameters:
InPlines - [in] Array of polylines to be joined (not modified)
OutPlines - [out] Resulting joined polylines and copies of polylines that were not joined to anything
are appended.
- join_tol - [in] Distance tolerance used to decide if endpoints are close enough
+ join_tol - [in] Distance tolerance used to decide if endpoints are close enough. Curves or segments with length
+ less than join_tol are NOT collapsed and can cause problems when endpoints do not match exactly.
kink_tol - [in] Angle in radians. If > 0.0, then curves within join_tol will only be joined if the angle between them
is less than kink_tol. If <= 0, then the angle will be ignored and only join_tol will be used.
bUseTanAngle - [in] If true, choose the best match using angle between tangents.
diff --git a/opennurbs_public.vcxproj b/opennurbs_public.vcxproj
index 7d59f3ca..98fa894b 100644
--- a/opennurbs_public.vcxproj
+++ b/opennurbs_public.vcxproj
@@ -160,6 +160,7 @@
+
@@ -318,6 +319,7 @@
+
@@ -435,6 +437,7 @@
+
diff --git a/opennurbs_public.xcodeproj/project.pbxproj b/opennurbs_public.xcodeproj/project.pbxproj
index a9ab6bb8..3e3bf542 100644
--- a/opennurbs_public.xcodeproj/project.pbxproj
+++ b/opennurbs_public.xcodeproj/project.pbxproj
@@ -337,6 +337,7 @@
1DC319EC1ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319921ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp */; };
1DC319ED1ED6534E00DE6D26 /* opennurbs_zlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DC319931ED6534E00DE6D26 /* opennurbs_zlib.cpp */; };
1DC319EE1ED6534E00DE6D26 /* opennurbs_zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DC319941ED6534E00DE6D26 /* opennurbs_zlib.h */; };
+ BE95E7A4235BCFA900A57145 /* opennurbs_subd_texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE95E7A3235BCFA800A57145 /* opennurbs_subd_texture.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -672,6 +673,7 @@
1DC319921ED6534E00DE6D26 /* opennurbs_zlib_memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_zlib_memory.cpp; sourceTree = ""; };
1DC319931ED6534E00DE6D26 /* opennurbs_zlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_zlib.cpp; sourceTree = ""; };
1DC319941ED6534E00DE6D26 /* opennurbs_zlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_zlib.h; sourceTree = ""; };
+ BE95E7A3235BCFA800A57145 /* opennurbs_subd_texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_subd_texture.cpp; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -1016,6 +1018,7 @@
1DC319521ED6534E00DE6D26 /* opennurbs_subd_ref.cpp */,
1DC319531ED6534E00DE6D26 /* opennurbs_subd_ring.cpp */,
1DC319541ED6534E00DE6D26 /* opennurbs_subd_sector.cpp */,
+ BE95E7A3235BCFA800A57145 /* opennurbs_subd_texture.cpp */,
1DC319551ED6534E00DE6D26 /* opennurbs_subd.cpp */,
1DC319571ED6534E00DE6D26 /* opennurbs_sum.cpp */,
1DC319581ED6534E00DE6D26 /* opennurbs_sumsurface.cpp */,
@@ -1427,6 +1430,7 @@
1DC319BF1ED6534E00DE6D26 /* opennurbs_text_style.cpp in Sources */,
1DC318071ED652B800DE6D26 /* opennurbs_cylinder.cpp in Sources */,
1DC319001ED652F800DE6D26 /* opennurbs_planesurface.cpp in Sources */,
+ BE95E7A4235BCFA900A57145 /* opennurbs_subd_texture.cpp in Sources */,
1DC318F41ED652F800DE6D26 /* opennurbs_optimize.cpp in Sources */,
1DC317D31ED652B800DE6D26 /* opennurbs_arccurve.cpp in Sources */,
1DC317ED1ED652B800DE6D26 /* opennurbs_brep_isvalid.cpp in Sources */,
diff --git a/opennurbs_public_staticlib.vcxproj b/opennurbs_public_staticlib.vcxproj
index 7b095d82..042b70ab 100644
--- a/opennurbs_public_staticlib.vcxproj
+++ b/opennurbs_public_staticlib.vcxproj
@@ -161,6 +161,7 @@
+
@@ -318,6 +319,7 @@
+
@@ -434,6 +436,7 @@
+
diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h
index cdc0503c..55d195f7 100644
--- a/opennurbs_public_version.h
+++ b/opennurbs_public_version.h
@@ -5,8 +5,8 @@
// at your disposal.
// To update version numbers, edit ..\build\build_dates.msbuild
-#define RMA_VERSION_MAJOR 6
-#define RMA_VERSION_MINOR 15
+#define RMA_VERSION_MAJOR 7
+#define RMA_VERSION_MINOR 0
////////////////////////////////////////////////////////////////
//
@@ -14,10 +14,10 @@
// first step in each build.
//
#define RMA_VERSION_YEAR 2019
-#define RMA_VERSION_MONTH 6
-#define RMA_VERSION_DATE 7
-#define RMA_VERSION_HOUR 19
-#define RMA_VERSION_MINUTE 45
+#define RMA_VERSION_MONTH 11
+#define RMA_VERSION_DATE 5
+#define RMA_VERSION_HOUR 17
+#define RMA_VERSION_MINUTE 46
////////////////////////////////////////////////////////////////
//
@@ -35,19 +35,20 @@
// 3 = build system release build
#define RMA_VERSION_BRANCH 0
-#define VERSION_WITH_COMMAS 6,15,19158,19450
-#define VERSION_WITH_PERIODS 6.15.19158.19450
+#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 SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library."
-#define RMA_VERSION_NUMBER_MAJOR_STRING "6"
-#define RMA_VERSION_NUMBER_MAJOR_WSTRING L"6"
+#define RMA_VERSION_NUMBER_MAJOR_STRING "7"
+#define RMA_VERSION_NUMBER_MAJOR_WSTRING L"7"
+#define RMA_PREVIOUS_VERSION_NUMBER_MAJOR_WSTRING L"6"
-#define RMA_VERSION_NUMBER_SR_STRING "SR15"
-#define RMA_VERSION_NUMBER_SR_WSTRING L"SR15"
+#define RMA_VERSION_NUMBER_SR_STRING "SR0"
+#define RMA_VERSION_NUMBER_SR_WSTRING L"SR0"
-#define RMA_VERSION_WITH_PERIODS_STRING "6.15.19158.19450"
-#define RMA_VERSION_WITH_PERIODS_WSTRING L"6.15.19158.19450"
+#define RMA_VERSION_WITH_PERIODS_STRING "7.0.19309.17460"
+#define RMA_VERSION_WITH_PERIODS_WSTRING L"7.0.19309.17460"
diff --git a/opennurbs_revsurface.cpp b/opennurbs_revsurface.cpp
index cdd9694a..6593dbde 100644
--- a/opennurbs_revsurface.cpp
+++ b/opennurbs_revsurface.cpp
@@ -779,6 +779,50 @@ bool ON_RevSurface::GetNurbFormParameterFromSurfaceParameter(
return rc;
}
+ON_Arc ON_RevSurface::IsoArc(
+ double curve_parameter
+) const
+{
+ for (;;)
+ {
+ if (nullptr == m_curve)
+ break;
+ // 8 December 2003 Chuck - fix iso extraction bug
+ // when m_angle[0] != 0.
+ ON_Circle circle;
+ ON_3dPoint P = m_curve->PointAt(curve_parameter);
+ if (false == P.IsValid())
+ break;
+ circle.plane.origin = m_axis.ClosestPointTo(P);
+ circle.plane.zaxis = m_axis.Tangent();
+ circle.plane.xaxis = P - circle.plane.origin;
+ circle.radius = circle.plane.xaxis.Length();
+ if (!circle.plane.xaxis.Unitize())
+ {
+ // 8 December 2003 Dale Lear - get valid zero radius
+ // arc/circle when revolute hits the axis.
+ // First: try middle of revolute for x-axis
+ P = m_curve->PointAt(m_curve->Domain().ParameterAt(0.5));
+ ON_3dPoint Q = m_axis.ClosestPointTo(P);
+ circle.plane.xaxis = P - Q;
+ if (!circle.plane.xaxis.Unitize())
+ {
+ // Then: just use a vector perp to zaxis
+ circle.plane.xaxis.PerpendicularTo(circle.plane.zaxis);
+ }
+ }
+ circle.plane.yaxis = ON_CrossProduct(circle.plane.zaxis, circle.plane.xaxis);
+ circle.plane.yaxis.Unitize();
+ circle.plane.UpdateEquation();
+ ON_Arc arc(circle, m_angle);
+ return arc;
+ }
+ ON_Arc arc;
+ arc.plane = ON_Plane::NanPlane;
+ arc.radius = ON_DBL_QNAN;
+ return arc;
+}
+
ON_Curve* ON_RevSurface::IsoCurve( int dir, double c ) const
{
diff --git a/opennurbs_revsurface.h b/opennurbs_revsurface.h
index 141a46af..723a1915 100644
--- a/opennurbs_revsurface.h
+++ b/opennurbs_revsurface.h
@@ -406,6 +406,11 @@ public:
double c
) const override;
+
+ ON_Arc IsoArc(
+ double curve_parameter
+ ) const;
+
/*
Description:
Removes the portions of the surface outside of the specified interval.
diff --git a/opennurbs_rtree.cpp b/opennurbs_rtree.cpp
index fa7ac00e..3ec14c38 100644
--- a/opennurbs_rtree.cpp
+++ b/opennurbs_rtree.cpp
@@ -2675,8 +2675,9 @@ double CalcRectVolumeHelper(const ON_RTreeBBox* a_rect)
r += d * d;
d = (a_rect->m_max[2] - a_rect->m_min[2]);
r += d * d;
- r = sqrt(r*0.5); // r = sqrt((dx^2 + dy^2 + dz^2)/2);
- return (r * r * r * 4.1887902047863909846168578443727); // 4/3 pi r^3
+ //r = sqrt(r*0.5); // r = sqrt((dx^2 + dy^2 + dz^2)/2);
+ //return (r * r * r * 4.1887902047863909846168578443727); // 4/3 pi r^3
+ return r;
#elif ( 2 == ON_RTree_NODE_DIM )
// 2d bounding circle volume
d = (a_rect->m_max[0] - a_rect->m_min[0]);
diff --git a/opennurbs_sleeplock.cpp b/opennurbs_sleeplock.cpp
index c5c783f3..cb370583 100644
--- a/opennurbs_sleeplock.cpp
+++ b/opennurbs_sleeplock.cpp
@@ -177,6 +177,11 @@ ON_SleepLockGuard::ON_SleepLockGuard(class ON_SleepLock& sleep_lock)
m_bIsManagingLock = m_sleep_lock.GetLock();
}
+ON_SleepLockGuard::ON_SleepLockGuard(class ON_FixedSizePool& fsp)
+ : m_sleep_lock(fsp.m_sleep_lock)
+{
+ m_bIsManagingLock = m_sleep_lock.GetLock();
+}
ON_SleepLockGuard::ON_SleepLockGuard(
class ON_SleepLock& sleep_lock,
diff --git a/opennurbs_sleeplock.h b/opennurbs_sleeplock.h
index 82dc331a..90aad61b 100644
--- a/opennurbs_sleeplock.h
+++ b/opennurbs_sleeplock.h
@@ -168,6 +168,15 @@ public:
*/
ON_SleepLockGuard(class ON_SleepLock& sleep_lock);
+ // Used by The ThreadSafe...() functions and for expert users
+ // to use when managing memory controlled by this pool. Best
+ // to ingnore this unless you have a very clear idea of what
+ // you are doing, why you are doing it, and when you are doing it.
+ // Otherwise, you'll find yourself waiting forever on a nested
+ // access request.
+ ON_SleepLockGuard(class ON_FixedSizePool& fsp);
+
+
/*
Description:
Calls sleep_lock.GetLock(interval_wait_msecs,max_wait_msecs).
diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp
index d509f53a..9b379a06 100644
--- a/opennurbs_statics.cpp
+++ b/opennurbs_statics.cpp
@@ -93,6 +93,15 @@ ON__UINT64 ON_TestClass::internal_CtorSerialNumberGenerator = 0;
ON__UINT64 ON_TestClass::internal_PopulationCounter = 0;
#endif
+ON__UINT64 ON_NextContentSerialNumber()
+{
+ static ON__UINT64 serial_number = 0;
+ // If it's ever an issue with multiple threads,
+ // this value can be made atomic, but that will slow down performance.
+ return (0 != ++serial_number) ? serial_number : ++serial_number;
+}
+
+
// It is critical that ON_ModelComponent::Internal_RuntimeSerialNumberGenerator
// be constructed before any instance of a class derived from ON_ModelComponent.
// That is why it is above the ClassId stuff in this .cpp file.
@@ -100,6 +109,11 @@ std::atomic ON_ModelComponent::Internal_RuntimeSerialNumberGenerator
std::atomic ON_SubDimple::Internal_RuntimeSerialNumberGenerator;
+ON_SubDComponentLocation ON_SubD::DefaultSubDAppearance = ON_SubDComponentLocation::Surface;
+
+const double ON_SubDSectorType::MinimumCornerAngleRadians = (2.0*ON_PI)/((double)(ON_SubDSectorType::MaximumCornerAngleIndex));
+const double ON_SubDSectorType::MaximumCornerAngleRadians = 2.0*ON_PI - ON_SubDSectorType::MinimumCornerAngleRadians;
+
ON_ClassId* ON_ClassId::m_p0 = 0; // static pointer to first id in list
ON_ClassId* ON_ClassId::m_p1 = 0; // static pointer to last id in list
int ON_ClassId::m_mark0 = 0;
@@ -165,11 +179,15 @@ const ON_UUID ON_rhino5_id = { 0x60515f84, 0x8f7f, 0x41da,{ 0x80, 0x1d, 0x1c, 0x
// {06BB1079-5A56-47A1-AD6D-0B45183D894B}
const ON_UUID ON_rhino6_id = { 0x6bb1079, 0x5a56, 0x47a1,{ 0xad, 0x6d, 0xb, 0x45, 0x18, 0x3d, 0x89, 0x4b } };
+// {78464C2C-9AEB-456E-8C27-865A524F5CA0}
+const ON_UUID ON_rhino7_id = { 0x78464c2c, 0x9aeb, 0x456e,{ 0x8c, 0x27, 0x86, 0x5a, 0x52, 0x4f, 0x5c, 0xa0 } };
+
+
// ON_rhino_id is always set to the value for the current version
// of Rhino. ON_rhino_id is the id that should be used as the
// userdata application id for userdata class definitions that are
// in the core Rhino executable.
-const ON_UUID ON_rhino_id = ON_rhino6_id;
+const ON_UUID ON_rhino_id = ON_rhino7_id;
// Used to identifiy userdata read from V2 files
// which were written before userdata had application ids.
@@ -196,12 +214,15 @@ const ON_UUID ON_opennurbs5_id = { 0xc8cda597, 0xd957, 0x4625,{ 0xa4, 0xb3, 0xa0
// {7B0B585D-7A31-45D0-925E-BDD7DDF3E4E3}
const ON_UUID ON_opennurbs6_id = { 0x7b0b585d, 0x7a31, 0x45d0,{ 0x92, 0x5e, 0xbd, 0xd7, 0xdd, 0xf3, 0xe4, 0xe3 } };
+// {523bfe6e-ef49-4b75-a8d6-253faf5044d3}
+const ON_UUID ON_opennurbs7_id = { 0x523bfe6e, 0xef49, 0x4b75,{ 0xa8, 0xd6, 0x25, 0x3f, 0xaf, 0x50, 0x44, 0xd3 } };
+
// ON_opennurbs_id is always set to the value for the current version
// of opennurbs. ON_opennurbs_id is the id that should be used as
// the userdata application id for userdata classes definitions that
// are in the opennurbs library.
-const ON_UUID ON_opennurbs_id = ON_opennurbs6_id;
+const ON_UUID ON_opennurbs_id = ON_opennurbs7_id;
const ON_UuidPairList ON_UuidPairList::EmptyList;
@@ -491,6 +512,8 @@ const wchar_t ON_wString::Backslash = (wchar_t)ON_UnicodeCodePoint::ON_Backslash
const wchar_t ON_wString::Underscore = (char)ON_UnicodeCodePoint::ON_Underscore;
const wchar_t ON_wString::Pipe = (wchar_t)ON_UnicodeCodePoint::ON_Pipe;
const wchar_t ON_wString::Tilde = (wchar_t)ON_UnicodeCodePoint::ON_Tilde;
+const wchar_t ON_wString::DecimalAsPeriod = (wchar_t)ON_UnicodeCodePoint::ON_Period;
+const wchar_t ON_wString::DecimalAsComma = (wchar_t)ON_UnicodeCodePoint::ON_Comma;
#if defined(ON_SIZEOF_WCHAR_T) && ON_SIZEOF_WCHAR_T >= 2
// ON_wString is UTF-16 encoded when sizeof(wchar_t) = 2
@@ -812,6 +835,8 @@ const ON_Color ON_Color::Gray160(160, 160, 160);
const ON_Color ON_Color::Gray230(230, 230, 230);
const ON_Color ON_Color::Gray250(250, 250, 250);
+const ON_4fColor ON_4fColor::Unset;
+
const ON_UuidIndex ON_UuidIndex::NilIndex = ON_UuidIndex();
const ON_UuidPtr ON_UuidPtr::NilPtr = ON_UuidPtr();
@@ -853,6 +878,12 @@ static ON_Plane ON_Plane_NanPlane()
}
const ON_Plane ON_Plane::NanPlane(ON_Plane_NanPlane());
+// ON_SubDDisplayParameters statics before ON_MeshParamters statics
+const ON_SubDDisplayParameters ON_SubDDisplayParameters::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDDisplayParameters);
+const ON_SubDDisplayParameters ON_SubDDisplayParameters::Course = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::CourseDensity);
+const ON_SubDDisplayParameters ON_SubDDisplayParameters::Default = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDDisplayParameters::DefaultDensity);
+
+
// {F15F67AA-4AF9-4B25-A3B8-517CEDDAB134}
const ON_UUID ON_MeshParameters::RhinoLegacyMesherId = { 0xf15f67aa, 0x4af9, 0x4b25,{ 0xa3, 0xb8, 0x51, 0x7c, 0xed, 0xda, 0xb1, 0x34 } };
@@ -1447,6 +1478,9 @@ static ON_TextureMapping SurfaceParameterTextureMappingInitializer()
}
const ON_TextureMapping ON_TextureMapping::SurfaceParameterTextureMapping(SurfaceParameterTextureMappingInitializer());
+const ON_MappingTag ON_MappingTag::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_MappingTag);
+const ON_MappingTag ON_MappingTag::SurfaceParameterMapping(ON_TextureMapping::SurfaceParameterTextureMapping,nullptr);
+
const ON_LinetypeSegment ON_LinetypeSegment::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_LinetypeSegment);
const ON_LinetypeSegment ON_LinetypeSegment::OneMillimeterLine(1.0, ON_LinetypeSegment::eSegType::stLine);
@@ -2283,20 +2317,25 @@ const ON_HatchPattern ON_HatchPattern::Grid60(Internal_LineHatchPatternInit(-7))
const ON_HatchPattern ON_HatchPattern::Plus(Internal_LineHatchPatternInit(-8)); // index = -8, id set, unique and persistent
const ON_HatchPattern ON_HatchPattern::Squares(Internal_LineHatchPatternInit(-9)); // index = -9, id set, unique and persistent
-#if defined(OPENNURBS_SUBD_WIP)
+
+const ON_Mesh ON_Mesh::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Mesh);
+const ON_MeshRef ON_MeshRef::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_MeshRef);
unsigned int ON_SubD::ErrorCount = 0;
-const ON_SubDComponentPtr ON_SubDComponentPtr::Null = { 0 };
+const ON_SubDVertexEdgeProperties ON_SubDVertexEdgeProperties::Zero ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDVertexEdgeProperties);
+
const ON_SubDVertexPtr ON_SubDVertexPtr::Null = { 0 };
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_SubDEdgeChain ON_SubDEdgeChain::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDEdgeChain);
-static ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint_Init(double x)
+static ON_SubDSectorSurfacePoint ON_SubDSectorLimitPoint_Init(double x)
{
- ON_SubDSectorLimitPoint lp;
+ ON_SubDSectorSurfacePoint lp;
memset(&lp, 0, sizeof(lp));
lp.m_limitP[0] = x;
@@ -2318,13 +2357,14 @@ static ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint_Init(double x)
return lp;
}
-const ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint::Unset = ON_SubDSectorLimitPoint_Init(ON_UNSET_VALUE);
-const ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint::Nan = ON_SubDSectorLimitPoint_Init(ON_DBL_QNAN);
-const ON_SubDSectorLimitPoint ON_SubDSectorLimitPoint::Zero = ON_SubDSectorLimitPoint_Init(0.0);
+const ON_SubDSectorSurfacePoint ON_SubDSectorSurfacePoint::Unset = ON_SubDSectorLimitPoint_Init(ON_UNSET_VALUE);
+const ON_SubDSectorSurfacePoint ON_SubDSectorSurfacePoint::Nan = ON_SubDSectorLimitPoint_Init(ON_DBL_QNAN);
+const ON_SubDSectorSurfacePoint ON_SubDSectorSurfacePoint::Zero = ON_SubDSectorLimitPoint_Init(0.0);
-const unsigned int ON_SubDLimitMeshFragment::MaximumSideSegmentCount = (1U << ON_SubDLimitMesh::MaximumDisplayDensity); // = 2^ON_SubDLimitMesh::MaximumDisplayDensity
+const ON_SubDVertexSurfacePointCoefficient ON_SubDVertexSurfacePointCoefficient::Zero = ON_SubDVertexSurfacePointCoefficient::Create(nullptr,nullptr,0.0);
+const ON_SubDVertexSurfacePointCoefficient ON_SubDVertexSurfacePointCoefficient::Nan = ON_SubDVertexSurfacePointCoefficient::Create(nullptr,nullptr,ON_DBL_QNAN);
+const ON_SubDVertexSurfacePointCoefficient ON_SubDVertexSurfacePointCoefficient::Unset = ON_SubDVertexSurfacePointCoefficient::Create(nullptr,nullptr,ON_UNSET_VALUE);
-const unsigned int ON_SubDSectorType::MaximumAngleIndex = 72;
const double ON_SubDSectorType::IgnoredCornerSectorAngle = 0.0;
const double ON_SubDSectorType::UnsetCornerSectorAngle = -8881.0;
const double ON_SubDSectorType::ErrorCornerSectorAngle = -9991.0;
@@ -2337,10 +2377,6 @@ const double ON_SubDSectorType::IgnoredSectorWeight = 0.0;
const double ON_SubDSectorType::UnsetSectorWeight = -8883.0;
const double ON_SubDSectorType::ErrorSectorWeight = -9993.0;
-const unsigned int ON_SubDVertex::MaximumEdgeCount = 0xFFF0U;
-const unsigned int ON_SubDVertex::MaximumFaceCount = 0xFFF0U;
-const unsigned int ON_SubDEdge::MaximumFaceCount = 0xFFF0U;
-const unsigned int ON_SubDFace::MaximumEdgeCount = 0xFFF0U;
const ON_SubDComponentRegionIndex ON_SubDComponentRegionIndex::Zero ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDComponentRegionIndex);
@@ -2396,24 +2432,27 @@ const ON_AggregateComponentStatus ON_AggregateComponentStatus::NotCurrent = ON_A
const ON_SubDComponentPoint ON_SubDComponentPoint::Unset = ON_SubDComponentPoint();
-static ON_SubDLimitMeshFragmentGrid EmptyLimitMeshFragmentGridInit()
+static ON_SubDMeshFragmentGrid EmptyLimitMeshFragmentGridInit()
{
- ON_SubDLimitMeshFragmentGrid empty;
+ ON_SubDMeshFragmentGrid empty;
memset(&empty, 0, sizeof(empty));
return empty;
}
-static ON_SubDLimitMeshFragment EmptyLimitMeshFragmentInit()
+static ON_SubDMeshFragment EmptyLimitMeshFragmentInit()
{
- ON_SubDLimitMeshFragment empty;
+ ON_SubDMeshFragment empty;
memset(&empty, 0, sizeof(empty));
return empty;
}
-const ON_SubDLimitMeshFragmentGrid ON_SubDLimitMeshFragmentGrid::Empty = EmptyLimitMeshFragmentGridInit();
-const ON_SubDLimitMeshFragment ON_SubDLimitMeshFragment::Empty = EmptyLimitMeshFragmentInit();
+const ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::Empty = EmptyLimitMeshFragmentGridInit();
+const ON_SubDMeshFragment ON_SubDMeshFragment::Empty = EmptyLimitMeshFragmentInit();
+
+const ON_SubDMeshFragmentGrid ON_SubDMeshFragmentGrid::OneQuadGrid = ON_SubDMeshFragmentGrid::QuadGridFromSideSegmentCount(1, 0);
+
static ON_SubDComponentBase UnsetComponentBaseInit()
{
// For efficiency, ON_SubDComponentBase() does not waste time
@@ -2459,38 +2498,33 @@ const ON_SubDVertex ON_SubDVertex::Empty = EmptyVertexInit();
const ON_SubDEdge ON_SubDEdge::Empty = EmptyEdgeInit();
const ON_SubDFace ON_SubDFace::Empty = EmptyFaceInit();
-const ON_SubDDisplayParameters ON_SubDDisplayParameters::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDDisplayParameters);
-const ON_SubDDisplayParameters ON_SubDDisplayParameters::DefaultDisplayMeshParameters = ON_SubDDisplayParameters::CreateFromDisplayDensity(ON_SubDLimitMesh::DefaultDisplayDensity);
-
const ON_SubD ON_SubD::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubD);
const ON_SubDRef ON_SubDRef::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDRef);
-const ON_SubDLimitMesh ON_SubDLimitMesh::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDLimitMesh);
+const ON_SubDMesh ON_SubDMesh::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDMesh);
const ON_SubDSectorType ON_SubDSectorType::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDSectorType);
const ON_SubDMatrix ON_SubDMatrix::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDMatrix);
const ON_SubDComponentRef ON_SubDComponentRef::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDComponentRef);
-static ON_SubDFromMeshOptions ON_SubDCreaseParameters_CreaseAt(
- ON_SubDFromMeshOptions::InteriorCreaseOption crease_type
+static ON_ToSubDParameters ON_SubDCreaseParameters_CreaseAt(
+ ON_ToSubDParameters::InteriorCreaseOption crease_type
)
{
- ON_SubDFromMeshOptions cp;
+ ON_ToSubDParameters cp;
cp.SetInteriorCreaseOption(crease_type);
return cp;
}
-static ON_SubDFromMeshOptions ON_SubDCreaseParameters_ConvexCorners()
+static ON_ToSubDParameters ON_SubDCreaseParameters_ConvexCorners()
{
- ON_SubDFromMeshOptions cp;
- cp.SetConvexCornerOption(ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner);
+ ON_ToSubDParameters cp;
+ cp.SetConvexCornerOption(ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner);
return cp;
}
-const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::Smooth ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDFromMeshOptions);
-const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::InteriorCreaseAtMeshCrease = ON_SubDCreaseParameters_CreaseAt(ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease);
-const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::InteriorCreaseAtMeshEdge = ON_SubDCreaseParameters_CreaseAt(ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge);
-const ON_SubDFromMeshOptions ON_SubDFromMeshOptions::ConvexCornerAtMeshCorner = ON_SubDCreaseParameters_ConvexCorners();
-
-#endif
+const ON_ToSubDParameters ON_ToSubDParameters::Smooth ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_ToSubDParameters);
+const ON_ToSubDParameters ON_ToSubDParameters::InteriorCreaseAtMeshCrease = ON_SubDCreaseParameters_CreaseAt(ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease);
+const ON_ToSubDParameters ON_ToSubDParameters::InteriorCreaseAtMeshEdge = ON_SubDCreaseParameters_CreaseAt(ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge);
+const ON_ToSubDParameters ON_ToSubDParameters::ConvexCornerAtMeshCorner = ON_SubDCreaseParameters_ConvexCorners();
unsigned int ON_ModelComponent::Internal_SystemComponentHelper()
{
diff --git a/opennurbs_string.h b/opennurbs_string.h
index 2dd6d156..ed0ec4c3 100644
--- a/opennurbs_string.h
+++ b/opennurbs_string.h
@@ -2152,6 +2152,8 @@ public:
static const wchar_t Underscore; // Unicode LOW LINE U+005F
static const wchar_t Pipe; // Unicode VERTICAL LINE U+007C
static const wchar_t Tilde; // Unicode TILDE U+007E
+ static const wchar_t DecimalAsPeriod; // Unicode PERIOD U+002E
+ static const wchar_t DecimalAsComma; // Unicode COMMA U+002C
#if defined(ON_SIZEOF_WCHAR_T) && ON_SIZEOF_WCHAR_T >= 2
// Never cast these values as "char"
diff --git a/opennurbs_string_compare.cpp b/opennurbs_string_compare.cpp
index af389fcc..0014984a 100644
--- a/opennurbs_string_compare.cpp
+++ b/opennurbs_string_compare.cpp
@@ -8,7 +8,7 @@
// 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 .
//
////////////////////////////////////////////////////////////////
@@ -19,7 +19,7 @@
#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
+// 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
@@ -60,7 +60,7 @@ static ON__UINT32 MapCodePointOrdinal(
ON_StringMapOrdinalType map_type
)
{
- // Converts ordinal "char" and "wchar_t" element values in the
+ // Converts ordinal "char" and "wchar_t" element values in the
// range 0x00 to maximum_singleton_value to "ingore case" ordinal equivalents.
// The returned value is always <= input value.
//
@@ -173,13 +173,13 @@ static ON__UINT32 MapCodePointOrdinal(
{
case 0x0130U: // LATIN CAPITAL LETTER I WITH DOT ABOVE
if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::LowerOrdinal == map_type)
- return 0x0069U; // 0x0069U:LATIN SMALL LETTER i
+ return 0x0069U; // 0x0069U: LATIN SMALL LETTER i
return unicode_code_point;
break;
case 0x0131U: // LATIN SMALL LETTER DOTLESS I
if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type)
- return 0x0049U; // 0x0049ULATIN CAPITAL LETTER I
+ return 0x0049U; // 0x0049U LATIN CAPITAL LETTER I
return unicode_code_point;
break;
@@ -190,7 +190,7 @@ static ON__UINT32 MapCodePointOrdinal(
case 0x0149U: // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type)
- return 0x004EU; // 0x004EULATIN CAPITAL LETTER N
+ return 0x004EU; // 0x004EU LATIN CAPITAL LETTER N
return unicode_code_point;
break;
@@ -287,7 +287,7 @@ static ON__UINT32 MapCodePointOrdinal(
return unicode_code_point;
}
-
+
if (unicode_code_point < 0x0370)
@@ -555,7 +555,7 @@ int ON_StringCompareOrdinalUTF8(
unsigned int c1, c2;
int i;
- const int element_count
+ const int element_count
= (element_count1 <= element_count2)
? element_count1
: element_count2;
@@ -584,7 +584,7 @@ int ON_StringCompareOrdinalUTF8(
return 1;
}
}
-
+
for (/*empty init*/; i < element_count1; i++)
{
if ( 0 != (*string1++))
@@ -612,7 +612,7 @@ int ON_StringCompareOrdinalUTF16(
unsigned int c1, c2;
int i;
- const int element_count
+ const int element_count
= (element_count1 <= element_count2)
? element_count1
: element_count2;
@@ -641,7 +641,7 @@ int ON_StringCompareOrdinalUTF16(
return 1;
}
}
-
+
for (/*empty init*/; i < element_count1; i++)
{
if ( 0 != (*string1++))
@@ -669,7 +669,7 @@ int ON_StringCompareOrdinalUTF32(
unsigned int c1, c2;
int i;
- const int element_count
+ const int element_count
= (element_count1 <= element_count2)
? element_count1
: element_count2;
@@ -698,7 +698,7 @@ int ON_StringCompareOrdinalUTF32(
return 1;
}
}
-
+
for (/*empty init*/; i < element_count1; i++)
{
if ( 0 != (*string1++))
@@ -828,7 +828,7 @@ int ON_String::ComparePath(
}
count2++;
}
-
+
if (count1 > 0 || count2 > 0)
{
// TODO
@@ -875,9 +875,9 @@ bool ON_String::EqualPath(
CHAR_STRING_EQUAL_PREAMBLE(path1,element_count1,path2,element_count2);
unsigned int c1=0, c2=0;
- const int element_count
- = (element_count1 <= element_count2)
- ? element_count1
+ const int element_count
+ = (element_count1 <= element_count2)
+ ? element_count1
: element_count2;
int i;
const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase();
@@ -902,7 +902,7 @@ bool ON_String::EqualPath(
return false;
}
}
-
+
for (/*empty init*/; i < element_count1; i++)
{
if ( 0 != (*path1++))
@@ -986,7 +986,7 @@ int ON_wString::ComparePath(
}
count2++;
}
-
+
if (count1 > 0 || count2 > 0)
{
// TODO
@@ -1037,9 +1037,9 @@ bool ON_wString::EqualPath(
WIDE_STRING_EQUAL_PREAMBLE(path1,element_count1,path2,element_count2);
unsigned int c1=0, c2=0;
- const int element_count
- = (element_count1 <= element_count2)
- ? element_count1
+ const int element_count
+ = (element_count1 <= element_count2)
+ ? element_count1
: element_count2;
int i;
const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase();
@@ -1064,7 +1064,7 @@ bool ON_wString::EqualPath(
return false;
}
}
-
+
for (/*empty init*/; i < element_count1; i++)
{
if ( 0 != (*path1++))
@@ -1159,7 +1159,7 @@ static unsigned int Internal_NameAttributeWideCharRank(
// 43 - (hyphen)
// 44 A
// 45 B
- // 46 C
+ // 46 C
// ...
// 69 Z
// 70 a
@@ -1285,7 +1285,7 @@ static unsigned int Internal_NameAttributeWideCharRank(
// 32 to 41 numerals 0 to 9 handled before this switch
- case 0x27: // 42 ' (apostrophe)
+ case 0x27: // 42 ' (apostrophe)
rank = 42;
break;
case 0x2D: // 43 - (hyphen)
@@ -1301,7 +1301,7 @@ static unsigned int Internal_NameAttributeWideCharRank(
break;
}
- return rank;
+ return rank;
}
int ON_wString::CompareAttributeName(
@@ -1400,29 +1400,29 @@ int ON_String::CompareNoCase( const unsigned char* s) const
bool ON_String::Equal(
const ON_String& other_string,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
) const
{
return ON_String::Equal(
- static_cast(*this),
- this->Length(),
- static_cast(other_string),
- other_string.Length(),
+ static_cast(*this),
+ this->Length(),
+ static_cast(other_string),
+ other_string.Length(),
locale,
bIgnoreCase );
}
bool ON_String::Equal(
const char* other_string,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
) const
{
return ON_String::Equal(
- static_cast(*this),
- this->Length(),
- other_string,
+ static_cast(*this),
+ this->Length(),
+ other_string,
-1,
locale,
bIgnoreCase );
@@ -1431,7 +1431,7 @@ bool ON_String::Equal(
bool ON_String::Equal(
const char* string1,
const char* string2,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
)
{
@@ -1443,7 +1443,7 @@ bool ON_String::Equal(
int element_count1,
const char* string2,
int element_count2,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
)
{
@@ -1467,16 +1467,16 @@ bool ON_String::Equal(
int ON_String::Compare(
const ON_String& other_string,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
) const
{
- return ON_String::Compare(
+ return ON_String::Compare(
static_cast< const char* >(*this),
this->Length(),
static_cast< const char* >(other_string),
other_string.Length(),
- locale,
+ locale,
bIgnoreCase
);
}
@@ -1484,16 +1484,16 @@ int ON_String::Compare(
int ON_String::Compare(
const char* other_string,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
) const
{
- return ON_String::Compare(
+ return ON_String::Compare(
static_cast< const char* >(*this),
this->Length(),
other_string,
-1,
- locale,
+ locale,
bIgnoreCase
);
}
@@ -1501,7 +1501,7 @@ int ON_String::Compare(
int ON_String::Compare(
const char* string1,
const char* string2,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
)
{
@@ -1513,7 +1513,7 @@ int ON_String::Compare(
int element_count1,
const char* string2,
int element_count2,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
)
{
@@ -1522,10 +1522,10 @@ int ON_String::Compare(
// TODO
// Actually do an invariant culture compare
// This involves NFC normalization and then using the correct CE to compare values.
- //
+ //
return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase );
//int n = (element_count1 <= element_count2) ? element_count1 : element_count2;
- //int rc
+ //int rc
// = bIgnoreCase
// ? _strnicmp_l(string1, string2, (size_t)n, locale.StringCollateAndMapLocalePtr())
// : _strncmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr());
@@ -1541,11 +1541,11 @@ int ON_String::Compare(
// TODO
// Actually do an invariant culture compare
// This involves NFC normalization and then using the correct CE to compare values.
- //
+ //
return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase );
//int n = (element_count1 <= element_count2) ? element_count1 : element_count2;
- //int rc
+ //int rc
// = bIgnoreCase
// ? strncasecmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr())
// : strncmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr());
@@ -1564,11 +1564,11 @@ int ON_String::Compare(
bool ON_wString::Equal(
const ON_wString& other_string,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
) const
{
- return ON_wString::Equal(
+ return ON_wString::Equal(
static_cast< const wchar_t* >(*this),
this->Length(),
static_cast< const wchar_t* >(other_string),
@@ -1580,11 +1580,11 @@ bool ON_wString::Equal(
bool ON_wString::Equal(
const wchar_t* other_string,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
) const
{
- return ON_wString::Equal(
+ return ON_wString::Equal(
static_cast< const wchar_t* >(*this),
this->Length(),
other_string,
@@ -1597,7 +1597,7 @@ bool ON_wString::Equal(
bool ON_wString::Equal(
const wchar_t* string1,
const wchar_t* string2,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
)
{
@@ -1609,7 +1609,7 @@ bool ON_wString::Equal(
int element_count1,
const wchar_t* string2,
int element_count2,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
)
{
@@ -1627,11 +1627,11 @@ bool ON_wString::Equal(
int ON_wString::Compare(
const ON_wString& other_string,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
) const
{
- return ON_wString::Compare(
+ return ON_wString::Compare(
static_cast< const wchar_t* >(*this),
this->Length(),
static_cast< const wchar_t* >(other_string),
@@ -1643,11 +1643,11 @@ int ON_wString::Compare(
int ON_wString::Compare(
const wchar_t* other_string,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
) const
{
- return ON_wString::Compare(
+ return ON_wString::Compare(
static_cast< const wchar_t* >(*this),
this->Length(),
other_string,
@@ -1660,7 +1660,7 @@ int ON_wString::Compare(
int ON_wString::Compare(
const wchar_t* string1,
const wchar_t* string2,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
)
{
@@ -1672,7 +1672,7 @@ int ON_wString::Compare(
int element_count1,
const wchar_t* string2,
int element_count2,
- const class ON_Locale& locale,
+ const class ON_Locale& locale,
bool bIgnoreCase
)
{
@@ -1683,13 +1683,13 @@ int ON_wString::Compare(
#if defined(ON_RUNTIME_WIN)
const bool bIsIsInvariantCulture = locale.IsInvariantCulture();
- wchar_t buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY];
- const wchar_t* locale_name
- = bIsIsInvariantCulture
+ wchar_t buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY];
+ const wchar_t* locale_name
+ = bIsIsInvariantCulture
? LOCALE_NAME_INVARIANT
: locale.GetWindowsLocaleName(buffer,sizeof(buffer)/sizeof(buffer[0]));
-
- const DWORD flags = ( bIgnoreCase )
+
+ const DWORD flags = ( bIgnoreCase )
? ((bIsIsInvariantCulture) ? NORM_IGNORECASE : LINGUISTIC_IGNORECASE)
: 0;
@@ -1711,20 +1711,20 @@ int ON_wString::Compare(
if (rc == CSTR_GREATER_THAN)
return 1;
-
+
#elif defined(ON_RUNTIME_APPLE)
- // I need a tool that is similar to
+ // I need a tool that is similar to
//
// wcscoll_l(const wchar_t*, const wchar_t*, locale_t )
//
// but it needs to take a count (n) and I need one that ignores case.
//
// It appears wcsncasecmp_l() does use locale to map case, but then does an ordinal compare.
- //
- //
+ //
+ //
//int n = (element_count1 <= element_count2) ? element_count1 : element_count2;
- //int rc
+ //int rc
// = bIgnoreCase
// ? wcsncasecmp_l(string1, string2, (size_t)n, locale.StringCollateAndMapLocalePtr())
// : wcsncmp(string1, string2, (size_t)n);
@@ -1754,7 +1754,7 @@ int ON_wString::Compare(
bool operator==( const ON_String& lhs, const ON_String& rhs )
{
const int length = lhs.Length();
- return
+ return
(length == rhs.Length())
&& ON_String::EqualOrdinal(
static_cast(lhs),
@@ -2041,14 +2041,14 @@ bool ON_String::EqualOrdinal(
) const
{
const int length = Length();
- return
- (length == other_string.Length())
+ return
+ (length == other_string.Length())
&& ON_String::EqualOrdinal(
static_cast< const char* >(*this),
length,
static_cast< const char* >(other_string),
length,
- bOrdinalIgnoreCase
+ bOrdinalIgnoreCase
);
}
@@ -2064,7 +2064,7 @@ bool ON_String::EqualOrdinal(
length,
other_string,
length,
- bOrdinalIgnoreCase
+ bOrdinalIgnoreCase
);
}
@@ -2135,14 +2135,14 @@ bool ON_wString::EqualOrdinal(
) const
{
const int length = Length();
- return
+ return
(length == other_string.Length())
&& ON_wString::EqualOrdinal(
static_cast< const wchar_t* >(*this),
length,
static_cast< const wchar_t* >(other_string),
length,
- bOrdinalIgnoreCase
+ bOrdinalIgnoreCase
);
}
@@ -2158,7 +2158,7 @@ bool ON_wString::EqualOrdinal(
length,
other_string,
length,
- bOrdinalIgnoreCase
+ bOrdinalIgnoreCase
);
}
@@ -2202,7 +2202,7 @@ bool ON_wString::EqualOrdinal(
return false;
}
}
-
+
for (/*empty init*/; i < element_count1; i++)
{
if ( 0 != (*string1++))
@@ -2388,7 +2388,7 @@ int ON_String::MapStringOrdinal(
}
else if ( 0 == mapped_string_capacity )
return element_count; // no +1 here
-
+
if ( element_count > mapped_string_capacity )
return 0;
@@ -2479,7 +2479,7 @@ int ON_wString::MapStringOrdinal(
}
else if ( 0 == mapped_string_capacity )
return element_count; // no +1 here
-
+
if ( element_count > mapped_string_capacity )
return 0;
@@ -2527,7 +2527,7 @@ ON_String ON_String::MapString(
int element_count
)
{
- bool bMappingNullTerminator;
+ bool bMappingNullTerminator;
if (element_count < 0)
{
@@ -2538,16 +2538,16 @@ ON_String ON_String::MapString(
}
else
{
- bMappingNullTerminator
- = element_count > 0
- && nullptr != string
- && 0 == string[element_count-1]
+ bMappingNullTerminator
+ = element_count > 0
+ && nullptr != string
+ && 0 == string[element_count-1]
&& (1 == element_count || 0 != string[element_count-2])
;
}
int mapped_string_capacity = ON_String::MapString(locale,map_type,string,element_count,nullptr,0);
-
+
if (mapped_string_capacity > 0)
{
ON_String mapped_string;
@@ -2557,8 +2557,8 @@ ON_String ON_String::MapString(
mapped_string.ReserveArray(mapped_string_capacity);
// Set mapped_string ON_wString header length value to mapped_length.
- int mapped_string_length
- = ( bMappingNullTerminator )
+ int mapped_string_length
+ = ( bMappingNullTerminator )
? (mapped_string_capacity-1)
: mapped_string_capacity;
mapped_string.SetLength(mapped_string_length);
@@ -2578,7 +2578,7 @@ ON_wString ON_wString::MapString(
int element_count
)
{
- bool bMappingNullTerminator;
+ bool bMappingNullTerminator;
if (element_count < 0)
{
@@ -2589,16 +2589,16 @@ ON_wString ON_wString::MapString(
}
else
{
- bMappingNullTerminator
- = element_count > 0
- && nullptr != string
- && 0 == string[element_count-1]
+ bMappingNullTerminator
+ = element_count > 0
+ && nullptr != string
+ && 0 == string[element_count-1]
&& (1 == element_count || 0 != string[element_count-2])
;
}
int mapped_string_capacity = ON_wString::MapString(locale,map_type,string,element_count,nullptr,0);
-
+
if (mapped_string_capacity > 0)
{
ON_wString mapped_string;
@@ -2608,8 +2608,8 @@ ON_wString ON_wString::MapString(
mapped_string.ReserveArray(mapped_string_capacity);
// Set mapped_string ON_wString header length value to mapped_length.
- int mapped_string_length
- = ( bMappingNullTerminator )
+ int mapped_string_length
+ = ( bMappingNullTerminator )
? (mapped_string_capacity-1)
: mapped_string_capacity;
mapped_string.SetLength(mapped_string_length);
diff --git a/opennurbs_string_format.cpp b/opennurbs_string_format.cpp
index 805be8d7..304b351e 100644
--- a/opennurbs_string_format.cpp
+++ b/opennurbs_string_format.cpp
@@ -1224,6 +1224,9 @@ int ON_wString::FormatVargsOutputCount(
return -1;
#else
#if defined(ON_COMPILER_GNU)
+ // 31 May 2019 S. Baer (RH-52038)
+ // TODO: The following code needs to be tested. This was added by request from a user that needed
+ // a GCC compile. This is obviously a cut and paste of the above clang code
wchar_t stack_buffer[1024];
ON_wStringBuffer buffer(stack_buffer, sizeof(stack_buffer) / sizeof(stack_buffer[0]));
size_t buffer_capacity = buffer.m_buffer_capacity;
diff --git a/opennurbs_string_scan.cpp b/opennurbs_string_scan.cpp
index 539d95d4..eff4b506 100644
--- a/opennurbs_string_scan.cpp
+++ b/opennurbs_string_scan.cpp
@@ -148,20 +148,21 @@ int ON_wString::ScanBufferVargs(
va_list args
)
{
-#if defined(ON_COMPILER_CLANG) || defined(ON_RUNTIME_LINUX)
-#if defined(ON_RUNTIME_ANDROID) || defined(ON_RUNTIME_LINUX)
if (nullptr == buffer || nullptr == format)
return -1;
- return swscanf(buffer, format, args);
-#else
- if (nullptr == buffer || nullptr == format || nullptr == args)
- return -1;
- return swscanf_l(buffer, ON_Locale::InvariantCulture.NumericLocalePtr(), format, args);
-#endif
-#else
- if (nullptr == buffer || nullptr == format || nullptr == args)
+
+#if defined(ON_RUNTIME_WIN)
+ if (nullptr == args)
return -1;
return _vswscanf_s_l(buffer, format, ON_Locale::InvariantCulture.NumericLocalePtr(), args);
+#elif defined(ON_RUNTIME_APPLE)
+ if (nullptr == args)
+ return -1;
+ return swscanf_l(buffer, _c_locale, format, args);
+#elif defined(ON_RUNTIME_ANDROID) || defined(ON_RUNTIME_LINUX)
+ return swscanf(buffer, format, args);
+#else
+ return swscanf(buffer, format, args);
#endif
}
diff --git a/opennurbs_string_values.cpp b/opennurbs_string_values.cpp
index 82e42a6d..a247c08c 100644
--- a/opennurbs_string_values.cpp
+++ b/opennurbs_string_values.cpp
@@ -264,6 +264,20 @@ double ON_CleanNumber(
return value;
}
+const ON_wString& ON_NTimesPowerOf10AsStringFail(const ON_wString& g_format, bool bLogError)
+{
+ // The number being formatted might be bogus.
+ // This can occur when values are not properly initialized.
+ if (bLogError) // <- good place for debugging breakpoint
+ {
+ ON_ERROR("Unexpected result.");
+ }
+
+ // g_format is a valid string representation of the input value, so
+ // the user is seeing something that is correct, but may have
+ // scientific notation or lots of digits.
+ return g_format;
+}
static
const ON_wString ON_NTimesPowerOf10AsString(
@@ -278,18 +292,29 @@ const ON_wString ON_NTimesPowerOf10AsString(
if (nullptr != clean_value)
*clean_value = value;
- // returns n*(10^e) as a pretty string.
- //const ON__UINT64 ten = 10;
+ if ( false == ON_IsValid(value) )
+ return ON_NTimesPowerOf10AsStringFail(g_format,false); // value is a nan or UNSET value.
+ // ON__UINT64 range is 0 to 18,446,744,073,709,551,615 = 1.8...e19
+ const double max_pretty_value = 1.0e18; // must be <= 18,446,744,073,709,551,615 ( 20 decimal digits )
+ const double min_pretty_value = 1.0 / max_pretty_value;
+ if (fabs(value) >= max_pretty_value || fabs(value) <= min_pretty_value)
+ return ON_NTimesPowerOf10AsStringFail(g_format,false); // value is too big or too small for a "pretty" format.
+
+
+ // returns n*(10^e) as a "pretty" string - no exponential notation that disturbs users.
ON__UINT64 q = 1;
ON__UINT64 i = 0;
ON__UINT64 f = 0;
-
if (e >= 0)
{
for (int ie = 0; ie < e; ie++)
q *= 10;
+
+ if (fabs((double)n)*fabs((double)q) >= max_pretty_value)
+ return ON_NTimesPowerOf10AsStringFail(g_format,false);
+
i = n*q;
f = 0;
}
@@ -298,6 +323,9 @@ const ON_wString ON_NTimesPowerOf10AsString(
// e is negative
for (int ie = 0; ie > e; ie--)
q *= 10;
+ if (fabs((double)n) <= min_pretty_value*fabs((double)q))
+ return ON_NTimesPowerOf10AsStringFail(g_format,false);
+
i = n / q;
f = n % q;
}
@@ -311,7 +339,7 @@ const ON_wString ON_NTimesPowerOf10AsString(
*clean_value = x;
return ON_wString::FormatToString(L"%llu", i);
}
- wchar_t sf[32] = { 0 };
+ wchar_t sf[32] = { 0 }; // 32 is more than enough to hold the maximum of 20 digits
size_t sf_capacity = sizeof(sf) / sizeof(sf[i]);
size_t sfi = 0;
for (ON__UINT64 r = q / 10; r > 0 && sfi < sf_capacity; r /= 10)
@@ -329,8 +357,7 @@ const ON_wString ON_NTimesPowerOf10AsString(
}
}
- ON_ERROR("Unexpected result.");
- return g_format;
+ return ON_NTimesPowerOf10AsStringFail(g_format,false);
}
diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp
index 1674b96a..49f73126 100644
--- a/opennurbs_subd.cpp
+++ b/opennurbs_subd.cpp
@@ -26,13 +26,6 @@
////////////////////////////////////////////////////////////////
*/
-#if defined(OPENNURBS_SUBD_WIP)
-
-void ON_SubDIncrementErrorCount()
-{
- ON_SubD::ErrorCount++;
-}
-
ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentPtrTypeFromUnsigned(
unsigned int element_pointer_type_as_unsigned
)
@@ -85,8 +78,8 @@ ON_SubD::EdgeTag ON_SubD::EdgeTagFromUnsigned(
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Unset);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Smooth);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Crease);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Sharp);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::X);
+ //ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::Sharp);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::EdgeTag::SmoothX);
}
return ON_SUBD_RETURN_ERROR(ON_SubD::EdgeTag::Unset);
}
@@ -98,37 +91,11 @@ bool ON_SubD::EdgeTagIsSet(
return (
ON_SubD::EdgeTag::Smooth == edge_tag
|| ON_SubD::EdgeTag::Crease == edge_tag
- || ON_SubD::EdgeTag::Sharp == edge_tag
- || ON_SubD::EdgeTag::X == edge_tag
+ //|| ON_SubD::EdgeTag::Sharp == edge_tag
+ || ON_SubD::EdgeTag::SmoothX == edge_tag
);
}
-ON_SubD::FacetType ON_SubD::FacetTypeFromUnsigned(
- unsigned int facet_type_as_unsigned
- )
-{
- switch (facet_type_as_unsigned)
- {
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::FacetType::Unset);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::FacetType::Tri);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::FacetType::Quad);
- }
- return ON_SUBD_RETURN_ERROR(ON_SubD::FacetType::Unset);
-}
-
-//ON_SubD::VertexEdgeOrder ON_SubD::VertexEdgeOrderFromUnsigned(
-// unsigned int vertex_edge_order_as_unsigned
-// )
-//{
-// switch (vertex_edge_order_as_unsigned)
-// {
-// ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexEdgeOrder::unset);
-// ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexEdgeOrder::radial);
-// ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::VertexEdgeOrder::notradial);
-// }
-// return ON_SUBD_RETURN_ERROR(ON_SubD::VertexEdgeOrder::unset);
-//}
-
ON_SubD::VertexFacetType ON_SubD::VertexFacetTypeFromUnsigned(
unsigned int vertex_facet_type_as_unsigned
)
@@ -144,53 +111,7 @@ ON_SubD::VertexFacetType ON_SubD::VertexFacetTypeFromUnsigned(
return ON_SUBD_RETURN_ERROR(ON_SubD::VertexFacetType::Unset);
}
-ON_SubD::SubDType ON_SubD::SubDTypeFromUnsigned(
- unsigned int subd_type_as_unsigned
- )
-{
- switch (subd_type_as_unsigned)
- {
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::Unset);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::Custom);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::TriLoopWarren);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::QuadCatmullClark);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::CustomTri);
- ON_ENUM_FROM_UNSIGNED_CASE(ON_SubD::SubDType::CustomQuad);
- }
- return ON_SUBD_RETURN_ERROR(ON_SubD::SubDType::Unset);
-}
-
-ON_SubD::SubDType ON_SubD::DefaultSubDType()
-{
- return ON_SubD::SubDType::QuadCatmullClark;
-}
-
-
-unsigned int ON_SubD::FacetEdgeCount(
- ON_SubD::FacetType facet_type
- )
-{
- if (ON_SubD::FacetType::Quad == facet_type)
- return 4;
- if (ON_SubD::FacetType::Tri == facet_type)
- return 3;
- return 0;
-}
-
-unsigned int ON_SubD::FacetEdgeCount(
- ON_SubD::SubDType subdivision_type
- )
-{
- if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type)
- return 4;
- if (ON_SubD::SubDType::TriLoopWarren == subdivision_type)
- return 3;
- return 0;
-
-}
-
unsigned int ON_SubDSectorType::SectorPointRingCountFromEdgeCount(
- ON_SubD::SubDType subd_type,
ON_SubD::VertexTag vertex_tag,
unsigned int sector_edge_count
)
@@ -200,33 +121,26 @@ unsigned int ON_SubDSectorType::SectorPointRingCountFromEdgeCount(
if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag)
{
// interior vertex
- if (ON_SubD::SubDType::QuadCatmullClark == subd_type)
- return (2 * sector_edge_count + 1);
- if (ON_SubD::SubDType::TriLoopWarren == subd_type)
- return (sector_edge_count + 1);
+ return (2 * sector_edge_count + 1);
}
if (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag)
{
// boundary vertex
- if (ON_SubD::SubDType::QuadCatmullClark == subd_type)
- return (2 * sector_edge_count);
- if (ON_SubD::SubDType::TriLoopWarren == subd_type)
- return (sector_edge_count + 1);
+ return (2 * sector_edge_count);
}
}
return ON_SUBD_RETURN_ERROR(0);
}
unsigned int ON_SubDSectorType::SectorPointRingCountFromFaceCount(
- ON_SubD::SubDType subd_type,
ON_SubD::VertexTag vertex_tag,
unsigned int sector_face_count
)
{
unsigned int sector_edge_count = ON_SubDSectorType::SectorEdgeCountFromFaceCount(vertex_tag,sector_face_count);
return (sector_edge_count > 0)
- ? ON_SubDSectorType::SectorPointRingCountFromEdgeCount(subd_type,vertex_tag,sector_edge_count)
+ ? ON_SubDSectorType::SectorPointRingCountFromEdgeCount(vertex_tag,sector_edge_count)
: ON_SUBD_RETURN_ERROR(0);
}
@@ -277,13 +191,45 @@ unsigned int ON_SubDSectorType::MinimumSectorFaceCount(
ON_SubD::VertexTag vertex_tag
)
{
- if (ON_SubD::VertexTag::Smooth == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag)
- return 3; // can be reduced to 2 after calculating special case matrix and eigenvalues
- if (ON_SubD::VertexTag::Corner == vertex_tag)
- return 1;
- if (ON_SubD::VertexTag::Crease == vertex_tag)
- return 1;
- return ON_UNSET_UINT_INDEX;
+ unsigned int minimum_sector_face_count;
+ switch (vertex_tag)
+ {
+ case ON_SubD::VertexTag::Unset:
+ ON_SUBD_ERROR("Unset tag.");
+ minimum_sector_face_count = ON_UNSET_UINT_INDEX;
+ break;
+
+ case ON_SubD::VertexTag::Smooth:
+ // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Smooth
+ // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients()
+ // for more details on how this case is handled.
+ minimum_sector_face_count = 2; // 3 without special case handling
+ break;
+
+ case ON_SubD::VertexTag::Crease:
+ // A "wire" crease can have zero faces - this is the minimum when faces exist
+ minimum_sector_face_count = 1;
+ break;
+
+ case ON_SubD::VertexTag::Corner:
+ // A "wire" corner can have zero faces - this is the minimum when faces exist
+ minimum_sector_face_count = 1;
+ break;
+
+ case ON_SubD::VertexTag::Dart:
+ // April 2019 Dale Lear - Catmull Clark Valence 2 Evaluation: Dart
+ // See comments in ON_SubDSectorType::GetSurfaceEvaluationCoefficients()
+ // for more details on how this case is handled.
+ minimum_sector_face_count = 2; // 3 without special case handling
+ break;
+
+ default:
+ ON_SUBD_ERROR("Invalid tag.");
+ minimum_sector_face_count = ON_UNSET_UINT_INDEX;
+ break;
+ }
+
+ return minimum_sector_face_count;
}
bool ON_SubD::IsValidSectorEdgeCount(
@@ -302,42 +248,6 @@ bool ON_SubD::IsValidSectorFaceCount(
return (sector_face_count >= ON_SubDSectorType::MinimumSectorFaceCount(vertex_tag) && sector_face_count <= ON_SubDVertex::MaximumFaceCount);
}
-bool ON_SubD::IsQuadOrTriFacetType(
- ON_SubD::FacetType facet_type
- )
-{
- return (ON_SubD::FacetType::Quad == facet_type || ON_SubD::FacetType::Tri == facet_type);
-}
-
-bool ON_SubD::IsQuadOrTriSubDType(
- ON_SubD::SubDType subdivision_type
- )
-{
- if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type || ON_SubD::SubDType::CustomQuad == subdivision_type)
- return true;
- if (ON_SubD::SubDType::TriLoopWarren == subdivision_type || ON_SubD::SubDType::CustomTri == subdivision_type)
- return true;
- return false;
-}
-
-ON_SubD::FacetType ON_SubD::FacetTypeFromSubDType(
- ON_SubD::SubDType subdivision_type
- )
-{
- if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type || ON_SubD::SubDType::CustomQuad == subdivision_type)
- return ON_SubD::FacetType::Quad;
- if (ON_SubD::SubDType::TriLoopWarren == subdivision_type || ON_SubD::SubDType::CustomTri == subdivision_type)
- return ON_SubD::FacetType::Tri;
- return ON_SubD::FacetType::Unset;
-}
-
-bool ON_SubD::PointRingHasFacePoints(
- ON_SubD::SubDType subdivision_type
- )
-{
- return (ON_SubD::SubDType::QuadCatmullClark == subdivision_type || ON_SubD::SubDType::CustomQuad == subdivision_type);
-}
-
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDVertexPtr
@@ -348,43 +258,48 @@ bool ON_SubDVertexPtr::IsNull() const
return (nullptr == ON_SUBD_VERTEX_POINTER(m_ptr));
}
+bool ON_SubDVertexPtr::IsNotNull() const
+{
+ return (nullptr != ON_SUBD_VERTEX_POINTER(m_ptr));
+}
+
class ON_SubDVertex* ON_SubDVertexPtr::Vertex() const
{
return ON_SUBD_VERTEX_POINTER(m_ptr);
}
-ON__UINT_PTR ON_SubDVertexPtr::VertexPtrMark() const
+ON__UINT_PTR ON_SubDVertexPtr::VertexDirection() const
{
- return ON_SUBD_VERTEX_MARK(m_ptr);
+ return ON_SUBD_VERTEX_DIRECTION(m_ptr);
}
-ON_ComponentStatus ON_SubDVertexPtr::Status() const
+const ON_ComponentStatus ON_SubDVertexPtr::Status() const
{
const ON_SubDVertex* vertex = ON_SUBD_VERTEX_POINTER(m_ptr);
return (nullptr == vertex) ? ON_ComponentStatus::NoneSet : vertex->m_status;
}
-class ON_SubDVertexPtr ON_SubDVertexPtr::Create(
+const ON_SubDVertexPtr ON_SubDVertexPtr::Create(
const class ON_SubDVertex* vertex
)
{
return ON_SubDVertexPtr::Create(vertex,0);
}
-class ON_SubDVertexPtr ON_SubDVertexPtr::Create(
+const ON_SubDVertexPtr ON_SubDVertexPtr::Create(
const class ON_SubDVertex* vertex,
ON__UINT_PTR vertex_mark
)
{
- ON_SubDVertexPtr vptr = { (ON__UINT_PTR)vertex | (vertex_mark & ON_SUBD_ELEMENT_MARK_MASK) };
+ ON_SubDVertexPtr vptr = { (ON__UINT_PTR)vertex | (vertex_mark & ON_SUBD_COMPONENT_DIRECTION_MASK) };
return vptr;
}
-class ON_SubDVertexPtr ON_SubDVertexPtr::Create(
+const ON_SubDVertexPtr ON_SubDVertexPtr::Create(
const class ON_SubDComponentPtr& vertex_element
)
{
- return ON_SubDVertexPtr::Create(vertex_element.Vertex(), vertex_element.ComponentMark());
+ return ON_SubDVertexPtr::Create(vertex_element.Vertex(), vertex_element.ComponentDirection());
}
//////////////////////////////////////////////////////////////////////////
@@ -398,11 +313,59 @@ bool ON_SubDEdgePtr::IsNull() const
return (nullptr == ON_SUBD_EDGE_POINTER(m_ptr));
}
+bool ON_SubDEdgePtr::IsNotNull() const
+{
+ return (nullptr != ON_SUBD_EDGE_POINTER(m_ptr));
+}
+
+bool ON_SubDEdgePtr::IsNotNullAndVerticesAreNotNull() const
+{
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
+ return (nullptr != e && nullptr != e->m_vertex[0] && nullptr != e->m_vertex[1]);
+}
+
+
class ON_SubDEdge* ON_SubDEdgePtr::Edge() const
{
return ON_SUBD_EDGE_POINTER(m_ptr);
}
+unsigned int ON_SubDEdgePtr::EdgeId() const
+{
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
+ return (nullptr != e) ? e->m_id : 0U;
+}
+
+unsigned int ON_SubDEdgePtr::EdgeFaceCount() const
+{
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
+ return (nullptr != e) ? ((unsigned int)e->m_face_count) : 0U;
+}
+
+bool ON_SubDEdgePtr::EdgeIsSmooth() const
+{
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
+ return (nullptr != e) ? e->IsSmooth() : false;
+}
+
+bool ON_SubDEdgePtr::EdgeIsCrease() const
+{
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
+ return (nullptr != e) ? e->IsCrease() : false;
+}
+
+bool ON_SubDEdgePtr::EdgeIsHardCrease() const
+{
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
+ return (nullptr != e) ? e->IsHardCrease() : false;
+}
+
+bool ON_SubDEdgePtr::EdgeIsDartCrease() const
+{
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
+ return (nullptr != e) ? e->IsDartCrease() : false;
+}
+
ON__UINT_PTR ON_SubDEdgePtr::EdgeDirection() const
{
return ON_SUBD_EDGE_DIRECTION(m_ptr);
@@ -426,31 +389,56 @@ const class ON_SubDVertex* ON_SubDEdgePtr::RelativeVertex(
return nullptr;
}
-ON_ComponentStatus ON_SubDEdgePtr::Status() const
+const ON_3dVector ON_SubDEdgePtr::RelativeDirection() const
+{
+ for (;;)
+ {
+ const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr);
+ if (nullptr == edge)
+ break;
+ if (nullptr == edge->m_vertex[0] || nullptr == edge->m_vertex[1])
+ break;
+ const int i0 = (0 != ON_SUBD_EDGE_DIRECTION(m_ptr)) ? 1 : 0;
+ const ON_3dPoint P0(edge->m_vertex[i0]->m_P);
+ const ON_3dPoint P1(edge->m_vertex[1-i0]->m_P);
+ return (P1 - P0);
+ }
+ return ON_3dVector::NanVector;
+}
+
+const ON_ComponentStatus ON_SubDEdgePtr::Status() const
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr);
return (nullptr == edge) ? ON_ComponentStatus::NoneSet : edge->m_status;
}
-ON_SubDEdgePtr ON_SubDEdgePtr::Reversed() const
+const ON_SubDEdgePtr ON_SubDEdgePtr::Reversed() const
{
return ON_SubDEdgePtr::Create(ON_SUBD_EDGE_POINTER(m_ptr), 1 - (m_ptr & 1));
}
-class ON_SubDEdgePtr ON_SubDEdgePtr::Create(
+const ON_SubDEdgePtr ON_SubDEdgePtr::Create(
+ const class ON_SubDEdge* edge
+ )
+{
+ ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge };
+ return eptr;
+}
+
+const ON_SubDEdgePtr ON_SubDEdgePtr::Create(
const class ON_SubDEdge* edge,
ON__UINT_PTR direction
)
{
- ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge | (direction & ON_SUBD_ELEMENT_MARK_MASK) };
+ ON_SubDEdgePtr eptr = { (ON__UINT_PTR)edge | (direction & ON_SUBD_COMPONENT_DIRECTION_MASK) };
return eptr;
}
-ON_SubDEdgePtr ON_SubDEdgePtr::Create(
+const ON_SubDEdgePtr ON_SubDEdgePtr::Create(
const class ON_SubDComponentPtr& edge_element
)
{
- return ON_SubDEdgePtr::Create(edge_element.Edge(), edge_element.ComponentMark());
+ return ON_SubDEdgePtr::Create(edge_element.Edge(), edge_element.ComponentDirection());
}
//////////////////////////////////////////////////////////////////////////
@@ -463,39 +451,95 @@ bool ON_SubDFacePtr::IsNull() const
return (nullptr == ON_SUBD_FACE_POINTER(m_ptr));
}
+bool ON_SubDFacePtr::IsNotNull() const
+{
+ return (nullptr != ON_SUBD_FACE_POINTER(m_ptr));
+}
+
ON_SubDFace* ON_SubDFacePtr::Face() const
{
return ON_SUBD_FACE_POINTER(m_ptr);
}
+unsigned int ON_SubDFacePtr::FaceId() const
+{
+ const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr);
+ return (nullptr != f) ? f->m_id : 0U;
+}
+
+unsigned int ON_SubDFacePtr::FaceEdgeCount() const
+{
+ const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr);
+ return (nullptr != f) ? ((unsigned int)f->m_edge_count) : 0U;
+}
+
ON__UINT_PTR ON_SubDFacePtr::FaceDirection() const
{
return ON_SUBD_FACE_DIRECTION(m_ptr);
}
-ON_ComponentStatus ON_SubDFacePtr::Status() const
+const ON_ComponentStatus ON_SubDFacePtr::Status() const
{
const ON_SubDFace* face = ON_SUBD_FACE_POINTER(m_ptr);
return (nullptr == face) ? ON_ComponentStatus::NoneSet : face->m_status;
}
-ON_SubDFacePtr ON_SubDFacePtr::Create(
+const ON_SubDFacePtr ON_SubDFacePtr::Create(
const class ON_SubDFace* face,
ON__UINT_PTR direction
)
{
- ON_SubDFacePtr fptr = { (ON__UINT_PTR)face | (direction & ON_SUBD_ELEMENT_MARK_MASK) };
+ ON_SubDFacePtr fptr = { (ON__UINT_PTR)face | (direction & ON_SUBD_COMPONENT_DIRECTION_MASK) };
return fptr;
}
-ON_SubDFacePtr ON_SubDFacePtr::Create(
+const ON_SubDFacePtr ON_SubDFacePtr::Create(
const class ON_SubDComponentPtr& face_element
)
{
- return ON_SubDFacePtr::Create(face_element.Face(), face_element.ComponentMark());
+ return ON_SubDFacePtr::Create(face_element.Face(), face_element.ComponentDirection());
}
+int ON_SubDFacePtr::Compare(
+ const ON_SubDFacePtr* lhs,
+ const ON_SubDFacePtr* rhs
+)
+{
+ if ( nullptr == lhs )
+ return 1;
+ if ( nullptr == rhs )
+ return -1;
+
+ if (lhs->m_ptr < rhs->m_ptr)
+ return -1;
+ if (lhs->m_ptr > rhs->m_ptr)
+ return 1;
+
+ return 0;
+}
+
+int ON_SubDFacePtr::CompareFacePointer(
+ const ON_SubDFacePtr* lhs,
+ const ON_SubDFacePtr* rhs
+)
+{
+ if (lhs == rhs)
+ return 0;
+ if ( nullptr == lhs )
+ return 1;
+ if ( nullptr == rhs )
+ return -1;
+
+ const ON__UINT_PTR lhs_ptr = (lhs->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
+ const ON__UINT_PTR rhs_ptr = (rhs->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
+ if (lhs_ptr < rhs_ptr)
+ return -1;
+ if (lhs_ptr > rhs_ptr)
+ return 1;
+ return 0;
+}
+
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDComponentPtr
@@ -503,45 +547,122 @@ ON_SubDFacePtr ON_SubDFacePtr::Create(
bool ON_SubDComponentPtr::IsNull() const
{
- return (0 == (ON_SUBD_ELEMENT_POINTER_MASK && m_ptr));
+ return (0 == (ON_SUBD_COMPONENT_POINTER_MASK && m_ptr));
}
bool ON_SubDComponentPtr::IsNotNull() const
{
- if (nullptr != ON_SUBD_EDGE_POINTER(m_ptr))
+ if (nullptr != ON_SUBD_COMPONENT_POINTER(m_ptr))
{
- switch (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr)
+ switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)
{
- case ON_SUBD_ELEMENT_TYPE_VERTEX:
- case ON_SUBD_ELEMENT_TYPE_EDGE:
- case ON_SUBD_ELEMENT_TYPE_FACE:
+ case ON_SUBD_COMPONENT_TYPE_VERTEX:
+ case ON_SUBD_COMPONENT_TYPE_EDGE:
+ case ON_SUBD_COMPONENT_TYPE_FACE:
return true;
}
}
return false;
}
+unsigned int ON_SubDComponentPtr::ComponentId() const
+{
+ const ON_SubDComponentBase* c = this->ComponentBase();
+ return (nullptr != c) ? c->m_id : 0U;
+}
+
+const ON_3dPoint ON_SubDComponentPtr::ControlNetCenterPoint() const
+{
+ switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)
+ {
+ case ON_SUBD_COMPONENT_TYPE_VERTEX:
+ {
+ const ON_SubDVertex* v = Vertex();
+ if (nullptr != v)
+ return v->ControlNetPoint();
+ }
+ break;
+ case ON_SUBD_COMPONENT_TYPE_EDGE:
+ {
+ const ON_SubDEdge* e = Edge();
+ if (nullptr != e)
+ return e->ControlNetCenterPoint();
+ }
+ break;
+ case ON_SUBD_COMPONENT_TYPE_FACE:
+ {
+ const ON_SubDFace* f = Face();
+ if (nullptr != f)
+ return f->ControlNetCenterPoint();
+ }
+ break;
+ }
+ return ON_3dPoint::NanPoint;
+}
+
+const ON_BoundingBox ON_SubDComponentPtr::ControlNetBoundingBox() const
+{
+ switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)
+ {
+ case ON_SUBD_COMPONENT_TYPE_VERTEX:
+ {
+ const ON_SubDVertex* v = Vertex();
+ if (nullptr != v)
+ return v->ControlNetBoundingBox();
+ }
+ break;
+ case ON_SUBD_COMPONENT_TYPE_EDGE:
+ {
+ const ON_SubDEdge* e = Edge();
+ if (nullptr != e)
+ return e->ControlNetBoundingBox();
+ }
+ break;
+ case ON_SUBD_COMPONENT_TYPE_FACE:
+ {
+ const ON_SubDFace* f = Face();
+ if (nullptr != f)
+ return f->ControlNetBoundingBox();
+ }
+ break;
+ }
+ return ON_BoundingBox::NanBoundingBox;
+}
+
+
+
+ON__UINT16 ON_SubDComponentPtr::Hash16FromTypeAndId() const
+{
+ const ON_SubDComponentBase* c = ComponentBase();
+ return (0 != c)
+ ? ON_CRC16((ON__UINT16)(ON_SUBD_COMPONENT_TYPE_MASK & m_ptr), sizeof(c->m_id), &(c->m_id))
+ : ((ON__UINT16)0U)
+ ;
+}
+
+ON__UINT32 ON_SubDComponentPtr::Hash32FromPointer() const
+{
+ const ON__UINT_PTR ptr = (ON__UINT_PTR)ComponentBase();
+ return ON_CRC32((ON__UINT32)(ON_SUBD_COMPONENT_TYPE_MASK & m_ptr), sizeof(ptr), &ptr);
+}
+
+
ON_SubDComponentPtr::Type ON_SubDComponentPtr::ComponentType() const
{
- switch (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr)
+ switch (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr)
{
- case ON_SUBD_ELEMENT_TYPE_VERTEX:
+ case ON_SUBD_COMPONENT_TYPE_VERTEX:
return ON_SubDComponentPtr::Type::Vertex;
- case ON_SUBD_ELEMENT_TYPE_EDGE:
+ case ON_SUBD_COMPONENT_TYPE_EDGE:
return ON_SubDComponentPtr::Type::Edge;
- case ON_SUBD_ELEMENT_TYPE_FACE:
+ case ON_SUBD_COMPONENT_TYPE_FACE:
return ON_SubDComponentPtr::Type::Face;
}
return ON_SubDComponentPtr::Type::Unset;
}
-ON__UINT_PTR ON_SubDComponentPtr::ComponentMark() const
-{
- return ON_SUBD_ELEMENT_MARK(m_ptr);
-}
-
-ON_ComponentStatus ON_SubDComponentPtr::Status() const
+const ON_ComponentStatus ON_SubDComponentPtr::Status() const
{
switch (ComponentType())
{
@@ -665,29 +786,151 @@ unsigned int ON_SubDComponentPtr::ClearStates(
return ON_SUBD_RETURN_ERROR(0);
}
+bool ON_SubDComponentPtr::Mark() const
+{
+ const ON_SubDComponentBase* c = this->ComponentBase();
+ return (nullptr != c) ? c->m_status.RuntimeMark() : false;
+}
-ON_SubDComponentPtr ON_SubDComponentPtr::ClearMark() const
+bool ON_SubDComponentPtr::ClearMark() const
+{
+ const ON_SubDComponentBase* c = this->ComponentBase();
+ return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false;
+}
+
+bool ON_SubDComponentPtr::SetMark() const
+{
+ const ON_SubDComponentBase* c = this->ComponentBase();
+ return (nullptr != c) ? c->m_status.SetRuntimeMark() : false;
+}
+
+bool ON_SubDComponentPtr::SetMark(
+ bool bMark
+) const
+{
+ const ON_SubDComponentBase* c = this->ComponentBase();
+ return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false;
+}
+
+bool ON_SubDVertexPtr::Mark() const
+{
+ const ON_SubDVertex* c = this->Vertex();
+ return (nullptr != c) ? c->m_status.RuntimeMark() : false;
+}
+
+bool ON_SubDVertexPtr::ClearMark() const
+{
+ const ON_SubDVertex* c = this->Vertex();
+ return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false;
+}
+
+bool ON_SubDVertexPtr::SetMark() const
+{
+ const ON_SubDVertex* c = this->Vertex();
+ return (nullptr != c) ? c->m_status.SetRuntimeMark() : false;
+}
+
+bool ON_SubDVertexPtr::SetMark(
+ bool bMark
+) const
+{
+ const ON_SubDVertex* c = this->Vertex();
+ return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false;
+}
+
+
+bool ON_SubDEdgePtr::Mark() const
+{
+ const ON_SubDEdge* c = this->Edge();
+ return (nullptr != c) ? c->m_status.RuntimeMark() : false;
+}
+
+bool ON_SubDEdgePtr::ClearMark() const
+{
+ const ON_SubDEdge* c = this->Edge();
+ return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false;
+}
+
+bool ON_SubDEdgePtr::SetMark() const
+{
+ const ON_SubDEdge* c = this->Edge();
+ return (nullptr != c) ? c->m_status.SetRuntimeMark() : false;
+}
+
+bool ON_SubDEdgePtr::SetMark(
+ bool bMark
+) const
+{
+ const ON_SubDEdge* c = this->Edge();
+ return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false;
+}
+
+
+bool ON_SubDFacePtr::Mark() const
+{
+ const ON_SubDFace* c = this->Face();
+ return (nullptr != c) ? c->m_status.RuntimeMark() : false;
+}
+
+bool ON_SubDFacePtr::ClearMark() const
+{
+ const ON_SubDFace* c = this->Face();
+ return (nullptr != c) ? c->m_status.ClearRuntimeMark() : false;
+}
+
+bool ON_SubDFacePtr::SetMark() const
+{
+ const ON_SubDFace* c = this->Face();
+ return (nullptr != c) ? c->m_status.SetRuntimeMark() : false;
+}
+
+bool ON_SubDFacePtr::SetMark(
+ bool bMark
+) const
+{
+ const ON_SubDFace* c = this->Face();
+ return (nullptr != c) ? c->m_status.SetRuntimeMark(bMark) : false;
+}
+
+ON__UINT_PTR ON_SubDComponentPtr::ComponentDirection() const
+{
+ return ON_SUBD_COMPONENT_DIRECTION(m_ptr);
+}
+
+const ON_SubDComponentPtr ON_SubDComponentPtr::ClearComponentDirection() const
{
ON_SubDComponentPtr component_ptr = *this;
- component_ptr.m_ptr &= (ON_SUBD_ELEMENT_POINTER_MASK | ON_SUBD_ELEMENT_TYPE_MASK);
+ component_ptr.m_ptr &= (ON_SUBD_COMPONENT_POINTER_MASK | ON_SUBD_COMPONENT_TYPE_MASK);
return component_ptr;
}
-ON_SubDComponentPtr ON_SubDComponentPtr::SetMark() const
+const ON_SubDComponentPtr ON_SubDComponentPtr::SetComponentDirection() const
{
ON_SubDComponentPtr component_ptr = *this;
- component_ptr.m_ptr |= ON_SUBD_ELEMENT_MARK_MASK;
+ component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK;
return component_ptr;
}
-ON_SubDComponentPtr ON_SubDComponentPtr::ToggleMark() const
+const ON_SubDComponentPtr ON_SubDComponentPtr::SetComponentDirection(ON__UINT_PTR dir) const
{
- return (0 != (m_ptr & ON_SUBD_ELEMENT_MARK_MASK)) ? ClearMark() : SetMark();
+ ON_SubDComponentPtr component_ptr = *this;
+ if (0 == dir)
+ component_ptr.m_ptr &= ~((ON__UINT_PTR)ON_SUBD_COMPONENT_DIRECTION_MASK);
+ else if (1 == dir)
+ component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK;
+ else
+ ON_SUBD_ERROR("Invalid dir parameter");
+ return component_ptr;
+}
+
+const ON_SubDComponentPtr ON_SubDComponentPtr::ToggleComponentDirection() const
+{
+ return (0 != (m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK)) ? ClearComponentDirection() : SetComponentDirection();
}
const ON_SubDComponentPtr ON_SubDComponentPtr::CreateNull(
ON_SubDComponentPtr::Type component_type,
- bool bMark
+ ON__UINT_PTR component_direction
)
{
ON_SubDComponentPtr component_ptr;
@@ -697,32 +940,32 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::CreateNull(
component_ptr.m_ptr = 0;
break;
case ON_SubDComponentPtr::Type::Vertex:
- component_ptr.m_ptr = ON_SUBD_ELEMENT_TYPE_VERTEX;
+ component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_VERTEX;
break;
case ON_SubDComponentPtr::Type::Edge:
- component_ptr.m_ptr = ON_SUBD_ELEMENT_TYPE_EDGE;
+ component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_EDGE;
break;
case ON_SubDComponentPtr::Type::Face:
- component_ptr.m_ptr = ON_SUBD_ELEMENT_TYPE_FACE;
+ component_ptr.m_ptr = ON_SUBD_COMPONENT_TYPE_FACE;
break;
default:
component_ptr.m_ptr = 0;
break;
}
- if (bMark)
- component_ptr.m_ptr |= ON_SUBD_ELEMENT_MARK_MASK;
+ if (1 == component_direction)
+ component_ptr.m_ptr |= ON_SUBD_COMPONENT_DIRECTION_MASK;
return component_ptr;
}
class ON_SubDComponentBase* ON_SubDComponentPtr::ComponentBase() const
{
- switch ((ON_SUBD_ELEMENT_TYPE_MASK & m_ptr))
+ switch ((ON_SUBD_COMPONENT_TYPE_MASK & m_ptr))
{
- case ON_SUBD_ELEMENT_TYPE_VERTEX:
- case ON_SUBD_ELEMENT_TYPE_EDGE:
- case ON_SUBD_ELEMENT_TYPE_FACE:
- return ((class ON_SubDComponentBase*)ON_SUBD_ELEMENT_POINTER(m_ptr));
+ case ON_SUBD_COMPONENT_TYPE_VERTEX:
+ case ON_SUBD_COMPONENT_TYPE_EDGE:
+ case ON_SUBD_COMPONENT_TYPE_FACE:
+ return ((class ON_SubDComponentBase*)ON_SUBD_COMPONENT_POINTER(m_ptr));
}
return nullptr;
}
@@ -730,30 +973,30 @@ class ON_SubDComponentBase* ON_SubDComponentPtr::ComponentBase() const
class ON_SubDVertex* ON_SubDComponentPtr::Vertex() const
{
- if (ON_SUBD_ELEMENT_TYPE_VERTEX == (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr))
+ if (ON_SUBD_COMPONENT_TYPE_VERTEX == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr))
return ON_SUBD_VERTEX_POINTER(m_ptr);
return nullptr;
}
class ON_SubDEdge* ON_SubDComponentPtr::Edge() const
{
- if (ON_SUBD_ELEMENT_TYPE_EDGE == (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr))
+ if (ON_SUBD_COMPONENT_TYPE_EDGE == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr))
return ON_SUBD_EDGE_POINTER(m_ptr);
return nullptr;
}
class ON_SubDFace* ON_SubDComponentPtr::Face() const
{
- if (ON_SUBD_ELEMENT_TYPE_FACE == (ON_SUBD_ELEMENT_TYPE_MASK & m_ptr))
+ if (ON_SUBD_COMPONENT_TYPE_FACE == (ON_SUBD_COMPONENT_TYPE_MASK & m_ptr))
return ON_SUBD_FACE_POINTER(m_ptr);
return nullptr;
}
-ON_SubDVertexPtr ON_SubDComponentPtr::VertexPtr() const
+const ON_SubDVertexPtr ON_SubDComponentPtr::VertexPtr() const
{
- ON__UINT_PTR element_type = ON_SUBD_ELEMENT_TYPE(m_ptr);
- if ( ON_SUBD_ELEMENT_TYPE_VERTEX == element_type)
- return ON_SubDVertexPtr::Create(Vertex(), ComponentMark());
+ ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr);
+ if ( ON_SUBD_COMPONENT_TYPE_VERTEX == element_type)
+ return ON_SubDVertexPtr::Create(Vertex(), ComponentDirection());
if ( 0 == element_type )
return ON_SubDVertexPtr::Null;
@@ -761,11 +1004,11 @@ ON_SubDVertexPtr ON_SubDComponentPtr::VertexPtr() const
return ON_SUBD_RETURN_ERROR(ON_SubDVertexPtr::Null);
}
-ON_SubDEdgePtr ON_SubDComponentPtr::EdgePtr() const
+const ON_SubDEdgePtr ON_SubDComponentPtr::EdgePtr() const
{
- ON__UINT_PTR element_type = ON_SUBD_ELEMENT_TYPE(m_ptr);
- if ( ON_SUBD_ELEMENT_TYPE_EDGE == element_type)
- return ON_SubDEdgePtr::Create(Edge(), ComponentMark());
+ ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr);
+ if ( ON_SUBD_COMPONENT_TYPE_EDGE == element_type)
+ return ON_SubDEdgePtr::Create(Edge(), ComponentDirection());
if ( 0 == element_type )
return ON_SubDEdgePtr::Null;
@@ -773,11 +1016,11 @@ ON_SubDEdgePtr ON_SubDComponentPtr::EdgePtr() const
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
}
-ON_SubDFacePtr ON_SubDComponentPtr::FacePtr() const
+const ON_SubDFacePtr ON_SubDComponentPtr::FacePtr() const
{
- ON__UINT_PTR element_type = ON_SUBD_ELEMENT_TYPE(m_ptr);
- if ( ON_SUBD_ELEMENT_TYPE_FACE == element_type)
- return ON_SubDFacePtr::Create(Face(), ComponentMark());
+ ON__UINT_PTR element_type = ON_SUBD_COMPONENT_TYPE(m_ptr);
+ if ( ON_SUBD_COMPONENT_TYPE_FACE == element_type)
+ return ON_SubDFacePtr::Create(Face(), ComponentDirection());
if ( 0 == element_type )
return ON_SubDFacePtr::Null;
@@ -791,7 +1034,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
{
if (nullptr != vertex)
{
- ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_ELEMENT_TYPE_VERTEX };
+ ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_COMPONENT_TYPE_VERTEX };
return vptr;
}
return ON_SubDComponentPtr::Null;
@@ -803,7 +1046,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
{
if (nullptr != edge)
{
- ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | ON_SUBD_ELEMENT_TYPE_EDGE };
+ ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | ON_SUBD_COMPONENT_TYPE_EDGE };
return eptr;
}
return ON_SubDComponentPtr::Null;
@@ -816,7 +1059,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
{
if (nullptr != face)
{
- ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | ON_SUBD_ELEMENT_TYPE_FACE };
+ ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | ON_SUBD_COMPONENT_TYPE_FACE };
return fptr;
}
return ON_SubDComponentPtr::Null;
@@ -829,7 +1072,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
{
if (nullptr != vertex)
{
- ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_ELEMENT_TYPE_VERTEX | (vertex_direction & ON_SUBD_ELEMENT_MARK_MASK) };
+ ON_SubDComponentPtr vptr = { (ON__UINT_PTR)vertex | ON_SUBD_COMPONENT_TYPE_VERTEX | (vertex_direction & ON_SUBD_COMPONENT_DIRECTION_MASK) };
return vptr;
}
return ON_SubDComponentPtr::Null;
@@ -842,7 +1085,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
{
if (nullptr != edge)
{
- ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | (ON_SUBD_ELEMENT_TYPE_EDGE | (edge_direction & ON_SUBD_ELEMENT_MARK_MASK)) };
+ ON_SubDComponentPtr eptr = { (ON__UINT_PTR)edge | (ON_SUBD_COMPONENT_TYPE_EDGE | (edge_direction & ON_SUBD_COMPONENT_DIRECTION_MASK)) };
return eptr;
}
return ON_SubDComponentPtr::Null;
@@ -855,7 +1098,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
{
if (nullptr != face)
{
- ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | (ON_SUBD_ELEMENT_TYPE_FACE | (face_direction & ON_SUBD_ELEMENT_MARK_MASK)) };
+ ON_SubDComponentPtr fptr = { (ON__UINT_PTR)face | (ON_SUBD_COMPONENT_TYPE_FACE | (face_direction & ON_SUBD_COMPONENT_DIRECTION_MASK)) };
return fptr;
}
return ON_SubDComponentPtr::Null;
@@ -865,7 +1108,7 @@ const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
ON_SubDVertexPtr vertexptr
)
{
- return Create(vertexptr.Vertex(), vertexptr.VertexPtrMark());
+ return Create(vertexptr.Vertex(), vertexptr.VertexDirection());
}
const ON_SubDComponentPtr ON_SubDComponentPtr::Create(
@@ -920,98 +1163,215 @@ int ON_SubDComponentPtr::CompareType(
return ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType());
}
-int ON_SubDComponentPtr::Compare(
+
+int ON_SubDComponentPtr::CompareComponent(
const ON_SubDComponentPtr* a,
const ON_SubDComponentPtr* b
- )
+)
{
- if ( a == b )
+ if (a == b)
return 0;
- if ( nullptr == a )
- return 1;
- if ( nullptr == b )
- return -1;
- int rc = ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType());
+ const int rc = ON_SubDComponentPtr::CompareComponentPtrType(a->ComponentType(), b->ComponentType());
if (0 == rc)
{
- if ( a->m_ptr < b->m_ptr )
+ const ON__UINT_PTR x = a->m_ptr;
+ const ON__UINT_PTR y = b->m_ptr;
+ if (x < y)
return -1;
- if ( a->m_ptr > b->m_ptr )
+ if (x > y)
+ return 1;
+ }
+ return rc;
+}
+
+int ON_SubDComponentPtr::CompareComponentAndDirection(
+ const ON_SubDComponentPtr* a,
+ const ON_SubDComponentPtr* b
+)
+{
+ if (a == b)
+ return 0;
+ const int rc = ON_SubDComponentPtr::CompareComponent(a, b);
+ if (0 == rc)
+ {
+ const ON__UINT_PTR x = (a->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
+ const ON__UINT_PTR y = (b->m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
+ if (x < y)
+ return -1;
+ if (x > y)
return 1;
}
return rc;
}
-int ON_SubDComponentPoint::CompareComponentPtr(
+int ON_SubDComponentPoint::CompareComponentAndDirection(
const ON_SubDComponentPoint* a,
const ON_SubDComponentPoint* b
)
{
if ( a == b)
- return 0;
-
+ return 0;
if ( nullptr == a)
return 1; // nullptr > non-null pointer.
-
if ( nullptr == b)
return -1; // nullptr > non-null pointer.
+ // 1st: compare component type
// unset < vertex < edge < face
- ON__UINT_PTR x = (ON_SUBD_ELEMENT_TYPE_MASK & a->m_component_ptr.m_ptr);
- ON__UINT_PTR y = (ON_SUBD_ELEMENT_TYPE_MASK & b->m_component_ptr.m_ptr);
+ ON__UINT_PTR x = (ON_SUBD_COMPONENT_TYPE_MASK & a->m_component_ptr.m_ptr);
+ ON__UINT_PTR y = (ON_SUBD_COMPONENT_TYPE_MASK & b->m_component_ptr.m_ptr);
if ( x < y )
return -1;
if ( x > y )
return 1;
- if ( a->m_component_ptr.m_ptr < b->m_component_ptr.m_ptr )
+ // 2nd: compare component pointer
+ x = (a->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
+ y = (b->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_POINTER_MASK);
+ if (x < y)
return -1;
- if ( a->m_component_ptr.m_ptr > b->m_component_ptr.m_ptr )
+ if (x > y)
+ return 1;
+
+ // 3rd: compare component direction flag
+ x = (a->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK);
+ y = (b->m_component_ptr.m_ptr & ON_SUBD_COMPONENT_DIRECTION_MASK);
+ if (x < y)
+ return -1;
+ if (x > y)
return 1;
return 0;
}
+
+const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::Create(ON_SubDComponentPtr first_ptr, ON_SubDComponentPtr second_ptr)
+{
+ ON_SubDComponentPtrPair p;
+ p.m_pair[0] = first_ptr;
+ p.m_pair[1] = second_ptr;
+ return p;
+}
+
+int ON_SubDComponentPtrPair::CompareComponent(
+ const ON_SubDComponentPtrPair* lhs,
+ const ON_SubDComponentPtrPair* rhs
+)
+{
+ if (lhs == rhs)
+ return 0;
+ // nulls sort to end.
+ if (nullptr == rhs)
+ return -1;
+ if (nullptr == lhs)
+ return 1;
+ int rc = ON_SubDComponentPtr::CompareComponent(&lhs->m_pair[0], &rhs->m_pair[0]);
+ if (0 == rc)
+ rc = ON_SubDComponentPtr::CompareComponent(&lhs->m_pair[1], &rhs->m_pair[1]);
+ return rc;
+}
+
+int ON_SubDComponentPtrPair::CompareComponentAndDirection(
+ const ON_SubDComponentPtrPair* lhs,
+ const ON_SubDComponentPtrPair* rhs
+)
+{
+ if (lhs == rhs)
+ return 0;
+ // nulls sort to end.
+ if (nullptr == rhs)
+ return -1;
+ if (nullptr == lhs)
+ return 1;
+ int rc = ON_SubDComponentPtr::CompareComponentAndDirection(&lhs->m_pair[0], &rhs->m_pair[0]);
+ if (0 == rc)
+ rc = ON_SubDComponentPtr::CompareComponentAndDirection(&lhs->m_pair[1], &rhs->m_pair[1]);
+ return rc;
+}
+
+
+int ON_SubDComponentPtrPair::CompareFirstPointer(
+ const ON_SubDComponentPtrPair* lhs,
+ const ON_SubDComponentPtrPair* rhs
+)
+{
+ if (lhs == rhs)
+ return 0;
+ // nulls sort to end.
+ if (nullptr == rhs)
+ return -1;
+ if (nullptr == lhs)
+ return 1;
+ const ON__UINT_PTR lhs_ptr = (ON_SUBD_COMPONENT_POINTER_MASK & lhs->m_pair[0].m_ptr);
+ const ON__UINT_PTR rhs_ptr = (ON_SUBD_COMPONENT_POINTER_MASK & rhs->m_pair[0].m_ptr);
+ if (lhs_ptr < rhs_ptr)
+ return -1;
+ if (lhs_ptr > rhs_ptr)
+ return 1;
+ return 0;
+}
+
+ON_SubDComponentPtr::Type ON_SubDComponentPtrPair::ComponentType() const
+{
+ const ON_SubDComponentPtr::Type type = m_pair[0].ComponentType();
+ return (type == m_pair[1].ComponentType()) ? type : ON_SubDComponentPtr::Type::Unset;
+}
+
+const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::SwapPair() const
+{
+ return ON_SubDComponentPtrPair::Create(m_pair[1], m_pair[0]);
+}
+
+const ON_SubDComponentPtr ON_SubDComponentPtrPair::First() const
+{
+ return m_pair[0];
+}
+
+const ON_SubDComponentPtr ON_SubDComponentPtrPair::Second() const
+{
+ return m_pair[1];
+}
+
//////////////////////////////////////////////////////////////////////////
//
-// ON_SubDFromMeshOptions
+// ON_ToSubDParameters
//
-ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::ConvexCornerOptionFromUnsigned(
+ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::ConvexCornerOptionFromUnsigned(
unsigned int convex_corner_option_as_unsigned
)
{
switch (convex_corner_option_as_unsigned)
{
- case (unsigned int)ON_SubDFromMeshOptions::ConvexCornerOption::Unset:
- return ON_SubDFromMeshOptions::ConvexCornerOption::Unset;
- case (unsigned int)ON_SubDFromMeshOptions::ConvexCornerOption::None:
- return ON_SubDFromMeshOptions::ConvexCornerOption::None;
- case (unsigned int)ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner:
- return ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner;
+ case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::Unset:
+ return ON_ToSubDParameters::ConvexCornerOption::Unset;
+ case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::None:
+ return ON_ToSubDParameters::ConvexCornerOption::None;
+ case (unsigned int)ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner:
+ return ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner;
default:
break;
}
- return ON_SubDFromMeshOptions::ConvexCornerOption::Unset;
+ return ON_ToSubDParameters::ConvexCornerOption::Unset;
}
-void ON_SubDFromMeshOptions::SetConvexCornerOption(
- ON_SubDFromMeshOptions::ConvexCornerOption convex_corner_option
+void ON_ToSubDParameters::SetConvexCornerOption(
+ ON_ToSubDParameters::ConvexCornerOption convex_corner_option
)
{
- m_convex_corner_option = ON_SubDFromMeshOptions::ConvexCornerOptionFromUnsigned((unsigned int)convex_corner_option);
+ m_convex_corner_option = ON_ToSubDParameters::ConvexCornerOptionFromUnsigned((unsigned int)convex_corner_option);
}
-ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::ConvexCornerTest() const
+ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::ConvexCornerTest() const
{
switch (m_convex_corner_option)
{
- case ON_SubDFromMeshOptions::ConvexCornerOption::Unset:
- case ON_SubDFromMeshOptions::ConvexCornerOption::None:
+ case ON_ToSubDParameters::ConvexCornerOption::Unset:
+ case ON_ToSubDParameters::ConvexCornerOption::None:
return m_convex_corner_option;
- case ON_SubDFromMeshOptions::ConvexCornerOption::AtMeshCorner:
+ case ON_ToSubDParameters::ConvexCornerOption::AtMeshCorner:
if ( m_maximum_convex_corner_edge_count >= 2
&& m_maximum_convex_corner_edge_count <= ON_SubDVertex::MaximumEdgeCount
&& m_maximum_convex_corner_angle_radians >= 0.0
@@ -1021,10 +1381,10 @@ ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::ConvexCornerT
break;
}
- return ON_SubDFromMeshOptions::ConvexCornerOption::Unset;
+ return ON_ToSubDParameters::ConvexCornerOption::Unset;
}
-void ON_SubDFromMeshOptions::SetMaximumConvexCornerEdgeCount(
+void ON_ToSubDParameters::SetMaximumConvexCornerEdgeCount(
unsigned int maximum_convex_corner_edge_count
)
{
@@ -1032,12 +1392,12 @@ void ON_SubDFromMeshOptions::SetMaximumConvexCornerEdgeCount(
m_maximum_convex_corner_edge_count = (unsigned short)maximum_convex_corner_edge_count;
}
-unsigned int ON_SubDFromMeshOptions::MaximumConvexCornerEdgeCount() const
+unsigned int ON_ToSubDParameters::MaximumConvexCornerEdgeCount() const
{
return m_maximum_convex_corner_edge_count;
}
-void ON_SubDFromMeshOptions::SetMaximumConvexCornerAngleRadians(
+void ON_ToSubDParameters::SetMaximumConvexCornerAngleRadians(
double maximum_convex_corner_angle_radians
)
{
@@ -1045,13 +1405,13 @@ void ON_SubDFromMeshOptions::SetMaximumConvexCornerAngleRadians(
m_maximum_convex_corner_angle_radians = maximum_convex_corner_angle_radians;
}
-double ON_SubDFromMeshOptions::MaximumConvexCornerAngleRadians() const
+double ON_ToSubDParameters::MaximumConvexCornerAngleRadians() const
{
return m_maximum_convex_corner_angle_radians;
}
-ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::CopyConvexCornerTest(
- ON_SubDFromMeshOptions source_parameters
+ON_ToSubDParameters::ConvexCornerOption ON_ToSubDParameters::CopyConvexCornerTest(
+ ON_ToSubDParameters source_parameters
)
{
SetConvexCornerOption(source_parameters.ConvexCornerTest());
@@ -1060,19 +1420,67 @@ ON_SubDFromMeshOptions::ConvexCornerOption ON_SubDFromMeshOptions::CopyConvexCor
return ConvexCornerTest();
}
-void ON_SubDFromMeshOptions::SetInteriorCreaseOption(
- ON_SubDFromMeshOptions::InteriorCreaseOption interior_crease_option
- )
+bool ON_ToSubDParameters::InterpolateMeshVertices() const
{
- m_interior_crease_option = ON_SubDFromMeshOptions::InteriorCreaseOptionFromUnsigned((unsigned int)interior_crease_option);
+ return m_bInterpolateMeshVertices;
}
-ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::InteriorCreaseTest() const
+void ON_ToSubDParameters::SetInterpolateMeshVertices(
+ bool bInterpolateMeshVertices
+)
+{
+ // Not supported in free opennurbs
+ m_bInterpolateMeshVertices = false;
+}
+
+bool ON_ToSubDParameters::MergeColinearBoundaryEdges() const
+{
+ // clear bit means true, set bit means false
+ return (0 == (ON_ToSubDParameters::MergeColinearBoundaryEdgesMask & m_merge_edges_bits));
+}
+
+void ON_ToSubDParameters::SetMergeColinearBoundaryEdges(
+ bool bAllowColinearBoundaryEdges
+)
+{
+ const unsigned char mask = ON_ToSubDParameters::MergeColinearBoundaryEdgesMask;
+ if (false == bAllowColinearBoundaryEdges)
+ m_merge_edges_bits |= mask; // set bit
+ else
+ m_merge_edges_bits &= ~mask; // clear bit
+}
+
+bool ON_ToSubDParameters::MergeColinearInteriorEdges() const
+{
+ // clear bit means true, set bit means false
+ return (0 == (ON_ToSubDParameters::MergeColinearInteriorEdgesMask & m_merge_edges_bits));
+}
+
+void ON_ToSubDParameters::SetMergeColinearInteriorEdges(
+ bool bAllowColinearInteriorEdges
+)
+{
+ const unsigned char mask = ON_ToSubDParameters::MergeColinearInteriorEdgesMask;
+ if (false == bAllowColinearInteriorEdges)
+ m_merge_edges_bits |= mask; // set bit
+ else
+ m_merge_edges_bits &= ~mask; // clear bit
+}
+
+
+void ON_ToSubDParameters::SetInteriorCreaseOption(
+ ON_ToSubDParameters::InteriorCreaseOption interior_crease_option
+ )
+{
+ m_interior_crease_option = ON_ToSubDParameters::InteriorCreaseOptionFromUnsigned((unsigned int)interior_crease_option);
+}
+
+ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::InteriorCreaseTest() const
{
return m_interior_crease_option;
}
-void ON_SubDFromMeshOptions::SetMinimumCreaseAngleRadians(
+void ON_ToSubDParameters::SetMinimumCreaseAngleRadians(
double minimum_crease_angle_radians
)
{
@@ -1081,13 +1489,13 @@ void ON_SubDFromMeshOptions::SetMinimumCreaseAngleRadians(
}
-double ON_SubDFromMeshOptions::MinimumCreaseAngleRadians() const
+double ON_ToSubDParameters::MinimumCreaseAngleRadians() const
{
return m_minimum_crease_angle_radians;
}
-ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::CopyInteriorCreaseTest(
- ON_SubDFromMeshOptions source_parameters
+ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::CopyInteriorCreaseTest(
+ ON_ToSubDParameters source_parameters
)
{
SetInteriorCreaseOption(source_parameters.InteriorCreaseTest());
@@ -1095,48 +1503,31 @@ ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::CopyInterio
return InteriorCreaseTest();
}
-ON_SubDFromMeshOptions::InteriorCreaseOption ON_SubDFromMeshOptions::InteriorCreaseOptionFromUnsigned(
+ON_ToSubDParameters::InteriorCreaseOption ON_ToSubDParameters::InteriorCreaseOptionFromUnsigned(
unsigned int interior_crease_option_as_unsigned
)
{
switch (interior_crease_option_as_unsigned)
{
- case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::Unset:
- return ON_SubDFromMeshOptions::InteriorCreaseOption::Unset;
+ case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::Unset:
+ return ON_ToSubDParameters::InteriorCreaseOption::Unset;
break;
- case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::None:
- return ON_SubDFromMeshOptions::InteriorCreaseOption::None;
+ case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::None:
+ return ON_ToSubDParameters::InteriorCreaseOption::None;
break;
- case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease:
- return ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshCrease;
+ case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease:
+ return ON_ToSubDParameters::InteriorCreaseOption::AtMeshCrease;
break;
- case (unsigned int)ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge:
- return ON_SubDFromMeshOptions::InteriorCreaseOption::AtMeshEdge;
+ case (unsigned int)ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge:
+ return ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge;
break;
default:
break;
}
- return ON_SubDFromMeshOptions::InteriorCreaseOption::Unset;
+ return ON_ToSubDParameters::InteriorCreaseOption::Unset;
}
-ON_SubD::SubDType ON_SubDFromMeshOptions::SubDType() const
-{
- return
- (ON_SubD::SubDType::Unset == m_subd_type)
- ? ON_SubD::DefaultSubDType()
- : m_subd_type;
-}
-
-void ON_SubDFromMeshOptions::SetSubDType(
- ON_SubD::SubDType subd_type
- )
-{
- if (subd_type == ON_SubD::SubDTypeFromUnsigned((unsigned int)subd_type) )
- {
- m_subd_type = subd_type;
- }
-}
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDVertex
@@ -1210,6 +1601,62 @@ unsigned int ON_SubDVertex::EdgeArrayIndex(
return ON_UNSET_UINT_INDEX;
}
+unsigned int ON_SubDVertex::MarkedEdgeCount() const
+{
+ unsigned int mark_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_status.RuntimeMark())
+ ++mark_count;
+ }
+ return mark_count;
+}
+
+unsigned int ON_SubDVertex::MarkedFaceCount() const
+{
+ unsigned int mark_count = 0;
+ for (unsigned short vfi = 0; vfi < m_face_count; ++vfi)
+ {
+ const ON_SubDFace* f = m_faces[vfi];
+ if (nullptr != f && f->m_status.RuntimeMark())
+ ++mark_count;
+ }
+ return mark_count;
+}
+
+
+unsigned int ON_SubDEdge::MarkedVertexCount() const
+{
+ unsigned int mark_count = 0;
+ for (unsigned evi = 0; evi < 2; ++evi)
+ {
+ const ON_SubDVertex* v = m_vertex[evi];
+ if (nullptr != v && v->m_status.RuntimeMark())
+ ++mark_count;
+ }
+ return mark_count;
+}
+
+unsigned int ON_SubDEdge::MarkedFaceCount() const
+{
+ unsigned int mark_count = 0;
+ const ON_SubDFacePtr* fptr = m_face2;
+ for (unsigned short efi = 0; efi < m_face_count; ++efi, ++fptr)
+ {
+ if (2 == efi)
+ {
+ fptr = m_facex;
+ if (nullptr == fptr)
+ break;
+ }
+ const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr->m_ptr);
+ if (nullptr != f && f->m_status.RuntimeMark())
+ ++mark_count;
+ }
+ return mark_count;
+}
+
unsigned int ON_SubDVertex::FaceCount() const
{
return m_face_count;
@@ -1245,101 +1692,29 @@ unsigned int ON_SubDVertex::FaceArrayIndex(
return ON_UNSET_UINT_INDEX;
}
-
-ON_SubD::FacetType ON_SubDVertex::FirstFaceFacetType() const
+unsigned int ON_SubDVertex::ReplaceFaceInArray(const ON_SubDFace * old_face, const ON_SubDFace * new_face)
{
- if (0 == m_face_count)
- return ON_SubD::FacetType::Unset;
-
- if (nullptr == m_faces)
- return ON_SubD::FacetType::Unset;
-
- if (nullptr == m_faces[0])
- return ON_SubD::FacetType::Unset;
-
- if (3 == m_faces[0]->m_edge_count)
- return ON_SubD::FacetType::Tri;
-
- if (4 == m_faces[0]->m_edge_count)
- return ON_SubD::FacetType::Quad;
-
- return ON_SubD::FacetType::Unset;
+ unsigned vfi = (nullptr != old_face && old_face != new_face) ? FaceArrayIndex(old_face) : ON_UNSET_UINT_INDEX;
+ if (ON_UNSET_UINT_INDEX == vfi)
+ return ON_UNSET_UINT_INDEX;
+ if (nullptr != new_face)
+ {
+ m_faces[vfi] = new_face;
+ }
+ else
+ {
+ const unsigned c = (unsigned)(m_face_count--);
+ while (++vfi < c)
+ m_faces[vfi - 1] = m_faces[vfi];
+ }
+ return vfi;
}
-
-bool ON_SubDVertex::IsTagged() const
+const ON_3dPoint ON_SubDVertex::ControlNetPoint() const
{
- return (ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag);
+ return ON_3dPoint(m_P);
}
-//static bool AllFacesHaveCorrectEdgeCount(
-// unsigned short correct_edge_count,
-// unsigned int face_count,
-// const ON_SubDFace*const* vertex_faces
-// )
-//{
-// if (nullptr == vertex_faces)
-// return false;
-// for (unsigned int vfi = 0; vfi < face_count; vfi++)
-// {
-// const ON_SubDFace* f = vertex_faces[vfi];
-// if (nullptr == f)
-// return false;
-// if (correct_edge_count != f->m_edge_count)
-// return false;
-// }
-// return true;
-//}
-
-//bool ON_SubDVertex::IsOrdinary(
-// ON_SubD::SubDType subdivision_type,
-// ON_SubD::VertexTag vertex_tag_filter,
-// bool bTestFaces
-// ) const
-//{
-// if (ON_SubD::VertexTag::Unset == vertex_tag_filter || vertex_tag_filter == m_vertex_tag)
-// {
-// if (ON_SubD::VertexTag::Smooth == m_vertex_tag)
-// {
-// if (m_edge_count == m_face_count)
-// {
-// if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type)
-// {
-// if (4 == m_edge_count)
-// return bTestFaces ? AllFacesHaveCorrectEdgeCount(4, m_face_count, m_faces) : true;
-// }
-// else if (ON_SubD::SubDType::TriLoopWarren == subdivision_type)
-// {
-// if (6 == m_edge_count)
-// return bTestFaces ? AllFacesHaveCorrectEdgeCount(3, m_face_count, m_faces) : true;
-// }
-// }
-// }
-// else if (ON_SubD::VertexTag::Crease == m_vertex_tag)
-// {
-// if (m_edge_count == m_face_count + 1)
-// {
-// if (ON_SubD::SubDType::QuadCatmullClark == subdivision_type)
-// {
-// if (3 == m_edge_count && 2 == EdgeCount(ON_SubD::EdgeTag::Crease))
-// {
-// return bTestFaces ? AllFacesHaveCorrectEdgeCount(4, m_face_count, m_faces) : true;
-// }
-// }
-// else if (ON_SubD::SubDType::TriLoopWarren == subdivision_type)
-// {
-// if (4 == m_edge_count && 2 == EdgeCount(ON_SubD::EdgeTag::Crease))
-// {
-// return bTestFaces ? AllFacesHaveCorrectEdgeCount(3, m_face_count, m_faces) : true;
-// }
-// }
-// }
-// }
-// }
-//
-// return false;
-//}
-
bool ON_SubDVertex::IsSmooth() const
{
return (ON_SubD::VertexTag::Smooth == m_vertex_tag);
@@ -1365,12 +1740,12 @@ bool ON_SubDVertex::IsCreaseOrCorner() const
return (ON_SubD::VertexTag::Crease == m_vertex_tag || ON_SubD::VertexTag::Corner == m_vertex_tag);
}
-bool ON_SubDVertex::IsCreaseOrCornerOrDart() const
+bool ON_SubDVertex::IsDartOrCreaseOrCorner() const
{
return (
- ON_SubD::VertexTag::Crease == m_vertex_tag
+ ON_SubD::VertexTag::Dart == m_vertex_tag
+ || ON_SubD::VertexTag::Crease == m_vertex_tag
|| ON_SubD::VertexTag::Corner == m_vertex_tag
- || ON_SubD::VertexTag::Dart == m_vertex_tag
);
}
@@ -1386,6 +1761,54 @@ bool ON_SubDVertex::IsSmoothOrCrease() const
return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Crease == m_vertex_tag);
}
+const ON_SubDVertexEdgeProperties ON_SubDVertex::EdgeProperties() const
+{
+ ON_SubDVertexEdgeProperties ep;
+
+ bool bFirstEdge = true;
+ const unsigned short edge_count = m_edge_count;
+ for (unsigned short vei = 0; vei < edge_count; vei++)
+ {
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
+ if (nullptr == e)
+ {
+ ep.m_null_edge_count++;
+ continue;
+ }
+
+ if (e->IsCrease())
+ ep.m_crease_edge_count++;
+ else if (e->IsSmooth())
+ ep.m_smooth_edge_count++;
+ else
+ ep.m_unset_edge_count++;
+
+ const unsigned short edge_face_count = e->m_face_count;
+ if (bFirstEdge)
+ {
+ bFirstEdge = false;
+ ep.m_min_edge_face_count = edge_face_count;
+ ep.m_max_edge_face_count = edge_face_count;
+ }
+ else if (edge_face_count < ep.m_min_edge_face_count)
+ ep.m_min_edge_face_count = edge_face_count;
+ else if (edge_face_count > ep.m_max_edge_face_count)
+ ep.m_max_edge_face_count = edge_face_count;
+
+ if (0 == edge_face_count)
+ ep.m_wire_edge_count++;
+ else if (1 == edge_face_count)
+ ep.m_boundary_edge_count++;
+ else if (2 == edge_face_count)
+ ep.m_interior_edge_count++;
+ else
+ ep.m_nonmanifold_edge_count++;
+
+ }
+ return ep;
+}
+
+
bool ON_SubDEdge::IsCrease() const
{
return (ON_SubD::EdgeTag::Crease == m_edge_tag) ? true : false;
@@ -1426,7 +1849,7 @@ unsigned int ON_SubDEdge::DartCount() const
bool ON_SubDEdge::IsSmooth() const
{
- return (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::X == m_edge_tag) ? true : false;
+ return (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::SmoothX == m_edge_tag) ? true : false;
}
bool ON_SubDEdge::IsSmoothNotX() const
@@ -1436,12 +1859,122 @@ bool ON_SubDEdge::IsSmoothNotX() const
bool ON_SubDEdge::IsSmoothX() const
{
- return (ON_SubD::EdgeTag::X == m_edge_tag) ? true : false;
+ return (ON_SubD::EdgeTag::SmoothX == m_edge_tag) ? true : false;
}
-bool ON_SubDVertex::IsStandard(
- ON_SubD::SubDType subdivision_type
- ) const
+bool ON_SubDVertex::IsSingleSectorVertex() const
+{
+ const bool bIsCreaseOrCorner = IsCreaseOrCorner();
+ if (bIsCreaseOrCorner)
+ {
+ if (m_face_count < 1 || m_face_count + 1 != m_edge_count)
+ return false;
+ }
+ else if (IsSmoothOrDart())
+ {
+ if (m_face_count < 2 || m_edge_count!= m_face_count)
+ return false;
+ }
+ else
+ return false;
+
+
+ unsigned short boundary_crease_count = 0;
+ unsigned short interior_crease_count = 0;
+ unsigned short interior_smooth_count = 0;
+ for (unsigned short vei = 0; vei < m_edge_count; ++vei)
+ {
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(this->m_edges[vei].m_ptr);
+ if (nullptr == e)
+ return false;
+
+ if (e->IsSmooth())
+ {
+ if (2 == e->m_face_count)
+ {
+ ++interior_smooth_count;
+ continue;
+ }
+ }
+ else if (ON_SubD::EdgeTag::Crease == e->m_edge_tag)
+ {
+ if (2 == e->m_face_count)
+ {
+ ++interior_crease_count;
+ if (ON_SubD::VertexTag::Dart == m_vertex_tag && 1 == interior_crease_count)
+ continue;
+ }
+ else if (1 == e->m_face_count)
+ {
+ ++boundary_crease_count;
+ if (bIsCreaseOrCorner && boundary_crease_count <= 2)
+ continue;
+ }
+ }
+
+ return false;
+ }
+
+ if (bIsCreaseOrCorner)
+ {
+ if (2 == boundary_crease_count && 2+interior_smooth_count == m_edge_count)
+ return true;
+ }
+ else if (ON_SubD::VertexTag::Dart == m_vertex_tag)
+ {
+ if (1 == interior_crease_count && 1+interior_smooth_count == m_edge_count)
+ return true;
+ }
+ else if (ON_SubD::VertexTag::Smooth == m_vertex_tag)
+ {
+ if (interior_smooth_count == m_edge_count)
+ return true;
+ }
+
+ return false;
+}
+
+bool ON_SubDVertex::IsManifoldBoundaryVertex() const
+{
+ return IsCreaseOrCorner() && IsSingleSectorVertex();
+}
+
+
+bool ON_SubDVertex::HasInteriorVertexTopology() const
+{
+ if (m_edge_count >= 2 && m_edge_count == m_face_count && nullptr != m_edges && nullptr != m_faces)
+ {
+ 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 || 2 != e->m_face_count)
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool ON_SubDVertex::HasBoundaryVertexTopology() const
+{
+ if (m_edge_count >= 2 && m_edge_count == m_face_count+1 && nullptr != m_edges && nullptr != m_faces)
+ {
+ unsigned boundary_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 || 0 == e->m_face_count || e->m_face_count > 2)
+ return false;
+ if (1 == e->m_face_count)
+ ++boundary_count;
+ }
+ if (2 == boundary_count)
+ return true;
+ }
+ return false;
+}
+
+bool ON_SubDVertex::IsStandard() const
{
if (nullptr == m_edges)
return false;
@@ -1457,12 +1990,7 @@ bool ON_SubDVertex::IsStandard(
if (face_count != ON_SubDSectorType::SectorFaceCountFromEdgeCount(m_vertex_tag, edge_count))
return false;
- const unsigned short f_edge_count
- = (ON_SubD::SubDType::QuadCatmullClark == subdivision_type)
- ? 4
- : ((ON_SubD::SubDType::TriLoopWarren == subdivision_type) ? 3 : 0);
- if (0 == f_edge_count)
- return false;
+ const unsigned short f_edge_count = 4;
unsigned int crease_edge_face_count = ON_UNSET_UINT_INDEX;
bool bTaggedVertex = false;
@@ -1569,7 +2097,7 @@ bool ON_SubDVertex::IsStandard(
creased_edge_count++;
if (creased_edge_count > 2)
return false;
- if (false == other_vertex->IsCreaseOrCornerOrDart())
+ if (false == other_vertex->IsDartOrCreaseOrCorner())
return false;
}
else
@@ -1602,53 +2130,101 @@ bool ON_SubDVertex::IsStandard(
}
-unsigned int ON_SubDEdge::EdgeFlags() const
+unsigned int ON_SubDEdge::EdgeAttributes() const
{
+ unsigned int edge_topology_attributes = 0U;
- if (nullptr == m_vertex[0] || nullptr == m_vertex[1] || m_vertex[0] == m_vertex[1])
- return ON_ComponentAttributes::EdgeFlags::Damaged;
+ if (nullptr == m_vertex[0] || nullptr == m_vertex[1])
+ {
+ edge_topology_attributes |= ON_ComponentAttributes::Damaged;
+ }
+ else
+ {
+ const double* P[2] = { m_vertex[0]->m_P, m_vertex[1]->m_P };
+ if (
+ fabs(P[0][0]) < ON_UNSET_POSITIVE_VALUE
+ && fabs(P[0][1]) < ON_UNSET_POSITIVE_VALUE
+ && fabs(P[0][2]) < ON_UNSET_POSITIVE_VALUE
+ && fabs(P[1][0]) < ON_UNSET_POSITIVE_VALUE
+ && fabs(P[1][1]) < ON_UNSET_POSITIVE_VALUE
+ && fabs(P[1][2]) < ON_UNSET_POSITIVE_VALUE
+ )
+ {
+ if (P[0][0] == P[1][0] && P[0][1] == P[1][1] && P[0][2] == P[1][2])
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::ZeroLength;
+ else
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::NonzeroLength;
+ }
+ if (m_vertex[0] != m_vertex[1])
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Open;
+ else
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Closed;
+ }
- unsigned int edge_topology_attributes = ON_ComponentAttributes::EdgeFlags::Open;
-
- const double* P[2] = { m_vertex[0]->m_P, m_vertex[1]->m_P };
- if (P[0][0] == P[1][0] && P[0][1] == P[1][1] && P[0][2] == P[1][2])
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Degenerate;
-
+ const ON_SubDFace* f[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr),ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) };
switch (m_face_count)
{
case 0:
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Wire;
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Wire;
break;
+
case 1:
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Boundary;
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Boundary;
+ if ( nullptr == f[0])
+ edge_topology_attributes |= ON_ComponentAttributes::Damaged;
break;
+
case 2:
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Interior;
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Interior;
+
+ if (IsSmooth())
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSmooth;
+ else if (IsCrease())
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorCrease;
+
+ if (nullptr != f[0] && nullptr != f[1])
{
- const ON_SubDFace* face[2] = { ON_SUBD_FACE_POINTER(m_face2[0].m_ptr), ON_SUBD_FACE_POINTER(m_face2[1].m_ptr) };
- if (nullptr == face[0] || nullptr == face[1] || face[0] == face[1])
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Damaged;
+ if (ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr))
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorNotOriented;
+ else
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorOriented;
+ if (f[0] != f[1])
+ {
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorTwoFaced;
+ }
else
{
- if (IsSmooth())
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Smooth;
- else if ( m_vertex[0]->IsDart() || m_vertex[1]->IsDart() )
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Dart;
- else
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Crease;
-
- ON__UINT_PTR dir[2] = { ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr), ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr) };
- if (dir[0] == dir[1])
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::NotOriented;
- else
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Oriented;
+ const unsigned int fecount = f[0]->EdgeCount();
+ const unsigned int fei0 = f[0]->EdgeArrayIndex(this);
+ if (fecount > 2 && fei0 < fecount)
+ {
+ for (unsigned int fei1 = fei0 + 1; fei1 < fecount; ++fei1)
+ {
+ if (this == f[0]->Edge(fei1))
+ {
+ if (f[0]->EdgeDirection(fei0) != f[0]->EdgeDirection(fei1))
+ {
+ if ( fei1+1 == fei0 || (0 == fei0 && fei1+1 == fecount))
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSlit;
+ else
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::InteriorSeam;
+ }
+ break;
+ }
+ }
+ }
}
}
+ else
+ {
+ edge_topology_attributes |= ON_ComponentAttributes::Damaged;
+ }
break;
+
default:
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Nonmanifold;
- if ( nullptr == m_facex )
- edge_topology_attributes |= ON_ComponentAttributes::EdgeFlags::Damaged;
+ edge_topology_attributes |= ON_ComponentAttributes::EdgeAttributes::Nonmanifold;
+ if ( nullptr == f[0] || nullptr == f[1] || nullptr == m_facex )
+ edge_topology_attributes |= ON_ComponentAttributes::Damaged;
break;
}
@@ -1764,7 +2340,7 @@ void ON_SubDComponentBase::CopyBaseFrom(
*this = *src;
m_subd_point1 = nullptr;
- ON_SUBD_CACHE_CLEAR_LIMIT_FLAG(m_saved_points_flags);
+ Internal_ClearSurfacePointFlag();
}
void ON_SubDEdge::CopyFrom(
@@ -1820,14 +2396,14 @@ unsigned int ON_SubDEdge::TaggedEndIndex() const
for (unsigned int evi = 0; evi < 2; evi++)
{
const ON_SubDVertex* v = m_vertex[evi];
- if (nullptr == v || false == v->IsTagged())
+ if (nullptr == v || false == v->IsDartOrCreaseOrCorner())
continue;
tagged_end_index = (3 == tagged_end_index) ? evi : 2;
}
return tagged_end_index;
}
-ON_SubDFacePtr ON_SubDEdge::FacePtr(
+const ON_SubDFacePtr ON_SubDEdge::FacePtr(
unsigned int i
) const
{
@@ -1840,6 +2416,22 @@ unsigned int ON_SubDEdge::FaceCount() const
return m_face_count;
}
+bool ON_SubDEdge::IsInteriorEdge() const
+{
+ for (;;)
+ {
+ if (2 != m_face_count)
+ break;
+ if (ON_SUBD_FACE_DIRECTION(m_face2[0].m_ptr) == ON_SUBD_FACE_DIRECTION(m_face2[1].m_ptr))
+ break;
+ if (nullptr == ON_SUBD_FACE_POINTER(m_face2[0].m_ptr))
+ break;
+ if (nullptr == ON_SUBD_FACE_POINTER(m_face2[1].m_ptr))
+ break;
+ }
+ return false;
+}
+
const class ON_SubDFace* ON_SubDEdge::Face(
unsigned int i
) const
@@ -1854,7 +2446,7 @@ ON__UINT_PTR ON_SubDEdge::FaceDirection(
return (i < 2) ? ON_SUBD_FACE_DIRECTION(m_face2[i].m_ptr) : ((i < m_face_count) ? ON_SUBD_FACE_DIRECTION(m_facex[i - 2].m_ptr) : 0);
}
-ON_SubDFacePtr ON_SubDEdge::FacePtr(
+const ON_SubDFacePtr ON_SubDEdge::FacePtrFromFace(
const class ON_SubDFace* f
) const
{
@@ -1907,6 +2499,45 @@ unsigned int ON_SubDEdge::FaceArrayIndex(
return ON_UNSET_UINT_INDEX;
}
+unsigned int ON_SubDEdge::ReplaceFaceInArray(const ON_SubDFace * old_face, const ON_SubDFace * new_face)
+{
+ unsigned efi = (nullptr != old_face && old_face != new_face) ? FaceArrayIndex(old_face) : ON_UNSET_UINT_INDEX;
+ if (ON_UNSET_UINT_INDEX == efi)
+ return ON_UNSET_UINT_INDEX;
+ ON_SubDFacePtr* fptr = (efi < 2) ? &m_face2[efi] : &m_facex[efi - 2];
+ if (nullptr != new_face)
+ {
+ *fptr = ON_SubDFacePtr::Create(new_face, fptr->FaceDirection());
+ }
+ else
+ {
+ unsigned efi1 = efi + 1;
+ ON_SubDFacePtr* fptr1 = (efi1 < 2) ? &m_face2[efi1] : &m_facex[efi1 - 2];
+ for (const unsigned c = (unsigned)(m_face_count--); efi1 < c; ++efi, ++efi1)
+ {
+ if (2 == efi)
+ fptr = m_facex;
+ else if (2 == efi1)
+ fptr1 = m_facex;
+ *fptr++ = *fptr1++;
+ }
+ }
+ return efi;
+}
+
+
+
+unsigned int ON_SubDEdge::VertexArrayIndex(const ON_SubDVertex * v) const
+{
+ if (nullptr == v || m_vertex[0] == m_vertex[1])
+ return ON_UNSET_UINT_INDEX;
+ if (v == m_vertex[0])
+ return 0;
+ if (v == m_vertex[1])
+ return 1;
+ return ON_UNSET_UINT_INDEX;
+}
+
const ON_SubDFace* ON_SubDEdge::NeighborFace(
const ON_SubDFace* face,
@@ -1960,6 +2591,44 @@ const ON_SubDFacePtr ON_SubDEdge::NeighborFacePtr(
return ON_SUBD_RETURN_ERROR(ON_SubDFacePtr::Null);
}
+const ON_SubDEdgePtr ON_SubDEdge::AdjacentEdgePtr(
+ unsigned int edge_vertex_index,
+ unsigned int i
+) const
+{
+ if ( edge_vertex_index >= 2 )
+ return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
+ const ON_SubDFacePtr fptr = FacePtr(i);
+ const ON_SubDFace* f = ON_SUBD_FACE_POINTER(fptr.m_ptr);
+ if (nullptr == f)
+ return ON_SubDEdgePtr::Null;
+ const unsigned int fe_count = f->m_edge_count;
+ if ( fe_count < 3 || fe_count > ON_SubDFace::MaximumEdgeCount)
+ return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
+ const unsigned int fei = f->EdgeArrayIndex(this);
+ if( fei >= fe_count)
+ return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
+ const ON_SubDEdgePtr eptr = f->EdgePtr(fei);
+ if ( this != ON_SUBD_EDGE_POINTER(eptr.m_ptr))
+ return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
+ const ON__UINT_PTR dir = ON_SUBD_FACE_DIRECTION(fptr.m_ptr);
+ if (dir != ON_SUBD_EDGE_DIRECTION(eptr.m_ptr))
+ return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
+ const unsigned int fei1
+ = (dir == ((ON__UINT_PTR)edge_vertex_index))
+ ? ((fei + fe_count - 1) % fe_count)
+ : ((fei + 1) % fe_count)
+ ;
+ return f->EdgePtr(fei1);
+}
+
+const ON_SubDEdge* ON_SubDEdge::AdjacentEdge(
+ unsigned int edge_vertex_index,
+ unsigned int i
+ ) const
+{
+ return ON_SUBD_EDGE_POINTER(AdjacentEdgePtr(edge_vertex_index, i).m_ptr);
+}
const class ON_SubDVertex* ON_SubDEdge::Vertex(
unsigned int i
@@ -1986,18 +2655,62 @@ const ON_SubDVertex* ON_SubDEdge::OtherEndVertex(
return nullptr;
}
-const ON_3dPoint ON_SubDEdge::EndPoint( unsigned int i) const
+const ON_SubDEdgePtr ON_SubDEdge::FromVertices(
+ const ON_SubDVertex* vertex0,
+ const ON_SubDVertex* vertex1
+)
+{
+ if (nullptr != vertex0 && nullptr != vertex1 && vertex0 != vertex1 && nullptr != vertex0->m_edges)
+ {
+ for (unsigned short vei = 0; vei < vertex0->m_edge_count; vei++)
+ {
+ const ON_SubDEdgePtr eptr = vertex0->m_edges[vei];
+ if (vertex1 == eptr.RelativeVertex(1) && vertex0 == eptr.RelativeVertex(0))
+ return eptr;
+ }
+ }
+ return ON_SubDEdgePtr::Null;
+}
+
+const ON_SubDEdge* ON_SubDEdge::FromVertices(
+ const ON_SubDVertex* vertex0,
+ const ON_SubDVertex* vertex1,
+ bool bIgnoreOrientation
+)
+{
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(ON_SubDEdge::FromVertices(vertex0, vertex1).m_ptr);
+ if ( nullptr != e && false == bIgnoreOrientation && vertex0 != e->m_vertex[0] )
+ e = nullptr;
+ return e;
+}
+
+const ON_3dPoint ON_SubDEdge::ControlNetPoint( unsigned int i) const
{
if (i >= 2 || nullptr == m_vertex[i])
return ON_3dPoint::NanPoint;
return (ON_3dPoint(m_vertex[i]->m_P));
}
-const ON_3dVector ON_SubDEdge::Direction() const
+const ON_3dVector ON_SubDEdge::ControlNetDirection() const
{
if (nullptr == m_vertex[0] || nullptr == m_vertex[1])
return ON_3dVector::NanVector;
- return (ON_3dPoint(m_vertex[1]->m_P) - ON_3dPoint(m_vertex[0]->m_P));
+ const ON_3dPoint P[2] = { ON_3dPoint(m_vertex[0]->m_P) ,ON_3dPoint(m_vertex[1]->m_P) };
+ return (P[0].IsValid() && P[1].IsValid()) ? (P[1] - P[0]) : ON_3dVector::NanVector;
+}
+
+const ON_3dVector ON_SubDEdge::ControlNetDirectionFrom(
+ const ON_SubDVertex* v
+) const
+{
+ if (nullptr != v)
+ {
+ if (v == m_vertex[0] && nullptr != m_vertex[1])
+ return ControlNetDirection();
+ if (v == m_vertex[1] && nullptr != m_vertex[0])
+ return -ControlNetDirection();
+ }
+ return ON_3dVector::NanVector;
}
@@ -2075,7 +2788,7 @@ void ON_SubDFace::CopyFrom(
}
}
-ON_SubDEdgePtr ON_SubDFace::EdgePtr(
+const ON_SubDEdgePtr ON_SubDFace::EdgePtr(
unsigned int i
) const
{
@@ -2087,6 +2800,112 @@ unsigned int ON_SubDFace::EdgeCount() const
return m_edge_count;
}
+unsigned int ON_SubDFace::MarkedEdgeCount() const
+{
+ unsigned int marked_edge_count = 0;
+ const ON_SubDEdgePtr* eptr = m_edge4;
+ for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr)
+ {
+ if (4 == fei)
+ {
+ eptr = m_edgex;
+ if (nullptr == eptr)
+ break;
+ }
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
+ if (nullptr != e && e->m_status.RuntimeMark())
+ ++marked_edge_count;
+ }
+ return marked_edge_count;
+}
+
+
+unsigned int ON_SubDFace::SetEdgeMarks(bool bMark) const
+{
+ bMark = bMark ? true : false; // so exact compare can be used
+ unsigned int changed_mark_count = 0;
+ const ON_SubDEdgePtr* eptr = m_edge4;
+ for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr)
+ {
+ if (4 == fei)
+ {
+ eptr = m_edgex;
+ if (nullptr == eptr)
+ break;
+ }
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
+ if (nullptr != e && bMark != e->m_status.RuntimeMark())
+ {
+ e->m_status.SetRuntimeMark(bMark);
+ ++changed_mark_count;
+ }
+ }
+ return changed_mark_count;
+}
+
+unsigned int ON_SubDFace::GetEdgeArray(
+ ON_SimpleArray< ON_SubDEdgePtr >& face_edge_array
+) const
+{
+ face_edge_array.SetCount(0);
+ const unsigned edge_count = m_edge_count;
+ face_edge_array.Reserve(edge_count);
+ face_edge_array.Append(edge_count <= 4 ? edge_count : 4U, m_edge4);
+ if (edge_count > 4)
+ {
+ if ( nullptr != m_edgex )
+ face_edge_array.Append(edge_count-4U, m_edgex);
+ else
+ {
+ for (unsigned fei = 4; fei < edge_count; ++fei)
+ face_edge_array.Append(ON_SubDEdgePtr::Null);
+ }
+ }
+ return edge_count;
+}
+
+unsigned int ON_SubDFace::SetVertexMarks(bool bMark) const
+{
+ bMark = bMark ? true : false; // so exact compare can be used
+ unsigned int changed_mark_count = 0;
+ const ON_SubDEdgePtr* eptr = m_edge4;
+ for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr)
+ {
+ if (4 == fei)
+ {
+ eptr = m_edgex;
+ if (nullptr == eptr)
+ break;
+ }
+ const ON_SubDVertex* v = eptr->RelativeVertex(0);
+ if (nullptr != v && bMark != v->m_status.RuntimeMark())
+ {
+ v->m_status.SetRuntimeMark(bMark);
+ ++changed_mark_count;
+ }
+ }
+ return changed_mark_count;
+}
+
+unsigned int ON_SubDFace::MarkedVertexCount() const
+{
+ unsigned int marked_vertex_count = 0;
+ const ON_SubDEdgePtr* eptr = m_edge4;
+ for (unsigned short fei = 0; fei < m_edge_count; ++fei, ++ eptr)
+ {
+ if (4 == fei)
+ {
+ eptr = m_edgex;
+ if (nullptr == eptr)
+ break;
+ }
+ const ON_SubDVertex* v = eptr->RelativeVertex(0);
+ if (nullptr != v && v->m_status.RuntimeMark())
+ ++marked_vertex_count;
+ }
+ return marked_vertex_count;
+}
+
const class ON_SubDVertex* ON_SubDFace::Vertex(
unsigned int i
) const
@@ -2096,6 +2915,14 @@ const class ON_SubDVertex* ON_SubDFace::Vertex(
return (nullptr != (e = ON_SUBD_EDGE_POINTER(edge_ptr))) ? e->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)] : nullptr;
}
+const ON_3dPoint ON_SubDFace::ControlNetPoint(unsigned int i) const
+{
+ const ON_SubDEdge* e;
+ const ON__UINT_PTR edge_ptr = (i < 4) ? m_edge4[i].m_ptr : ((i < m_edge_count) ? m_edgex[i - 4].m_ptr : 0);
+ const ON_SubDVertex* v = (nullptr != (e = ON_SUBD_EDGE_POINTER(edge_ptr))) ? e->m_vertex[ON_SUBD_EDGE_DIRECTION(edge_ptr)] : nullptr;
+ return (nullptr != v) ? ON_3dPoint(v->m_P) : ON_3dPoint::NanPoint;
+}
+
const ON_SubDVertex* ON_SubDFace::QuadOppositeVertex(
const ON_SubDVertex* vertex
) const
@@ -2174,7 +3001,7 @@ ON__UINT_PTR ON_SubDFace::EdgeDirection(
}
-ON_SubDEdgePtr ON_SubDFace::EdgePtr(
+const ON_SubDEdgePtr ON_SubDFace::EdgePtrFromEdge(
const class ON_SubDEdge* e
) const
{
@@ -2263,6 +3090,42 @@ unsigned int ON_SubDFace::NextEdgeArrayIndex(
return (edge_array_index < edge_count) ? ((edge_array_index + 1) % edge_count) : ON_UNSET_UINT_INDEX;
}
+bool ON_SubDVertex::RemoveEdgeFromArray(const ON_SubDEdge * e)
+{
+ if (nullptr == e || 0 == m_edge_count || nullptr == m_edges)
+ return ON_SUBD_RETURN_ERROR(false);
+ unsigned short new_count = 0;
+ for (unsigned short vei = 0; vei < m_edge_count; ++vei)
+ {
+ const ON_SubDEdgePtr eptr = m_edges[vei];
+ if (e == ON_SUBD_EDGE_POINTER(eptr.m_ptr))
+ continue;
+ m_edges[new_count++] = eptr;
+ }
+ if (new_count == m_edge_count)
+ return false;
+ m_edge_count = new_count;
+ return true;
+}
+
+bool ON_SubDVertex::RemoveFaceFromArray(const ON_SubDFace * f)
+{
+ if (nullptr == f || 0 == m_face_count || nullptr == m_faces)
+ return ON_SUBD_RETURN_ERROR(false);
+ unsigned short new_count = 0;
+ for (unsigned short vfi = 0; vfi < m_face_count; ++vfi)
+ {
+ const ON_SubDFace* vf = m_faces[vfi];
+ if (f == vf)
+ continue;
+ m_faces[new_count++] = vf;
+ }
+ if (new_count == m_face_count)
+ return false;
+ m_face_count = new_count;
+ return true;
+}
+
bool ON_SubDEdge::RemoveFaceFromArray(
const ON_SubDFace* f
)
@@ -2443,6 +3306,56 @@ bool ON_SubDFace::RemoveEdgeFromArray(
return true;
}
+bool ON_SubDFace::RotateEdgeArray(
+ unsigned int fei0
+)
+{
+ if (0 == fei0)
+ return true;
+
+ const unsigned int edge_count = m_edge_count;
+ if (edge_count < 2 || edge_count > ON_SubDFace::MaximumEdgeCount || fei0 >= edge_count)
+ return false;
+
+ ON_SubDEdgePtr stack_eptr[8];
+ ON_SubDEdgePtr* eptr
+ = (edge_count*sizeof(stack_eptr[0]) > sizeof(stack_eptr))
+ ? ((ON_SubDEdgePtr*)onmalloc(edge_count * sizeof(eptr[0])))
+ : stack_eptr;
+ if (nullptr == eptr)
+ return false;
+
+ ON_SubDEdgePtr* feptr = m_edge4;
+ for (unsigned int fei = 0; fei < edge_count; fei++)
+ {
+ if (4 == fei)
+ {
+ feptr = m_edgex;
+ if (nullptr == feptr)
+ {
+ if ( eptr != stack_eptr )
+ onfree(eptr);
+ return false;
+ }
+ }
+ eptr[fei] = *feptr++;
+ }
+
+ feptr = m_edge4;
+ for (unsigned int fei = 0; fei < edge_count; fei++)
+ {
+ if (4 == fei)
+ feptr = m_edgex;
+ *feptr++ = eptr[(fei + fei0) % edge_count];
+ }
+ if ( eptr != stack_eptr )
+ onfree(eptr);
+
+ return true;
+}
+
+
+
bool ON_SubDFace::RemoveEdgeFromArray(
const ON_SubDEdge* e
@@ -2461,6 +3374,7 @@ bool ON_SubDFace::RemoveEdgeFromArray(
for (i++; i < m_edge_count; i++)
m_edge4[i - 1] = m_edge4[i];
m_edge_count--;
+ m_edge4[m_edge_count] = ON_SubDEdgePtr::Null;
return true;
}
}
@@ -2477,6 +3391,7 @@ bool ON_SubDFace::RemoveEdgeFromArray(
for (i = 5; i < m_edge_count; i++)
m_edgex[i - 5] = m_edgex[i - 4];
m_edge_count--;
+ m_edgex[m_edge_count-4] = ON_SubDEdgePtr::Null;
return true;
}
}
@@ -2487,6 +3402,7 @@ bool ON_SubDFace::RemoveEdgeFromArray(
for (i++; i < m_edge_count; i++)
m_edgex[i - 5] = m_edgex[i - 4];
m_edge_count--;
+ m_edgex[m_edge_count-4] = ON_SubDEdgePtr::Null;
return true;
}
}
@@ -2571,6 +3487,77 @@ ON__UINT64 ON_SubD::RuntimeSerialNumber() const
}
+ON__UINT64 ON_SubD::ContentSerialNumber() const
+{
+ ON_SubDimple* subdimple = m_subdimple_sp.get();
+ return (nullptr != subdimple) ? subdimple->ContentSerialNumber() : 0;
+}
+
+ON__UINT64 ON_SubD::ChangeContentSerialNumberForExperts()
+{
+ if (this == &ON_SubD::Empty)
+ return 0;
+ ON_SubDimple* subdimple = m_subdimple_sp.get();
+ return (nullptr != subdimple) ? subdimple->ChangeContentSerialNumber() : 0;
+}
+
+ON_SubDComponentLocation ON_SubD::ToggleSubDAppearanceValue(ON_SubDComponentLocation subd_appearance)
+{
+ if (ON_SubDComponentLocation::Surface == subd_appearance)
+ return ON_SubDComponentLocation::ControlNet;
+ if (ON_SubDComponentLocation::ControlNet == subd_appearance)
+ return ON_SubDComponentLocation::Surface;
+ return subd_appearance;
+}
+
+ON_SubDComponentLocation ON_SubDMeshFragmentIterator::SubDAppearance() const
+{
+ return
+ (ON_SubDComponentLocation::Surface == m_subd_appearance_override || ON_SubDComponentLocation::ControlNet == m_subd_appearance_override)
+ ? m_subd_appearance_override
+ : SubD().SubDAppearance();
+}
+
+void ON_SubDMeshFragmentIterator::SetSubDAppearanceOverride(ON_SubDComponentLocation subd_appearance_override)
+{
+ if (m_subd_appearance_override != subd_appearance_override )
+ m_subd_appearance_override = subd_appearance_override;
+}
+
+ON_SubDComponentLocation ON_SubD::SubDAppearance() const
+{
+ const ON_SubDimple* subdimple = this->SubDimple();
+ return (nullptr != subdimple) ? subdimple->SubDAppearance() : ON_SubD::DefaultSubDAppearance;
+}
+
+ON_SubDComponentLocation ON_SubDimple::SubDAppearance() const
+{
+ return m_subd_appearance;
+}
+
+void ON_SubD::SetSubDAppearance(ON_SubDComponentLocation subd_appearance) const
+{
+ if (
+ subd_appearance != SubDAppearance()
+ && (ON_SubDComponentLocation::Surface == subd_appearance || ON_SubDComponentLocation::ControlNet == subd_appearance)
+ )
+ {
+ const ON_SubDimple* subdimple = const_cast(this)->SubDimple(true);
+ if (nullptr != subdimple)
+ subdimple->SetSubDAppearance(subd_appearance);
+ }
+}
+
+void ON_SubDimple::SetSubDAppearance(ON_SubDComponentLocation subd_appearance) const
+{
+ if (
+ subd_appearance != m_subd_appearance
+ && (ON_SubDComponentLocation::Surface == subd_appearance || ON_SubDComponentLocation::ControlNet == subd_appearance)
+ )
+ {
+ m_subd_appearance = subd_appearance;
+ }
+}
//////////////////////////////////////////////////////////////////////////
//
@@ -2597,14 +3584,13 @@ static bool EdgeVertexWeightIsSet(
static bool EdgeSectorWeightIsValid(
double edge_vertex_weight,
- ON_SubD::SubDType subdivision_type,
const ON_SubDEdge* edge
)
{
if (0.0 <= edge_vertex_weight && edge_vertex_weight < 1.0)
return true;
- if (ON_SubDSectorType::UnsetSectorWeight == edge_vertex_weight && ON_SubD::SubDType::Unset == subdivision_type && nullptr != edge && 0 == edge->m_level)
+ if (ON_SubDSectorType::UnsetSectorWeight == edge_vertex_weight && nullptr != edge && 0 == edge->SubdivisionLevel())
return true;
return false;
@@ -2614,7 +3600,6 @@ static bool IsValidVertexEdgeLink(
const ON_SubDVertex* vertex,
const ON_SubDEdge* edge,
ON__UINT_PTR end_index,
- ON_SubD::SubDType subdivision_type,
bool bSilentError
)
{
@@ -2627,19 +3612,15 @@ static bool IsValidVertexEdgeLink(
if (edge->m_vertex[end_index] != vertex)
return ON_SubDIsNotValid(bSilentError);
- if (vertex->m_level != edge->m_level)
+ if (vertex->SubdivisionLevel() != edge->SubdivisionLevel())
return ON_SubDIsNotValid(bSilentError);
- if (false == EdgeSectorWeightIsValid(edge->m_sector_coefficient[end_index],subdivision_type,edge))
+ if (false == EdgeSectorWeightIsValid(edge->m_sector_coefficient[end_index],edge))
return ON_SubDIsNotValid(bSilentError);
- //const ON__UINT_PTR corner_type = edge->m_vertex[end_index].CornerType();
- //if (ON_SubDVertexPtr::NoCorner != corner_type && ON_SubD::VertexTag::Corner != vertex->m_vertex_tag)
- // return ON_SubDIsNotValid(bSilentError);
-
if ( edge->IsSmooth() )
{
- // edge->m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::X
+ // edge->m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX
if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag)
{
if (false == (0.0 == edge->m_sector_coefficient[end_index]))
@@ -2648,7 +3629,7 @@ static bool IsValidVertexEdgeLink(
else
{
const unsigned int tagged_end_index = edge->TaggedEndIndex();
- if (ON_SubD::EdgeTag::X == edge->m_edge_tag)
+ if (ON_SubD::EdgeTag::SmoothX == edge->m_edge_tag)
{
if (2 != tagged_end_index)
return ON_SubDIsNotValid(bSilentError);
@@ -2658,25 +3639,17 @@ static bool IsValidVertexEdgeLink(
if (tagged_end_index != (unsigned int)end_index)
return ON_SubDIsNotValid(bSilentError);
}
+
+ ON_SubDSectorType st = ON_SubDSectorType::Create(edge,(unsigned int)end_index);
+ if (!st.IsValid())
+ return ON_SubDIsNotValid(bSilentError);
- if (ON_SubD::SubDType::Unset == subdivision_type)
- {
- if (false == (ON_SubDSectorType::UnsetSectorWeight == edge->m_sector_coefficient[end_index]))
- return ON_SubDIsNotValid(bSilentError);
- }
- else
- {
- ON_SubDSectorType st = ON_SubDSectorType::Create(subdivision_type,edge,(unsigned int)end_index);
- 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]))
+ return ON_SubDIsNotValid(bSilentError);
- const double expected_vertex_weight = st.SectorWeight();
- if (false == (expected_vertex_weight == edge->m_sector_coefficient[end_index]))
- return ON_SubDIsNotValid(bSilentError);
-
- if (false == EdgeVertexWeightIsSet(expected_vertex_weight))
- return ON_SubDIsNotValid(bSilentError);
- }
+ if (false == EdgeVertexWeightIsSet(expected_vertex_weight))
+ return ON_SubDIsNotValid(bSilentError);
}
}
else if(ON_SubD::EdgeTag::Crease == edge->m_edge_tag)
@@ -2710,7 +3683,7 @@ static bool IsValidVertexFaceLink(
if (nullptr == vertex || nullptr == face)
return ON_SubDIsNotValid(bSilentError);
- if (vertex->m_level != face->m_level)
+ if (vertex->SubdivisionLevel() != face->SubdivisionLevel())
return ON_SubDIsNotValid(bSilentError);
const unsigned int vertex_face_count = vertex->m_face_count;
@@ -2776,7 +3749,7 @@ static bool IsValidEdgeFaceLink(
if (nullptr == edge || nullptr == face)
return ON_SubDIsNotValid(bSilentError);
- if (edge->m_level != face->m_level)
+ if (edge->SubdivisionLevel() != face->SubdivisionLevel())
return ON_SubDIsNotValid(bSilentError);
const unsigned int edge_face_count = edge->m_face_count;
@@ -2830,6 +3803,200 @@ static bool IsValidEdgeFaceLink(
return true;
}
+class ON_Internal_DamagedMarker
+{
+public:
+ ON_Internal_DamagedMarker() = default;
+ ~ON_Internal_DamagedMarker()
+ {
+ if (nullptr != m_subd_component)
+ m_subd_component->m_status.SetDamagedState(true);
+ }
+
+private:
+ ON_Internal_DamagedMarker(const ON_Internal_DamagedMarker&) = delete;
+ ON_Internal_DamagedMarker& operator=(const ON_Internal_DamagedMarker&) = delete;
+
+public:
+ ON_Internal_DamagedMarker(const ON_SubDComponentBase* subd_component)
+ : m_subd_component(subd_component)
+ {}
+
+ void ClearComponent()
+ {
+ m_subd_component = nullptr;
+ }
+
+private:
+ const ON_SubDComponentBase* m_subd_component = nullptr;
+};
+
+static bool IsValidSubDVertexTag(
+ const ON_SubDVertex* vertex,
+ bool bSilentError
+)
+{
+ if (nullptr == vertex)
+ return true; // this error detected elsewhere.
+
+ ON_Internal_DamagedMarker dm(vertex);
+
+ const unsigned short vertex_edge_count = vertex->m_edge_count;
+ unsigned short crease_edge_count = 0;
+ unsigned short smooth_edge_count = 0;
+ for (unsigned short vei = 0; vei < vertex->m_edge_count; vei++)
+ {
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(vertex->m_edges[vei].m_ptr);
+ if (nullptr == e)
+ continue;
+ if (e->IsSmooth())
+ ++smooth_edge_count;
+ else if (e->IsCrease())
+ ++crease_edge_count;
+ }
+ const bool bValidEdgeTags = (vertex_edge_count == crease_edge_count + smooth_edge_count);
+
+ switch (vertex->m_vertex_tag)
+ {
+ case ON_SubD::VertexTag::Unset:
+ return ON_SubDIsNotValid(bSilentError);
+ break;
+
+ case ON_SubD::VertexTag::Smooth:
+ if (false == bValidEdgeTags)
+ break; // invalid edge tags detected in IsValidSubDEdgeTag();
+
+ if (
+ 0 != crease_edge_count
+ || vertex_edge_count < 2
+ || vertex_edge_count != smooth_edge_count
+ || vertex_edge_count != vertex->m_face_count
+ )
+ {
+ return ON_SubDIsNotValid(bSilentError);
+ }
+ break;
+
+ case ON_SubD::VertexTag::Crease:
+ if (false == bValidEdgeTags)
+ break; // invalid edge tags detected in IsValidSubDEdgeTag();
+
+ if ( 2 != crease_edge_count )
+ {
+ return ON_SubDIsNotValid(bSilentError);
+ }
+ break;
+
+ case ON_SubD::VertexTag::Corner:
+ if (false == bValidEdgeTags)
+ break; // invalid edge tags detected in IsValidSubDEdgeTag();
+
+ if ( crease_edge_count < 2 )
+ {
+ return ON_SubDIsNotValid(bSilentError);
+ }
+ break;
+
+ case ON_SubD::VertexTag::Dart:
+ if (false == bValidEdgeTags)
+ break; // invalid edge tags detected in IsValidSubDEdgeTag();
+
+ if (
+ 1 != crease_edge_count
+ || vertex_edge_count < 2
+ || vertex_edge_count != smooth_edge_count + crease_edge_count
+ || vertex_edge_count != vertex->m_face_count
+ )
+ {
+ return ON_SubDIsNotValid(bSilentError);
+ }
+ break;
+
+ default:
+ return ON_SubDIsNotValid(bSilentError);
+ break;
+ }
+
+ dm.ClearComponent();
+ return true;
+}
+
+static bool IsValidSubDEdgeTag(
+ const ON_SubDEdge* edge,
+ bool bSilentError
+)
+{
+ if (nullptr == edge)
+ return true; // this error detected elsewhere.
+
+ //ON_SubD::VertexTag vtag[2] = { ON_SubD::VertexTag::Unset,ON_SubD::VertexTag::Unset };
+ unsigned int smooth_vertex_count = 0;
+ unsigned int crease_vertex_count = 0;
+ unsigned int corner_vertex_count = 0;
+ unsigned int dart_vertex_count = 0;
+
+ for ( unsigned int evi = 0; evi < 2; evi++)
+ {
+ if (nullptr == edge->m_vertex[evi])
+ return true; // topology errors detected elsewhere
+ switch (edge->m_vertex[evi]->m_vertex_tag)
+ {
+ case ON_SubD::VertexTag::Smooth:
+ ++smooth_vertex_count;
+ break;
+ case ON_SubD::VertexTag::Crease:
+ ++crease_vertex_count;
+ break;
+ case ON_SubD::VertexTag::Corner:
+ ++corner_vertex_count;
+ break;
+ case ON_SubD::VertexTag::Dart:
+ ++dart_vertex_count;
+ break;
+ }
+ };
+
+ if (2 != smooth_vertex_count + crease_vertex_count + corner_vertex_count + dart_vertex_count)
+ return true; // invalid vertex tags detected in IsValidSubDVertexTag();
+
+ ON_Internal_DamagedMarker dm(edge);
+
+ //const unsigned short edge_face_count = edge->m_face_count;
+ switch(edge->m_edge_tag)
+ {
+
+ case ON_SubD::EdgeTag::Unset:
+ return ON_SubDIsNotValid(bSilentError);
+ break;
+
+ case ON_SubD::EdgeTag::Smooth:
+ if ( 2 != edge->m_face_count)
+ return ON_SubDIsNotValid(bSilentError);
+ if ( smooth_vertex_count < 1)
+ return ON_SubDIsNotValid(bSilentError);
+ break;
+
+ case ON_SubD::EdgeTag::Crease:
+ if ( 0 != smooth_vertex_count )
+ return ON_SubDIsNotValid(bSilentError);
+ break;
+
+ case ON_SubD::EdgeTag::SmoothX:
+ if ( 2 != edge->m_face_count)
+ return ON_SubDIsNotValid(bSilentError);
+ if ( 0 != smooth_vertex_count )
+ return ON_SubDIsNotValid(bSilentError);
+ break;
+
+ default:
+ return ON_SubDIsNotValid(bSilentError);
+ break;
+ }
+
+ dm.ClearComponent();
+ return true;
+}
+
static bool IsValidSubDVertex(
const ON_SubDVertex* vertex,
@@ -2842,7 +4009,7 @@ static bool IsValidSubDVertex(
if (nullptr == vertex)
return ON_SubDIsNotValid(bSilentError);
- if (level != vertex->m_level)
+ if (vertex->SubdivisionLevel() != level)
return ON_SubDIsNotValid(bSilentError);
if (nullptr != vertex_id_range)
@@ -2853,8 +4020,13 @@ static bool IsValidSubDVertex(
return ON_SubDIsNotValid(bSilentError);
}
- if ( vertex->m_edge_count < vertex->m_face_count)
- return ON_SubDIsNotValid(bSilentError);
+ ON_Internal_DamagedMarker dm(vertex);
+
+ if (vertex->m_edge_count < vertex->m_face_count)
+ {
+ if ( ON_SubD::VertexTag::Corner != vertex->m_vertex_tag || vertex->m_edge_count < 3 )
+ return ON_SubDIsNotValid(bSilentError);
+ }
if (vertex->m_edge_count > 0 && nullptr == vertex->m_edges)
return ON_SubDIsNotValid(bSilentError);
@@ -2868,18 +4040,24 @@ static bool IsValidSubDVertex(
if (vertex->m_edge_count != vertex->m_face_count)
return ON_SubDIsNotValid(bSilentError);
break;
+
case ON_SubD::VertexTag::Crease:
- if (vertex->m_face_count <= 0)
+ if ( vertex->m_edge_count < 2 )
return ON_SubDIsNotValid(bSilentError);
break;
+
case ON_SubD::VertexTag::Corner:
+ if ( vertex->m_edge_count < 1 )
+ return ON_SubDIsNotValid(bSilentError);
break;
+
case ON_SubD::VertexTag::Dart: // interior vertex
if (level > 0 && ordinary_valence_count != vertex->m_edge_count)
return ON_SubDIsNotValid(bSilentError);
if (vertex->m_edge_count != vertex->m_face_count)
return ON_SubDIsNotValid(bSilentError);
break;
+
default:
// invalid value for this enum
return ON_SubDIsNotValid(bSilentError);
@@ -2902,9 +4080,11 @@ static bool IsValidSubDVertex(
return ON_SubDIsNotValid(bSilentError);
}
+ dm.ClearComponent();
return true;
}
+
static bool IsValidSubDEdge(
const ON_SubDEdge* edge,
unsigned short level,
@@ -2915,7 +4095,7 @@ static bool IsValidSubDEdge(
if (nullptr == edge)
return ON_SubDIsNotValid(bSilentError);
- if (level != edge->m_level)
+ if (edge->SubdivisionLevel() != level)
return ON_SubDIsNotValid(bSilentError);
if (nullptr != edge_id_range)
@@ -2926,6 +4106,8 @@ static bool IsValidSubDEdge(
return ON_SubDIsNotValid(bSilentError);
}
+ ON_Internal_DamagedMarker dm(edge);
+
const ON_SubDVertex* edge_vertex[2] = { 0 };
for (unsigned int i = 0; i < 2; i++)
{
@@ -2939,7 +4121,7 @@ static bool IsValidSubDEdge(
if (edge->IsSmooth())
{
- // m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::X
+ // m_edge_tag is ON_SubD::EdgeTag::Smooth or ON_SubD::EdgeTag::SmoothX
if ( 2 != edge->m_face_count)
return ON_SubDIsNotValid(bSilentError);
}
@@ -2951,6 +4133,7 @@ static bool IsValidSubDEdge(
if (edge->m_face_count > 2 && nullptr == edge->m_facex)
return ON_SubDIsNotValid(bSilentError);
+ dm.ClearComponent();
return true;
}
@@ -2966,7 +4149,7 @@ static bool IsValidSubDFace(
if (nullptr == face)
return ON_SubDIsNotValid(bSilentError);
- if (level != face->m_level)
+ if (face->SubdivisionLevel() != level)
return ON_SubDIsNotValid(bSilentError);
if (nullptr != face_id_range)
@@ -2977,6 +4160,8 @@ static bool IsValidSubDFace(
return ON_SubDIsNotValid(bSilentError);
}
+ ON_Internal_DamagedMarker dm(face);
+
if (face->m_edge_count < 3)
return ON_SubDIsNotValid(bSilentError);
@@ -2986,6 +4171,8 @@ static bool IsValidSubDFace(
if (level > 0 && ordinary_face_edge_count != face->m_edge_count)
return ON_SubDIsNotValid(bSilentError);
+ dm.ClearComponent();
+
return true;
}
@@ -3002,12 +4189,11 @@ bool ON_SubDimple::IsValidLevel(
const ON_SubDLevel* level = m_levels[level_index];
if ( nullptr == level)
return ON_SubDIsNotValid(bSilentError);
+ level->ClearComponentDamagedState();
if ( level->m_level_index != level_index)
return ON_SubDIsNotValid(bSilentError);
- const ON_SubD::SubDType subdivision_type = level->m_subdivision_type;
-
if (level_index <= 0)
{
if (level->m_vertex_count < 3)
@@ -3028,8 +4214,6 @@ bool ON_SubDimple::IsValidLevel(
return ON_SubDIsNotValid(bSilentError);
if (level->m_face_count <= previous_level->m_face_count)
return ON_SubDIsNotValid(bSilentError);
- if (ON_SubD::SubDType::TriLoopWarren != level->m_subdivision_type && ON_SubD::SubDType::QuadCatmullClark != level->m_subdivision_type)
- return ON_SubDIsNotValid(bSilentError);
}
if (nullptr == level->m_vertex[0])
@@ -3212,7 +4396,7 @@ bool ON_SubDimple::IsValidLevel(
edge = vertex->Edge(i);
if (false == IsValidSubDEdge(edge, expected_level, e_id_range, bSilentError))
return false;
- if (false == IsValidVertexEdgeLink(vertex, edge, vertex->EdgeDirection(i), subdivision_type, bSilentError))
+ if (false == IsValidVertexEdgeLink(vertex, edge, vertex->EdgeDirection(i), bSilentError))
return false;
}
@@ -3234,7 +4418,7 @@ bool ON_SubDimple::IsValidLevel(
vertex = edge->m_vertex[i];
if (false == IsValidSubDVertex(vertex, expected_level, v_id_range, level->m_ordinary_vertex_valence, bSilentError))
return false;
- if (false == IsValidVertexEdgeLink(vertex, edge, i, subdivision_type, bSilentError))
+ if (false == IsValidVertexEdgeLink(vertex, edge, i, bSilentError))
return false;
}
@@ -3270,6 +4454,32 @@ bool ON_SubDimple::IsValidLevel(
}
}
+
+ // edge tag validation
+ for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
+ {
+ if (false == IsValidSubDEdgeTag(edge, bSilentError))
+ return false;
+ }
+
+ // vertex tag validation
+ for (vertex = level->m_vertex[0]; nullptr != vertex; vertex = vertex->m_next_vertex)
+ {
+ if (false == IsValidSubDVertexTag(vertex, bSilentError))
+ return false;
+ }
+
+ // edge length validation
+ for (edge = level->m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
+ {
+ const ON_3dPoint P[2] = { edge->m_vertex[0]->ControlNetPoint(), edge->m_vertex[1]->ControlNetPoint() };
+ if (false == (P[0] != P[1]))
+ {
+ edge->m_status.SetDamagedState(true);
+ return ON_SubDIsNotValid(bSilentError);
+ }
+ }
+
return true;
}
@@ -3374,10 +4584,17 @@ unsigned int ON_SubD::DumpTopology(
const ON__UINT64 runtime_sn = (text_log.IsTextHash()) ? 0 : RuntimeSerialNumber(); // TextHash ignores settings that don't depend on 3dm file content.
+ const ON_wString subd_texture_domain = ON_SubD::TextureDomainTypeToString(this->TextureDomainType());
if (level_count > 1)
- text_log.Print(L"SubD[%" PRIu64 "]: %u levels. Level %u is active.\n", runtime_sn, level_count, active_level_index);
+ text_log.Print(L"SubD[%" PRIu64 "]: %u levels. Level %u is active. texture domain type = %ls.\n",
+ runtime_sn,
+ level_count,
+ active_level_index,
+ static_cast(subd_texture_domain)
+ );
else
- text_log.Print(L"SubD[%" PRIu64 "]:\n", runtime_sn );
+ text_log.Print(L"SubD[%" PRIu64 "]: texture domain type = %ls.\n", runtime_sn, static_cast(subd_texture_domain));
+
ON_SubDLevelIterator lit(subdimple->LevelIterator());
@@ -3463,7 +4680,7 @@ unsigned int ON_SubDLevel::DumpTopology(
unsigned int uniformN = 0;
for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face)
{
- if (face_count >= m_face_count && f->m_level != level_index)
+ if (face_count >= m_face_count && f->SubdivisionLevel() != level_index)
break;
face_count++;
unsigned int N = f->EdgeCount();
@@ -3525,7 +4742,7 @@ unsigned int ON_SubDLevel::DumpTopology(
unsigned int max_vertex_id = 0;
for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex)
{
- if (vertex_count >= m_vertex_count && v->m_level != level_index)
+ if (vertex_count >= m_vertex_count && v->SubdivisionLevel() != level_index)
break;
if (v->m_id > max_vertex_id)
max_vertex_id = v->m_id;
@@ -3572,15 +4789,54 @@ unsigned int ON_SubDLevel::DumpTopology(
vertex_dump_count++;
ON_TextLogIndent vindent(text_log);
+ const ON_3dPoint P0(v->ControlNetPoint());
+
+ ON_wString vtag;
+ switch (v->m_vertex_tag)
+ {
+ case ON_SubD::VertexTag::Unset:
+ vtag = L"Unset";
+ break;
+ case ON_SubD::VertexTag::Smooth:
+ vtag = L"Smooth";
+ break;
+ case ON_SubD::VertexTag::Crease:
+ vtag = L"Crease";
+ break;
+ case ON_SubD::VertexTag::Corner:
+ vtag = L"Corner";
+ break;
+ case ON_SubD::VertexTag::Dart:
+ vtag = L"Dart";
+ break;
+ default:
+ vtag = L"INVALID";
+ break;
+ }
+
text_log.Print(
- "v%u: (%g, %g, %g)\n",
- v->m_id, v->m_P[0], v->m_P[1], v->m_P[2]
+ "v%u: %ls (%g, %g, %g)\n",
+ v->m_id,
+ static_cast(vtag),
+ P0.x, P0.y, P0.z
);
text_log.PushIndent();
+ ON_3dVector D(ON_3dVector::NanVector);
+ if (v->GetSubdivisionDisplacement(&D.x) && D.IsValid())
+ text_log.Print( "v.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z );
+
+ ON_3dPoint P1(ON_3dPoint::NanPoint);
+ if (v->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid())
+ text_log.Print( "v.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z );
+
+ ON_3dPoint S(ON_3dPoint::NanPoint);
+ if (v->GetSavedSurfacePoint(&S.x) && S.IsValid())
+ text_log.Print( "v.SurfacePoint: (%g, %g, %g)\n", S.x, S.y, S.z );
+
const unsigned int vertex_edge_count = v->m_edge_count;
- text_log.Print("vEdges[%u] = {", vertex_edge_count);
+ text_log.Print("v.Edges[%u] = {", vertex_edge_count);
prefix[0] = ON_String::Space;
prefix[1] = error_code_point;
prefix[2] = 'e';
@@ -3666,7 +4922,7 @@ unsigned int ON_SubDLevel::DumpTopology(
unsigned int max_edge_id = 0;
for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge)
{
- if (edge_count >= m_edge_count && e->m_level != level_index)
+ if (edge_count >= m_edge_count && e->SubdivisionLevel() != level_index)
break;
if (e->m_id > max_edge_id)
max_edge_id = e->m_id;
@@ -3712,9 +4968,30 @@ unsigned int ON_SubDLevel::DumpTopology(
edge_dump_count++;
ON_TextLogIndent eindent(text_log);
+ ON_wString etag;
+ switch (e->m_edge_tag)
+ {
+ case ON_SubD::EdgeTag::Unset:
+ etag = L"Unset";
+ break;
+ case ON_SubD::EdgeTag::Smooth:
+ etag = L"Smooth";
+ break;
+ case ON_SubD::EdgeTag::Crease:
+ etag = L"Crease";
+ break;
+ case ON_SubD::EdgeTag::SmoothX:
+ etag = L"SmmothX";
+ break;
+ default:
+ etag = L"INVALID";
+ break;
+ }
+
text_log.Print(
- "e%u: (",
- e->m_id
+ "e%u: %ls (",
+ e->m_id,
+ static_cast(etag)
);
prefix[0] = ON_String::Space;
@@ -3742,8 +5019,16 @@ unsigned int ON_SubDLevel::DumpTopology(
text_log.PushIndent();
+ ON_3dVector D(ON_3dVector::NanVector);
+ if (e->GetSubdivisionDisplacement(&D.x) && D.IsValid())
+ text_log.Print( "e.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z );
+
+ ON_3dPoint P1(ON_3dPoint::NanPoint);
+ if (e->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid())
+ text_log.Print( "e.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z );
+
const unsigned int edge_face_count = e->m_face_count;
- text_log.Print("eFaces[%u] = {", edge_face_count);
+ text_log.Print("e.Faces[%u] = {", edge_face_count);
prefix[0] = ON_String::Space;
prefix[1] = error_code_point;
prefix[2] = 'f';
@@ -3762,7 +5047,7 @@ unsigned int ON_SubDLevel::DumpTopology(
if (nullptr != f)
{
fid = f->m_id;
- ON_SubDEdgePtr eptr = f->EdgePtr(e);
+ ON_SubDEdgePtr eptr = f->EdgePtrFromEdge(e);
if (eptr.Edge() == e && eptr.EdgeDirection() == edge_fdir)
{
prefix[1] = (0 == edge_fdir) ? '+' : '-';
@@ -3806,7 +5091,7 @@ unsigned int ON_SubDLevel::DumpTopology(
unsigned int max_face_id = 0;
for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face)
{
- if (face_count >= m_face_count && f->m_level != level_index)
+ if (face_count >= m_face_count && f->SubdivisionLevel() != level_index)
break;
if (f->m_id > max_face_id)
max_face_id = f->m_id;
@@ -3859,8 +5144,17 @@ unsigned int ON_SubDLevel::DumpTopology(
text_log.PushIndent();
+ ON_3dVector D(ON_3dVector::NanVector);
+ if (f->GetSubdivisionDisplacement(&D.x) && D.IsValid())
+ text_log.Print( "f.SubdivisionDisplacement: (%g, %g, %g)\n", D.x, D.y, D.z );
+
+ ON_3dPoint P1(ON_3dPoint::NanPoint);
+ if (f->GetSavedSubdivisionPoint(&P1.x) && P1.IsValid())
+ text_log.Print( "f.SubdivisionPoint: (%g, %g, %g)\n", P1.x, P1.y, P1.z );
+
+
const unsigned int face_edge_count = f->m_edge_count;
- text_log.Print("fEdges[%u] = {", face_edge_count);
+ text_log.Print("f.Edges[%u] = {", face_edge_count);
prefix[0] = ON_String::Space;
prefix[1] = error_code_point;
prefix[2] = 'e';
@@ -3879,7 +5173,7 @@ unsigned int ON_SubDLevel::DumpTopology(
if (nullptr != e)
{
eid = e->m_id;
- ON_SubDFacePtr fptr = e->FacePtr(f);
+ ON_SubDFacePtr fptr = e->FacePtrFromFace(f);
if (fptr.Face() == f && fptr.FaceDirection() == face_edir)
{
prefix[1] = (0 == face_edir) ? '+' : '-';
@@ -3898,7 +5192,7 @@ unsigned int ON_SubDLevel::DumpTopology(
? last_edge->m_vertex[0 == last_eptr.EdgeDirection() ? 1 : 0]
: nullptr;
- text_log.Print("fVertices[%u] = {", face_edge_count);
+ text_log.Print("f.Vertices[%u] = {", face_edge_count);
prefix[0] = ON_String::Space;
prefix[1] = error_code_point;
prefix[2] = 'v';
@@ -3935,6 +5229,97 @@ unsigned int ON_SubDLevel::DumpTopology(
}
text_log.Print(" }\n");
+ if (f->TextureDomainIsSet())
+ {
+ const ON_wString s = ON_SubD::TextureDomainTypeToString(f->TextureDomainType());
+ const bool bGridOrder = true;
+ const bool bNormalized = false;
+ ON_2dPoint corners[4] = {
+ f->TextureDomainCorner(bGridOrder,bNormalized,0),
+ f->TextureDomainCorner(bGridOrder,bNormalized,1),
+ f->TextureDomainCorner(bGridOrder,bNormalized,2),
+ f->TextureDomainCorner(bGridOrder,bNormalized,3)
+ };
+ text_log.Print("%ls texture domain. Grid corners: (%g,%g), (%g,%g), (%g,%g), (%g,%g)\n",
+ static_cast(s),
+ corners[0].x, corners[0].y,
+ corners[1].x, corners[1].y,
+ corners[2].x, corners[2].y,
+ corners[3].x, corners[3].y
+ );
+ ////const ON_SubDMeshFragment *fragments = f->MeshFragments();
+ ////if (nullptr != fragments)
+ ////{
+ ////ON_TextLogIndent indent1(text_log);
+ //// unsigned short fragment_count = 0;
+ //// const unsigned short expected_fragment_count = (unsigned short)((4 == face_edge_count) ? 1 : face_edge_count);
+ //// ON_3dPoint fragmentT[4];
+ //// bool bDamagedOrIncompleteFragments = false;
+ //// for (unsigned short fragdex = 0; fragdex <= expected_fragment_count && nullptr != fragments; fragdex++, fragments = fragments->m_next_fragment)
+ //// {
+ //// ++fragment_count;
+ //// if (expected_fragment_count != fragments->m_face_fragment_count)
+ //// {
+ //// bDamagedOrIncompleteFragments = true;
+ //// break;
+ //// }
+ //// if (fragdex != fragments->m_face_fragment_index)
+ //// {
+ //// bDamagedOrIncompleteFragments = true;
+ //// break;
+ //// }
+ //// }
+ //// if (expected_fragment_count == fragment_count)
+ //// {
+ //// fragments = f->MeshFragments();
+ //// if (1 == fragment_count)
+ //// {
+ //// if (fragments->GetTextureCoordinteCorners(bGridOrder, fragmentT))
+ //// {
+ //// text_log.Print("Quad face fragment texture corners = (%g,%g,%g), (%g,%g,%g), (%g,%g,%g), (%g,%g,%g)\n",
+ //// fragmentT[0].x, fragmentT[0].y, fragmentT[0].z,
+ //// fragmentT[1].x, fragmentT[1].y, fragmentT[1].z,
+ //// fragmentT[2].x, fragmentT[2].y, fragmentT[2].z,
+ //// fragmentT[3].x, fragmentT[3].y, fragmentT[3].z
+ //// );
+ //// }
+ //// else
+ //// {
+ //// text_log.Print("Quad face fragment texture corners not available.\n");
+ //// }
+ //// }
+ //// else
+ //// {
+ //// text_log.Print("%u face fragments.\n", (unsigned)expected_fragment_count);
+ //// ON_TextLogIndent indent2(text_log);
+ //// for (unsigned short fragdex = 0; fragdex <= expected_fragment_count && nullptr != fragments; fragdex++, fragments = fragments->m_next_fragment)
+ //// {
+ //// if (fragments->GetTextureCoordinteCorners(bGridOrder, fragmentT))
+ //// {
+ //// text_log.Print("fragment[%u] texture corners = (%g,%g,%g), (%g,%g,%g), (%g,%g,%g), (%g,%g,%g)\n",
+ //// (unsigned)fragdex,
+ //// fragmentT[0].x, fragmentT[0].y, fragmentT[0].z,
+ //// fragmentT[1].x, fragmentT[1].y, fragmentT[1].z,
+ //// fragmentT[2].x, fragmentT[2].y, fragmentT[2].z,
+ //// fragmentT[3].x, fragmentT[3].y, fragmentT[3].z
+ //// );
+ //// }
+ //// else
+ //// {
+ //// text_log.Print("fragment[%u] texture corners not available.\n", (unsigned)fragdex);
+ //// }
+ //// }
+ //// }
+ //// }
+ //// else if (nullptr == fragments)
+ //// {
+ //// bDamagedOrIncompleteFragments = true;
+ //// }
+ //// if (bDamagedOrIncompleteFragments )
+ //// text_log.Print("Damaged or incomplete mesh fragments.\n");
+ ////}
+ }
+
text_log.PopIndent();
}
@@ -4017,13 +5402,11 @@ void ON_SubD::DestroyRuntimeCache( bool bDelete )
const ON_SubDLevel* level = dimple->SubDLevel(level_index);
if (level)
{
- level->ClearBoundingBox();
- level->ClearEdgeFlags();
- level->ClearSubdivisonAndLimitPoints();
- level->m_limit_mesh = ON_SubDLimitMesh::Empty;
+ level->ClearEvaluationCache();
level->AggregateComponentStatus().MarkAsNotCurrent();
}
}
+ dimple->ChangeContentSerialNumber();
}
return;
}
@@ -4051,7 +5434,8 @@ bool ON_SubD::GetBBox(
ON_BoundingBox bbox = ON_BoundingBox::EmptyBoundingBox;
bool rc = false;
- bbox = ActiveLevel().BoundingBox();
+ // GetBBox must always returns the control net box - it contains both the surface and the control net.
+ bbox = ActiveLevel().ControlNetBoundingBox();
rc = bbox.IsValid();
if (rc)
{
@@ -4129,9 +5513,9 @@ bool ON_SubD::HasBrepForm() const
}
//virtual
-ON_Brep* ON_SubD::BrepForm( ON_Brep*) const
+ON_Brep* ON_SubD::BrepForm( ON_Brep* destination_brep) const
{
- return 0;
+ return nullptr;
}
//virtual
@@ -4200,7 +5584,7 @@ void ON_SubD::ShareDimple(const ON_SubD& other_subd)
}
}
-void ON_SubD::ShareDimple(const class ON_SubDLimitMeshImpl& subd_limple)
+void ON_SubD::ShareDimple(const class ON_SubDMeshImpl& subd_limple)
{
std::shared_ptr limple_sp(subd_limple.m_subdimple_wp.lock());
@@ -4210,7 +5594,7 @@ void ON_SubD::ShareDimple(const class ON_SubDLimitMeshImpl& subd_limple)
{
// weak pointer is nullptr, meaning there are no known references to the
// subd used to create this limit mesh.
- const_cast(subd_limple).ClearFragmentFacePointers(true);
+ const_cast(subd_limple).ClearFragmentFacePointers(true);
}
if (subd_imple != limple_sp.get())
@@ -4220,7 +5604,7 @@ void ON_SubD::ShareDimple(const class ON_SubDLimitMeshImpl& subd_limple)
}
}
-void ON_SubD::SwapDimple(class ON_SubDLimitMeshImpl& subd_limple )
+void ON_SubD::SwapDimple(class ON_SubDMeshImpl& subd_limple )
{
std::shared_ptr limple_sp(subd_limple.m_subdimple_wp.lock());
if (m_subdimple_sp.get() != limple_sp.get())
@@ -4269,15 +5653,6 @@ void ON_SubD::Destroy()
m_subdimple_sp.reset();
}
-bool ON_SubD::SetSubDType(
- ON_SubD::SubDType subdivision_type
- )
-{
- ON_SubDimple* subdimple = SubDimple(true);
- return (subdimple) ? subdimple->SetSubDType(subdivision_type) : ON_SUBD_RETURN_ERROR(false);
-}
-
-
class ON_SubDVertex* ON_SubD::AddVertex(
const double* P
)
@@ -4293,7 +5668,8 @@ class ON_SubDVertex* ON_SubD::AddVertex(
ON_SubDimple* subdimple = SubDimple(true);
if ( 0 == subdimple )
return 0;
- class ON_SubDVertex* v = subdimple->AllocateVertex(vertex_tag, 0, P); // 0 = level
+ const unsigned int level_index = subdimple->ActiveLevelIndex();
+ class ON_SubDVertex* v = subdimple->AllocateVertex(vertex_tag, level_index, P); // 0 = level
subdimple->AddVertexToLevel(v);
return v;
}
@@ -4313,6 +5689,9 @@ class ON_SubDEdge* ON_SubDimple::AddEdge(
if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v1_sector_weight,true) )
return ON_SUBD_RETURN_ERROR(nullptr);
+ if ( nullptr != v0 && nullptr != v1 && v0->SubdivisionLevel() != v1->SubdivisionLevel() )
+ return ON_SUBD_RETURN_ERROR(nullptr);
+
const bool bEdgeTagSet = ON_SubD::EdgeTagIsSet(edge_tag);
if ( bEdgeTagSet
@@ -4341,6 +5720,13 @@ class ON_SubDEdge* ON_SubDimple::AddEdge(
if ( nullptr == e)
return ON_SUBD_RETURN_ERROR(nullptr);
+ if ( nullptr != v0 )
+ e->SetSubdivisionLevel(v0->SubdivisionLevel());
+ else if ( nullptr != v1 )
+ e->SetSubdivisionLevel(v1->SubdivisionLevel());
+ else if ( ActiveLevelIndex() < ON_UNSET_UINT_INDEX )
+ e->SetSubdivisionLevel(ActiveLevelIndex());
+
for (unsigned int i = 0; i < 2; i++)
{
ON_SubDVertex* v = (i ? v1 : v0);
@@ -4356,9 +5742,6 @@ class ON_SubDEdge* ON_SubDimple::AddEdge(
return ON_SUBD_RETURN_ERROR(nullptr);
}
v->m_edges[v->m_edge_count++] = ON_SubDEdgePtr::Create(e, i);
- if (e->m_level < v->m_level)
- e->m_level = v->m_level;
- //v->m_vertex_edge_order = ON_SubD::VertexEdgeOrder::unset;
}
}
@@ -4412,7 +5795,7 @@ ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext(
if ( ON_SubD::VertexTagIsSet(v0_tag) && ON_SubD::VertexTagIsSet(v1_tag) )
{
if (2 == edge_face_count)
- edge_tag = ON_SubD::EdgeTag::X;
+ edge_tag = ON_SubD::EdgeTag::SmoothX;
break;
}
@@ -4422,6 +5805,69 @@ ON_SubD::EdgeTag ON_SubD::EdgeTagFromContext(
return edge_tag;
}
+const ON_SubDVertex* ON_SubD::FindVertex(
+ const double* control_net_point,
+ double distance_tolerance
+) const
+{
+ if (nullptr == control_net_point )
+ return nullptr;
+ const ON_3dPoint P(control_net_point);
+ if ( false == P.IsValid() )
+ return nullptr;
+ if ( false == (0.0 <= distance_tolerance))
+ return nullptr;
+
+ const ON_SubDVertex* best_v = nullptr;
+ ON_SubDVertexIterator vit(*this);
+ for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex())
+ {
+ const double d = P.DistanceTo(v->ControlNetPoint());
+ if (0.0 == d)
+ return v;
+ if (d < distance_tolerance && (nullptr == best_v || d <= distance_tolerance) )
+ {
+ distance_tolerance = d;
+ best_v = v;
+ }
+ }
+ return best_v;
+}
+
+const ON_SubDVertex* ON_SubD::FindOrAddVertex(
+ const double* control_net_point,
+ double distance_tolerance
+)
+{
+ if (nullptr == control_net_point)
+ return nullptr;
+ const ON_3dPoint P(control_net_point);
+ if (false == P.IsValid())
+ return nullptr;
+ if (false == (0.0 <= distance_tolerance))
+ return nullptr;
+ const ON_SubDVertex* v = FindVertex(&P.x, distance_tolerance);
+ return (nullptr != v) ? v : AddVertex(&P.x);
+}
+
+const ON_SubDEdgePtr ON_SubD::FindEdge(
+ const class ON_SubDVertex* v0,
+ const class ON_SubDVertex* v1
+) const
+{
+ return ON_SubDEdge::FromVertices(v0, v1);
+}
+
+const ON_SubDEdgePtr ON_SubD::FindOrAddEdge(
+ class ON_SubDVertex* v0,
+ class ON_SubDVertex* v1
+)
+{
+ ON_SubDEdgePtr eptr = ON_SubDEdge::FromVertices(v0, v1);
+ if (nullptr == eptr.Edge())
+ eptr = ON_SubDEdgePtr::Create(AddEdge(v0, v1), 0);
+ return eptr;
+}
class ON_SubDEdge* ON_SubD::AddEdge(
ON_SubDVertex* v0,
@@ -4437,21 +5883,10 @@ class ON_SubDEdge* ON_SubD::AddEdge(
ON_SubDVertex* v1
)
{
- // NO automatic setting - causes more problems than it helps.
+ // NO automatic edge tag setting - causes more problems than it helps.
// Users can call ON_SubD::EdgeTagFromContext() if they want to
// preset the edge tag based on context.
- ////if (ON_SubD::EdgeTag::Unset == edge_tag && nullptr != v0 && nullptr != v1 )
- ////{
- //// if ( v0->IsCreaseOrCornerOrDart() && v1->IsCreaseOrCornerOrDart() )
- //// edge_tag = ON_SubD::EdgeTag::Crease;
- //// else if ( ON_SubD::VertexTag::Unset != v0->m_vertex_tag
- //// && ON_SubD::VertexTag::Unset != v1->m_vertex_tag
- //// && (v0->IsSmooth() || v1->IsSmooth())
- //// )
- //// edge_tag = ON_SubD::EdgeTag::Smooth;
- ////}
-
return AddEdgeWithSectorCoefficients(
edge_tag,
v0,
@@ -4475,17 +5910,47 @@ ON_SubDEdge* ON_SubD::AddEdgeWithSectorCoefficients(
return ON_SUBD_RETURN_ERROR(nullptr);
}
+
class ON_SubDFace* ON_SubDimple::AddFace(
unsigned int edge_count,
const ON_SubDEdgePtr* edge
+)
+{
+ return AddFace(nullptr, edge_count, edge);
+}
+
+class ON_SubDFace* ON_SubDimple::AddFace(
+ const ON_SubDFace* candidate_face,
+ unsigned int edge_count,
+ const ON_SubDEdgePtr* edge
)
{
if ( edge_count > 0 && nullptr == edge)
return ON_SUBD_RETURN_ERROR(nullptr);
- ON_SubDFace* f = AllocateFace();
+ unsigned f_level = 0;
+ bool bHaveLevel = false;
+ for (unsigned int i = 0; i < edge_count; i++)
+ {
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(edge[i].m_ptr);
+ if (nullptr == e)
+ continue;
+ if (bHaveLevel)
+ {
+ if (e->SubdivisionLevel() != f_level)
+ return ON_SUBD_RETURN_ERROR(nullptr);
+ }
+ else
+ {
+ f_level = e->SubdivisionLevel();
+ bHaveLevel = true;
+ }
+ }
+
+ ON_SubDFace* f = AllocateFace(candidate_face);
if ( nullptr == f)
return ON_SUBD_RETURN_ERROR(nullptr);
+ f->SetSubdivisionLevel(f_level);
if (edge_count > 0)
{
@@ -4498,9 +5963,7 @@ class ON_SubDFace* ON_SubDimple::AddFace(
}
}
-
ON_SubDEdgePtr* f_edge = f->m_edge4;
- unsigned short f_level = 0;
for (unsigned int i = 0; i < edge_count; i++)
{
if (4 == i)
@@ -4546,13 +6009,14 @@ class ON_SubDFace* ON_SubDimple::AddFace(
}
else
{
- if (2 == e->m_face_count)
- {
- // Getting this error in a valid situation means it is time
- // to support non-manifold subdivision objects.
- ON_SubDIncrementErrorCount();
- ON_WARNING("creating non-manifold subdivision object");
- }
+ // Dale Lear, April 3, 2019 RH-49843 - we support non-manifold SubD objects now.
+ //if (2 == e->m_face_count)
+ //{
+ // // Getting this error in a valid situation means it is time
+ // // to support non-manifold subdivision objects.
+ // ON_SubDIncrementErrorCount();
+ // ON_WARNING("creating non-manifold subdivision object");
+ //}
if (false == m_heap.GrowEdgeFaceArrayByOne(e))
{
e->m_status.SetDamagedState(true);
@@ -4561,11 +6025,7 @@ class ON_SubDFace* ON_SubDimple::AddFace(
e_faces = e->m_facex - 2;
}
e_faces[e->m_face_count++] = ON_SubDFacePtr::Create(f, eptr);
- if (f_level < e->m_level)
- f_level = e->m_level;
}
-
- f->m_level = f_level;
f->m_edge_count = (unsigned short)edge_count;
}
@@ -4575,114 +6035,79 @@ class ON_SubDFace* ON_SubDimple::AddFace(
return f;
}
+bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCoefficientsOnly)
+{
+ const double input_sector_coefficient[2] = { m_sector_coefficient[0], m_sector_coefficient[1] };
+ if (bUnsetEdgeSectorCoefficientsOnly)
+ {
+ if (input_sector_coefficient[0] >= 0.0 && input_sector_coefficient[0] <= 1.0
+ && input_sector_coefficient[1] >= 0.0 && input_sector_coefficient[1] <= 1.0
+ )
+ return false;
+ }
+ m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
+ m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
+ 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();
+ }
+ else if (2 == tagged_end_index)
+ {
+ if (ON_SubD::EdgeTag::Smooth == m_edge_tag && 2 == m_face_count )
+ m_edge_tag = ON_SubD::EdgeTag::SmoothX;
+
+ if (ON_SubD::EdgeTag::Smooth == m_edge_tag)
+ 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();
+ }
+ }
+ }
+
+ const bool bNoChanges
+ = input_sector_coefficient[0] == m_sector_coefficient[0]
+ && input_sector_coefficient[1] == m_sector_coefficient[1];
+
+ return (false == bNoChanges);
+}
+
unsigned int ON_SubDLevel::UpdateEdgeSectorCoefficients(
bool bUnsetEdgeSectorCoefficientsOnly
)
{
unsigned int changed_edge_count = 0;
- const bool bUnsetSubdivisiontType = (false == ON_SubD::IsQuadOrTriSubDType(m_subdivision_type));
-
for (const ON_SubDEdge* edge = m_edge[0]; nullptr != edge; edge = edge->m_next_edge)
{
- ON_SubDEdge* e = const_cast(edge);
- if (bUnsetEdgeSectorCoefficientsOnly)
- {
- if ( e->m_sector_coefficient[0] >= 0.0 && e->m_sector_coefficient[0] <= 1.0
- && e->m_sector_coefficient[1] >= 0.0 && e->m_sector_coefficient[1] <= 1.0
- )
- continue;
- }
- const double m_sector_coefficient0[2] = { e->m_sector_coefficient[0], e->m_sector_coefficient[1] };
- e->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
- e->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
- if (ON_SubD::EdgeTag::Smooth == edge->m_edge_tag || ON_SubD::EdgeTag::X == edge->m_edge_tag)
- {
- const unsigned int tagged_end_index = edge->TaggedEndIndex();
- if (tagged_end_index < 2)
- {
- e->m_sector_coefficient[tagged_end_index]
- = bUnsetSubdivisiontType
- ? ON_SubDSectorType::UnsetSectorWeight
- : ON_SubDSectorType::Create(m_subdivision_type, edge, tagged_end_index).SectorWeight();
- }
- else if (2 == tagged_end_index)
- {
- if (ON_SubD::EdgeTag::Smooth == edge->m_edge_tag)
- e->m_edge_tag = ON_SubD::EdgeTag::Crease;
- else if (ON_SubD::EdgeTag::X == edge->m_edge_tag)
- {
- e->m_sector_coefficient[0]
- = bUnsetSubdivisiontType
- ? ON_SubDSectorType::UnsetSectorWeight
- : ON_SubDSectorType::Create(m_subdivision_type, edge, 0).SectorWeight();
- e->m_sector_coefficient[1]
- = bUnsetSubdivisiontType
- ? ON_SubDSectorType::UnsetSectorWeight
- : ON_SubDSectorType::Create(m_subdivision_type, edge, 1).SectorWeight();
- }
- }
- }
- if (!(m_sector_coefficient0[0] == e->m_sector_coefficient[0] && m_sector_coefficient0[1] == e->m_sector_coefficient[1]))
- changed_edge_count++;
+ if (const_cast(edge)->UpdateEdgeSectorCoefficientsForExperts(bUnsetEdgeSectorCoefficientsOnly))
+ ++changed_edge_count;
}
-
return changed_edge_count;
}
-bool ON_SubDLevel::SetSubDType(
- ON_SubD::SubDType subd_type
- )
-{
- const bool bQuadSubD = (ON_SubD::SubDType::QuadCatmullClark == subd_type);
- const bool bTriSubD = (ON_SubD::SubDType::TriLoopWarren == subd_type);
- if (false == bQuadSubD && false == bTriSubD)
- return ON_SUBD_RETURN_ERROR(false);
-
- const bool bUpdateEdgeWeights = (m_subdivision_type != subd_type);
-
- if (bQuadSubD)
- {
- m_subdivision_type = subd_type;
- m_ordinary_vertex_valence = 4;
- m_ordinary_face_edge_count = 4;
- }
- else if (bTriSubD)
- {
- m_subdivision_type = subd_type;
- m_ordinary_vertex_valence = 6;
- m_ordinary_face_edge_count = 3;
- }
-
- if (bUpdateEdgeWeights)
- {
- UpdateEdgeSectorCoefficients(false);
- }
-
- return true;
-}
-
-bool ON_SubDimple::SetSubDType(
- ON_SubD::SubDType subd_type
- )
-{
- ON_SubDLevel* subd_level = ActiveLevel(0 == m_levels.UnsignedCount());
- if (nullptr == subd_level)
- return ON_SUBD_RETURN_ERROR(false);
-
- return subd_level->SetSubDType(subd_type);
-}
-
class ON_SubDFace* ON_SubD::AddTriangleFace(
- class ON_SubDEdge* edge0, bool bReverseEdge0,
- class ON_SubDEdge* edge1, bool bReverseEdge1,
- class ON_SubDEdge* edge2, bool bReverseEdge2
+ class ON_SubDEdge* edge0,
+ class ON_SubDEdge* edge1,
+ class ON_SubDEdge* edge2
)
{
- return AddTriangleFace(
- ON_SubDEdgePtr::Create(edge0, bReverseEdge0 ? 1 : 0),
- ON_SubDEdgePtr::Create(edge1, bReverseEdge1 ? 1 : 0),
- ON_SubDEdgePtr::Create(edge2, bReverseEdge2 ? 1 : 0)
- );
+ ON_SubDEdge* edges[3] = { edge0,edge1,edge2 };
+ return AddFace(edges, 3);
+}
+
+class ON_SubDFace* ON_SubD::AddQuadFace(
+ class ON_SubDEdge* edge0,
+ class ON_SubDEdge* edge1,
+ class ON_SubDEdge* edge2,
+ class ON_SubDEdge* edge3
+ )
+{
+ ON_SubDEdge* edges[4] = { edge0,edge1,edge2,edge3 };
+ return AddFace(edges, 4);
}
class ON_SubDFace* ON_SubD::AddTriangleFace(
@@ -4692,22 +6117,7 @@ class ON_SubDFace* ON_SubD::AddTriangleFace(
)
{
ON_SubDEdgePtr eptr3[3] = { edge0,edge1,edge2 };
- return AddFace(3, eptr3);
-}
-
-class ON_SubDFace* ON_SubD::AddQuadFace(
- class ON_SubDEdge* edge0, bool bReverseEdge0,
- class ON_SubDEdge* edge1, bool bReverseEdge1,
- class ON_SubDEdge* edge2, bool bReverseEdge2,
- class ON_SubDEdge* edge3, bool bReverseEdge3
-)
-{
- return AddQuadFace(
- ON_SubDEdgePtr::Create(edge0, bReverseEdge0 ? 1 : 0),
- ON_SubDEdgePtr::Create(edge1, bReverseEdge1 ? 1 : 0),
- ON_SubDEdgePtr::Create(edge2, bReverseEdge2 ? 1 : 0),
- ON_SubDEdgePtr::Create(edge3, bReverseEdge3 ? 1 : 0)
- );
+ return AddFace(eptr3, 3);
}
class ON_SubDFace* ON_SubD::AddQuadFace(
@@ -4718,18 +6128,88 @@ class ON_SubDFace* ON_SubD::AddQuadFace(
)
{
ON_SubDEdgePtr eptr4[4] = { edge0,edge1,edge2,edge3 };
- return AddFace(4, eptr4);
+ return AddFace(eptr4, 4);
}
class ON_SubDFace* ON_SubD::AddFace(
- unsigned int edge_count,
- const ON_SubDEdgePtr* edge
+ const ON_SimpleArray& edges
+)
+{
+ return AddFace(edges.Array(), edges.UnsignedCount());
+}
+
+class ON_SubDFace* ON_SubD::AddFace(
+ const ON_SimpleArray& edges
+)
+{
+ return AddFace(edges.Array(), edges.UnsignedCount());
+}
+
+class ON_SubDFace* ON_SubD::AddFace(
+ ON_SubDEdge *const* edge,
+ unsigned int edge_count
+ )
+{
+ if (edge_count < 3 || nullptr == edge)
+ return ON_SUBD_RETURN_ERROR(nullptr);
+ if (nullptr == edge[0] || nullptr == edge[0]->m_vertex[0] || nullptr == edge[0]->m_vertex[1] || edge[0]->m_vertex[0] == edge[0]->m_vertex[1])
+ return ON_SUBD_RETURN_ERROR(nullptr);
+ if ( edge[0] == edge[edge_count-1] )
+ return ON_SUBD_RETURN_ERROR(nullptr);
+ ON_SubDEdgePtr* eptr = (ON_SubDEdgePtr*)onmalloc(edge_count * sizeof(*eptr));
+ if (nullptr == eptr)
+ return ON_SUBD_RETURN_ERROR(nullptr);
+ eptr[0] = ON_SubDEdgePtr::Create(
+ edge[0],
+ (edge[0]->m_vertex[0] == edge[1]->m_vertex[0] || edge[0]->m_vertex[0] == edge[1]->m_vertex[1]) ? 1 : 0
+ );
+ eptr[edge_count - 1] = ON_SubDEdgePtr::Null;
+ for (unsigned int fei = 1; fei < edge_count; ++fei)
+ {
+ if (nullptr == edge[fei] || nullptr == edge[fei]->m_vertex[0] || nullptr == edge[fei]->m_vertex[1] || edge[fei]->m_vertex[0] == edge[fei]->m_vertex[1])
+ break;
+ if (edge[fei - 1] == edge[fei])
+ break;
+ const ON_SubDVertex* v = eptr[fei - 1].RelativeVertex(1);
+ if (nullptr == v)
+ break;
+ eptr[fei] = ON_SubDEdgePtr::Create(edge[fei], v == edge[fei]->m_vertex[0] ? 0 : 1);
+ if (v != eptr[fei].RelativeVertex(0))
+ break;
+ }
+
+ ON_SubDFace* face
+ = (eptr[edge_count - 1].IsNull() && (eptr[0].RelativeVertex(0) != eptr[edge_count - 1].RelativeVertex(1)))
+ ? AddFace(eptr, edge_count)
+ : nullptr;
+ onfree(eptr);
+ if (nullptr == face)
+ {
+ ON_SUBD_ERROR("Invalid input edge[] array");
+ }
+ return face;
+}
+
+class ON_SubDFace* ON_SubD::AddFace(
+ const ON_SubDEdgePtr* edge,
+ unsigned int edge_count
)
{
ON_SubDimple* subdimple = SubDimple(true);
return (nullptr != subdimple) ? subdimple->AddFace(edge_count, edge) : nullptr;
}
+
+class ON_SubDFace* ON_SubD::AddFace(
+ const ON_SubDFace* candidate_face,
+ const ON_SubDEdgePtr* edge,
+ unsigned int edge_count
+ )
+{
+ ON_SubDimple* subdimple = SubDimple(true);
+ return (nullptr != subdimple) ? subdimple->AddFace(candidate_face, edge_count, edge) : nullptr;
+}
+
bool ON_SubD::AddFaceEdgeConnection(
ON_SubDFace* face,
unsigned int i,
@@ -4849,15 +6329,64 @@ bool ON_SubD::RemoveFaceEdgeConnection(
return ON_SUBD_RETURN_ERROR(false);
ON_SubDEdge* edge = removed_edge.Edge();
- if ( nullptr == edge )
- return ON_SUBD_RETURN_ERROR(false);
-
- if (false == edge->RemoveFaceFromArray(face))
- return ON_SUBD_RETURN_ERROR(false);
+ if (nullptr != edge)
+ {
+ if (false == edge->RemoveFaceFromArray(face))
+ return ON_SUBD_RETURN_ERROR(false);
+ }
return true;
}
+bool ON_SubD::RemoveFaceConnections(
+ ON_SubDFace* face
+)
+{
+ if ( nullptr == face )
+ {
+ return ON_SUBD_RETURN_ERROR(false);
+ }
+ if (face->m_edge_count > 0)
+ {
+ ON_SubDEdgePtr removed_edge;
+ for (unsigned short fei = face->m_edge_count; fei > 0; --fei)
+ {
+ removed_edge = ON_SubDEdgePtr::Null;
+ if (false == face->RemoveEdgeFromArray(fei - 1, removed_edge))
+ return ON_SUBD_RETURN_ERROR(false);
+ ON_SubDEdge* edge = removed_edge.Edge();
+ if (nullptr != edge)
+ {
+ if (false == edge->RemoveFaceFromArray(face))
+ return ON_SUBD_RETURN_ERROR(false);
+
+ for (int evi = 0; evi < 2; ++evi)
+ {
+ ON_SubDVertex* v = const_cast(edge->m_vertex[evi]);
+ if (nullptr != v)
+ {
+ for (unsigned short vfi = 0; vfi < v->m_face_count; ++vfi)
+ {
+ if (face == v->m_faces[vfi])
+ {
+ for (++vfi; vfi < v->m_face_count; ++vfi)
+ v->m_faces[vfi - 1] = v->m_faces[vfi];
+ v->m_face_count--;
+ break;
+ }
+ }
+ }
+ }
+
+ }
+ }
+ face->m_edge_count = 0;
+ }
+
+ return true;
+}
+
+
static bool ON_SubDFace_GetSubdivisionPointError(
const class ON_SubDFace* face,
@@ -4873,24 +6402,42 @@ static bool ON_SubDFace_GetSubdivisionPointError(
return ON_SUBD_RETURN_ERROR(false);
}
+const ON_3dPoint ON_SubDFace::SubdivisionPoint() const
+{
+ ON_3dPoint S;
+ return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint;
+}
+
bool ON_SubDFace::GetSubdivisionPoint(
- ON_SubD::SubDType subd_type,
- bool bUseSavedSubdivisionPoint,
- double subdivision_point[3]
- ) const
+ double subdivision_point[3]
+) const
+{
+ if (nullptr == subdivision_point)
+ return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, false);
+
+ if (GetSavedSubdivisionPoint(subdivision_point))
+ return true;
+
+ if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point))
+ {
+ SetSavedSubdivisionPoint(subdivision_point);
+ return true;
+ }
+
+ return false;
+}
+
+bool ON_SubDFace::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const
{
if (nullptr == subdivision_point)
return ON_SubDFace_GetSubdivisionPointError(this,subdivision_point,false);
- if (bUseSavedSubdivisionPoint && GetSavedSubdivisionPoint(subd_type,subdivision_point))
- return true;
-
const unsigned int count = m_edge_count;
if (count < 3)
return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true);
double displacementV[3] = { 0 };
- const bool bApplyDisplacement = GetDisplacement(subd_type,displacementV);
+ const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV);
const class ON_SubDEdgePtr* edge_ptr = m_edge4;
@@ -4929,16 +6476,11 @@ bool ON_SubDFace::GetSubdivisionPoint(
subdivision_point[2] += displacementV[2];
}
- if (bUseSavedSubdivisionPoint)
- SetSavedSubdivisionPoint(subd_type,subdivision_point);
-
-
return true;
}
if (3 == count)
{
- // most common case in triangle subdivision schemes and
// 2nd most common case in quad subdivision schemes
subdivision_point[0] = (vertexP[0][0] + vertexP[1][0] + vertexP[2][0]) / 3.0;
subdivision_point[1] = (vertexP[0][1] + vertexP[1][1] + vertexP[2][1]) / 3.0;
@@ -4951,9 +6493,6 @@ bool ON_SubDFace::GetSubdivisionPoint(
subdivision_point[2] += displacementV[2];
}
- if (bUseSavedSubdivisionPoint)
- SetSavedSubdivisionPoint(subd_type,subdivision_point);
-
return true;
}
@@ -5013,53 +6552,64 @@ bool ON_SubDFace::GetSubdivisionPoint(
subdivision_point[1] += displacementV[1];
subdivision_point[2] += displacementV[2];
}
-
- if (bUseSavedSubdivisionPoint)
- SetSavedSubdivisionPoint(subd_type,subdivision_point);
-
return true;
}
+int ON_SubDComponentBase::CompareId(
+ const ON_SubDComponentBase* lhs,
+ const ON_SubDComponentBase* rhs
+)
+{
+ if (lhs == rhs)
+ return 0;
+ // nulls at end of list
+ if (nullptr == rhs)
+ return -1;
+ if (nullptr == lhs)
+ return 1;
+ if (lhs->m_id < rhs->m_id)
+ return -1;
+ if (lhs->m_id > rhs->m_id)
+ return 1;
+ return 0;
+}
+
+void ON_SubDComponentBase::ClearSavedSubdivisionPoint() const
+{
+ Internal_ClearSubdivisionPointAndSurfacePointFlags();
+}
+
bool ON_SubDComponentBase::SetSavedSubdivisionPoint(
- ON_SubD::SubDType subd_type,
const double subdivision_point[3]
) const
{
- if (ON_SubD::SubDType::Unset == subd_type)
+ if (nullptr == subdivision_point)
{
- ClearSavedSubdivisionPoint();
+ Internal_ClearSubdivisionPointAndSurfacePointFlags();
return true;
}
- if ( nullptr != subdivision_point
- && ON_IsValid(subdivision_point[0])
+ if ( ON_IsValid(subdivision_point[0])
&& ON_IsValid(subdivision_point[1])
&& ON_IsValid(subdivision_point[2])
)
{
- const unsigned char c = (unsigned char)subd_type;
- if ( c != ON_SUBD_CACHE_TYPE(m_saved_points_flags))
- m_saved_points_flags = 0U;
m_saved_subd_point1[0] = subdivision_point[0];
m_saved_subd_point1[1] = subdivision_point[1];
m_saved_subd_point1[2] = subdivision_point[2];
- m_saved_points_flags |= (ON_SUBD_CACHE_POINT_FLAG_MASK | c);
+ m_saved_points_flags |= ON_SUBD_CACHE_POINT_FLAG_BIT;
return true;
}
- ClearSavedSubdivisionPoint();
+ Internal_ClearSubdivisionPointAndSurfacePointFlags();
return ON_SUBD_RETURN_ERROR(false);
}
bool ON_SubDComponentBase::GetSavedSubdivisionPoint(
- ON_SubD::SubDType subd_type,
double subdivision_point[3]
) const
{
- if ( 0 == (ON_SUBD_CACHE_POINT_FLAG_MASK & m_saved_points_flags) )
- return false;
-
- if ( subd_type != (ON_SubD::SubDType)ON_SUBD_CACHE_TYPE(m_saved_points_flags) )
+ if ( 0 == (ON_SUBD_CACHE_POINT_FLAG_BIT & m_saved_points_flags) )
return false;
if (nullptr != subdivision_point)
@@ -5072,12 +6622,81 @@ bool ON_SubDComponentBase::GetSavedSubdivisionPoint(
return true;
}
+const ON_3dPoint ON_SubDComponentBase::SavedSubdivisionPoint() const
+{
+ ON_3dPoint p(ON_3dPoint::NanPoint);
+ return GetSavedSubdivisionPoint(&p.x) ? p : ON_3dPoint::NanPoint;
+}
+
+unsigned const ON_SubDComponentBase::SubdivisionLevel() const
+{
+ return (unsigned)m_level;
+}
+
+void ON_SubDComponentBase::SetSubdivisionLevel(unsigned level)
+{
+ if (level <= 255U)
+ m_level = ((unsigned char)level);
+}
const ON_ComponentStatus ON_SubDComponentBase::Status() const
{
return m_status;
}
+bool ON_SubDComponentBase::IsActive() const
+{
+ return (m_id >= 0 && m_archive_id != ON_UNSET_UINT_INDEX);
+}
+
+
+bool ON_SubDComponentBase::Mark() const
+{
+ return m_status.RuntimeMark();
+}
+
+bool ON_SubDComponentBase::ClearMark() const
+{
+ return m_status.ClearRuntimeMark();
+}
+
+bool ON_SubDComponentBase::SetMark() const
+{
+ return m_status.SetRuntimeMark();
+}
+
+bool ON_SubDComponentBase::SetMark(
+ bool bMark
+) const
+{
+ return m_status.SetRuntimeMark(bMark);
+}
+
+bool ON_SubDComponentPtr::IsActive() const
+{
+ const ON_SubDComponentBase* c = this->ComponentBase();
+ return (nullptr != c) ? c->IsActive() : false;
+}
+
+bool ON_SubDVertexPtr::IsActive() const
+{
+ const ON_SubDVertex* v = ON_SUBD_VERTEX_POINTER(m_ptr);
+ return (nullptr != v) ? v->IsActive() : false;
+}
+
+bool ON_SubDEdgePtr::IsActive() const
+{
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_ptr);
+ return (nullptr != e) ? e->IsActive() : false;
+}
+
+bool ON_SubDFacePtr::IsActive() const
+{
+ const ON_SubDFace* f = ON_SUBD_FACE_POINTER(m_ptr);
+ return (nullptr != f) ? f->IsActive() : false;
+}
+
+
const ON_ComponentStatus ON_SubDVertex::NeighborhoodStatusLogicalOr(
bool bIncludeEdges,
bool bIncludeFaces
@@ -5178,7 +6797,7 @@ const ON_ComponentStatus ON_SubDFace::NeighborhoodStatusLogicalOr(bool bIncludeV
static void Internal_ClearFaceNeighborhoodCache(const ON_SubDFace* face)
{
// Clear cached values for every component associated with this face.
- face->ClearSavedSubdivisionPoint();
+ face->ClearSavedSubdivisionPoints();
const ON_SubDEdgePtr* eptr = face->m_edge4;
for (unsigned int efi = 0; efi < face->m_edge_count; efi++)
{
@@ -5191,16 +6810,13 @@ static void Internal_ClearFaceNeighborhoodCache(const ON_SubDFace* face)
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
if (nullptr != edge)
{
- edge->ClearSavedSubdivisionPoint();
- edge->UnsetSectorCoefficients();
+ edge->ClearSavedSubdivisionPoints();
+ edge->UnsetSectorCoefficientsForExperts();
for (unsigned int evi = 0; evi < 2; evi++)
{
const ON_SubDVertex* vertex = edge->m_vertex[evi];
if (nullptr != vertex)
- {
- vertex->ClearSavedSubdivisionPoint();
- vertex->ClearSavedLimitPoints();
- }
+ vertex->ClearSavedSubdivisionPoints();
}
}
eptr++;
@@ -5209,8 +6825,7 @@ static void Internal_ClearFaceNeighborhoodCache(const ON_SubDFace* face)
void ON_SubDVertex::VertexModifiedNofification() const
{
- ClearSavedSubdivisionPoint();
- ClearSavedLimitPoints();
+ ClearSavedSubdivisionPoints();
if (nullptr != m_edges)
{
for (unsigned short vei = 0; vei < m_edge_count; vei++)
@@ -5218,8 +6833,8 @@ void ON_SubDVertex::VertexModifiedNofification() const
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
if (nullptr != edge)
{
- edge->ClearSavedSubdivisionPoint();
- edge->UnsetSectorCoefficients();
+ edge->ClearSavedSubdivisionPoints();
+ edge->UnsetSectorCoefficientsForExperts();
}
}
@@ -5237,8 +6852,8 @@ void ON_SubDVertex::VertexModifiedNofification() const
void ON_SubDEdge::EdgeModifiedNofification() const
{
- ClearSavedSubdivisionPoint();
- UnsetSectorCoefficients();
+ ClearSavedSubdivisionPoints();
+ UnsetSectorCoefficientsForExperts();
for (unsigned int evi = 0; evi < 2; evi++)
{
const_cast(this)->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorWeight;
@@ -5265,12 +6880,33 @@ void ON_SubDEdge::EdgeModifiedNofification() const
}
}
-void ON_SubDEdge::UnsetSectorCoefficients() 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;
}
+void ON_SubDVertex::UnsetSectorCoefficientsForExperts(unsigned int relative_edge_end_dex) const
+{
+ for (unsigned short vei = 0; vei < m_edge_count; ++vei)
+ {
+ ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
+ if (nullptr == e)
+ continue;
+ ON__UINT_PTR edir = ON_SUBD_EDGE_DIRECTION(m_edges[vei].m_ptr);
+ const unsigned evi
+ = (relative_edge_end_dex < 2)
+ ? ((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;
+ else
+ {
+ e->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
+ e->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
+ }
+ }
+}
void ON_SubDFace::FaceModifiedNofification() const
{
@@ -5309,77 +6945,103 @@ void ON_SubDFace::FaceModifiedNofification() const
}
}
-void ON_SubDComponentBase::ClearSavedSubdivisionPoint() const
+void ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags() const
+{
+ 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
+{
+ return (0 != ON_SUBD_CACHE_POINT_FLAG(m_saved_points_flags));
+}
+
+void ON_SubDComponentBase::Internal_ClearSubdivisionPointFlag() const
{
ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags);
}
-ON_SubD::SubDType ON_SubDComponentBase::SavedSubdivisionPointType() const
+bool ON_SubDComponentBase::Internal_SurfacePointFlag() const
{
- return
- (0 != (ON_SUBD_CACHE_POINT_FLAG_MASK & m_saved_points_flags))
- ? ((ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags))
- : ON_SubD::SubDType::Unset;
+ return (0 != ON_SUBD_CACHE_LIMITLOC_FLAG(m_saved_points_flags));
}
-ON_SubD::SubDType ON_SubDComponentBase::DisplacementType() const
+bool ON_SubDComponentBase::Internal_ControlNetFragmentFlag() const
{
- return
- (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags))
- ? ((ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags))
- : ON_SubD::SubDType::Unset;
+ return (0 != ON_SUBD_CACHE_CTRLNETFRAG_FLAG(m_saved_points_flags));
}
-bool ON_SubDComponentBase::SetDisplacement(
- ON_SubD::SubDType subd_type,
+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
+{
+ return
+ (0 != ON_SUBD_CACHE_POINT_FLAG(m_saved_points_flags))
+ ? (ON_IS_VALID(m_saved_subd_point1[0]) && ON_IS_VALID(m_saved_subd_point1[1]) && ON_IS_VALID(m_saved_subd_point1[2]))
+ : false;
+}
+
+bool ON_SubDComponentBase::SubdivisionDisplacementIsNonzero() const
+{
+ return (0 != ON_SUBD_CACHE_DISPLACEMENT_FLAG(m_saved_points_flags))
+ ? (
+ (0.0 != m_displacement_V[0] || 0.0 != m_displacement_V[1] || 0.0 != m_displacement_V[2])
+ && ON_IS_VALID(m_displacement_V[0]) && ON_IS_VALID(m_displacement_V[1]) && ON_IS_VALID(m_displacement_V[2])
+ )
+ : false;
+}
+
+bool ON_SubDComponentBase::SetSubdivisionDisplacement(
const double displacement[3]
)
{
- if ( ON_SubD::SubDType::Unset != subd_type
- && nullptr != displacement
- && ON_IsValid(displacement[0]) && ON_IsValid(displacement[1]) && ON_IsValid(displacement[2])
+ if (
+ nullptr == displacement
+ || (0.0 == displacement[0] && 0.0 == displacement[1] && 0.0 == displacement[2])
)
{
- if (0.0 == displacement[0] && 0.0 == displacement[1] && 0.0 == displacement[2])
- {
- ClearDisplacement();
- return true;
- }
- ON_SubD::SubDType f = (ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags);
- if ( subd_type != f )
- m_saved_points_flags = (unsigned char)f;
- m_saved_points_flags |= ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK;
+ ClearSubdivisionDisplacement();
+ return true;
+ }
+
+ if ( ON_IsValid(displacement[0]) && ON_IsValid(displacement[1]) && ON_IsValid(displacement[2]) )
+ {
+ m_saved_points_flags |= ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT;
m_displacement_V[0] = displacement[0];
m_displacement_V[1] = displacement[1];
m_displacement_V[2] = displacement[2];
return true;
}
- if (ON_SubD::SubDType::Unset == subd_type)
- {
- ClearDisplacement();
- return true;
- }
+ ClearSubdivisionDisplacement();
return ON_SUBD_RETURN_ERROR(false);
}
-void ON_SubDComponentBase::ClearDisplacement() const
+void ON_SubDComponentBase::ClearSubdivisionDisplacement() const
{
- if (0 != (m_saved_points_flags & ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK))
+ if (0 != (m_saved_points_flags & ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT))
{
ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags);
ON_SUBD_CACHE_CLEAR_DISPLACEMENT_FLAG(m_saved_points_flags);
}
}
-bool ON_SubDComponentBase::GetDisplacement(
- ON_SubD::SubDType subd_type,
+bool ON_SubDComponentBase::GetSubdivisionDisplacement(
double displacement[3]
) const
{
- const bool rc = (0 != (ON_SUBD_CACHE_DISPLACEMENT_FLAG_MASK & m_saved_points_flags))
- && subd_type == (ON_SubD::SubDType)(ON_SUBD_CACHE_TYPE_MASK & m_saved_points_flags);
+ const bool rc = (0 != (ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT & m_saved_points_flags));
if (nullptr != displacement)
{
if (rc)
@@ -5398,6 +7060,27 @@ bool ON_SubDComponentBase::GetDisplacement(
return rc;
}
+const ON_3dVector ON_SubDComponentBase::SubdivisionDisplacement() const
+{
+ ON_3dVector v(ON_3dVector::ZeroVector);
+ return GetSubdivisionDisplacement(&v.x) ? v : ON_3dVector::ZeroVector;
+}
+
+void ON_SubDComponentBase::Internal_SetSavedSurfacePointFlag(bool bSavedSurfacePointFlag) const
+{
+ if (bSavedSurfacePointFlag)
+ m_saved_points_flags |= ON_SUBD_CACHE_LIMITLOC_FLAG_BIT;
+ else
+ Internal_ClearSurfacePointFlag();
+}
+
+void ON_SubDComponentBase::Internal_SetSavedControlNetFragmentFlag(bool bSavedControlNetFragmentFlag) const
+{
+ if (bSavedControlNetFragmentFlag)
+ m_saved_points_flags |= ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT;
+ else
+ Internal_ClearSurfacePointFlag();
+}
bool ON_SubDFace::ReverseEdgeList()
{
@@ -5601,24 +7284,55 @@ unsigned int ON_SubDEdge::GetFacePointSum(
return n;
}
+const ON_3dPoint ON_SubDEdge::SubdivisionPoint() const
+{
+ ON_3dPoint S;
+ return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint;
+}
+
bool ON_SubDEdge::GetSubdivisionPoint(
- ON_SubD::SubDType subd_type,
- bool bUseSavedSubdivisionPoint,
- double subdivision_point[3]
- ) const
+ double subdivision_point[3]
+) const
{
if (nullptr == subdivision_point)
return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, false);
- if (bUseSavedSubdivisionPoint && GetSavedSubdivisionPoint(subd_type, subdivision_point))
+ if (GetSavedSubdivisionPoint(subdivision_point))
return true;
+ if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point))
+ {
+ SetSavedSubdivisionPoint(subdivision_point);
+ return true;
+ }
+
+ return false;
+}
+
+const ON_3dPoint ON_SubDEdge::ControlNetCenterPoint() const
+{
+ return 0.5*(ControlNetPoint(0) + ControlNetPoint(1));
+}
+
+const ON_3dVector ON_SubDEdge::ControlNetCenterNormal(
+ unsigned int edge_face_index
+) const
+{
+ const ON_SubDFace* face = Face(edge_face_index);
+ return (nullptr != face) ? face->ControlNetCenterNormal() : ON_3dVector::NanVector;
+}
+
+bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const
+{
+ if (nullptr == subdivision_point)
+ return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, false);
+
const ON_SubDVertex* edge_vertex[2] = { m_vertex[0], m_vertex[1] };
if (nullptr == edge_vertex[0] || nullptr == edge_vertex[1])
return ON_SubDEdge_GetSubdivisionPointError(this, subdivision_point, nullptr, true);
double displacementV[3] = { 0 };
- const bool bApplyDisplacement = GetDisplacement(subd_type,displacementV);
+ const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV);
const double* edgeP[2] = { edge_vertex[0]->m_P, edge_vertex[1]->m_P };
@@ -5651,7 +7365,7 @@ bool ON_SubDEdge::GetSubdivisionPoint(
if (
ON_UNSET_UINT_INDEX == tagged_end
|| 0.5 == m_sector_coefficient[tagged_end]
- || (ON_SubD::EdgeTag::X == m_edge_tag)
+ || (ON_SubD::EdgeTag::SmoothX == m_edge_tag)
)
{
// ignore edge weights
@@ -5694,9 +7408,6 @@ bool ON_SubDEdge::GetSubdivisionPoint(
subdivision_point[2] += displacementV[2];
}
- if (bUseSavedSubdivisionPoint)
- SetSavedSubdivisionPoint(subd_type,subdivision_point);
-
return true;
}
@@ -5714,9 +7425,6 @@ bool ON_SubDEdge::GetSubdivisionPoint(
subdivision_point[2] += displacementV[2];
}
- if (bUseSavedSubdivisionPoint)
- SetSavedSubdivisionPoint(subd_type,subdivision_point);
-
return true;
}
@@ -5734,9 +7442,6 @@ bool ON_SubDEdge::GetSubdivisionPoint(
subdivision_point[2] += displacementV[2];
}
- if (bUseSavedSubdivisionPoint)
- SetSavedSubdivisionPoint(subd_type,subdivision_point);
-
return true;
}
@@ -5753,9 +7458,6 @@ bool ON_SubDEdge::GetSubdivisionPoint(
subdivision_point[2] += displacementV[2];
}
- if (bUseSavedSubdivisionPoint)
- SetSavedSubdivisionPoint(subd_type,subdivision_point);
-
return true;
}
@@ -5919,380 +7621,6 @@ private:
ON_ScratchBuffer& operator-(const ON_ScratchBuffer&);
};
-////static bool IsSmoothManifoldEdge(const ON_SubDEdge* edge)
-////{
-//// return (nullptr != edge && (ON_SubD::EdgeTag::Smooth == edge->m_edge_tag || ON_SubD::EdgeTag::X == edge->m_edge_tag) && 2 == edge->m_face_count);
-////}
-
-////unsigned int ON_SubDimple::GetSector(
-//// const ON_SubDFace* starting_face,
-//// ON__UINT_PTR face_vertex_index,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// sector.m_edge_count = 0;
-//// sector.m_face_count = 0;
-////
-//// if (nullptr == starting_face || face_vertex_index >= starting_face->m_edge_count)
-//// return GetSectorError(sector);
-////
-//// unsigned short face_edge_index = (unsigned short)face_vertex_index;
-//// ON__UINT_PTR edge0_ptr = starting_face->EdgePtr(face_edge_index > 0 ? (face_edge_index - 1) : (starting_face->m_edge_count - 1)).m_ptr;
-//// ON__UINT_PTR edge1_ptr = starting_face->EdgePtr(face_edge_index).m_ptr;
-//// const ON_SubDEdge* edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr);
-//// const ON_SubDEdge* edge1 = ON_SUBD_EDGE_POINTER(edge1_ptr);
-//// if (nullptr == edge0 || nullptr == edge1)
-//// return GetSectorError(sector);
-//// ON__UINT_PTR edge0_end = 1 - ON_SUBD_EDGE_DIRECTION(edge0_ptr);
-//// ON__UINT_PTR edge1_end = ON_SUBD_EDGE_DIRECTION(edge1_ptr);
-////
-//// const ON_SubDVertex* vertex = edge0->m_vertex[edge0_end];
-//// if ( nullptr == vertex || vertex->m_face_count < 1 || vertex->m_edge_count < 2)
-//// return GetSectorError(sector);
-//// if (vertex != edge1->m_vertex[edge1_end])
-//// return GetSectorError(sector);
-////
-//// const unsigned int sector_capacity = vertex->m_face_count;
-//// const size_t buffer_capacity = (size_t)(3*sector_capacity + 2);
-//// ON__UINT_PTR stack_buffer[3 * 16 + 2];
-//// ON_ScratchBuffer buffer(buffer_capacity*sizeof(stack_buffer[0]), stack_buffer, sizeof(stack_buffer));
-//// if (nullptr == buffer.Buffer())
-//// return GetSectorError(sector);
-////
-//// const ON_SubDFace** sector_faces = (const ON_SubDFace**)buffer.Buffer();
-//// const ON_SubDEdge** sector_edges = (const ON_SubDEdge**)(sector_faces + sector_capacity);
-//// ON__UINT_PTR* sector_edge_ends = (ON__UINT_PTR*)(sector_edges + (sector_capacity + 1));
-////
-//// const ON_SubDFace* face0 = starting_face;
-//// sector_faces[0] = face0;
-//// sector_edges[0] = edge0;
-//// sector_edge_ends[0] = edge0_end;
-//// sector_edges[1] = edge1;
-//// sector_edge_ends[1] = edge1_end;
-//// unsigned int sector_index = 1;
-//// unsigned int right_side_sector_count = 1;
-////
-//// if (false == IsSmoothManifoldEdge(edge0) && false == IsSmoothManifoldEdge(edge1))
-//// {
-//// // both edges act as creases
-//// sector_edges[sector_capacity] = edge1;
-//// sector_edge_ends[sector_capacity] = edge1_end;
-//// }
-//// else
-//// {
-//// // at least one input edge is a smooth manifold edge (2 faces)
-//// for (unsigned int sector_side = 0; sector_side < 2; sector_side++)
-//// {
-//// const ON_SubDFace* face1 = nullptr;
-//// while (sector_index < sector_capacity)
-//// {
-//// if (false == IsSmoothManifoldEdge(edge1))
-//// break;
-////
-//// face1 = edge1->NeighborFace(face0,true);
-//// if (sector_faces[0] == face1)
-//// {
-//// // circled around vertex back where we started.
-//// // Since the edge trap at the end of the for loop
-//// // did not break, this is an error condition.
-//// return GetSectorError(sector);
-//// }
-////
-//// if (nullptr == face1 || face0 == face1)
-//// return GetSectorError(sector);
-////
-//// unsigned int face1_edge_count = face1->m_edge_count;
-//// if (face1_edge_count < 2)
-//// return GetSectorError(sector);
-////
-//// unsigned int face1_edge0_index = face1->EdgeArrayIndex(edge1);
-//// if (ON_UNSET_UINT_INDEX == face1_edge0_index)
-//// return GetSectorError(sector);
-//// edge0_ptr = face1->EdgePtr(face1_edge0_index).m_ptr;
-//// edge0 = ON_SUBD_EDGE_POINTER(edge0_ptr);
-//// if (edge0 != edge1)
-//// return GetSectorError(sector);
-//// ON__UINT_PTR edge0_dir = ON_SUBD_EDGE_DIRECTION(edge0_ptr); // edge0 - face1 natural orientation relationship
-//// edge0_end = edge1_end;
-////
-//// // If 1 == (edge0_end + edge0_dir), then face0 and face1 have same natural
-//// // orientations across shared edge and new edge1 = next edge on face1.
-//// // If 1 != edge0_to_edge1_index_delta, then face0 and face1 opposite same natural
-//// // orientations across shared edge and new edge1 = prev edge on face1.
-//// unsigned int face1_edge1_index
-//// = (1 == (edge0_end + edge0_dir))
-//// ? (face1_edge0_index + 1)
-//// : (face1_edge0_index + (face1_edge_count - 1));
-//// face1_edge1_index %= face1_edge_count;
-////
-//// edge1_ptr = face1->EdgePtr(face1_edge1_index).m_ptr;
-//// edge1 = ON_SUBD_EDGE_POINTER(edge1_ptr);
-//// if (nullptr == edge1)
-//// return GetSectorError(sector);
-//// ON__UINT_PTR edge1_dir = ON_SUBD_EDGE_DIRECTION(edge1_ptr); // edge1 - face1 natural orientation relationship
-//// edge1_end = (1 == ((edge0_end + edge0_dir + edge1_dir) % 2)) ? 0 : 1;
-//// if (vertex != edge1->m_vertex[edge1_end])
-//// return GetSectorError(sector);
-//// if (vertex == edge1->m_vertex[1 - edge1_end])
-//// return GetSectorError(sector);
-////
-//// face0 = face1;
-//// sector_faces[sector_index] = face0;
-//// if (0 == sector_side)
-//// {
-//// sector_edges[sector_index] = edge0;
-//// sector_edge_ends[sector_index] = edge0_end;
-//// sector_index++;
-//// if (edge1 == sector_edges[0])
-//// {
-//// // circled around back to where we started
-//// break;
-//// }
-//// }
-//// else
-//// {
-//// sector_edges[sector_index] = edge1;
-//// sector_edge_ends[sector_index] = edge1_end;
-//// sector_index++;
-//// }
-//// }
-////
-//// if (0 == sector_side)
-//// {
-//// // finished first side
-////
-//// // Mark where the first side information ends.
-//// right_side_sector_count = sector_index;
-////
-//// // Save the final boundary information in a place where
-//// // the 2nd pass will not write over it.
-//// sector_edges[sector_capacity] = edge1;
-//// sector_edge_ends[sector_capacity] = edge1_end;
-////
-//// if (sector_edges[0] == sector_edges[sector_capacity])
-//// {
-//// // If sector_edge_ends[0] is a smooth 2-faced edge, then
-//// // we circled around the vertex through smooth 2-faced edges
-//// // until we got back to the starting vertex.
-//// //
-//// // If sector_edge_ends[0] is a crease, then the vertex is a dart.
-//// //
-//// // If sector_edge_ends[0] has more than 2 faces, then we've gone
-//// // around a manifold sector that "begins/ends" at sector_edge_ends[0]
-//// break;
-//// }
-////
-//// // prepare for second side
-//// face0 = sector_faces[0];
-//// edge1 = sector_edges[0];
-//// edge1_end = sector_edge_ends[0];
-//// }
-//// else
-//// {
-//// // finished second side
-//// if (sector_faces[0] == face1)
-//// return GetSectorError(sector);
-////
-//// break;
-//// }
-//// }
-//// }
-////
-//// if (0 == sector_index)
-//// return GetSectorError(sector);
-////
-//// if (nullptr == sector_edges[0] || nullptr == sector_edges[sector_index - 1] || nullptr == sector_edges[sector_capacity])
-//// return GetSectorError(sector);
-////
-//// if (IsSmoothManifoldEdge(sector_edges[sector_capacity]))
-//// {
-//// if (right_side_sector_count < 2)
-//// return GetSectorError(sector);
-//// if (sector_index > right_side_sector_count)
-//// return GetSectorError(sector);
-//// if (sector_edges[0] != sector_edges[sector_capacity])
-//// return GetSectorError(sector);
-//// }
-//// else if (sector_index > right_side_sector_count)
-//// {
-//// if (IsSmoothManifoldEdge(sector_edges[sector_index-1]))
-//// return GetSectorError(sector);
-//// }
-////
-//// if (!InitializeSector(vertex, sector))
-//// return GetSectorError(sector);
-////
-//// if (false == const_cast(this)->m_heap.GrowVertexEdgeArray(§or, sector_index + 1))
-//// return GetSectorError(sector);
-//// if (false == const_cast(this)->m_heap.GrowVertexFaceArray(§or, sector_index))
-//// return GetSectorError(sector);
-//// unsigned int sector_face_count = 0;
-//// while (sector_index > right_side_sector_count)
-//// {
-//// sector_index--;
-//// sector.m_edges[sector_face_count] = ON_SubDEdgePtr::Create(sector_edges[sector_index], sector_edge_ends[sector_index]);
-//// sector.m_faces[sector_face_count] = sector_faces[sector_index];
-//// sector_face_count++;
-//// }
-//// for (sector_index = 0; sector_index < right_side_sector_count; sector_index++)
-//// {
-//// sector.m_edges[sector_face_count] = ON_SubDEdgePtr::Create(sector_edges[sector_index], sector_edge_ends[sector_index]);
-//// sector.m_faces[sector_face_count] = sector_faces[sector_index];
-//// sector_face_count++;
-//// }
-////
-//// sector.m_edges[sector_face_count] = ON_SubDEdgePtr::Create(sector_edges[sector_capacity], sector_edge_ends[sector_capacity]);
-//// sector.m_face_count = (unsigned short)sector_face_count;
-//// sector.m_edge_count = sector.m_face_count+1;
-////
-//// return sector_face_count;
-////}
-
-////unsigned int ON_SubDimple::GetSector(
-//// const ON_SubDVertex* vertex,
-//// const ON_SubDFace* face,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// unsigned int face_vertex_index = (nullptr != face) ? face->VertexIndex(vertex) : ON_UNSET_UINT_INDEX;
-//// if (ON_UNSET_UINT_INDEX == face_vertex_index)
-//// return GetSectorError(sector);
-//// return GetSector(face, face_vertex_index, sector);
-////}
-////
-////unsigned int ON_SubDimple::GetSector(
-//// const ON_SubDVertex* vertex,
-//// ON_SubDFacePtr face_ptr,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// if (nullptr == vertex)
-//// return GetSectorError(sector);
-//// return GetSector(vertex, ON_SUBD_FACE_POINTER(face_ptr.m_ptr), sector);
-////}
-////
-////unsigned int ON_SubDimple::GetSector(
-//// const ON_SubDEdge* smooth_edge,
-//// ON__UINT_PTR smooth_edge_end_index,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// if (nullptr == smooth_edge || smooth_edge_end_index > 1)
-//// return GetSectorError(sector);
-//// const ON_SubDVertex* vertex = smooth_edge->m_vertex[smooth_edge_end_index];
-//// if (nullptr == vertex)
-//// return GetSectorError(sector);
-////
-//// switch (smooth_edge->m_face_count)
-//// {
-//// case 0:
-//// if (!InitializeSector(vertex, sector))
-//// break;
-//// sector.m_edges = (ON_SubDEdgePtr*)const_cast(this)->m_heap.GrowArrayByOneElement(sector.m_edge_count, (ON__UINT_PTR*)sector.m_edges);
-//// sector.m_edges[sector.m_edge_count++] = ON_SubDEdgePtr::Create(smooth_edge, smooth_edge_end_index);
-//// return true;
-//// break;
-////
-//// case 1:
-//// return GetSector(vertex, ON_SUBD_FACE_POINTER(smooth_edge->m_face2[0].m_ptr), sector);
-//// break;
-////
-//// case 2:
-//// if (ON_SubD::EdgeTag::Smooth == smooth_edge->m_edge_tag)
-//// return GetSector(vertex, ON_SUBD_FACE_POINTER(smooth_edge->m_face2[0].m_ptr), sector);
-//// break;
-//// }
-////
-//// return GetSectorError(sector);
-////}
-////
-////unsigned int ON_SubDimple::GetSector(
-//// const ON_SubDVertex* vertex,
-//// const ON_SubDEdge* smooth_edge,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// if (nullptr == vertex || nullptr == smooth_edge)
-//// return GetSectorError(sector);
-//// unsigned int smooth_edge_end_index
-//// = (vertex == smooth_edge->m_vertex[0])
-//// ? 0
-//// : ((vertex == smooth_edge->m_vertex[1]) ? 1 : ON_UNSET_UINT_INDEX);
-//// if (ON_UNSET_UINT_INDEX == smooth_edge_end_index)
-//// return GetSectorError(sector);
-//// return GetSector(smooth_edge, smooth_edge_end_index, sector);
-////}
-////
-////
-////unsigned int ON_SubDimple::GetSector(
-//// ON_SubDEdgePtr smooth_edge_ptr,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// return GetSector(ON_SUBD_EDGE_POINTER(smooth_edge_ptr.m_ptr), ON_SUBD_EDGE_DIRECTION(smooth_edge_ptr.m_ptr), sector);
-////}
-////
-////unsigned int ON_SubD::GetSector(
-//// const ON_SubDFace* face,
-//// ON__UINT_PTR face_vertex_index,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// const ON_SubDimple* subdimple = SubDimple();
-//// return (nullptr != subdimple) ? subdimple->GetSector(face, face_vertex_index, sector) : GetSectorError(sector);
-////}
-////
-////unsigned int ON_SubD::GetSector(
-//// const ON_SubDVertex* vertex,
-//// const ON_SubDFace* face,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// const ON_SubDimple* subdimple = SubDimple();
-//// return (nullptr != subdimple) ? subdimple->GetSector(vertex, face, sector) : GetSectorError(sector);
-////}
-////
-////unsigned int ON_SubD::GetSector(
-//// const ON_SubDVertex* vertex,
-//// ON_SubDFacePtr face_ptr,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// const ON_SubDimple* subdimple = SubDimple();
-//// return (nullptr != subdimple) ? subdimple->GetSector(vertex, face_ptr, sector) : GetSectorError(sector);
-////}
-////
-////unsigned int ON_SubD::GetSector(
-//// const ON_SubDVertex* vertex,
-//// const ON_SubDEdge* smooth_edge,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// const ON_SubDimple* subdimple = SubDimple();
-//// return (nullptr != subdimple) ? subdimple->GetSector(vertex, smooth_edge, sector) : GetSectorError(sector);
-////}
-////
-////unsigned int ON_SubD::GetSector(
-//// const ON_SubDEdge* smooth_edge,
-//// ON__UINT_PTR smooth_edge_end_index,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// const ON_SubDimple* subdimple = SubDimple();
-//// return (nullptr != subdimple) ? subdimple->GetSector(smooth_edge, smooth_edge_end_index, sector) : GetSectorError(sector);
-////}
-////
-////unsigned int ON_SubD::GetSector(
-//// ON_SubDEdgePtr smooth_edge_ptr,
-//// ON_SubDVertex& sector
-//// ) const
-////{
-//// const ON_SubDimple* subdimple = SubDimple();
-//// return (nullptr != subdimple) ? subdimple->GetSector(smooth_edge_ptr, sector) : GetSectorError(sector);
-////}
-
-
-
class FACE_AND_FACE_POINT
{
public:
@@ -6312,17 +7640,17 @@ int FACE_AND_FACE_POINT::CompareFacePointer(const void* a, const void* b)
return 0;
}
-bool ON_SubDSectorLimitPoint::IsUnset() const
+bool ON_SubDSectorSurfacePoint::IsUnset() const
{
return (m_limitP[0] == ON_UNSET_VALUE);
}
-bool ON_SubDSectorLimitPoint::IsNan() const
+bool ON_SubDSectorSurfacePoint::IsNan() const
{
return !(m_limitP[0] == m_limitP[0]);
}
-bool ON_SubDSectorLimitPoint::IsZero() const
+bool ON_SubDSectorSurfacePoint::IsZero() const
{
const double* p = m_limitP;
const double* p1 = p+12;
@@ -6334,7 +7662,9 @@ bool ON_SubDSectorLimitPoint::IsZero() const
return true;
}
-bool ON_SubDSectorLimitPoint::IsSet() const
+bool ON_SubDSectorSurfacePoint::IsSet(
+ bool bUndefinedNormalIsPossible
+) const
{
double x, y;
const double* p = m_limitP;
@@ -6346,37 +7676,40 @@ bool ON_SubDSectorLimitPoint::IsSet() const
return false;
}
- p = m_limitT1;
- p1 = p+6;
- while (p < p1)
+ if (false == bUndefinedNormalIsPossible)
{
- const double* p2 = p+3;
+ 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 (!(y != 0.0))
+ return false;
+ }
+
+ p = m_limitN;
+ p1 = p + 3;
y = 0.0;
- while (p < p2)
+ while (p < p1)
{
x = *p++;
if (ON_UNSET_VALUE == x || !(x == x))
return false;
- if ( 0.0 != x )
- y = x;
+ y += x * x;
}
- if (!(y != 0.0))
+ if (!(fabs(y - 1.0) <= 1e-4))
return false;
}
- p = m_limitN;
- p1 = p+3;
- y = 0.0;
- while (p < p1)
- {
- x = *p++;
- if (ON_UNSET_VALUE == x || !(x == x))
- return false;
- y += x*x;
- }
- if (!(fabs(y - 1.0) <= 1e-4))
- return false;
-
return true;
}
@@ -6390,12 +7723,10 @@ void ON_SubDVertex::CopyFrom(
if (nullptr == src)
src = &ON_SubDVertex::Empty;
- ClearSavedLimitPoints();
+ ClearSavedSubdivisionPoints();
CopyBaseFrom(src);
m_vertex_tag = src->m_vertex_tag;
- //m_vertex_edge_order = src->m_vertex_edge_order;
- //m_vertex_facet_type = src->m_vertex_facet_type;
m_P[0] = src->m_P[0];
m_P[1] = src->m_P[1];
@@ -6404,14 +7735,13 @@ void ON_SubDVertex::CopyFrom(
if (bCopyLimitPointList)
{
- ON_SubD::SubDType limit_point_subd_type = src->SavedLimitPointType();
- if (ON_SubD::SubDType::Unset != limit_point_subd_type)
+ if ( src->SurfacePointIsSet() )
{
- for (const ON_SubDSectorLimitPoint* p = &src->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point)
+ for (const ON_SubDSectorSurfacePoint* p = &src->m_limit_point; nullptr != p; p = p->m_next_sector_limit_point)
{
- ON_SubDSectorLimitPoint limit_point = *p;
- limit_point.m_next_sector_limit_point = (ON_SubDSectorLimitPoint*)1; // disable checks
- SetSavedLimitPoint(limit_point_subd_type, limit_point);
+ ON_SubDSectorSurfacePoint limit_point = *p;
+ limit_point.m_next_sector_limit_point = (ON_SubDSectorSurfacePoint*)1; // disable checks
+ SetSavedSurfacePoint( true, limit_point);
}
}
}
@@ -6457,7 +7787,7 @@ static bool ON_SubDVertex_GetSubdivisionPointError(
ON_SubDIncrementErrorCount();
vertex->m_status.SetDamagedState(bDamagedState);
- vertex->ClearSavedSubdivisionPoint();
+ vertex->ClearSavedSubdivisionPoints();
if (nullptr != vertexP)
{
@@ -6469,17 +7799,39 @@ static bool ON_SubDVertex_GetSubdivisionPointError(
return true;
}
-bool ON_SubDVertex::GetGeneralQuadSubdivisionPoint(
+bool ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(
const class ON_SubDVertex* vertex,
- bool bUseSavedSubdivisionPoint,
double vertex_point[3]
- )
+)
{
- const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::QuadCatmullClark;
+ if (nullptr != vertex_point)
+ {
+ vertex_point[0] = ON_DBL_QNAN;
+ vertex_point[1] = ON_DBL_QNAN;
+ vertex_point[2] = ON_DBL_QNAN;
+ }
+
+ if (nullptr == vertex)
+ {
+ ON_SUBD_ERROR("input vertex is nullptr.");
+ return false;
+ }
+
+
+ const unsigned int n = vertex->m_face_count;
+ if (nullptr == vertex
+ || nullptr == vertex->m_faces
+ || nullptr == vertex->m_edges
+ || vertex->m_face_count != vertex->m_edge_count
+ || n < ON_SubDSectorType::MinimumSectorFaceCount(ON_SubD::VertexTag::Smooth)
+ )
+ {
+ ON_SUBD_ERROR("input vertex is not valid.");
+ return false;
+ }
const double* vertexP = vertex->m_P;
- const unsigned int n = vertex->m_face_count;
// It is critical to use the centroids of the neighboring faces
// in this step because the number of edges in each face's
@@ -6492,7 +7844,7 @@ bool ON_SubDVertex::GetGeneralQuadSubdivisionPoint(
if (nullptr != face)
{
double faceC[3];
- if (face->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoint, faceC))
+ if (face->GetSubdivisionPoint( faceC))
{
facePsum[0] += faceC[0];
facePsum[1] += faceC[1];
@@ -6535,15 +7887,11 @@ bool ON_SubDVertex::GetGeneralQuadSubdivisionPoint(
vertex_point[1] = v_weight*vertexP[1] + ef_weight*(edgePsum[1] + facePsum[1]);
vertex_point[2] = v_weight*vertexP[2] + ef_weight*(edgePsum[2] + facePsum[2]);
- if (bUseSavedSubdivisionPoint)
- vertex->SetSavedSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,vertex_point);
-
return true;
}
-bool ON_SubDVertex::GetQuadPoint(
+bool ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint(
const class ON_SubDVertex* vertex,
- bool bUseSavedSubdivisionPoint,
double vertex_point[3]
)
{
@@ -6558,7 +7906,8 @@ bool ON_SubDVertex::GetQuadPoint(
const unsigned int n = (nullptr != vertex->m_edges ? vertex->m_edge_count : 0);
if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag || ON_SubD::VertexTag::Dart == vertex->m_vertex_tag)
{
- if (n < 3 || n != vertex->m_face_count || nullptr == vertex->m_faces)
+ const unsigned int minimum_n = ON_SubDSectorType::MinimumSectorEdgeCount(vertex->m_vertex_tag);
+ if (n < minimum_n || n != vertex->m_face_count || nullptr == vertex->m_faces)
return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true);
double facePsum[3] = { 0 };
@@ -6580,7 +7929,7 @@ bool ON_SubDVertex::GetQuadPoint(
for (unsigned int i = 0; i < n; i++)
{
const ON_SubDFace* vface = vertex_faces[i];
- const unsigned int face_n = ON_SubDVertex::GetFacePointSum(vface, vertex, sum);
+ const unsigned int face_n = ON_SubDVertex::Internal_GetFacePointSum(vface, vertex, sum);
if (4 != face_n)
{
// The first face is a quadrangle and this face is not a quadrangle.
@@ -6588,7 +7937,7 @@ bool ON_SubDVertex::GetQuadPoint(
// It is critical to use the centroids of the neighboring faces
// for this vertex subdivision point because the number of edges
// in each face's boundary is not constant.
- return ON_SubDVertex::GetGeneralQuadSubdivisionPoint(vertex, bUseSavedSubdivisionPoint, vertex_point);
+ return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point);
}
facePsum[0] += sum[0];
facePsum[1] += sum[1];
@@ -6608,7 +7957,7 @@ bool ON_SubDVertex::GetQuadPoint(
// It is critical to use the centroids of the neighboring faces
// for this vertex subdivision point because the number of edges
// in each face's boundary is not constant.
- return ON_SubDVertex::GetGeneralQuadSubdivisionPoint(vertex, bUseSavedSubdivisionPoint, vertex_point);
+ return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point);
}
}
}
@@ -6623,7 +7972,7 @@ bool ON_SubDVertex::GetQuadPoint(
// in each face's boundary may not constant. In any case, this
// situation is not common and typically happens only on the
// first subdivision step.
- return ON_SubDVertex::GetGeneralQuadSubdivisionPoint(vertex, bUseSavedSubdivisionPoint, vertex_point);
+ return ON_SubDVertex::Internal_GetGeneralQuadSubdivisionPoint(vertex, vertex_point);
}
double edgePsum[3] = { 0 };
@@ -6669,9 +8018,6 @@ bool ON_SubDVertex::GetQuadPoint(
vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2];
}
- if (bUseSavedSubdivisionPoint)
- vertex->SetSavedSubdivisionPoint(ON_SubD::SubDType::QuadCatmullClark,vertex_point);
-
return true;
}
@@ -6680,24 +8026,41 @@ bool ON_SubDVertex::GetQuadPoint(
}
+const ON_3dPoint ON_SubDVertex::SubdivisionPoint() const
+{
+ ON_3dPoint S;
+ return (GetSubdivisionPoint(&S.x) && S.IsValid()) ? S : ON_3dPoint::NanPoint;
+}
+
bool ON_SubDVertex::GetSubdivisionPoint(
- ON_SubD::SubDType subd_type,
- bool bUseSavedSubdivisionPoint,
- double subdivision_point[3]
- ) const
+ double subdivision_point[3]
+) const
+{
+ if (nullptr == subdivision_point)
+ return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, nullptr, false);
+
+ if (GetSavedSubdivisionPoint(subdivision_point))
+ return true;
+
+ if (EvaluateCatmullClarkSubdivisionPoint(subdivision_point))
+ {
+ SetSavedSubdivisionPoint(subdivision_point);
+ return true;
+ }
+
+ return false;
+}
+
+bool ON_SubDVertex::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[3]) const
{
// This function is used to convert an arbitrary control polygon into the
// "level 1" subD. It cannot use the faster sub-D formulas because
// a face can have an arbitrary number of edges.
- if (nullptr == subdivision_point
- || (ON_SubD::SubDType::TriLoopWarren != subd_type && ON_SubD::SubDType::QuadCatmullClark != subd_type))
+ if (nullptr == subdivision_point )
return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, nullptr, false);
- if ( bUseSavedSubdivisionPoint && GetSavedSubdivisionPoint(subd_type,subdivision_point) )
- return true;
-
double displacementV[3] = { 0 };
- const bool bApplyDisplacement = GetDisplacement(subd_type,displacementV);
+ const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV);
const double* vertexP = m_P;
@@ -6707,10 +8070,7 @@ bool ON_SubDVertex::GetSubdivisionPoint(
if (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Dart == m_vertex_tag)
{
- if (ON_SubD::SubDType::QuadCatmullClark == subd_type)
- return ON_SubDVertex::GetQuadPoint(this, bUseSavedSubdivisionPoint, subdivision_point);
- else if (ON_SubD::SubDType::TriLoopWarren == subd_type)
- return ON_SubDVertex::GetTriPoint(this, bUseSavedSubdivisionPoint, subdivision_point);
+ return ON_SubDVertex::Internal_GetCatmullClarkSubdivisionPoint(this, subdivision_point);
}
if (ON_SubD::VertexTag::Crease == m_vertex_tag)
@@ -6765,9 +8125,6 @@ bool ON_SubDVertex::GetSubdivisionPoint(
subdivision_point[2] += displacementV[2];
}
- if (bUseSavedSubdivisionPoint)
- SetSavedSubdivisionPoint(subd_type,subdivision_point);
-
return true;
}
@@ -6788,9 +8145,6 @@ bool ON_SubDVertex::GetSubdivisionPoint(
subdivision_point[2] += displacementV[2];
}
- if (bUseSavedSubdivisionPoint)
- SetSavedSubdivisionPoint(subd_type,subdivision_point);
-
return true;
}
@@ -6798,7 +8152,7 @@ bool ON_SubDVertex::GetSubdivisionPoint(
return ON_SubDVertex_GetSubdivisionPointError(this, subdivision_point, vertexP, true);
}
-unsigned int ON_SubDVertex::GetFacePointSum(
+unsigned int ON_SubDVertex::Internal_GetFacePointSum(
const ON_SubDFace* face,
const ON_SubDVertex* vertex,
double* facePsum
@@ -6900,85 +8254,239 @@ unsigned int ON_SubDVertex::GetFacePointSum(
return 0;
}
-bool ON_SubDVertex::GetTriPoint(
- const class ON_SubDVertex* vertex,
- bool bUseSavedSubdivisionPoint,
- double vertex_point[3]
- )
+bool ON_SubDimple::LocalSubdivide(
+ ON_SubDFace const*const* face_list,
+ size_t face_list_count
+)
{
- if (nullptr == vertex || nullptr == vertex_point)
- return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, nullptr, false);
+ if (nullptr == face_list || face_list_count < 1 || m_levels.UnsignedCount() < 1)
+ return false;
- const double* vertexP = vertex->m_P;
-
- const unsigned int n = (nullptr != vertex->m_edges ? vertex->m_edge_count : 0);
- if (ON_SubD::VertexTag::Smooth == vertex->m_vertex_tag || ON_SubD::VertexTag::Dart == vertex->m_vertex_tag)
+ unsigned int level0_index = ON_UNSET_UINT_INDEX;
+ for (size_t i = 0; i < face_list_count; ++i)
{
- if (n < 3)
- return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true);
+ const ON_SubDFace* f = face_list[i];
+ if (nullptr == f || f->m_edge_count < 3 || f->SubdivisionLevel() >= m_levels.UnsignedCount())
+ continue;
+ level0_index = f->SubdivisionLevel();
+ break;
+ }
+
+ if (level0_index >= m_levels.UnsignedCount() || nullptr == m_levels[level0_index])
+ return ON_SUBD_RETURN_ERROR(false);
- double edgePsum[3] = { 0 };
- const ON_SubDEdgePtr* edges = vertex->m_edges;
- ON__UINT_PTR e_ptr;
- for (unsigned int i = 0; i < n; i++)
+ ClearHigherSubdivisionLevels(level0_index + 1);
+ if (level0_index + 1 != m_levels.UnsignedCount())
+ return ON_SUBD_RETURN_ERROR(false);
+
+ m_active_level = m_levels[level0_index];
+ if ( nullptr == m_active_level || 0 == m_active_level->m_face_count)
+ return ON_SUBD_RETURN_ERROR(false);
+
+ ON_SubDLevel& level0 = *m_levels[level0_index];
+ m_active_level = &level0;
+
+ level0.ClearRuntimeMarks(true, true, true);
+
+ unsigned face_count = 0;
+ unsigned edge_count = 0;
+ unsigned vertex_count = 0;
+ ON_3dPoint P;
+ for (size_t i = 0; i < face_list_count; ++i)
+ {
+ const ON_SubDFace* f = face_list[i];
+ if (nullptr == f || f->m_edge_count < 3 || level0_index != f->SubdivisionLevel() )
+ continue;
+ if (f->m_status.RuntimeMark())
+ continue;
+ f->m_status.SetRuntimeMark();
+ if ( false == f->GetSubdivisionPoint(P))
+ return ON_SUBD_RETURN_ERROR(false);
+ const ON_SubDEdgePtr* eptr = f->m_edge4;
+ for (unsigned short fei = 0; fei < f->m_edge_count; ++fei, ++eptr)
{
- e_ptr = edges[i].m_ptr;
- const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(e_ptr);
- if (nullptr != edge)
+ if (4 == fei)
{
- const ON_SubDVertex* edge_vertex = (vertex != edge->m_vertex[0]) ? edge->m_vertex[0] : edge->m_vertex[1];
- if (nullptr != edge_vertex)
- {
- const double* edgeP = edge_vertex->m_P;
- edgePsum[0] += edgeP[0];
- edgePsum[1] += edgeP[1];
- edgePsum[2] += edgeP[2];
- continue;
- }
+ eptr = f->m_edgex;
+ if (nullptr == eptr)
+ return ON_SUBD_RETURN_ERROR(false);
}
- // treat missing or damaged face as infinitesimally small
- edgePsum[0] += vertexP[0];
- edgePsum[1] += vertexP[1];
- edgePsum[2] += vertexP[2];
+ ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
+ if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1])
+ return ON_SUBD_RETURN_ERROR(false);
+ if (e->m_status.RuntimeMark())
+ continue;
+ e->UpdateEdgeSectorCoefficientsForExperts(true);
+ e->m_status.SetRuntimeMark();
+ if (false == e->GetSubdivisionPoint(P))
+ return ON_SUBD_RETURN_ERROR(false);
+ for (unsigned evi = 0; evi < 2; ++evi)
+ {
+ if (e->m_vertex[evi]->m_status.RuntimeMark())
+ continue;
+ e->m_vertex[evi]->m_status.SetRuntimeMark();
+ if ( false == e->m_vertex[evi]->GetSubdivisionPoint(P) )
+ return ON_SUBD_RETURN_ERROR(false);
+ ++vertex_count;
+ }
+ ++edge_count;
}
+ ++face_count;
+ }
+ if (face_count < 1 || edge_count < 3 || vertex_count < 3)
+ return false;
+ if (face_count >= level0.m_face_count)
+ return GlobalSubdivide();
- double v_weight, e_weight;
- if (3 == n)
- {
- v_weight = 0.4375; // 7/16
- e_weight = 0.1875; // 3/16 = (9/16) / 3
- }
- else
- {
- v_weight = 0.625; // 5/8
- e_weight = 0.375/((double)n); // = (3/8)/n
- }
- vertex_point[0] = v_weight*vertexP[0] + e_weight*edgePsum[0];
- vertex_point[1] = v_weight*vertexP[1] + e_weight*edgePsum[1];
- vertex_point[2] = v_weight*vertexP[2] + e_weight*edgePsum[2];
+ // Get face subdivision points
+ ON_SimpleArray faces(face_count);
+ ON_SimpleArray face_points(face_count);
+ for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face)
+ {
+ if (false == f0->m_status.RuntimeMark())
+ continue;
+ faces.Append(const_cast(f0));
+ P = f0->SubdivisionPoint();
+ if ( false == P.IsValid())
+ return ON_SUBD_RETURN_ERROR(false);
+ face_points.Append(P);
+ }
+ if (face_count != face_points.UnsignedCount())
+ return ON_SUBD_RETURN_ERROR(false);
- if (bUseSavedSubdivisionPoint)
- vertex->SetSavedSubdivisionPoint(ON_SubD::SubDType::TriLoopWarren,vertex_point);
+ // Get edge subdivision points
+ ON_SimpleArray edge_points(edge_count);
+ ON_SimpleArray edges(edge_count);
+ for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge)
+ {
+ if (false == e0->m_status.RuntimeMark())
+ continue;
+ P = (e0->IsSmooth() && 2 == e0->m_face_count && 2 == e0->MarkedFaceCount()) ? e0->SubdivisionPoint() : e0->ControlNetCenterPoint();
+ edge_points.Append(P);
+ edges.Append(const_cast(e0));
+ }
+ if ( edge_count != edge_points.UnsignedCount())
+ return ON_SUBD_RETURN_ERROR(false);
- return true;
+ // Set vertex points
+ for (const ON_SubDVertex* v0 = level0.m_vertex[0]; nullptr != v0; v0 = v0->m_next_vertex)
+ {
+ if (false == v0->m_status.RuntimeMark() || v0->m_edge_count < 2 || ((unsigned)v0->m_edge_count) != v0->MarkedEdgeCount())
+ continue;
+ P = v0->SubdivisionPoint();
+ ON_SubDVertex* v = const_cast(v0);
+ v->m_P[0] = P.x;
+ v->m_P[1] = P.y;
+ v->m_P[2] = P.z;
}
- // vertex->m_vertex_tag is damaged
- return ON_SubDVertex_GetSubdivisionPointError(vertex, vertex_point, vertexP, true);
+ // split edges
+ for (unsigned edex = 0; edex < edge_count; ++edex)
+ {
+ ON_SubDEdge* e = edges[edex];
+ e->EdgeModifiedNofification();
+ const ON_SubDEdge* new_edge = SplitEdge(e, edge_points[edex]);
+ if (nullptr == new_edge)
+ return ON_SUBD_RETURN_ERROR(false);
+ new_edge->m_status.ClearRuntimeMark();
+ e->m_status.SetRuntimeMark();
+ }
+
+ ON_SimpleArray fbdry(32);
+ ON_SimpleArray radial_edges(32);
+ for (unsigned fdex = 0; fdex < face_count; ++fdex)
+ {
+ ON_SubDFace* f = faces[fdex];
+ f->FaceModifiedNofification();
+ P = face_points[fdex];
+ fbdry.SetCount(0);
+ const unsigned e_count = f->GetEdgeArray(fbdry);
+ if (e_count < 6 || 0 != e_count %2)
+ return ON_SUBD_RETURN_ERROR(false);
+
+ // Get edges[] with edge[0] = marked edges.
+ const ON_SubDEdgePtr* eptrs = fbdry.Array();
+ if (nullptr != eptrs[0].RelativeVertex(0) && false == eptrs[0].RelativeVertex(0)->Mark() )
+ {
+ fbdry.Append(eptrs[0]);
+ eptrs = fbdry.Array() + 1;
+ }
+
+ // save face status and candidate_face
+ const ON_ComponentStatus fstatus = f->m_status;
+ const ON_SubDFace* candidate_face = f;
+
+ for (unsigned fei = 0; fei < e_count; ++fei)
+ {
+ ON_SubDEdge* e = eptrs[fei].Edge();
+ if ( nullptr == e)
+ return ON_SUBD_RETURN_ERROR(false);
+ const ON_SubDVertex* ev[2] = { eptrs[fei].RelativeVertex(0), eptrs[fei].RelativeVertex(1) };
+ if ( nullptr == ev[0] || nullptr == ev[1] || ev[0] == ev[1])
+ return ON_SUBD_RETURN_ERROR(false);
+ if (0 == fei % 2)
+ {
+ if (false == ev[0]->Mark())
+ return ON_SUBD_RETURN_ERROR(false);
+ if (ev[1]->Mark())
+ return ON_SUBD_RETURN_ERROR(false);
+ }
+ else
+ {
+ if (ev[0]->Mark())
+ return ON_SUBD_RETURN_ERROR(false);
+ if (false == ev[1]->Mark())
+ return ON_SUBD_RETURN_ERROR(false);
+ }
+ }
+
+ // remove face that will be subdivided;
+ for (unsigned short fei = 0; fei < f->m_edge_count; ++fei)
+ {
+ eptrs[fei].Edge()->RemoveFaceFromArray(f);
+ const_cast(eptrs[fei].RelativeVertex(0))->RemoveFaceFromArray(f);
+ }
+ ReturnFace(f);
+
+ ON_SubDVertex* center = AllocateVertex(ON_SubD::VertexTag::Smooth, level0_index, &P.x);
+ AddVertexToLevel(center);
+
+ radial_edges.SetCount(0);
+ 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);
+ radial_edges.Append(r);
+ }
+
+ ON_SubDEdge* r[2] = { nullptr,radial_edges[e_count / 2 - 1] };
+ for (unsigned fei = 0; fei < e_count; fei += 2)
+ {
+ r[0] = r[1];
+ r[1] = radial_edges[fei / 2];
+ const ON_SubDEdgePtr qbdry[4] = {
+ ON_SubDEdgePtr::Create(r[0],0),
+ eptrs[(fei + e_count - 1) % e_count],
+ eptrs[fei],
+ ON_SubDEdgePtr::Create(r[1],1)
+ };
+ ON_SubDFace* q = AddFace(candidate_face, 4, qbdry);
+ candidate_face = nullptr;
+ q->m_status = fstatus;
+ }
+
+ }
+
+ level0.ClearRuntimeMarks(true, true, false);
+ level0.ClearBoundingBox();
+ level0.ClearEvaluationCache();
+ level0.ClearEdgeFlags();
+
+ return true;
}
-
-unsigned int ON_SubDimple::GlobalSubdivide(
- ON_SubD::SubDType subdivision_type,
- bool bUseSavedSubdivisionPoints
- )
+unsigned int ON_SubDimple::GlobalSubdivide()
{
- const bool bQuadSubD = (ON_SubD::SubDType::QuadCatmullClark == subdivision_type);
- const bool bTriSubD = (ON_SubD::SubDType::TriLoopWarren == subdivision_type);
-
- if (false == bQuadSubD && false == bTriSubD)
- return ON_SUBD_RETURN_ERROR(0);
-
if (m_levels.UnsignedCount() <= 0)
return ON_SUBD_RETURN_ERROR(0U);
const unsigned int level0_index = m_levels.UnsignedCount()-1;
@@ -6986,57 +8494,48 @@ unsigned int ON_SubDimple::GlobalSubdivide(
if ( nullptr == m_levels[level0_index])
return ON_SUBD_RETURN_ERROR(0U);
- const ON_SubDLevel& level0 = *m_levels[level0_index];
+ ON_SubDLevel& level0 = *m_levels[level0_index];
if (level0.IsEmpty())
return ON_SUBD_RETURN_ERROR(0U);
if ( level0.m_edge_count <= 0U )
return ON_SUBD_RETURN_ERROR(0U);
+ level0.UpdateEdgeSectorCoefficients(true);
+
const unsigned int level1_index = level0_index+1;
- if (0 == level0_index && subdivision_type != level0.m_subdivision_type )
- {
- if (false == m_levels[level0_index]->SetSubDType(subdivision_type))
- return ON_SUBD_RETURN_ERROR(0);
- }
-
ON_SubDLevel* level1 = SubDLevel(level1_index,true);
if ( nullptr == level1 )
return ON_SUBD_RETURN_ERROR(0);
- if (false == level1->SetSubDType(subdivision_type))
- return ON_SUBD_RETURN_ERROR(0);
double P[3];
ON_SubDVertex* v;
- if (bQuadSubD)
+ // Add face points
+ for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face)
{
- // Add face points
- for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face)
+ if (false == f0->GetSubdivisionPoint(P))
+ continue;
+ if (nullptr == f0->m_subd_point1)
{
- if (false == f0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoints, P))
- continue;
- if (nullptr == f0->m_subd_point1)
- {
- const_cast(f0)->m_subd_point1 = v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, P);
- AddVertexToLevel(v);
- }
- else
- {
- v = const_cast(f0->m_subd_point1);
- v->m_P[0] = P[0];
- v->m_P[1] = P[1];
- v->m_P[2] = P[2];
- }
+ const_cast(f0)->m_subd_point1 = v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, P);
+ AddVertexToLevel(v);
+ }
+ else
+ {
+ v = const_cast(f0->m_subd_point1);
+ v->m_P[0] = P[0];
+ v->m_P[1] = P[1];
+ v->m_P[2] = P[2];
}
}
// Add edge points
for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge)
{
- if (false == e0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoints, P))
+ if (false == e0->GetSubdivisionPoint(P))
continue;
- // (the subdivision point of an edge tagged as ON_SubD::EdgeTag::X is a smooth vertex.)
+ // (the subdivision point of an edge tagged as ON_SubD::EdgeTag::SmoothX is a smooth vertex.)
const ON_SubD::VertexTag vertex_tag
= ON_SubD::EdgeTag::Crease == e0->m_edge_tag
? ON_SubD::VertexTag::Crease
@@ -7059,7 +8558,7 @@ unsigned int ON_SubDimple::GlobalSubdivide(
// Add vertex points
for (const ON_SubDVertex* v0 = level0.m_vertex[0]; nullptr != v0; v0 = v0->m_next_vertex)
{
- if (false == v0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoints, P))
+ if (false == v0->GetSubdivisionPoint(P))
continue;
if (nullptr == v0->m_subd_point1)
{
@@ -7073,12 +8572,9 @@ unsigned int ON_SubDimple::GlobalSubdivide(
v->m_P[0] = P[0];
v->m_P[1] = P[1];
v->m_P[2] = P[2];
- }
-
+ }
}
- bool bUpdateEdgeWeights = false;
-
// subdivide edges
for (const ON_SubDEdge* e0 = level0.m_edge[0]; nullptr != e0; e0 = e0->m_next_edge)
{
@@ -7087,25 +8583,8 @@ unsigned int ON_SubDimple::GlobalSubdivide(
ON_SubDVertex* end_vertex[2] = { const_cast(e0->m_vertex[0]->m_subd_point1), const_cast(e0->m_vertex[1]->m_subd_point1) };
ON_SubDVertex* mid_vertex = const_cast(e0->m_subd_point1);
double w[2] = { e0->m_sector_coefficient[0], e0->m_sector_coefficient[1] };
- if (bTriSubD && ON_SubD::EdgeTag::Smooth == e0->m_edge_tag && !(0.0 == w[0] && 0.0 == w[1]))
- {
- // If a neighboring face is not a triangle, the weight will need to be recalculated.
- for (unsigned int i = 0; i < e0->m_face_count; i++)
- {
- const ON_SubDFace* f = e0->Face(i);
- if (nullptr != f && 3 != f->m_edge_count)
- {
- bUpdateEdgeWeights = true;
- if (!(0.0 == w[0]))
- w[0] = ON_SubDSectorType::UnsetSectorWeight;
- if (!(0.0 == w[1]))
- w[1] = ON_SubDSectorType::UnsetSectorWeight;
- break;
- }
- }
- }
ON_SubD::EdgeTag edge_tag = e0->m_edge_tag;
- if (ON_SubD::EdgeTag::X == edge_tag && 2 == e0->m_face_count)
+ if (ON_SubD::EdgeTag::SmoothX == edge_tag && 2 == e0->m_face_count)
{
if ( nullptr != mid_vertex && ON_SubD::VertexTag::Smooth == mid_vertex->m_vertex_tag )
edge_tag = ON_SubD::EdgeTag::Smooth;
@@ -7114,71 +8593,36 @@ unsigned int ON_SubDimple::GlobalSubdivide(
AddEdge(edge_tag, mid_vertex, 0.0, end_vertex[1], w[1]);
}
- // subdivide faces
- if (bTriSubD)
+ for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face)
{
- for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face)
- {
- bool bUnsetEdgeWeight = false;
- GlobalTriSubdivideFace(f0, bUseSavedSubdivisionPoints, &bUnsetEdgeWeight);
- if (bUnsetEdgeWeight)
- bUpdateEdgeWeights = true;
- }
-
- if (bUpdateEdgeWeights)
- {
- for (const ON_SubDEdge* e1 = level1->m_edge[0]; nullptr != e1; e1 = e1->m_next_edge)
- {
- if (ON_SubD::EdgeTag::Smooth != e1->m_edge_tag)
- continue;
- for (unsigned int i = 0; i < 2; i++)
- {
- if (ON_SubDSectorType::UnsetSectorWeight == e1->m_sector_coefficient[i])
- {
- const double w = ON_SubDSectorType::Create(subdivision_type, e1, i).SectorWeight();
- const_cast(e1)->m_sector_coefficient[i] = w;
- }
- }
- }
- }
- }
-
- if (bQuadSubD)
- {
- for (const ON_SubDFace* f0 = level0.m_face[0]; nullptr != f0; f0 = f0->m_next_face)
- {
- GlobalQuadSubdivideFace(bUseSavedSubdivisionPoints,f0);
- }
+ Internal_GlobalQuadSubdivideFace(f0);
}
return level1_index;
}
-unsigned int ON_SubDimple::GlobalQuadSubdivideFace(
- bool bUseSavedSubdivisionPoint,
+unsigned int ON_SubDimple::Internal_GlobalQuadSubdivideFace(
const ON_SubDFace* f0
)
{
// This is a private member function.
// The caller insures f0 != nullptr.
- const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::QuadCatmullClark;
-
const unsigned int f0_edge_count = f0->m_edge_count;
if (f0_edge_count < 3)
return 0;
const unsigned int parent_face_id = f0->m_id;
- const unsigned int zero_face_id = (0 == f0->m_level) ? parent_face_id : f0->m_zero_face_id;
+ const unsigned int zero_face_id = (0 == f0->SubdivisionLevel()) ? parent_face_id : f0->m_zero_face_id;
if (nullptr == f0->m_subd_point1)
{
// add face centroid
double faceC[3];
- if (false == f0->GetSubdivisionPoint(subdivision_type, bUseSavedSubdivisionPoint, faceC))
+ if (false == f0->GetSubdivisionPoint( faceC))
return 0;
- f0->SetSavedSubdivisionPoint(subdivision_type,faceC);
- unsigned int level1_index = f0->m_level + 1;
+ f0->SetSavedSubdivisionPoint(faceC);
+ unsigned int level1_index = f0->SubdivisionLevel() + 1;
ON_SubDVertex* v = AllocateVertex(ON_SubD::VertexTag::Smooth, level1_index, faceC );
AddVertexToLevel(v);
const_cast(f0)->m_subd_point1 = v;
@@ -7201,7 +8645,7 @@ unsigned int ON_SubDimple::GlobalQuadSubdivideFace(
unsigned int f1_count = 0;
- const double w_2facesector = ON_SubDSectorType::CreaseSectorWeight(subdivision_type, 2);
+ const double w_2facesector = ON_SubDSectorType::CreaseSectorWeight(2);
for (unsigned int i = 0; i < f0_edge_count; i++)
{
@@ -7274,465 +8718,89 @@ unsigned int ON_SubDimple::GlobalQuadSubdivideFace(
return f1_count;
}
-
-static double TriCornerSectorWeight(
- ON_SubDEdgePtr e0_ptr,
- ON_SubDEdgePtr e1_ptr,
- ON_SubD::VertexTag vertex_tag
- )
+bool ON_SubD::GlobalSubdivide()
{
- const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::TriLoopWarren;
-
- if (ON_SubD::VertexTag::Smooth == vertex_tag)
- return 0.0;
-
- if (ON_SubD::VertexTag::Unset == vertex_tag)
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
-
- const ON_SubDEdge* e0 = ON_SUBD_EDGE_POINTER(e0_ptr.m_ptr);
- if (nullptr == e0)
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
- ON__INT_PTR e0dir = ON_SUBD_EDGE_DIRECTION(e0_ptr.m_ptr);
-
- const ON_SubDEdge* e1 = ON_SUBD_EDGE_POINTER(e1_ptr.m_ptr);
- if (nullptr == e1)
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
- ON__INT_PTR e1dir = ON_SUBD_EDGE_DIRECTION(e1_ptr.m_ptr);
-
- // flip direction of e0 so that both edges are leaving the vertex
- e0dir = 1 - e0dir;
-
- if (ON_SubD::EdgeTag::Crease == e0->m_edge_tag && ON_SubD::EdgeTag::Crease == e1->m_edge_tag)
- {
- // The radial edge we are about to add has two faces in its sector between the
- // creased edges e0 and e1.
- unsigned int sector_face_count = 2;
- if (ON_SubD::VertexTag::Crease == vertex_tag)
- {
- return ON_SubDSectorType::CreaseSectorWeight(subdivision_type, sector_face_count);
- }
-
- if (ON_SubD::VertexTag::Corner == vertex_tag)
- {
- const double corner_sector_angle_radians
- = ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(ON_SubDEdgePtr::Create(e0, e0dir), ON_SubDEdgePtr::Create(e1, e1dir));
- return ON_SubDSectorType::CreateCornerSectorType(subdivision_type, sector_face_count, corner_sector_angle_radians).SectorWeight();
- }
-
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
- }
-
- if (ON_SubD::VertexTag::Crease == vertex_tag || ON_SubD::VertexTag::Corner == vertex_tag || ON_SubD::VertexTag::Dart == vertex_tag)
- {
- // The weight calculation requires all edges in the sector exist
- // and has to be delayed until the subdivision topology is complete.
- return ON_SubDSectorType::UnsetSectorWeight;
- }
-
- double w0
- = (ON_SubD::EdgeTag::Smooth == e0->m_edge_tag)
- ? e0->m_sector_coefficient[e0dir]
- : ON_SubDSectorType::UnsetSectorWeight;
-
- double w1
- = (ON_SubD::EdgeTag::Smooth == e1->m_edge_tag)
- ? e1->m_sector_coefficient[e1dir]
- : ON_SubDSectorType::UnsetSectorWeight;
-
- double w = (w0 == w1) ? w0 : ((ON_SubDSectorType::UnsetSectorWeight != w0) ? w0 : w1);
- if (w == w && ON_SubDSectorType::UnsetSectorWeight != w)
- return w;
-
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
+ return GlobalSubdivide(1U);
}
-unsigned int ON_SubDimple::GlobalTriSubdivideFace(
- const ON_SubDFace* f0,
- bool bUseSavedSubdivisionPoint,
- bool* bUnsetEdgeWeight
- )
-{
- const ON_SubD::SubDType subdivision_type = ON_SubD::SubDType::TriLoopWarren;
-
- // This is a private member function.
- // The caller insures f0 != nullptr and bUnsetEdgeWeight != nullptr.
-
- *bUnsetEdgeWeight = false;
-
- const unsigned int f0_edge_count = f0->m_edge_count;
- if (f0_edge_count < 3)
- return 0;
-
- const unsigned int parent_face_id = f0->m_id;
- const unsigned int zero_face_id = (0 == f0->m_level) ? parent_face_id : f0->m_zero_face_id;
-
-
- ON_SubDEdge* E0[3];
- ON__UINT_PTR E0dir[3];
- ON_SubDEdge* E1[9];
- ON_SubDEdgePtr f1edges[3];
- ON__UINT_PTR e_ptr;
- ON_SubDFace* f1;
-
- e_ptr = f0->EdgePtr(f0_edge_count - 1).m_ptr;
- E0[1] = ON_SUBD_EDGE_POINTER(e_ptr);
- E0dir[1] = ON_SUBD_EDGE_DIRECTION(e_ptr);
- E1[2] = nullptr;
-
- unsigned int f1_count = 0;
-
- if (3 == f0_edge_count)
- {
- unsigned int j = 0;
- for (unsigned int i = 0; i < 3; i++)
- {
- e_ptr = f0->m_edge4[i].m_ptr;
- E0[i] = ON_SUBD_EDGE_POINTER(e_ptr);
- if (nullptr == E0[i] || nullptr == E0[i]->m_subd_point1)
- break;
- E0dir[i] = ON_SUBD_EDGE_DIRECTION(e_ptr);
-
- e_ptr = E0[i]->m_subd_point1->m_edges[E0dir[i]].m_ptr;
- E1[j] = ON_SUBD_EDGE_POINTER(e_ptr);
- if (nullptr == E1[j])
- break;
- j++;
-
- e_ptr = E0[i]->m_subd_point1->m_edges[1 - E0dir[i]].m_ptr;
- E1[j] = ON_SUBD_EDGE_POINTER(e_ptr);
- if (nullptr == E1[j])
- break;
- j++;
- }
- if (6 != j)
- return 0;
-
- // The value of E0[0]->m_subd_point1->m_vertex_tag should be either
- // ON_SubD::VertexTag::Smooth or ON_SubD::VertexTag::Crease. In the
- // case when it's value is "crease", the resulting edge end weight
- // will be 0.5 because the edge has three adjacent faces and "theta"
- // will be pi/3.
- // The resulting tri edge weight is 0.5 = 1/3 + 1/3*cos(pi/3).
- const double w_3facesector = ON_SubDSectorType::CreaseSectorWeight(subdivision_type, 3);
-
- double w0 = (ON_SubD::VertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0;
- double w1 = (ON_SubD::VertexTag::Crease == E0[2]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0;
- E1[6] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast(E0[0]->m_subd_point1), w0, const_cast(E0[2]->m_subd_point1), w1);
- w0 = w1;
- w1 = (ON_SubD::VertexTag::Crease == E0[1]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0;
- E1[7] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast(E0[2]->m_subd_point1), w0, const_cast(E0[1]->m_subd_point1), w1);
- w0 = w1;
- w1 = (ON_SubD::VertexTag::Crease == E0[0]->m_subd_point1->m_vertex_tag) ? w_3facesector : 0.0;
- E1[8] = AddEdge(ON_SubD::EdgeTag::Smooth, const_cast(E0[1]->m_subd_point1), w0, const_cast