mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 19:46:08 +08:00
Co-authored-by: croudyj <croudyj@gmail.com> Co-authored-by: Dale Fugier <dale@mcneel.com> Co-authored-by: Dale Lear <dalelear@mcneel.com> Co-authored-by: piac <giulio@mcneel.com> Co-authored-by: Steve Baer <steve@mcneel.com>
518 lines
14 KiB
C++
518 lines
14 KiB
C++
|
|
// Copyright (c) 1993-2022 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 <http://www.opennurbs.org>.
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#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
|
|
|
|
#pragma region DistanceFormat
|
|
|
|
void ON_NumberFormatter::Fraction(double value_in, int& wholenumber, int& numerator, int& denominator, int precision)
|
|
{
|
|
int sign = 1;
|
|
if (0 > value_in)
|
|
{
|
|
value_in = -value_in;
|
|
sign = -1;
|
|
}
|
|
denominator = 1 << precision;
|
|
double value = double(int(value_in * denominator + 0.5)) / double(denominator);
|
|
wholenumber = int(floor(value));
|
|
numerator = int((value - wholenumber) * double(denominator));
|
|
while ((numerator % 2) == 0 && (numerator != 0))
|
|
{
|
|
numerator /= 2;
|
|
denominator /= 2;
|
|
}
|
|
wholenumber *= sign;
|
|
}
|
|
|
|
double ON_NumberFormatter::RoundOff(double n, double r)
|
|
{
|
|
if (0.0 == r)
|
|
return n;
|
|
|
|
if (0.0 > r)
|
|
r = -r;
|
|
|
|
if (2.0 * ON_ZERO_TOLERANCE < r)
|
|
{
|
|
double d = n;
|
|
double sign = 1.0;
|
|
if (d < 0.0)
|
|
{
|
|
sign = -1.0;
|
|
d = -d;
|
|
}
|
|
// Increase input by 1/2 of round-off
|
|
double k = d + (r / 2.0);
|
|
double m = fmod(k, r);
|
|
if (m == m) // fmod can return NaN
|
|
{
|
|
n = k - m;
|
|
n *= sign;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
void ON_NumberFormatter::SuppressZeros(
|
|
ON_wString& dist,
|
|
ON_DimStyle::suppress_zero sz)
|
|
{
|
|
switch (sz)
|
|
{
|
|
default:
|
|
case ON_DimStyle::suppress_zero::None:
|
|
break;
|
|
case ON_DimStyle::suppress_zero::SuppressLeading:
|
|
if (1 < dist.Length() && L'0' == dist[0])
|
|
dist = dist.Right(dist.Length() - 1);
|
|
break;
|
|
case ON_DimStyle::suppress_zero::SuppressLeadingAndTrailing:
|
|
case ON_DimStyle::suppress_zero::SuppressTrailing:
|
|
{
|
|
int zero = dist.ReverseFind(L"0");
|
|
int dot = dist.ReverseFind(L".");
|
|
if (dot > 0 && zero > dot)
|
|
{
|
|
dist.TrimRight(L"0");
|
|
dist.TrimRight(L".");
|
|
}
|
|
if (ON_DimStyle::suppress_zero::SuppressLeadingAndTrailing == sz
|
|
&& 1 < dist.Length() && L'0' == dist[0])
|
|
dist = dist.Right(dist.Length() - 1);
|
|
}
|
|
break;
|
|
case ON_DimStyle::suppress_zero::SuppressZeroFeet:
|
|
if (dist.Left(3).EqualOrdinal(L"0\'-", true))
|
|
dist = dist.Right(dist.Length() - 3);
|
|
break;
|
|
case ON_DimStyle::suppress_zero::SuppressZeroFeetAndZeroInches:
|
|
if (dist.Left(3).EqualOrdinal(L"0\'-", true))
|
|
dist = dist.Right(dist.Length() - 3);
|
|
case ON_DimStyle::suppress_zero::SuppressZeroInches:
|
|
if (dist.Right(3).EqualOrdinal(L"-0\"", true))
|
|
dist = dist.Left(dist.Length() - 3);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// bFormatIsAccurate sets numeric display to pretty or accurate.
|
|
bool ON_NumberFormatter::bFormatIsAccurate = false;
|
|
|
|
|
|
bool ON_NumberFormatter::FormatNumber(
|
|
double inputdistance,
|
|
ON_DimStyle::OBSOLETE_length_format output_lengthformat,
|
|
double round_off,
|
|
int precision,
|
|
ON_DimStyle::suppress_zero zero_suppress,
|
|
bool bracket_fractions,
|
|
ON_wString& output)
|
|
{
|
|
const int max_string_length = 30; // maximum number of characters before %g is used
|
|
|
|
if (!ON_IsValid(inputdistance))
|
|
{
|
|
if (ON_UNSET_VALUE == inputdistance)
|
|
output = "unset number";
|
|
else
|
|
output = "invalid number";
|
|
return false;
|
|
}
|
|
|
|
if (bFormatIsAccurate)
|
|
{
|
|
output.Format(L"%.17g", inputdistance);
|
|
return true;
|
|
}
|
|
|
|
if (0 > precision)
|
|
precision = 0;
|
|
|
|
double distance = inputdistance;
|
|
|
|
bool is_negative = false;
|
|
if (distance < 0.0)
|
|
{
|
|
is_negative = true;
|
|
distance = -distance;
|
|
output += L'-';
|
|
}
|
|
|
|
distance = RoundOff(distance, round_off);
|
|
|
|
ON_wString sFormat, sFormat2;
|
|
switch (output_lengthformat)
|
|
{
|
|
default:
|
|
case(ON_DimStyle::OBSOLETE_length_format::Decimal):
|
|
{
|
|
if (precision >= 1 && precision < 10)
|
|
{
|
|
// 7 July 2006 Lowell and Dale L. (From old RhFormatNumber())
|
|
// number like .1495 end up being .149499999999999999999999999
|
|
// and get rounded to .149 instead of .150.
|
|
// By adding a bit or two at the end we hide this problem. Since precision < 10,
|
|
// we are several decimal places away from modifying anything that matters.
|
|
// (Note: input * ON_EPSILON didn't work)
|
|
double e = fabs(distance)*1.0e-12;
|
|
distance += e;
|
|
}
|
|
sFormat2.Format(L"%%.%df", precision);
|
|
sFormat.Format(sFormat2, distance);
|
|
|
|
// Look for garbage in Format result
|
|
if (sFormat.Length() > max_string_length)
|
|
sFormat = ON_wString::FromNumber(distance);
|
|
|
|
ON_NumberFormatter::SuppressZeros(sFormat, zero_suppress);
|
|
break;
|
|
}
|
|
|
|
case(ON_DimStyle::OBSOLETE_length_format::Fractional):
|
|
{
|
|
int numerator = 0;
|
|
int denominator = 1 << precision;
|
|
double decimal, wholenumber;
|
|
|
|
decimal = modf(distance, &wholenumber);
|
|
|
|
// check that fractional part is more than 1/2 of denominator
|
|
// otherwise there's no fraction to print
|
|
decimal *= denominator;
|
|
if (decimal > 0.5)
|
|
{
|
|
decimal += .5;
|
|
numerator = (int)floor(decimal);
|
|
if (numerator != 0)
|
|
{
|
|
if (numerator == denominator)
|
|
{
|
|
numerator = 0;
|
|
wholenumber++;
|
|
}
|
|
else
|
|
{
|
|
//finds lowest form of fraction if divisor is even
|
|
while ((numerator % 2 == 0) && (denominator % 2 == 0))
|
|
{
|
|
numerator /= 2;
|
|
denominator /= 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (0 == wholenumber && 0 == numerator)
|
|
sFormat.Format(L"0");
|
|
else
|
|
{
|
|
if (0 != wholenumber)
|
|
{
|
|
if (0 != numerator)
|
|
// RH-71619, removed space
|
|
//sFormat.Format(L"%d ", (int)wholenumber);
|
|
sFormat.Format(L"%d", (int)wholenumber);
|
|
else
|
|
sFormat.Format(L"%d", (int)wholenumber);
|
|
}
|
|
|
|
if (0 != numerator)
|
|
{
|
|
if(bracket_fractions)
|
|
sFormat2.Format(L"[[%d/%d]]", numerator, denominator);
|
|
else
|
|
sFormat2.Format(L"%d/%d", numerator, denominator);
|
|
sFormat += sFormat2;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sFormat.Format(L"%d", (int)wholenumber);
|
|
}
|
|
if (sFormat.Length() > max_string_length)
|
|
{
|
|
sFormat = ON_wString::FromNumber(distance);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case(ON_DimStyle::OBSOLETE_length_format::FeetInches):
|
|
{
|
|
// Units for distance have to be feet here
|
|
int numerator = 0;
|
|
int denominator = 1 << precision;
|
|
double decfeet = 0.0;
|
|
double decinches = modf(distance, &decfeet);
|
|
double decfrac = modf(decinches * 12.0, &decinches);
|
|
int wholeinches = (int)decinches;
|
|
int wholefeet = (int)decfeet;
|
|
|
|
// decimal is the fractional remainder to make a fraction of an inch
|
|
decfrac *= denominator;
|
|
if (decfrac > 0.5)
|
|
{
|
|
decfrac += .5;
|
|
numerator = (int)floor(decfrac);
|
|
|
|
if (0 != numerator)
|
|
{
|
|
while ((numerator % 2 == 0) && (denominator % 2 == 0)) // reduce fraction
|
|
{
|
|
numerator /= 2;
|
|
denominator /= 2;
|
|
}
|
|
}
|
|
if (0 != numerator && numerator == denominator)
|
|
{
|
|
numerator = 0;
|
|
wholeinches++;
|
|
}
|
|
if (wholeinches >= 12)
|
|
{
|
|
wholeinches = 0;
|
|
wholefeet++;
|
|
}
|
|
}
|
|
|
|
ON_wString sInches;
|
|
bool include_feet = (0 != wholefeet ||
|
|
(ON_DimStyle::suppress_zero::SuppressZeroFeet != zero_suppress &&
|
|
ON_DimStyle::suppress_zero::SuppressZeroFeetAndZeroInches != zero_suppress));
|
|
|
|
bool include_inches = (0 != wholeinches || 0 != numerator ||
|
|
(ON_DimStyle::suppress_zero::SuppressZeroInches != zero_suppress &&
|
|
ON_DimStyle::suppress_zero::SuppressZeroFeetAndZeroInches != zero_suppress));
|
|
|
|
if (include_feet)
|
|
{
|
|
sFormat.Format(L"%d\'", wholefeet);
|
|
if (include_inches)
|
|
sFormat += L'-';
|
|
}
|
|
if (include_inches)
|
|
{
|
|
if (0 != numerator)
|
|
{
|
|
if (wholeinches > 0 || include_feet)
|
|
{
|
|
if (bracket_fractions)
|
|
// RH-71619, removed space
|
|
//sInches.Format(L"%d [[%d/%d]]\"", wholeinches, numerator, denominator);
|
|
sInches.Format(L"%d[[%d/%d]]\"", wholeinches, numerator, denominator);
|
|
else
|
|
sInches.Format(L"%d %d/%d\"", wholeinches, numerator, denominator);
|
|
}
|
|
else
|
|
{
|
|
if (bracket_fractions)
|
|
sInches.Format(L"[[%d/%d]]\"", numerator, denominator);
|
|
else
|
|
sInches.Format(L"%d/%d\"", numerator, denominator);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sInches.Format(L"%d\"", wholeinches);
|
|
}
|
|
sFormat += sInches;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (sFormat.Length() > max_string_length)
|
|
{
|
|
sFormat = ON_wString::FromNumber(distance);
|
|
}
|
|
output += sFormat;
|
|
return true;
|
|
}
|
|
|
|
bool ON_NumberFormatter::FormatLength(
|
|
double inputdistance,
|
|
ON_DimStyle::LengthDisplay output_lengthdisplay,
|
|
double round_off,
|
|
int precision,
|
|
ON_DimStyle::suppress_zero zero_suppress,
|
|
bool bracket_fractions,
|
|
ON_wString& output)
|
|
{
|
|
ON_DimStyle::OBSOLETE_length_format output_lengthformat = ON_DimStyle::OBSOLETE_length_format::Decimal;
|
|
if (output_lengthdisplay == ON_DimStyle::LengthDisplay::FeetAndInches)
|
|
output_lengthformat = ON_DimStyle::OBSOLETE_length_format::FeetInches;
|
|
else if (output_lengthdisplay == ON_DimStyle::LengthDisplay::InchesFractional)
|
|
output_lengthformat = ON_DimStyle::OBSOLETE_length_format::Fractional;
|
|
|
|
return ON_NumberFormatter::FormatNumber(
|
|
inputdistance,
|
|
output_lengthformat,
|
|
round_off,
|
|
precision,
|
|
zero_suppress,
|
|
bracket_fractions,
|
|
output);
|
|
}
|
|
|
|
#pragma endregion DistanceFormat
|
|
|
|
#pragma region AngleFormat
|
|
|
|
|
|
bool ON_NumberFormatter::FormatAngleStringDMS(double angle_radians, ON_wString& formatted_string)
|
|
{
|
|
return ON_NumberFormatter::FormatAngleStringDMS(angle_radians, 2, formatted_string);
|
|
//bool rc = false;
|
|
|
|
//int sign = 1;
|
|
//int degrees = 0;
|
|
//int minutes = 0;
|
|
//int seconds = 0;
|
|
//formatted_string.Empty();
|
|
|
|
//double angle_degrees = ON_RADIANS_TO_DEGREES * angle_radians;
|
|
//if (ON_IsValid(angle_degrees))
|
|
//{
|
|
// double d_seconds;
|
|
|
|
// if (angle_degrees < 0.0)
|
|
// {
|
|
// sign = -1;
|
|
// angle_degrees = -angle_degrees;
|
|
// }
|
|
|
|
// d_seconds = angle_degrees * 3600;
|
|
// seconds = (int)(d_seconds + 0.5);
|
|
// minutes = seconds / 60;
|
|
// seconds = seconds % 60;
|
|
// degrees = minutes / 60;
|
|
// minutes = minutes % 60;
|
|
|
|
// degrees *= sign;
|
|
// formatted_string.Format(L"%d%lc %d\' %d\"", degrees, ON_wString::DegreeSymbol, minutes, seconds);
|
|
// rc = true;
|
|
//}
|
|
//return rc;
|
|
}
|
|
|
|
bool ON_NumberFormatter::FormatAngleStringDMS(double angle_radians, int resolution, ON_wString& formatted_string)
|
|
{
|
|
bool rc = false;
|
|
formatted_string.Empty();
|
|
int idegrees = 0;
|
|
int iminutes = 0;
|
|
int iseconds = 0;
|
|
|
|
double angle_degrees = ON_RADIANS_TO_DEGREES * angle_radians;
|
|
int sign = 1;
|
|
if (angle_degrees < 0.0)
|
|
{
|
|
sign = -1;
|
|
angle_degrees = -angle_degrees;
|
|
}
|
|
angle_degrees = RoundOff(angle_degrees, 1e-8);
|
|
double minutes = (angle_degrees - floor(angle_degrees));
|
|
minutes *= 60.0;
|
|
double seconds = (minutes - floor(minutes));
|
|
seconds *= 60.0;
|
|
|
|
if (resolution < 0)
|
|
resolution = 0;
|
|
if (resolution > 6)
|
|
resolution = 6;
|
|
|
|
if (resolution == 0)
|
|
{
|
|
idegrees = (int)floor(angle_degrees + 0.5);
|
|
idegrees *= sign;
|
|
rc = formatted_string.Format(L"%d%lc", idegrees, ON_wString::DegreeSymbol);
|
|
}
|
|
else
|
|
{
|
|
idegrees = (int)floor(angle_degrees);
|
|
if (resolution == 1)
|
|
{
|
|
iminutes = (int)floor(minutes + 0.5);
|
|
rc = formatted_string.Format(L"%d%lc %d\'", idegrees, ON_wString::DegreeSymbol, iminutes);
|
|
}
|
|
else
|
|
{
|
|
iminutes = (int)floor(minutes);
|
|
if (resolution == 2)
|
|
{
|
|
iseconds = (int)floor(seconds + 0.5);
|
|
if (iseconds >= 60)
|
|
{
|
|
iseconds -= 60;
|
|
iminutes += 1;
|
|
}
|
|
if (iminutes >= 60)
|
|
{
|
|
iminutes -= 60;
|
|
idegrees += 1;
|
|
}
|
|
rc = formatted_string.Format(L"%d%lc %d\' %d\"", idegrees, ON_wString::DegreeSymbol, iminutes, iseconds);
|
|
}
|
|
else
|
|
{
|
|
if (seconds >= 60.0)
|
|
{
|
|
seconds -= 60.0;
|
|
iminutes += 1;
|
|
}
|
|
if (iminutes >= 60)
|
|
{
|
|
iminutes -= 60;
|
|
idegrees += 1;
|
|
}
|
|
ON_wString fmt;
|
|
fmt.Format(L"%%d%%lc %%d\' %%.%dlf\"", resolution - 2);
|
|
rc = formatted_string.Format(fmt.Array(), idegrees, ON_wString::DegreeSymbol, iminutes, seconds);
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_NumberFormatter::FormatAngleStringDecimal(
|
|
double angle,
|
|
int resolution,
|
|
double roundoff,
|
|
ON_DimStyle::suppress_zero zero_suppress,
|
|
ON_wString& formatted_string)
|
|
{
|
|
formatted_string.Empty();
|
|
|
|
return ON_NumberFormatter::FormatNumber(
|
|
angle,
|
|
ON_DimStyle::OBSOLETE_length_format::Decimal,
|
|
roundoff,
|
|
resolution,
|
|
zero_suppress,
|
|
false,
|
|
formatted_string);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma endregion
|