// // BSplineCurve.h from Geometric Tools along with all the GTC headers // it requires. // disable warning messages #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wunused-value" #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #endif #define LogError(a) (0); return 0; #define LogAssert(a,b) (0) // ----------------------------------------------------------------------- // Mathematics/Math.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.09.03 #pragma once // This file extends the support to include convenient constants and // functions. The shared constants for CPU, Intel SSE and GPU lead to // correctly rounded approximations of the constants when using 'float' or // 'double'. The file also includes a type trait, is_arbitrary_precision, // to support selecting between floating-point arithmetic (float, double, //long double) or arbitrary-precision arithmetic (BSNumber, BSRational) // in an implementation using std::enable_if. There is also a type trait, // has_division_operator, to support selecting between numeric types that // have a division operator (BSRational) and those that do not have a // division operator (BSNumber). #include #include #include #include // Maximum number of iterations for bisection before a subinterval // degenerates to a single point. TODO: Verify these. I used the formula: // 3 + std::numeric_limits::digits - std::numeric_limits::min_exponent. // IEEEBinary16: digits = 11, min_exponent = -13 // float: digits = 27, min_exponent = -125 // double: digits = 53, min_exponent = -1021 // BSNumber and BSRational use std::numeric_limits::max(), // but maybe these should be set to a large number or be user configurable. // The MAX_BISECTIONS_GENERIC is an arbitrary choice for now and is used // in template code where Real is the template parameter. #define GTE_C_MAX_BISECTIONS_FLOAT16 27u #define GTE_C_MAX_BISECTIONS_FLOAT32 155u #define GTE_C_MAX_BISECTIONS_FLOAT64 1077u #define GTE_C_MAX_BISECTIONS_BSNUMBER 0xFFFFFFFFu #define GTE_C_MAX_BISECTIONS_BSRATIONAL 0xFFFFFFFFu #define GTE_C_MAX_BISECTIONS_GENERIC 2048u // Constants involving pi. #define GTE_C_PI 3.1415926535897931 #define GTE_C_HALF_PI 1.5707963267948966 #define GTE_C_QUARTER_PI 0.7853981633974483 #define GTE_C_TWO_PI 6.2831853071795862 #define GTE_C_INV_PI 0.3183098861837907 #define GTE_C_INV_TWO_PI 0.1591549430918953 #define GTE_C_INV_HALF_PI 0.6366197723675813 // Conversions between degrees and radians. #define GTE_C_DEG_TO_RAD 0.0174532925199433 #define GTE_C_RAD_TO_DEG 57.295779513082321 // Common constants. #define GTE_C_SQRT_2 1.4142135623730951 #define GTE_C_INV_SQRT_2 0.7071067811865475 #define GTE_C_LN_2 0.6931471805599453 #define GTE_C_INV_LN_2 1.4426950408889634 #define GTE_C_LN_10 2.3025850929940459 #define GTE_C_INV_LN_10 0.43429448190325176 // Constants for minimax polynomial approximations to sqrt(x). // The algorithm minimizes the maximum absolute error on [1,2]. #define GTE_C_SQRT_DEG1_C0 +1.0 #define GTE_C_SQRT_DEG1_C1 +4.1421356237309505e-01 #define GTE_C_SQRT_DEG1_MAX_ERROR 1.7766952966368793e-2 #define GTE_C_SQRT_DEG2_C0 +1.0 #define GTE_C_SQRT_DEG2_C1 +4.8563183076125260e-01 #define GTE_C_SQRT_DEG2_C2 -7.1418268388157458e-02 #define GTE_C_SQRT_DEG2_MAX_ERROR 1.1795695163108744e-3 #define GTE_C_SQRT_DEG3_C0 +1.0 #define GTE_C_SQRT_DEG3_C1 +4.9750045320242231e-01 #define GTE_C_SQRT_DEG3_C2 -1.0787308044477850e-01 #define GTE_C_SQRT_DEG3_C3 +2.4586189615451115e-02 #define GTE_C_SQRT_DEG3_MAX_ERROR 1.1309620116468910e-4 #define GTE_C_SQRT_DEG4_C0 +1.0 #define GTE_C_SQRT_DEG4_C1 +4.9955939832918816e-01 #define GTE_C_SQRT_DEG4_C2 -1.2024066151943025e-01 #define GTE_C_SQRT_DEG4_C3 +4.5461507257698486e-02 #define GTE_C_SQRT_DEG4_C4 -1.0566681694362146e-02 #define GTE_C_SQRT_DEG4_MAX_ERROR 1.2741170151556180e-5 #define GTE_C_SQRT_DEG5_C0 +1.0 #define GTE_C_SQRT_DEG5_C1 +4.9992197660031912e-01 #define GTE_C_SQRT_DEG5_C2 -1.2378506719245053e-01 #define GTE_C_SQRT_DEG5_C3 +5.6122776972699739e-02 #define GTE_C_SQRT_DEG5_C4 -2.3128836281145482e-02 #define GTE_C_SQRT_DEG5_C5 +5.0827122737047148e-03 #define GTE_C_SQRT_DEG5_MAX_ERROR 1.5725568940708201e-6 #define GTE_C_SQRT_DEG6_C0 +1.0 #define GTE_C_SQRT_DEG6_C1 +4.9998616695784914e-01 #define GTE_C_SQRT_DEG6_C2 -1.2470733323278438e-01 #define GTE_C_SQRT_DEG6_C3 +6.0388587356982271e-02 #define GTE_C_SQRT_DEG6_C4 -3.1692053551807930e-02 #define GTE_C_SQRT_DEG6_C5 +1.2856590305148075e-02 #define GTE_C_SQRT_DEG6_C6 -2.6183954624343642e-03 #define GTE_C_SQRT_DEG6_MAX_ERROR 2.0584155535630089e-7 #define GTE_C_SQRT_DEG7_C0 +1.0 #define GTE_C_SQRT_DEG7_C1 +4.9999754817809228e-01 #define GTE_C_SQRT_DEG7_C2 -1.2493243476353655e-01 #define GTE_C_SQRT_DEG7_C3 +6.1859954146370910e-02 #define GTE_C_SQRT_DEG7_C4 -3.6091595023208356e-02 #define GTE_C_SQRT_DEG7_C5 +1.9483946523450868e-02 #define GTE_C_SQRT_DEG7_C6 -7.5166134568007692e-03 #define GTE_C_SQRT_DEG7_C7 +1.4127567687864939e-03 #define GTE_C_SQRT_DEG7_MAX_ERROR 2.8072302919734948e-8 #define GTE_C_SQRT_DEG8_C0 +1.0 #define GTE_C_SQRT_DEG8_C1 +4.9999956583056759e-01 #define GTE_C_SQRT_DEG8_C2 -1.2498490369914350e-01 #define GTE_C_SQRT_DEG8_C3 +6.2318494667579216e-02 #define GTE_C_SQRT_DEG8_C4 -3.7982961896432244e-02 #define GTE_C_SQRT_DEG8_C5 +2.3642612312869460e-02 #define GTE_C_SQRT_DEG8_C6 -1.2529377587270574e-02 #define GTE_C_SQRT_DEG8_C7 +4.5382426960713929e-03 #define GTE_C_SQRT_DEG8_C8 -7.8810995273670414e-04 #define GTE_C_SQRT_DEG8_MAX_ERROR 3.9460605685825989e-9 // Constants for minimax polynomial approximations to 1/sqrt(x). // The algorithm minimizes the maximum absolute error on [1,2]. #define GTE_C_INVSQRT_DEG1_C0 +1.0 #define GTE_C_INVSQRT_DEG1_C1 -2.9289321881345254e-01 #define GTE_C_INVSQRT_DEG1_MAX_ERROR 3.7814314552701983e-2 #define GTE_C_INVSQRT_DEG2_C0 +1.0 #define GTE_C_INVSQRT_DEG2_C1 -4.4539812104566801e-01 #define GTE_C_INVSQRT_DEG2_C2 +1.5250490223221547e-01 #define GTE_C_INVSQRT_DEG2_MAX_ERROR 4.1953446330581234e-3 #define GTE_C_INVSQRT_DEG3_C0 +1.0 #define GTE_C_INVSQRT_DEG3_C1 -4.8703230993068791e-01 #define GTE_C_INVSQRT_DEG3_C2 +2.8163710486669835e-01 #define GTE_C_INVSQRT_DEG3_C3 -8.7498013749463421e-02 #define GTE_C_INVSQRT_DEG3_MAX_ERROR 5.6307702007266786e-4 #define GTE_C_INVSQRT_DEG4_C0 +1.0 #define GTE_C_INVSQRT_DEG4_C1 -4.9710061558048779e-01 #define GTE_C_INVSQRT_DEG4_C2 +3.4266247597676802e-01 #define GTE_C_INVSQRT_DEG4_C3 -1.9106356536293490e-01 #define GTE_C_INVSQRT_DEG4_C4 +5.2608486153198797e-02 #define GTE_C_INVSQRT_DEG4_MAX_ERROR 8.1513919987605266e-5 #define GTE_C_INVSQRT_DEG5_C0 +1.0 #define GTE_C_INVSQRT_DEG5_C1 -4.9937760586004143e-01 #define GTE_C_INVSQRT_DEG5_C2 +3.6508741295133973e-01 #define GTE_C_INVSQRT_DEG5_C3 -2.5884890281853501e-01 #define GTE_C_INVSQRT_DEG5_C4 +1.3275782221320753e-01 #define GTE_C_INVSQRT_DEG5_C5 -3.2511945299404488e-02 #define GTE_C_INVSQRT_DEG5_MAX_ERROR 1.2289367475583346e-5 #define GTE_C_INVSQRT_DEG6_C0 +1.0 #define GTE_C_INVSQRT_DEG6_C1 -4.9987029229547453e-01 #define GTE_C_INVSQRT_DEG6_C2 +3.7220923604495226e-01 #define GTE_C_INVSQRT_DEG6_C3 -2.9193067713256937e-01 #define GTE_C_INVSQRT_DEG6_C4 +1.9937605991094642e-01 #define GTE_C_INVSQRT_DEG6_C5 -9.3135712130901993e-02 #define GTE_C_INVSQRT_DEG6_C6 +2.0458166789566690e-02 #define GTE_C_INVSQRT_DEG6_MAX_ERROR 1.9001451223750465e-6 #define GTE_C_INVSQRT_DEG7_C0 +1.0 #define GTE_C_INVSQRT_DEG7_C1 -4.9997357250704977e-01 #define GTE_C_INVSQRT_DEG7_C2 +3.7426216884998809e-01 #define GTE_C_INVSQRT_DEG7_C3 -3.0539882498248971e-01 #define GTE_C_INVSQRT_DEG7_C4 +2.3976005607005391e-01 #define GTE_C_INVSQRT_DEG7_C5 -1.5410326351684489e-01 #define GTE_C_INVSQRT_DEG7_C6 +6.5598809723041995e-02 #define GTE_C_INVSQRT_DEG7_C7 -1.3038592450470787e-02 #define GTE_C_INVSQRT_DEG7_MAX_ERROR 2.9887724993168940e-7 #define GTE_C_INVSQRT_DEG8_C0 +1.0 #define GTE_C_INVSQRT_DEG8_C1 -4.9999471066120371e-01 #define GTE_C_INVSQRT_DEG8_C2 +3.7481415745794067e-01 #define GTE_C_INVSQRT_DEG8_C3 -3.1023804387422160e-01 #define GTE_C_INVSQRT_DEG8_C4 +2.5977002682930106e-01 #define GTE_C_INVSQRT_DEG8_C5 -1.9818790717727097e-01 #define GTE_C_INVSQRT_DEG8_C6 +1.1882414252613671e-01 #define GTE_C_INVSQRT_DEG8_C7 -4.6270038088550791e-02 #define GTE_C_INVSQRT_DEG8_C8 +8.3891541755747312e-03 #define GTE_C_INVSQRT_DEG8_MAX_ERROR 4.7596926146947771e-8 // Constants for minimax polynomial approximations to sin(x). // The algorithm minimizes the maximum absolute error on [-pi/2,pi/2]. #define GTE_C_SIN_DEG3_C0 +1.0 #define GTE_C_SIN_DEG3_C1 -1.4727245910375519e-01 #define GTE_C_SIN_DEG3_MAX_ERROR 1.3481903639145865e-2 #define GTE_C_SIN_DEG5_C0 +1.0 #define GTE_C_SIN_DEG5_C1 -1.6600599923812209e-01 #define GTE_C_SIN_DEG5_C2 +7.5924178409012000e-03 #define GTE_C_SIN_DEG5_MAX_ERROR 1.4001209384639779e-4 #define GTE_C_SIN_DEG7_C0 +1.0 #define GTE_C_SIN_DEG7_C1 -1.6665578084732124e-01 #define GTE_C_SIN_DEG7_C2 +8.3109378830028557e-03 #define GTE_C_SIN_DEG7_C3 -1.8447486103462252e-04 #define GTE_C_SIN_DEG7_MAX_ERROR 1.0205878936686563e-6 #define GTE_C_SIN_DEG9_C0 +1.0 #define GTE_C_SIN_DEG9_C1 -1.6666656235308897e-01 #define GTE_C_SIN_DEG9_C2 +8.3329962509886002e-03 #define GTE_C_SIN_DEG9_C3 -1.9805100675274190e-04 #define GTE_C_SIN_DEG9_C4 +2.5967200279475300e-06 #define GTE_C_SIN_DEG9_MAX_ERROR 5.2010746265374053e-9 #define GTE_C_SIN_DEG11_C0 +1.0 #define GTE_C_SIN_DEG11_C1 -1.6666666601721269e-01 #define GTE_C_SIN_DEG11_C2 +8.3333303183525942e-03 #define GTE_C_SIN_DEG11_C3 -1.9840782426250314e-04 #define GTE_C_SIN_DEG11_C4 +2.7521557770526783e-06 #define GTE_C_SIN_DEG11_C5 -2.3828544692960918e-08 #define GTE_C_SIN_DEG11_MAX_ERROR 1.9295870457014530e-11 // Constants for minimax polynomial approximations to cos(x). // The algorithm minimizes the maximum absolute error on [-pi/2,pi/2]. #define GTE_C_COS_DEG2_C0 +1.0 #define GTE_C_COS_DEG2_C1 -4.0528473456935105e-01 #define GTE_C_COS_DEG2_MAX_ERROR 5.4870946878404048e-2 #define GTE_C_COS_DEG4_C0 +1.0 #define GTE_C_COS_DEG4_C1 -4.9607181958647262e-01 #define GTE_C_COS_DEG4_C2 +3.6794619653489236e-02 #define GTE_C_COS_DEG4_MAX_ERROR 9.1879932449712154e-4 #define GTE_C_COS_DEG6_C0 +1.0 #define GTE_C_COS_DEG6_C1 -4.9992746217057404e-01 #define GTE_C_COS_DEG6_C2 +4.1493920348353308e-02 #define GTE_C_COS_DEG6_C3 -1.2712435011987822e-03 #define GTE_C_COS_DEG6_MAX_ERROR 9.2028470133065365e-6 #define GTE_C_COS_DEG8_C0 +1.0 #define GTE_C_COS_DEG8_C1 -4.9999925121358291e-01 #define GTE_C_COS_DEG8_C2 +4.1663780117805693e-02 #define GTE_C_COS_DEG8_C3 -1.3854239405310942e-03 #define GTE_C_COS_DEG8_C4 +2.3154171575501259e-05 #define GTE_C_COS_DEG8_MAX_ERROR 5.9804533020235695e-8 #define GTE_C_COS_DEG10_C0 +1.0 #define GTE_C_COS_DEG10_C1 -4.9999999508695869e-01 #define GTE_C_COS_DEG10_C2 +4.1666638865338612e-02 #define GTE_C_COS_DEG10_C3 -1.3888377661039897e-03 #define GTE_C_COS_DEG10_C4 +2.4760495088926859e-05 #define GTE_C_COS_DEG10_C5 -2.6051615464872668e-07 #define GTE_C_COS_DEG10_MAX_ERROR 2.7006769043325107e-10 // Constants for minimax polynomial approximations to tan(x). // The algorithm minimizes the maximum absolute error on [-pi/4,pi/4]. #define GTE_C_TAN_DEG3_C0 1.0 #define GTE_C_TAN_DEG3_C1 4.4295926544736286e-01 #define GTE_C_TAN_DEG3_MAX_ERROR 1.1661892256204731e-2 #define GTE_C_TAN_DEG5_C0 1.0 #define GTE_C_TAN_DEG5_C1 3.1401320403542421e-01 #define GTE_C_TAN_DEG5_C2 2.0903948109240345e-01 #define GTE_C_TAN_DEG5_MAX_ERROR 5.8431854390143118e-4 #define GTE_C_TAN_DEG7_C0 1.0 #define GTE_C_TAN_DEG7_C1 3.3607213284422555e-01 #define GTE_C_TAN_DEG7_C2 1.1261037305184907e-01 #define GTE_C_TAN_DEG7_C3 9.8352099470524479e-02 #define GTE_C_TAN_DEG7_MAX_ERROR 3.5418688397723108e-5 #define GTE_C_TAN_DEG9_C0 1.0 #define GTE_C_TAN_DEG9_C1 3.3299232843941784e-01 #define GTE_C_TAN_DEG9_C2 1.3747843432474838e-01 #define GTE_C_TAN_DEG9_C3 3.7696344813028304e-02 #define GTE_C_TAN_DEG9_C4 4.6097377279281204e-02 #define GTE_C_TAN_DEG9_MAX_ERROR 2.2988173242199927e-6 #define GTE_C_TAN_DEG11_C0 1.0 #define GTE_C_TAN_DEG11_C1 3.3337224456224224e-01 #define GTE_C_TAN_DEG11_C2 1.3264516053824593e-01 #define GTE_C_TAN_DEG11_C3 5.8145237645931047e-02 #define GTE_C_TAN_DEG11_C4 1.0732193237572574e-02 #define GTE_C_TAN_DEG11_C5 2.1558456793513869e-02 #define GTE_C_TAN_DEG11_MAX_ERROR 1.5426257940140409e-7 #define GTE_C_TAN_DEG13_C0 1.0 #define GTE_C_TAN_DEG13_C1 3.3332916426394554e-01 #define GTE_C_TAN_DEG13_C2 1.3343404625112498e-01 #define GTE_C_TAN_DEG13_C3 5.3104565343119248e-02 #define GTE_C_TAN_DEG13_C4 2.5355038312682154e-02 #define GTE_C_TAN_DEG13_C5 1.8253255966556026e-03 #define GTE_C_TAN_DEG13_C6 1.0069407176615641e-02 #define GTE_C_TAN_DEG13_MAX_ERROR 1.0550264249037378e-8 // Constants for minimax polynomial approximations to acos(x), where the // approximation is of the form acos(x) = sqrt(1 - x)*p(x) with p(x) a // polynomial. The algorithm minimizes the maximum error // |acos(x)/sqrt(1-x) - p(x)| on [0,1]. At the same time we get an // approximation for asin(x) = pi/2 - acos(x). #define GTE_C_ACOS_DEG1_C0 +1.5707963267948966 #define GTE_C_ACOS_DEG1_C1 -1.5658276442180141e-01 #define GTE_C_ACOS_DEG1_MAX_ERROR 1.1659002803738105e-2 #define GTE_C_ACOS_DEG2_C0 +1.5707963267948966 #define GTE_C_ACOS_DEG2_C1 -2.0347053865798365e-01 #define GTE_C_ACOS_DEG2_C2 +4.6887774236182234e-02 #define GTE_C_ACOS_DEG2_MAX_ERROR 9.0311602490029258e-4 #define GTE_C_ACOS_DEG3_C0 +1.5707963267948966 #define GTE_C_ACOS_DEG3_C1 -2.1253291899190285e-01 #define GTE_C_ACOS_DEG3_C2 +7.4773789639484223e-02 #define GTE_C_ACOS_DEG3_C3 -1.8823635069382449e-02 #define GTE_C_ACOS_DEG3_MAX_ERROR 9.3066396954288172e-5 #define GTE_C_ACOS_DEG4_C0 +1.5707963267948966 #define GTE_C_ACOS_DEG4_C1 -2.1422258835275865e-01 #define GTE_C_ACOS_DEG4_C2 +8.4936675142844198e-02 #define GTE_C_ACOS_DEG4_C3 -3.5991475120957794e-02 #define GTE_C_ACOS_DEG4_C4 +8.6946239090712751e-03 #define GTE_C_ACOS_DEG4_MAX_ERROR 1.0930595804481413e-5 #define GTE_C_ACOS_DEG5_C0 +1.5707963267948966 #define GTE_C_ACOS_DEG5_C1 -2.1453292139805524e-01 #define GTE_C_ACOS_DEG5_C2 +8.7973089282889383e-02 #define GTE_C_ACOS_DEG5_C3 -4.5130266382166440e-02 #define GTE_C_ACOS_DEG5_C4 +1.9467466687281387e-02 #define GTE_C_ACOS_DEG5_C5 -4.3601326117634898e-03 #define GTE_C_ACOS_DEG5_MAX_ERROR 1.3861070257241426-6 #define GTE_C_ACOS_DEG6_C0 +1.5707963267948966 #define GTE_C_ACOS_DEG6_C1 -2.1458939285677325e-01 #define GTE_C_ACOS_DEG6_C2 +8.8784960563641491e-02 #define GTE_C_ACOS_DEG6_C3 -4.8887131453156485e-02 #define GTE_C_ACOS_DEG6_C4 +2.7011519960012720e-02 #define GTE_C_ACOS_DEG6_C5 -1.1210537323478320e-02 #define GTE_C_ACOS_DEG6_C6 +2.3078166879102469e-03 #define GTE_C_ACOS_DEG6_MAX_ERROR 1.8491291330427484e-7 #define GTE_C_ACOS_DEG7_C0 +1.5707963267948966 #define GTE_C_ACOS_DEG7_C1 -2.1459960076929829e-01 #define GTE_C_ACOS_DEG7_C2 +8.8986946573346160e-02 #define GTE_C_ACOS_DEG7_C3 -5.0207843052845647e-02 #define GTE_C_ACOS_DEG7_C4 +3.0961594977611639e-02 #define GTE_C_ACOS_DEG7_C5 -1.7162031184398074e-02 #define GTE_C_ACOS_DEG7_C6 +6.7072304676685235e-03 #define GTE_C_ACOS_DEG7_C7 -1.2690614339589956e-03 #define GTE_C_ACOS_DEG7_MAX_ERROR 2.5574620927948377e-8 #define GTE_C_ACOS_DEG8_C0 +1.5707963267948966 #define GTE_C_ACOS_DEG8_C1 -2.1460143648688035e-01 #define GTE_C_ACOS_DEG8_C2 +8.9034700107934128e-02 #define GTE_C_ACOS_DEG8_C3 -5.0625279962389413e-02 #define GTE_C_ACOS_DEG8_C4 +3.2683762943179318e-02 #define GTE_C_ACOS_DEG8_C5 -2.0949278766238422e-02 #define GTE_C_ACOS_DEG8_C6 +1.1272900916992512e-02 #define GTE_C_ACOS_DEG8_C7 -4.1160981058965262e-03 #define GTE_C_ACOS_DEG8_C8 +7.1796493341480527e-04 #define GTE_C_ACOS_DEG8_MAX_ERROR 3.6340015129032732e-9 // Constants for minimax polynomial approximations to atan(x). // The algorithm minimizes the maximum absolute error on [-1,1]. #define GTE_C_ATAN_DEG3_C0 +1.0 #define GTE_C_ATAN_DEG3_C1 -2.1460183660255172e-01 #define GTE_C_ATAN_DEG3_MAX_ERROR 1.5970326392614240e-2 #define GTE_C_ATAN_DEG5_C0 +1.0 #define GTE_C_ATAN_DEG5_C1 -3.0189478312144946e-01 #define GTE_C_ATAN_DEG5_C2 +8.7292946518897740e-02 #define GTE_C_ATAN_DEG5_MAX_ERROR 1.3509832247372636e-3 #define GTE_C_ATAN_DEG7_C0 +1.0 #define GTE_C_ATAN_DEG7_C1 -3.2570157599356531e-01 #define GTE_C_ATAN_DEG7_C2 +1.5342994884206673e-01 #define GTE_C_ATAN_DEG7_C3 -4.2330209451053591e-02 #define GTE_C_ATAN_DEG7_MAX_ERROR 1.5051227215514412e-4 #define GTE_C_ATAN_DEG9_C0 +1.0 #define GTE_C_ATAN_DEG9_C1 -3.3157878236439586e-01 #define GTE_C_ATAN_DEG9_C2 +1.8383034738018011e-01 #define GTE_C_ATAN_DEG9_C3 -8.9253037587244677e-02 #define GTE_C_ATAN_DEG9_C4 +2.2399635968909593e-02 #define GTE_C_ATAN_DEG9_MAX_ERROR 1.8921598624582064e-5 #define GTE_C_ATAN_DEG11_C0 +1.0 #define GTE_C_ATAN_DEG11_C1 -3.3294527685374087e-01 #define GTE_C_ATAN_DEG11_C2 +1.9498657165383548e-01 #define GTE_C_ATAN_DEG11_C3 -1.1921576270475498e-01 #define GTE_C_ATAN_DEG11_C4 +5.5063351366968050e-02 #define GTE_C_ATAN_DEG11_C5 -1.2490720064867844e-02 #define GTE_C_ATAN_DEG11_MAX_ERROR 2.5477724974187765e-6 #define GTE_C_ATAN_DEG13_C0 +1.0 #define GTE_C_ATAN_DEG13_C1 -3.3324998579202170e-01 #define GTE_C_ATAN_DEG13_C2 +1.9856563505717162e-01 #define GTE_C_ATAN_DEG13_C3 -1.3374657325451267e-01 #define GTE_C_ATAN_DEG13_C4 +8.1675882859940430e-02 #define GTE_C_ATAN_DEG13_C5 -3.5059680836411644e-02 #define GTE_C_ATAN_DEG13_C6 +7.2128853633444123e-03 #define GTE_C_ATAN_DEG13_MAX_ERROR 3.5859104691865484e-7 // Constants for minimax polynomial approximations to exp2(x) = 2^x. // The algorithm minimizes the maximum absolute error on [0,1]. #define GTE_C_EXP2_DEG1_C0 1.0 #define GTE_C_EXP2_DEG1_C1 1.0 #define GTE_C_EXP2_DEG1_MAX_ERROR 8.6071332055934313e-2 #define GTE_C_EXP2_DEG2_C0 1.0 #define GTE_C_EXP2_DEG2_C1 6.5571332605741528e-01 #define GTE_C_EXP2_DEG2_C2 3.4428667394258472e-01 #define GTE_C_EXP2_DEG2_MAX_ERROR 3.8132476831060358e-3 #define GTE_C_EXP2_DEG3_C0 1.0 #define GTE_C_EXP2_DEG3_C1 6.9589012084456225e-01 #define GTE_C_EXP2_DEG3_C2 2.2486494900110188e-01 #define GTE_C_EXP2_DEG3_C3 7.9244930154334980e-02 #define GTE_C_EXP2_DEG3_MAX_ERROR 1.4694877755186408e-4 #define GTE_C_EXP2_DEG4_C0 1.0 #define GTE_C_EXP2_DEG4_C1 6.9300392358459195e-01 #define GTE_C_EXP2_DEG4_C2 2.4154981722455560e-01 #define GTE_C_EXP2_DEG4_C3 5.1744260331489045e-02 #define GTE_C_EXP2_DEG4_C4 1.3701998859367848e-02 #define GTE_C_EXP2_DEG4_MAX_ERROR 4.7617792624521371e-6 #define GTE_C_EXP2_DEG5_C0 1.0 #define GTE_C_EXP2_DEG5_C1 6.9315298010274962e-01 #define GTE_C_EXP2_DEG5_C2 2.4014712313022102e-01 #define GTE_C_EXP2_DEG5_C3 5.5855296413199085e-02 #define GTE_C_EXP2_DEG5_C4 8.9477503096873079e-03 #define GTE_C_EXP2_DEG5_C5 1.8968500441332026e-03 #define GTE_C_EXP2_DEG5_MAX_ERROR 1.3162098333463490e-7 #define GTE_C_EXP2_DEG6_C0 1.0 #define GTE_C_EXP2_DEG6_C1 6.9314698914837525e-01 #define GTE_C_EXP2_DEG6_C2 2.4023013440952923e-01 #define GTE_C_EXP2_DEG6_C3 5.5481276898206033e-02 #define GTE_C_EXP2_DEG6_C4 9.6838443037086108e-03 #define GTE_C_EXP2_DEG6_C5 1.2388324048515642e-03 #define GTE_C_EXP2_DEG6_C6 2.1892283501756538e-04 #define GTE_C_EXP2_DEG6_MAX_ERROR 3.1589168225654163e-9 #define GTE_C_EXP2_DEG7_C0 1.0 #define GTE_C_EXP2_DEG7_C1 6.9314718588750690e-01 #define GTE_C_EXP2_DEG7_C2 2.4022637363165700e-01 #define GTE_C_EXP2_DEG7_C3 5.5505235570535660e-02 #define GTE_C_EXP2_DEG7_C4 9.6136265387940512e-03 #define GTE_C_EXP2_DEG7_C5 1.3429234504656051e-03 #define GTE_C_EXP2_DEG7_C6 1.4299202757683815e-04 #define GTE_C_EXP2_DEG7_C7 2.1662892777385423e-05 #define GTE_C_EXP2_DEG7_MAX_ERROR 6.6864513925679603e-11 // Constants for minimax polynomial approximations to log2(x). // The algorithm minimizes the maximum absolute error on [1,2]. // The polynomials all have constant term zero. #define GTE_C_LOG2_DEG1_C1 +1.0 #define GTE_C_LOG2_DEG1_MAX_ERROR 8.6071332055934202e-2 #define GTE_C_LOG2_DEG2_C1 +1.3465553856377803 #define GTE_C_LOG2_DEG2_C2 -3.4655538563778032e-01 #define GTE_C_LOG2_DEG2_MAX_ERROR 7.6362868906658110e-3 #define GTE_C_LOG2_DEG3_C1 +1.4228653756681227 #define GTE_C_LOG2_DEG3_C2 -5.8208556916449616e-01 #define GTE_C_LOG2_DEG3_C3 +1.5922019349637218e-01 #define GTE_C_LOG2_DEG3_MAX_ERROR 8.7902902652883808e-4 #define GTE_C_LOG2_DEG4_C1 +1.4387257478171547 #define GTE_C_LOG2_DEG4_C2 -6.7778401359918661e-01 #define GTE_C_LOG2_DEG4_C3 +3.2118898377713379e-01 #define GTE_C_LOG2_DEG4_C4 -8.2130717995088531e-02 #define GTE_C_LOG2_DEG4_MAX_ERROR 1.1318551355360418e-4 #define GTE_C_LOG2_DEG5_C1 +1.4419170408633741 #define GTE_C_LOG2_DEG5_C2 -7.0909645927612530e-01 #define GTE_C_LOG2_DEG5_C3 +4.1560609399164150e-01 #define GTE_C_LOG2_DEG5_C4 -1.9357573729558908e-01 #define GTE_C_LOG2_DEG5_C5 +4.5149061716699634e-02 #define GTE_C_LOG2_DEG5_MAX_ERROR 1.5521274478735858e-5 #define GTE_C_LOG2_DEG6_C1 +1.4425449435950917 #define GTE_C_LOG2_DEG6_C2 -7.1814525675038965e-01 #define GTE_C_LOG2_DEG6_C3 +4.5754919692564044e-01 #define GTE_C_LOG2_DEG6_C4 -2.7790534462849337e-01 #define GTE_C_LOG2_DEG6_C5 +1.2179791068763279e-01 #define GTE_C_LOG2_DEG6_C6 -2.5841449829670182e-02 #define GTE_C_LOG2_DEG6_MAX_ERROR 2.2162051216689793e-6 #define GTE_C_LOG2_DEG7_C1 +1.4426664401536078 #define GTE_C_LOG2_DEG7_C2 -7.2055423726162360e-01 #define GTE_C_LOG2_DEG7_C3 +4.7332419162501083e-01 #define GTE_C_LOG2_DEG7_C4 -3.2514018752954144e-01 #define GTE_C_LOG2_DEG7_C5 +1.9302965529095673e-01 #define GTE_C_LOG2_DEG7_C6 -7.8534970641157997e-02 #define GTE_C_LOG2_DEG7_C7 +1.5209108363023915e-02 #define GTE_C_LOG2_DEG7_MAX_ERROR 3.2546531700261561e-7 #define GTE_C_LOG2_DEG8_C1 +1.4426896453621882 #define GTE_C_LOG2_DEG8_C2 -7.2115893912535967e-01 #define GTE_C_LOG2_DEG8_C3 +4.7861716616785088e-01 #define GTE_C_LOG2_DEG8_C4 -3.4699935395019565e-01 #define GTE_C_LOG2_DEG8_C5 +2.4114048765477492e-01 #define GTE_C_LOG2_DEG8_C6 -1.3657398692885181e-01 #define GTE_C_LOG2_DEG8_C7 +5.1421382871922106e-02 #define GTE_C_LOG2_DEG8_C8 -9.1364020499895560e-03 #define GTE_C_LOG2_DEG8_MAX_ERROR 4.8796219218050219e-8 // These functions are convenient for some applications. The classes // BSNumber, BSRational and IEEEBinary16 have implementations that // (for now) use typecasting to call the 'float' or 'double' versions. namespace gte { inline float atandivpi(float x) { return std::atan(x) * (float)GTE_C_INV_PI; } inline float atan2divpi(float y, float x) { return std::atan2(y, x) * (float)GTE_C_INV_PI; } inline float clamp(float x, float xmin, float xmax) { return (x <= xmin ? xmin : (x >= xmax ? xmax : x)); } inline float cospi(float x) { return std::cos(x * (float)GTE_C_PI); } inline float exp10(float x) { return std::exp(x * (float)GTE_C_LN_10); } inline float invsqrt(float x) { return 1.0f / std::sqrt(x); } inline int isign(float x) { return (x > 0.0f ? 1 : (x < 0.0f ? -1 : 0)); } inline float saturate(float x) { return (x <= 0.0f ? 0.0f : (x >= 1.0f ? 1.0f : x)); } inline float sign(float x) { return (x > 0.0f ? 1.0f : (x < 0.0f ? -1.0f : 0.0f)); } inline float sinpi(float x) { return std::sin(x * (float)GTE_C_PI); } inline float sqr(float x) { return x * x; } inline double atandivpi(double x) { return std::atan(x) * GTE_C_INV_PI; } inline double atan2divpi(double y, double x) { return std::atan2(y, x) * GTE_C_INV_PI; } inline double clamp(double x, double xmin, double xmax) { return (x <= xmin ? xmin : (x >= xmax ? xmax : x)); } inline double cospi(double x) { return std::cos(x * GTE_C_PI); } inline double exp10(double x) { return std::exp(x * GTE_C_LN_10); } inline double invsqrt(double x) { return 1.0 / std::sqrt(x); } inline double sign(double x) { return (x > 0.0 ? 1.0 : (x < 0.0 ? -1.0 : 0.0f)); } inline int isign(double x) { return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0)); } inline double saturate(double x) { return (x <= 0.0 ? 0.0 : (x >= 1.0 ? 1.0 : x)); } inline double sinpi(double x) { return std::sin(x * GTE_C_PI); } inline double sqr(double x) { return x * x; } } // Type traits to support std::enable_if conditional compilation for // numerical computations. namespace gte { // The trait is_arbitrary_precision for type T of float, double or // long double generates is_arbitrary_precision::value of false. The // implementations for arbitrary-precision arithmetic are found in // GteArbitraryPrecision.h. template struct is_arbitrary_precision_internal : std::false_type {}; // EricChristoffersen 1/26/2020: Replace remove_cv_t with pre-c14 syntax so will compile with xcode. template struct is_arbitrary_precision : is_arbitrary_precision_internal::type>::type {}; // The trait has_division_operator for type T of float, double or // long double generates has_division_operator::value of true. The // implementations for arbitrary-precision arithmetic are found in // GteArbitraryPrecision.h. template struct has_division_operator_internal : std::false_type {}; // EricChristoffersen 1/26/2020: Replace remove_cv_t with pre-c14 syntax so will compile with xcode. template struct has_division_operator : has_division_operator_internal::type>::type {}; template <> struct has_division_operator_internal : std::true_type {}; template <> struct has_division_operator_internal : std::true_type {}; template <> struct has_division_operator_internal : std::true_type {}; } // ----------------------------------------------------------------------- // Mathematics/Array2.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include #include // The Array2 class represents a 2-dimensional array that minimizes the number // of new and delete calls. The T objects are stored in a contiguous array. namespace gte { template class Array2 { public: // Construction. The first constructor generates an array of objects // that are owned by Array2. The second constructor is given an array // of objects that are owned by the caller. The array has bound0 // columns and bound1 rows. Array2(size_t bound0, size_t bound1) : mBound0(bound0), mBound1(bound1), mObjects(bound0 * bound1), mIndirect1(bound1) { SetPointers(mObjects.data()); } Array2(size_t bound0, size_t bound1, T* objects) : mBound0(bound0), mBound1(bound1), mIndirect1(bound1) { SetPointers(objects); } // Support for dynamic resizing, copying, or moving. If 'other' does // not own the original 'objects', they are not copied by the // assignment operator. Array2() : mBound0(0), mBound1(0) { } Array2(Array2 const& other) : mBound0(other.mBound0), mBound1(other.mBound1) { *this = other; } Array2& operator=(Array2 const& other) { // The copy is valid whether or not other.mObjects has elements. mObjects = other.mObjects; SetPointers(other); return *this; } Array2(Array2&& other) noexcept : mBound0(other.mBound0), mBound1(other.mBound1) { *this = std::move(other); } Array2& operator=(Array2&& other) noexcept { // The move is valid whether or not other.mObjects has elements. mObjects = std::move(other.mObjects); SetPointers(other); return *this; } // Access to the array. Sample usage is // Array2 myArray(3, 2); // T* row1 = myArray[1]; // T row1Col2 = myArray[1][2]; inline size_t GetBound0() const { return mBound0; } inline size_t GetBound1() const { return mBound1; } inline T const* operator[](int row) const { return mIndirect1[row]; } inline T* operator[](int row) { return mIndirect1[row]; } private: void SetPointers(T* objects) { for (size_t i1 = 0; i1 < mBound1; ++i1) { size_t j0 = mBound0 * i1; // = bound0*(i1 + j1) where j1 = 0 mIndirect1[i1] = &objects[j0]; } } void SetPointers(Array2 const& other) { mBound0 = other.mBound0; mBound1 = other.mBound1; mIndirect1.resize(mBound1); if (mBound0 > 0) { // The objects are owned. SetPointers(mObjects.data()); } else if (mIndirect1.size() > 0) { // The objects are not owned. SetPointers(other.mIndirect1[0]); } // else 'other' is an empty Array2. } size_t mBound0, mBound1; std::vector mObjects; std::vector mIndirect1; }; } // ----------------------------------------------------------------------- // Mathematics/BasisFunction.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once //#include //#include //#include #include #include namespace gte { template struct UniqueKnot { Real t; int multiplicity; }; template struct BasisFunctionInput { // The members are uninitialized. BasisFunctionInput() { } // Construct an open uniform curve with t in [0,1]. BasisFunctionInput(int inNumControls, int inDegree) : numControls(inNumControls), degree(inDegree), uniform(true), periodic(false), numUniqueKnots(numControls - degree + 1), uniqueKnots(numUniqueKnots) { uniqueKnots.front().t = (Real)0; uniqueKnots.front().multiplicity = degree + 1; for (int i = 1; i <= numUniqueKnots - 2; ++i) { uniqueKnots[i].t = i / (numUniqueKnots - (Real)1); uniqueKnots[i].multiplicity = 1; } uniqueKnots.back().t = (Real)1; uniqueKnots.back().multiplicity = degree + 1; } int numControls; int degree; bool uniform; bool periodic; int numUniqueKnots; std::vector> uniqueKnots; }; template class BasisFunction { public: // Let n be the number of control points. Let d be the degree, where // 1 <= d <= n-1. The number of knots is k = n + d + 1. The knots // are t[i] for 0 <= i < k and must be nondecreasing, t[i] <= t[i+1], // but a knot value can be repeated. Let s be the number of distinct // knots. Let the distinct knots be u[j] for 0 <= j < s, so u[j] < // u[j+1] for all j. The set of u[j] is called a 'breakpoint // sequence'. Let m[j] >= 1 be the multiplicity; that is, if t[i] is // the first occurrence of u[j], then t[i+r] = t[i] for 1 <= r < m[j]. // The multiplicities have the constraints m[0] <= d+1, m[s-1] <= d+1, // and m[j] <= d for 1 <= j <= s-2. Also, k = sum_{j=0}^{s-1} m[j], // which says the multiplicities account for all k knots. // // Given a knot vector (t[0],...,t[n+d]), the domain of the // corresponding B-spline curve is the interval [t[d],t[n]]. // // The corresponding B-spline or NURBS curve is characterized as // follows. See "Geometric Modeling with Splines: An Introduction" by // Elaine Cohen, Richard F. Riesenfeld and Gershon Elber, AK Peters, // 2001, Natick MA. The curve is 'open' when m[0] = m[s-1] = d+1; // otherwise, it is 'floating'. An open curve is uniform when the // knots t[d] through t[n] are equally spaced; that is, t[i+1] - t[i] // are a common value for d <= i <= n-1. By implication, s = n-d+1 // and m[j] = 1 for 1 <= j <= s-2. An open curve that does not // satisfy these conditions is said to be nonuniform. A floating // curve is uniform when m[j] = 1 for 0 <= j <= s-1 and t[i+1] - t[i] // are a common value for 0 <= i <= k-2; otherwise, the floating curve // is nonuniform. // // A special case of a floating curve is a periodic curve. The intent // is that the curve is closed, so the first and last control points // should be the same, which ensures C^{0} continuity. Higher-order // continuity is obtained by repeating more control points. If the // control points are P[0] through P[n-1], append the points P[0] // through P[d-1] to ensure C^{d-1} continuity. Additionally, the // knots must be chosen properly. You may choose t[d] through t[n] as // you wish. The other knots are defined by // t[i] - t[i-1] = t[n-d+i] - t[n-d+i-1] // t[n+i] - t[n+i-1] = t[d+i] - t[d+i-1] // for 1 <= i <= d. // Construction and destruction. The determination that the curve is // open or floating is based on the multiplicities. The 'uniform' // input is used to avoid misclassifications due to floating-point // rounding errors. Specifically, the breakpoints might be equally // spaced (uniform) as real numbers, but the floating-point // representations can have rounding errors that cause the knot // differences not to be exactly the same constant. A periodic curve // can have uniform or nonuniform knots. This object makes copies of // the input arrays. BasisFunction() : mNumControls(0), mDegree(0), mTMin((Real)0), mTMax((Real)0), mTLength((Real)0), mOpen(false), mUniform(false), mPeriodic(false) { } BasisFunction(BasisFunctionInput const& input) : mNumControls(0), mDegree(0), mTMin((Real)0), mTMax((Real)0), mTLength((Real)0), mOpen(false), mUniform(false), mPeriodic(false) { Create(input); } ~BasisFunction() { } // No copying is allowed. BasisFunction(BasisFunction const&) = delete; BasisFunction& operator=(BasisFunction const&) = delete; // Support for explicit creation in classes that have std::array // members involving BasisFunction. This is a call-once function. void Create(BasisFunctionInput const& input) { LogAssert(mNumControls == 0 && mDegree == 0, "Object already created."); LogAssert(input.numControls >= 2, "Invalid number of control points."); LogAssert(1 <= input.degree && input.degree < input.numControls, "Invalid degree."); LogAssert(input.numUniqueKnots >= 2, "Invalid number of unique knots."); mNumControls = (input.periodic ? input.numControls + input.degree : input.numControls); mDegree = input.degree; mTMin = (Real)0; mTMax = (Real)0; mTLength = (Real)0; mOpen = false; mUniform = input.uniform; mPeriodic = input.periodic; for (int i = 0; i < 4; ++i) { mJet[i] = Array2(); } mUniqueKnots.resize(input.numUniqueKnots); std::copy(input.uniqueKnots.begin(), input.uniqueKnots.begin() + input.numUniqueKnots, mUniqueKnots.begin()); Real u = mUniqueKnots.front().t; for (int i = 1; i < input.numUniqueKnots - 1; ++i) { Real uNext = mUniqueKnots[i].t; LogAssert(u < uNext, "Unique knots are not strictly increasing."); u = uNext; } int mult0 = mUniqueKnots.front().multiplicity; LogAssert(mult0 >= 1 && mult0 <= mDegree + 1, "Invalid first multiplicity."); int mult1 = mUniqueKnots.back().multiplicity; LogAssert(mult1 >= 1 && mult1 <= mDegree + 1, "Invalid last multiplicity."); for (int i = 1; i <= input.numUniqueKnots - 2; ++i) { LogAssert(mUniqueKnots[i].multiplicity >= 1 && mUniqueKnots[i].multiplicity <= mDegree + 1, "Invalid interior multiplicity."); } mOpen = (mult0 == mult1 && mult0 == mDegree + 1); mKnots.resize(mNumControls + mDegree + 1); mKeys.resize(input.numUniqueKnots); int sum = 0; for (int i = 0, j = 0; i < input.numUniqueKnots; ++i) { Real tCommon = mUniqueKnots[i].t; int mult = mUniqueKnots[i].multiplicity; for (int k = 0; k < mult; ++k, ++j) { mKnots[j] = tCommon; } mKeys[i].first = tCommon; mKeys[i].second = sum - 1; sum += mult; } mTMin = mKnots[mDegree]; mTMax = mKnots[mNumControls]; mTLength = mTMax - mTMin; size_t numRows = mDegree + 1; size_t numCols = mNumControls + mDegree; size_t numBytes = numRows * numCols * sizeof(Real); for (int i = 0; i < 4; ++i) { mJet[i] = Array2(numCols, numRows); std::memset(mJet[i][0], 0, numBytes); } } // Member access. inline int GetNumControls() const { return mNumControls; } inline int GetDegree() const { return mDegree; } inline int GetNumUniqueKnots() const { return static_cast(mUniqueKnots.size()); } inline int GetNumKnots() const { return static_cast(mKnots.size()); } inline Real GetMinDomain() const { return mTMin; } inline Real GetMaxDomain() const { return mTMax; } inline bool IsOpen() const { return mOpen; } inline bool IsUniform() const { return mUniform; } inline bool IsPeriodic() const { return mPeriodic; } inline UniqueKnot const* GetUniqueKnots() const { return &mUniqueKnots[0]; } inline Real const* GetKnots() const { return &mKnots[0]; } // Evaluation of the basis function and its derivatives through // order 3. For the function value only, pass order 0. For the // function and first derivative, pass order 1, and so on. void Evaluate(Real t, unsigned int order, int& minIndex, int& maxIndex) const { LogAssert(order <= 3, "Invalid order."); int i = GetIndex(t); mJet[0][0][i] = (Real)1; if (order >= 1) { mJet[1][0][i] = (Real)0; if (order >= 2) { mJet[2][0][i] = (Real)0; if (order >= 3) { mJet[3][0][i] = (Real)0; } } } Real n0 = t - mKnots[i], n1 = mKnots[i + 1] - t; Real e0, e1, d0, d1, invD0, invD1; int j; for (j = 1; j <= mDegree; j++) { d0 = mKnots[i + j] - mKnots[i]; d1 = mKnots[i + 1] - mKnots[i - j + 1]; invD0 = (d0 > (Real)0 ? (Real)1 / d0 : (Real)0); invD1 = (d1 > (Real)0 ? (Real)1 / d1 : (Real)0); e0 = n0 * mJet[0][j - 1][i]; mJet[0][j][i] = e0 * invD0; e1 = n1 * mJet[0][j - 1][i - j + 1]; mJet[0][j][i - j] = e1 * invD1; if (order >= 1) { e0 = n0 * mJet[1][j - 1][i] + mJet[0][j - 1][i]; mJet[1][j][i] = e0 * invD0; e1 = n1 * mJet[1][j - 1][i - j + 1] - mJet[0][j - 1][i - j + 1]; mJet[1][j][i - j] = e1 * invD1; if (order >= 2) { e0 = n0 * mJet[2][j - 1][i] + ((Real)2) * mJet[1][j - 1][i]; mJet[2][j][i] = e0 * invD0; e1 = n1 * mJet[2][j - 1][i - j + 1] - ((Real)2) * mJet[1][j - 1][i - j + 1]; mJet[2][j][i - j] = e1 * invD1; if (order >= 3) { e0 = n0 * mJet[3][j - 1][i] + ((Real)3) * mJet[2][j - 1][i]; mJet[3][j][i] = e0 * invD0; e1 = n1 * mJet[3][j - 1][i - j + 1] - ((Real)3) * mJet[2][j - 1][i - j + 1]; mJet[3][j][i - j] = e1 * invD1; } } } } for (j = 2; j <= mDegree; ++j) { for (int k = i - j + 1; k < i; ++k) { n0 = t - mKnots[k]; n1 = mKnots[k + j + 1] - t; d0 = mKnots[k + j] - mKnots[k]; d1 = mKnots[k + j + 1] - mKnots[k + 1]; invD0 = (d0 > (Real)0 ? (Real)1 / d0 : (Real)0); invD1 = (d1 > (Real)0 ? (Real)1 / d1 : (Real)0); e0 = n0 * mJet[0][j - 1][k]; e1 = n1 * mJet[0][j - 1][k + 1]; mJet[0][j][k] = e0 * invD0 + e1 * invD1; if (order >= 1) { e0 = n0 * mJet[1][j - 1][k] + mJet[0][j - 1][k]; e1 = n1 * mJet[1][j - 1][k + 1] - mJet[0][j - 1][k + 1]; mJet[1][j][k] = e0 * invD0 + e1 * invD1; if (order >= 2) { e0 = n0 * mJet[2][j - 1][k] + ((Real)2) * mJet[1][j - 1][k]; e1 = n1 * mJet[2][j - 1][k + 1] - ((Real)2) * mJet[1][j - 1][k + 1]; mJet[2][j][k] = e0 * invD0 + e1 * invD1; if (order >= 3) { e0 = n0 * mJet[3][j - 1][k] + ((Real)3) * mJet[2][j - 1][k]; e1 = n1 * mJet[3][j - 1][k + 1] - ((Real)3) * mJet[2][j - 1][k + 1]; mJet[3][j][k] = e0 * invD0 + e1 * invD1; } } } } } minIndex = i - mDegree; maxIndex = i; } // Access the results of the call to Evaluate(...). The index i must // satisfy minIndex <= i <= maxIndex. If it is not, the function // returns zero. The separation of evaluation and access is based on // local control of the basis function; that is, only the accessible // values are (potentially) not zero. Real GetValue(unsigned int order, int i) const { if (order < 4) { if (0 <= i && i < mNumControls + mDegree) { return mJet[order][mDegree][i]; } LogError("Invalid index."); } LogError("Invalid order."); } private: // Determine the index i for which knot[i] <= t < knot[i+1]. The // t-value is modified (wrapped for periodic splines, clamped for // nonperiodic splines). int GetIndex(Real& t) const { // Find the index i for which knot[i] <= t < knot[i+1]. if (mPeriodic) { // Wrap to [tmin,tmax]. Real r = std::fmod(t - mTMin, mTLength); if (r < (Real)0) { r += mTLength; } t = mTMin + r; } // Clamp to [tmin,tmax]. For the periodic case, this handles // small numerical rounding errors near the domain endpoints. if (t <= mTMin) { t = mTMin; return mDegree; } if (t >= mTMax) { t = mTMax; return mNumControls - 1; } // At this point, tmin < t < tmax. for (auto const& key : mKeys) { if (t < key.first) { return key.second; } } // We should not reach this code. LogError("Unexpected condition."); } // Constructor inputs and values derived from them. int mNumControls; int mDegree; Real mTMin, mTMax, mTLength; bool mOpen; bool mUniform; bool mPeriodic; std::vector> mUniqueKnots; std::vector mKnots; // Lookup information for the GetIndex() function. The first element // of the pair is a unique knot value. The second element is the // index in mKnots[] for the last occurrence of that knot value. std::vector> mKeys; // Storage for the basis functions and their first three derivatives; // mJet[i] is array[d+1][n+d]. mutable std::array, 4> mJet; }; } // ----------------------------------------------------------------------- // Mathematics/RootsPolynomia.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.12.05 #pragma once //#include #include #include #include // The Find functions return the number of roots, if any, and this number // of elements of the outputs are valid. If the polynomial is identically // zero, Find returns 1. // // Some root-bounding algorithms for real-valued roots are mentioned next for // the polynomial p(t) = c[0] + c[1]*t + ... + c[d-1]*t^{d-1} + c[d]*t^d. // // 1. The roots must be contained by the interval [-M,M] where // M = 1 + max{|c[0]|, ..., |c[d-1]|}/|c[d]| >= 1 // is called the Cauchy bound. // // 2. You may search for roots in the interval [-1,1]. Define // q(t) = t^d*p(1/t) = c[0]*t^d + c[1]*t^{d-1} + ... + c[d-1]*t + c[d] // The roots of p(t) not in [-1,1] are the roots of q(t) in [-1,1]. // // 3. Between two consecutive roots of the derivative p'(t), say, r0 < r1, // the function p(t) is strictly monotonic on the open interval (r0,r1). // If additionally, p(r0) * p(r1) <= 0, then p(x) has a unique root on // the closed interval [r0,r1]. Thus, one can compute the derivatives // through order d for p(t), find roots for the derivative of order k+1, // then use these to bound roots for the derivative of order k. // // 4. Sturm sequences of polynomials may be used to determine bounds on the // roots. This is a more sophisticated approach to root bounding than item 3. // Moreover, a Sturm sequence allows you to compute the number of real-valued // roots on a specified interval. // // 5. For the low-degree Solve* functions, see // https://www.geometrictools.com/Documentation/LowDegreePolynomialRoots.pdf // FOR INTERNAL USE ONLY (unit testing). Do not define the symbol // GTE_ROOTS_LOW_DEGREE_UNIT_TEST in your own code. #if defined(GTE_ROOTS_LOW_DEGREE_UNIT_TEST) extern void RootsLowDegreeBlock(int); #define GTE_ROOTS_LOW_DEGREE_BLOCK(block) RootsLowDegreeBlock(block) #else #define GTE_ROOTS_LOW_DEGREE_BLOCK(block) #endif namespace gte { template class RootsPolynomial { public: // Low-degree root finders. These use exact rational arithmetic for // theoretically correct root classification. The roots themselves // are computed with mixed types (rational and floating-point // arithmetic). The Rational type must support rational arithmetic // (+, -, *, /); for example, BSRational suffices. The // Rational class must have single-input constructors where the input // is type Real. This ensures you can call the Solve* functions with // floating-point inputs; they will be converted to Rational // implicitly. The highest-order coefficients must be nonzero // (p2 != 0 for quadratic, p3 != 0 for cubic, and p4 != 0 for // quartic). template static void SolveQuadratic(Rational const& p0, Rational const& p1, Rational const& p2, std::map& rmMap) { Rational const rat2 = 2; Rational q0 = p0 / p2; Rational q1 = p1 / p2; Rational q1half = q1 / rat2; Rational c0 = q0 - q1half * q1half; std::map rmLocalMap; SolveDepressedQuadratic(c0, rmLocalMap); rmMap.clear(); for (auto& rm : rmLocalMap) { Rational root = rm.first - q1half; rmMap.insert(std::make_pair((Real)root, rm.second)); } } template static void SolveCubic(Rational const& p0, Rational const& p1, Rational const& p2, Rational const& p3, std::map& rmMap) { Rational const rat2 = 2, rat3 = 3; Rational q0 = p0 / p3; Rational q1 = p1 / p3; Rational q2 = p2 / p3; Rational q2third = q2 / rat3; Rational c0 = q0 - q2third * (q1 - rat2 * q2third * q2third); Rational c1 = q1 - q2 * q2third; std::map rmLocalMap; SolveDepressedCubic(c0, c1, rmLocalMap); rmMap.clear(); for (auto& rm : rmLocalMap) { Rational root = rm.first - q2third; rmMap.insert(std::make_pair((Real)root, rm.second)); } } template static void SolveQuartic(Rational const& p0, Rational const& p1, Rational const& p2, Rational const& p3, Rational const& p4, std::map& rmMap) { Rational const rat2 = 2, rat3 = 3, rat4 = 4, rat6 = 6; Rational q0 = p0 / p4; Rational q1 = p1 / p4; Rational q2 = p2 / p4; Rational q3 = p3 / p4; Rational q3fourth = q3 / rat4; Rational q3fourthSqr = q3fourth * q3fourth; Rational c0 = q0 - q3fourth * (q1 - q3fourth * (q2 - q3fourthSqr * rat3)); Rational c1 = q1 - rat2 * q3fourth * (q2 - rat4 * q3fourthSqr); Rational c2 = q2 - rat6 * q3fourthSqr; std::map rmLocalMap; SolveDepressedQuartic(c0, c1, c2, rmLocalMap); rmMap.clear(); for (auto& rm : rmLocalMap) { Rational root = rm.first - q3fourth; rmMap.insert(std::make_pair((Real)root, rm.second)); } } // Return only the number of real-valued roots and their // multiplicities. info.size() is the number of real-valued roots // and info[i] is the multiplicity of root corresponding to index i. template static void GetRootInfoQuadratic(Rational const& p0, Rational const& p1, Rational const& p2, std::vector& info) { Rational const rat2 = 2; Rational q0 = p0 / p2; Rational q1 = p1 / p2; Rational q1half = q1 / rat2; Rational c0 = q0 - q1half * q1half; info.clear(); info.reserve(2); GetRootInfoDepressedQuadratic(c0, info); } template static void GetRootInfoCubic(Rational const& p0, Rational const& p1, Rational const& p2, Rational const& p3, std::vector& info) { Rational const rat2 = 2, rat3 = 3; Rational q0 = p0 / p3; Rational q1 = p1 / p3; Rational q2 = p2 / p3; Rational q2third = q2 / rat3; Rational c0 = q0 - q2third * (q1 - rat2 * q2third * q2third); Rational c1 = q1 - q2 * q2third; info.clear(); info.reserve(3); GetRootInfoDepressedCubic(c0, c1, info); } template static void GetRootInfoQuartic(Rational const& p0, Rational const& p1, Rational const& p2, Rational const& p3, Rational const& p4, std::vector& info) { Rational const rat2 = 2, rat3 = 3, rat4 = 4, rat6 = 6; Rational q0 = p0 / p4; Rational q1 = p1 / p4; Rational q2 = p2 / p4; Rational q3 = p3 / p4; Rational q3fourth = q3 / rat4; Rational q3fourthSqr = q3fourth * q3fourth; Rational c0 = q0 - q3fourth * (q1 - q3fourth * (q2 - q3fourthSqr * rat3)); Rational c1 = q1 - rat2 * q3fourth * (q2 - rat4 * q3fourthSqr); Rational c2 = q2 - rat6 * q3fourthSqr; info.clear(); info.reserve(4); GetRootInfoDepressedQuartic(c0, c1, c2, info); } // General equations: sum_{i=0}^{d} c(i)*t^i = 0. The input array 'c' // must have at least d+1 elements and the output array 'root' must // have at least d elements. // Find the roots on (-infinity,+infinity). static int Find(int degree, Real const* c, unsigned int maxIterations, Real* roots) { if (degree >= 0 && c) { Real const zero = (Real)0; while (degree >= 0 && c[degree] == zero) { --degree; } if (degree > 0) { // Compute the Cauchy bound. Real const one = (Real)1; Real invLeading = one / c[degree]; Real maxValue = zero; for (int i = 0; i < degree; ++i) { Real value = std::fabs(c[i] * invLeading); if (value > maxValue) { maxValue = value; } } Real bound = one + maxValue; return FindRecursive(degree, c, -bound, bound, maxIterations, roots); } else if (degree == 0) { // The polynomial is a nonzero constant. return 0; } else { // The polynomial is identically zero. roots[0] = zero; return 1; } } else { // Invalid degree or c. return 0; } } // If you know that p(tmin) * p(tmax) <= 0, then there must be at // least one root in [tmin, tmax]. Compute it using bisection. static bool Find(int degree, Real const* c, Real tmin, Real tmax, unsigned int maxIterations, Real& root) { Real const zero = (Real)0; Real pmin = Evaluate(degree, c, tmin); if (pmin == zero) { root = tmin; return true; } Real pmax = Evaluate(degree, c, tmax); if (pmax == zero) { root = tmax; return true; } if (pmin * pmax > zero) { // It is not known whether the interval bounds a root. return false; } if (tmin >= tmax) { // Invalid ordering of interval endpoitns. return false; } for (unsigned int i = 1; i <= maxIterations; ++i) { root = ((Real)0.5) * (tmin + tmax); // This test is designed for 'float' or 'double' when tmin // and tmax are consecutive floating-point numbers. if (root == tmin || root == tmax) { break; } Real p = Evaluate(degree, c, root); Real product = p * pmin; if (product < zero) { tmax = root; pmax = p; } else if (product > zero) { tmin = root; pmin = p; } else { break; } } return true; } private: // Support for the Solve* functions. template static void SolveDepressedQuadratic(Rational const& c0, std::map& rmMap) { Rational const zero = 0; if (c0 < zero) { // Two simple roots. Rational root1 = (Rational)std::sqrt((double)-c0); Rational root0 = -root1; rmMap.insert(std::make_pair(root0, 1)); rmMap.insert(std::make_pair(root1, 1)); GTE_ROOTS_LOW_DEGREE_BLOCK(0); } else if (c0 == zero) { // One double root. rmMap.insert(std::make_pair(zero, 2)); GTE_ROOTS_LOW_DEGREE_BLOCK(1); } else // c0 > 0 { // A complex-conjugate pair of roots. // Complex z0 = -q1/2 - i*sqrt(c0); // Complex z0conj = -q1/2 + i*sqrt(c0); GTE_ROOTS_LOW_DEGREE_BLOCK(2); } } template static void SolveDepressedCubic(Rational const& c0, Rational const& c1, std::map& rmMap) { // Handle the special case of c0 = 0, in which case the polynomial // reduces to a depressed quadratic. Rational const zero = 0; if (c0 == zero) { SolveDepressedQuadratic(c1, rmMap); auto iter = rmMap.find(zero); if (iter != rmMap.end()) { // The quadratic has a root of zero, so the multiplicity // must be increased. ++iter->second; GTE_ROOTS_LOW_DEGREE_BLOCK(3); } else { // The quadratic does not have a root of zero. Insert the // one for the cubic. rmMap.insert(std::make_pair(zero, 1)); GTE_ROOTS_LOW_DEGREE_BLOCK(4); } return; } // Handle the special case of c0 != 0 and c1 = 0. double const oneThird = 1.0 / 3.0; if (c1 == zero) { // One simple real root. Rational root0; if (c0 > zero) { root0 = (Rational)-std::pow((double)c0, oneThird); GTE_ROOTS_LOW_DEGREE_BLOCK(5); } else { root0 = (Rational)std::pow(-(double)c0, oneThird); GTE_ROOTS_LOW_DEGREE_BLOCK(6); } rmMap.insert(std::make_pair(root0, 1)); // One complex conjugate pair. // Complex z0 = root0*(-1 - i*sqrt(3))/2; // Complex z0conj = root0*(-1 + i*sqrt(3))/2; return; } // At this time, c0 != 0 and c1 != 0. Rational const rat2 = 2, rat3 = 3, rat4 = 4, rat27 = 27, rat108 = 108; Rational delta = -(rat4 * c1 * c1 * c1 + rat27 * c0 * c0); if (delta > zero) { // Three simple roots. Rational deltaDiv108 = delta / rat108; Rational betaRe = -c0 / rat2; Rational betaIm = std::sqrt(deltaDiv108); Rational theta = std::atan2(betaIm, betaRe); Rational thetaDiv3 = theta / rat3; double angle = (double)thetaDiv3; Rational cs = (Rational)std::cos(angle); Rational sn = (Rational)std::sin(angle); Rational rhoSqr = betaRe * betaRe + betaIm * betaIm; Rational rhoPowThird = (Rational)std::pow((double)rhoSqr, 1.0 / 6.0); Rational temp0 = rhoPowThird * cs; Rational temp1 = rhoPowThird * sn * (Rational)std::sqrt(3.0); Rational root0 = rat2 * temp0; Rational root1 = -temp0 - temp1; Rational root2 = -temp0 + temp1; rmMap.insert(std::make_pair(root0, 1)); rmMap.insert(std::make_pair(root1, 1)); rmMap.insert(std::make_pair(root2, 1)); GTE_ROOTS_LOW_DEGREE_BLOCK(7); } else if (delta < zero) { // One simple root. Rational deltaDiv108 = delta / rat108; Rational temp0 = -c0 / rat2; Rational temp1 = (Rational)std::sqrt(-(double)deltaDiv108); Rational temp2 = temp0 - temp1; Rational temp3 = temp0 + temp1; if (temp2 >= zero) { temp2 = (Rational)std::pow((double)temp2, oneThird); GTE_ROOTS_LOW_DEGREE_BLOCK(8); } else { temp2 = (Rational)-std::pow(-(double)temp2, oneThird); GTE_ROOTS_LOW_DEGREE_BLOCK(9); } if (temp3 >= zero) { temp3 = (Rational)std::pow((double)temp3, oneThird); GTE_ROOTS_LOW_DEGREE_BLOCK(10); } else { temp3 = (Rational)-std::pow(-(double)temp3, oneThird); GTE_ROOTS_LOW_DEGREE_BLOCK(11); } Rational root0 = temp2 + temp3; rmMap.insert(std::make_pair(root0, 1)); // One complex conjugate pair. // Complex z0 = (-root0 - i*sqrt(3*root0*root0+4*c1))/2; // Complex z0conj = (-root0 + i*sqrt(3*root0*root0+4*c1))/2; } else // delta = 0 { // One simple root and one double root. Rational root0 = -rat3 * c0 / (rat2 * c1); Rational root1 = -rat2 * root0; rmMap.insert(std::make_pair(root0, 2)); rmMap.insert(std::make_pair(root1, 1)); GTE_ROOTS_LOW_DEGREE_BLOCK(12); } } template static void SolveDepressedQuartic(Rational const& c0, Rational const& c1, Rational const& c2, std::map& rmMap) { // Handle the special case of c0 = 0, in which case the polynomial // reduces to a depressed cubic. Rational const zero = 0; if (c0 == zero) { SolveDepressedCubic(c1, c2, rmMap); auto iter = rmMap.find(zero); if (iter != rmMap.end()) { // The cubic has a root of zero, so the multiplicity must // be increased. ++iter->second; GTE_ROOTS_LOW_DEGREE_BLOCK(13); } else { // The cubic does not have a root of zero. Insert the one // for the quartic. rmMap.insert(std::make_pair(zero, 1)); GTE_ROOTS_LOW_DEGREE_BLOCK(14); } return; } // Handle the special case of c1 = 0, in which case the quartic is // a biquadratic // x^4 + c1*x^2 + c0 = (x^2 + c2/2)^2 + (c0 - c2^2/4) if (c1 == zero) { SolveBiquadratic(c0, c2, rmMap); return; } // At this time, c0 != 0 and c1 != 0, which is a requirement for // the general solver that must use a root of a special cubic // polynomial. Rational const rat2 = 2, rat4 = 4, rat8 = 8, rat12 = 12, rat16 = 16; Rational const rat27 = 27, rat36 = 36; Rational c0sqr = c0 * c0, c1sqr = c1 * c1, c2sqr = c2 * c2; Rational delta = c1sqr * (-rat27 * c1sqr + rat4 * c2 * (rat36 * c0 - c2sqr)) + rat16 * c0 * (c2sqr * (c2sqr - rat8 * c0) + rat16 * c0sqr); Rational a0 = rat12 * c0 + c2sqr; Rational a1 = rat4 * c0 - c2sqr; if (delta > zero) { if (c2 < zero && a1 < zero) { // Four simple real roots. std::map rmCubicMap; SolveCubic(c1sqr - rat4 * c0 * c2, rat8 * c0, rat4 * c2, -rat8, rmCubicMap); Rational t = (Rational)rmCubicMap.rbegin()->first; Rational alphaSqr = rat2 * t - c2; Rational alpha = (Rational)std::sqrt((double)alphaSqr); double sgnC1; if (c1 > zero) { sgnC1 = 1.0; GTE_ROOTS_LOW_DEGREE_BLOCK(15); } else { sgnC1 = -1.0; GTE_ROOTS_LOW_DEGREE_BLOCK(16); } Rational arg = t * t - c0; Rational beta = (Rational)(sgnC1 * std::sqrt(std::max((double)arg, 0.0))); Rational D0 = alphaSqr - rat4 * (t + beta); Rational sqrtD0 = (Rational)std::sqrt(std::max((double)D0, 0.0)); Rational D1 = alphaSqr - rat4 * (t - beta); Rational sqrtD1 = (Rational)std::sqrt(std::max((double)D1, 0.0)); Rational root0 = (alpha - sqrtD0) / rat2; Rational root1 = (alpha + sqrtD0) / rat2; Rational root2 = (-alpha - sqrtD1) / rat2; Rational root3 = (-alpha + sqrtD1) / rat2; rmMap.insert(std::make_pair(root0, 1)); rmMap.insert(std::make_pair(root1, 1)); rmMap.insert(std::make_pair(root2, 1)); rmMap.insert(std::make_pair(root3, 1)); } else // c2 >= 0 or a1 >= 0 { // Two complex-conjugate pairs. The values alpha, D0 // and D1 are those of the if-block. // Complex z0 = (alpha - i*sqrt(-D0))/2; // Complex z0conj = (alpha + i*sqrt(-D0))/2; // Complex z1 = (-alpha - i*sqrt(-D1))/2; // Complex z1conj = (-alpha + i*sqrt(-D1))/2; GTE_ROOTS_LOW_DEGREE_BLOCK(17); } } else if (delta < zero) { // Two simple real roots, one complex-conjugate pair. std::map rmCubicMap; SolveCubic(c1sqr - rat4 * c0 * c2, rat8 * c0, rat4 * c2, -rat8, rmCubicMap); Rational t = (Rational)rmCubicMap.rbegin()->first; Rational alphaSqr = rat2 * t - c2; Rational alpha = (Rational)std::sqrt(std::max((double)alphaSqr, 0.0)); double sgnC1; if (c1 > zero) { sgnC1 = 1.0; // Leads to BLOCK(18) } else { sgnC1 = -1.0; // Leads to BLOCK(19) } Rational arg = t * t - c0; Rational beta = (Rational)(sgnC1 * std::sqrt(std::max((double)arg, 0.0))); Rational root0, root1; if (sgnC1 > 0.0) { Rational D1 = alphaSqr - rat4 * (t - beta); Rational sqrtD1 = (Rational)std::sqrt(std::max((double)D1, 0.0)); root0 = (-alpha - sqrtD1) / rat2; root1 = (-alpha + sqrtD1) / rat2; // One complex conjugate pair. // Complex z0 = (alpha - i*sqrt(-D0))/2; // Complex z0conj = (alpha + i*sqrt(-D0))/2; GTE_ROOTS_LOW_DEGREE_BLOCK(18); } else { Rational D0 = alphaSqr - rat4 * (t + beta); Rational sqrtD0 = (Rational)std::sqrt(std::max((double)D0, 0.0)); root0 = (alpha - sqrtD0) / rat2; root1 = (alpha + sqrtD0) / rat2; // One complex conjugate pair. // Complex z0 = (-alpha - i*sqrt(-D1))/2; // Complex z0conj = (-alpha + i*sqrt(-D1))/2; GTE_ROOTS_LOW_DEGREE_BLOCK(19); } rmMap.insert(std::make_pair(root0, 1)); rmMap.insert(std::make_pair(root1, 1)); } else // delta = 0 { if (a1 > zero || (c2 > zero && (a1 != zero || c1 != zero))) { // One double real root, one complex-conjugate pair. Rational const rat9 = 9; Rational root0 = -c1 * a0 / (rat9 * c1sqr - rat2 * c2 * a1); rmMap.insert(std::make_pair(root0, 2)); // One complex conjugate pair. // Complex z0 = -root0 - i*sqrt(c2 + root0^2); // Complex z0conj = -root0 + i*sqrt(c2 + root0^2); GTE_ROOTS_LOW_DEGREE_BLOCK(20); } else { Rational const rat3 = 3; if (a0 != zero) { // One double real root, two simple real roots. Rational const rat9 = 9; Rational root0 = -c1 * a0 / (rat9 * c1sqr - rat2 * c2 * a1); Rational alpha = rat2 * root0; Rational beta = c2 + rat3 * root0 * root0; Rational discr = alpha * alpha - rat4 * beta; Rational temp1 = (Rational)std::sqrt((double)discr); Rational root1 = (-alpha - temp1) / rat2; Rational root2 = (-alpha + temp1) / rat2; rmMap.insert(std::make_pair(root0, 2)); rmMap.insert(std::make_pair(root1, 1)); rmMap.insert(std::make_pair(root2, 1)); GTE_ROOTS_LOW_DEGREE_BLOCK(21); } else { // One triple real root, one simple real root. Rational root0 = -rat3 * c1 / (rat4 * c2); Rational root1 = -rat3 * root0; rmMap.insert(std::make_pair(root0, 3)); rmMap.insert(std::make_pair(root1, 1)); GTE_ROOTS_LOW_DEGREE_BLOCK(22); } } } } template static void SolveBiquadratic(Rational const& c0, Rational const& c2, std::map& rmMap) { // Solve 0 = x^4 + c2*x^2 + c0 = (x^2 + c2/2)^2 + a1, where // a1 = c0 - c2^2/2. We know that c0 != 0 at the time of the // function call, so x = 0 is not a root. The condition c1 = 0 // implies the quartic Delta = 256*c0*a1^2. Rational const zero = 0, rat2 = 2, rat256 = 256; Rational c2Half = c2 / rat2; Rational a1 = c0 - c2Half * c2Half; Rational delta = rat256 * c0 * a1 * a1; if (delta > zero) { if (c2 < zero) { if (a1 < zero) { // Four simple roots. Rational temp0 = (Rational)std::sqrt(-(double)a1); Rational temp1 = -c2Half - temp0; Rational temp2 = -c2Half + temp0; Rational root1 = (Rational)std::sqrt((double)temp1); Rational root0 = -root1; Rational root2 = (Rational)std::sqrt((double)temp2); Rational root3 = -root2; rmMap.insert(std::make_pair(root0, 1)); rmMap.insert(std::make_pair(root1, 1)); rmMap.insert(std::make_pair(root2, 1)); rmMap.insert(std::make_pair(root3, 1)); GTE_ROOTS_LOW_DEGREE_BLOCK(23); } else // a1 > 0 { // Two simple complex conjugate pairs. // double thetaDiv2 = atan2(sqrt(a1), -c2/2) / 2.0; // double cs = cos(thetaDiv2), sn = sin(thetaDiv2); // double length = pow(c0, 0.25); // Complex z0 = length*(cs + i*sn); // Complex z0conj = length*(cs - i*sn); // Complex z1 = length*(-cs + i*sn); // Complex z1conj = length*(-cs - i*sn); GTE_ROOTS_LOW_DEGREE_BLOCK(24); } } else // c2 >= 0 { // Two simple complex conjugate pairs. // Complex z0 = -i*sqrt(c2/2 - sqrt(-a1)); // Complex z0conj = +i*sqrt(c2/2 - sqrt(-a1)); // Complex z1 = -i*sqrt(c2/2 + sqrt(-a1)); // Complex z1conj = +i*sqrt(c2/2 + sqrt(-a1)); GTE_ROOTS_LOW_DEGREE_BLOCK(25); } } else if (delta < zero) { // Two simple real roots. Rational temp0 = (Rational)std::sqrt(-(double)a1); Rational temp1 = -c2Half + temp0; Rational root1 = (Rational)std::sqrt((double)temp1); Rational root0 = -root1; rmMap.insert(std::make_pair(root0, 1)); rmMap.insert(std::make_pair(root1, 1)); // One complex conjugate pair. // Complex z0 = -i*sqrt(c2/2 + sqrt(-a1)); // Complex z0conj = +i*sqrt(c2/2 + sqrt(-a1)); GTE_ROOTS_LOW_DEGREE_BLOCK(26); } else // delta = 0 { if (c2 < zero) { // Two double real roots. Rational root1 = (Rational)std::sqrt(-(double)c2Half); Rational root0 = -root1; rmMap.insert(std::make_pair(root0, 2)); rmMap.insert(std::make_pair(root1, 2)); GTE_ROOTS_LOW_DEGREE_BLOCK(27); } else // c2 > 0 { // Two double complex conjugate pairs. // Complex z0 = -i*sqrt(c2/2); // multiplicity 2 // Complex z0conj = +i*sqrt(c2/2); // multiplicity 2 GTE_ROOTS_LOW_DEGREE_BLOCK(28); } } } // Support for the GetNumRoots* functions. template static void GetRootInfoDepressedQuadratic(Rational const& c0, std::vector& info) { Rational const zero = 0; if (c0 < zero) { // Two simple roots. info.push_back(1); info.push_back(1); } else if (c0 == zero) { // One double root. info.push_back(2); // root is zero } else // c0 > 0 { // A complex-conjugate pair of roots. } } template static void GetRootInfoDepressedCubic(Rational const& c0, Rational const& c1, std::vector& info) { // Handle the special case of c0 = 0, in which case the polynomial // reduces to a depressed quadratic. Rational const zero = 0; if (c0 == zero) { if (c1 == zero) { info.push_back(3); // triple root of zero } else { info.push_back(1); // simple root of zero GetRootInfoDepressedQuadratic(c1, info); } return; } Rational const rat4 = 4, rat27 = 27; Rational delta = -(rat4 * c1 * c1 * c1 + rat27 * c0 * c0); if (delta > zero) { // Three simple real roots. info.push_back(1); info.push_back(1); info.push_back(1); } else if (delta < zero) { // One simple real root. info.push_back(1); } else // delta = 0 { // One simple real root and one double real root. info.push_back(1); info.push_back(2); } } template static void GetRootInfoDepressedQuartic(Rational const& c0, Rational const& c1, Rational const& c2, std::vector& info) { // Handle the special case of c0 = 0, in which case the polynomial // reduces to a depressed cubic. Rational const zero = 0; if (c0 == zero) { if (c1 == zero) { if (c2 == zero) { info.push_back(4); // quadruple root of zero } else { info.push_back(2); // double root of zero GetRootInfoDepressedQuadratic(c2, info); } } else { info.push_back(1); // simple root of zero GetRootInfoDepressedCubic(c1, c2, info); } return; } // Handle the special case of c1 = 0, in which case the quartic is // a biquadratic // x^4 + c1*x^2 + c0 = (x^2 + c2/2)^2 + (c0 - c2^2/4) if (c1 == zero) { GetRootInfoBiquadratic(c0, c2, info); return; } // At this time, c0 != 0 and c1 != 0, which is a requirement for // the general solver that must use a root of a special cubic // polynomial. Rational const rat4 = 4, rat8 = 8, rat12 = 12, rat16 = 16; Rational const rat27 = 27, rat36 = 36; Rational c0sqr = c0 * c0, c1sqr = c1 * c1, c2sqr = c2 * c2; Rational delta = c1sqr * (-rat27 * c1sqr + rat4 * c2 * (rat36 * c0 - c2sqr)) + rat16 * c0 * (c2sqr * (c2sqr - rat8 * c0) + rat16 * c0sqr); Rational a0 = rat12 * c0 + c2sqr; Rational a1 = rat4 * c0 - c2sqr; if (delta > zero) { if (c2 < zero && a1 < zero) { // Four simple real roots. info.push_back(1); info.push_back(1); info.push_back(1); info.push_back(1); } else // c2 >= 0 or a1 >= 0 { // Two complex-conjugate pairs. } } else if (delta < zero) { // Two simple real roots, one complex-conjugate pair. info.push_back(1); info.push_back(1); } else // delta = 0 { if (a1 > zero || (c2 > zero && (a1 != zero || c1 != zero))) { // One double real root, one complex-conjugate pair. info.push_back(2); } else { if (a0 != zero) { // One double real root, two simple real roots. info.push_back(2); info.push_back(1); info.push_back(1); } else { // One triple real root, one simple real root. info.push_back(3); info.push_back(1); } } } } template static void GetRootInfoBiquadratic(Rational const& c0, Rational const& c2, std::vector& info) { // Solve 0 = x^4 + c2*x^2 + c0 = (x^2 + c2/2)^2 + a1, where // a1 = c0 - c2^2/2. We know that c0 != 0 at the time of the // function call, so x = 0 is not a root. The condition c1 = 0 // implies the quartic Delta = 256*c0*a1^2. Rational const zero = 0, rat2 = 2, rat256 = 256; Rational c2Half = c2 / rat2; Rational a1 = c0 - c2Half * c2Half; Rational delta = rat256 * c0 * a1 * a1; if (delta > zero) { if (c2 < zero) { if (a1 < zero) { // Four simple roots. info.push_back(1); info.push_back(1); info.push_back(1); info.push_back(1); } else // a1 > 0 { // Two simple complex conjugate pairs. } } else // c2 >= 0 { // Two simple complex conjugate pairs. } } else if (delta < zero) { // Two simple real roots, one complex conjugate pair. info.push_back(1); info.push_back(1); } else // delta = 0 { if (c2 < zero) { // Two double real roots. info.push_back(2); info.push_back(2); } else // c2 > 0 { // Two double complex conjugate pairs. } } } // Support for the Find functions. static int FindRecursive(int degree, Real const* c, Real tmin, Real tmax, unsigned int maxIterations, Real* roots) { // The base of the recursion. Real const zero = (Real)0; Real root = zero; if (degree == 1) { int numRoots; if (c[1] != zero) { root = -c[0] / c[1]; numRoots = 1; } else if (c[0] == zero) { root = zero; numRoots = 1; } else { numRoots = 0; } if (numRoots > 0 && tmin <= root && root <= tmax) { roots[0] = root; return 1; } return 0; } // Find the roots of the derivative polynomial scaled by 1/degree. // The scaling avoids the factorial growth in the coefficients; // for example, without the scaling, the high-order term x^d // becomes (d!)*x through multiple differentiations. With the // scaling we instead get x. This leads to better numerical // behavior of the root finder. int derivDegree = degree - 1; std::vector derivCoeff(derivDegree + 1); std::vector derivRoots(derivDegree); for (int i = 0; i <= derivDegree; ++i) { derivCoeff[i] = c[i + 1] * (Real)(i + 1) / (Real)degree; } int numDerivRoots = FindRecursive(degree - 1, &derivCoeff[0], tmin, tmax, maxIterations, &derivRoots[0]); int numRoots = 0; if (numDerivRoots > 0) { // Find root on [tmin,derivRoots[0]]. if (Find(degree, c, tmin, derivRoots[0], maxIterations, root)) { roots[numRoots++] = root; } // Find root on [derivRoots[i],derivRoots[i+1]]. for (int i = 0; i <= numDerivRoots - 2; ++i) { if (Find(degree, c, derivRoots[i], derivRoots[i + 1], maxIterations, root)) { roots[numRoots++] = root; } } // Find root on [derivRoots[numDerivRoots-1],tmax]. if (Find(degree, c, derivRoots[numDerivRoots - 1], tmax, maxIterations, root)) { roots[numRoots++] = root; } } else { // The polynomial is monotone on [tmin,tmax], so has at most one root. if (Find(degree, c, tmin, tmax, maxIterations, root)) { roots[numRoots++] = root; } } return numRoots; } static Real Evaluate(int degree, Real const* c, Real t) { int i = degree; Real result = c[i]; while (--i >= 0) { result = t * result + c[i]; } return result; } }; } // ----------------------------------------------------------------------- // Mathematics/Integration.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once //#include #include #include namespace gte { template class Integration { public: // A simple algorithm, but slow to converge as the number of samples // is increased. The 'numSamples' needs to be two or larger. static Real TrapezoidRule(int numSamples, Real a, Real b, std::function const& integrand) { Real h = (b - a) / (Real)(numSamples - 1); Real result = (Real)0.5 * (integrand(a) + integrand(b)); for (int i = 1; i <= numSamples - 2; ++i) { result += integrand(a + i * h); } result *= h; return result; } // The trapezoid rule is used to generate initial estimates, but then // Richardson extrapolation is used to improve the estimates. This is // preferred over TrapezoidRule. The 'order' must be positive. static Real Romberg(int order, Real a, Real b, std::function const& integrand) { Real const half = (Real)0.5; std::vector> rom(order); Real h = b - a; rom[0][0] = half * h * (integrand(a) + integrand(b)); for (int i0 = 2, p0 = 1; i0 <= order; ++i0, p0 *= 2, h *= half) { // Approximations via the trapezoid rule. Real sum = (Real)0; int i1; for (i1 = 1; i1 <= p0; ++i1) { sum += integrand(a + h * (i1 - half)); } // Richardson extrapolation. rom[0][1] = half * (rom[0][0] + h * sum); for (int i2 = 1, p2 = 4; i2 < i0; ++i2, p2 *= 4) { rom[i2][1] = (p2 * rom[i2 - 1][1] - rom[i2 - 1][0]) / (p2 - 1); } for (i1 = 0; i1 < i0; ++i1) { rom[i1][0] = rom[i1][1]; } } Real result = rom[order - 1][0]; return result; } // Gaussian quadrature estimates the integral of a function f(x) // defined on [-1,1] using // integral_{-1}^{1} f(t) dt = sum_{i=0}^{n-1} c[i]*f(r[i]) // where r[i] are the roots to the Legendre polynomial p(t) of degree // n and // c[i] = integral_{-1}^{1} prod_{j=0,j!=i} (t-r[j]/(r[i]-r[j]) dt // To integrate over [a,b], a transformation to [-1,1] is applied // internally: x - ((b-a)*t + (b+a))/2. The Legendre polynomials are // generated by // P[0](x) = 1, P[1](x) = x, // P[k](x) = ((2*k-1)*x*P[k-1](x) - (k-1)*P[k-2](x))/k, k >= 2 // Implementing the polynomial generation is simple, and computing the // roots requires a numerical method for finding polynomial roots. // The challenging task is to develop an efficient algorithm for // computing the coefficients c[i] for a specified degree. The // 'degree' must be two or larger. static void ComputeQuadratureInfo(int degree, std::vector& roots, std::vector& coefficients) { Real const zero = (Real)0; Real const one = (Real)1; Real const half = (Real)0.5; std::vector> poly(degree + 1); poly[0].resize(1); poly[0][0] = one; poly[1].resize(2); poly[1][0] = zero; poly[1][1] = one; for (int n = 2; n <= degree; ++n) { Real mult0 = (Real)(n - 1) / (Real)n; Real mult1 = (Real)(2 * n - 1) / (Real)n; poly[n].resize(n + 1); poly[n][0] = -mult0 * poly[n - 2][0]; for (int i = 1; i <= n - 2; ++i) { poly[n][i] = mult1 * poly[n - 1][i - 1] - mult0 * poly[n - 2][i]; } poly[n][n - 1] = mult1 * poly[n - 1][n - 2]; poly[n][n] = mult1 * poly[n - 1][n - 1]; } roots.resize(degree); RootsPolynomial::Find(degree, &poly[degree][0], 2048, &roots[0]); coefficients.resize(roots.size()); size_t n = roots.size() - 1; std::vector subroots(n); for (size_t i = 0; i < roots.size(); ++i) { Real denominator = (Real)1; for (size_t j = 0, k = 0; j < roots.size(); ++j) { if (j != i) { subroots[k++] = roots[j]; denominator *= roots[i] - roots[j]; } } std::array delta = { -one - subroots.back(), +one - subroots.back() }; std::vector> weights(n); weights[0][0] = half * delta[0] * delta[0]; weights[0][1] = half * delta[1] * delta[1]; for (size_t k = 1; k < n; ++k) { Real dk = (Real)k; Real mult = -dk / (dk + (Real)2); weights[k][0] = mult * delta[0] * weights[k - 1][0]; weights[k][1] = mult * delta[1] * weights[k - 1][1]; } struct Info { int numBits; std::array product; }; int numElements = (1 << static_cast(n - 1)); std::vector info(numElements); info[0].numBits = 0; info[0].product[0] = one; info[0].product[1] = one; for (int ipow = 1, r = 0; ipow < numElements; ipow <<= 1, ++r) { info[ipow].numBits = 1; info[ipow].product[0] = -one - subroots[r]; info[ipow].product[1] = +one - subroots[r]; for (int m = 1, j = ipow + 1; m < ipow; ++m, ++j) { info[j].numBits = info[m].numBits + 1; info[j].product[0] = info[ipow].product[0] * info[m].product[0]; info[j].product[1] = info[ipow].product[1] * info[m].product[1]; } } std::vector> sum(n); std::array zero2 = { zero, zero }; std::fill(sum.begin(), sum.end(), zero2); for (size_t k = 0; k < info.size(); ++k) { sum[info[k].numBits][0] += info[k].product[0]; sum[info[k].numBits][1] += info[k].product[1]; } std::array total = zero2; for (size_t k = 0; k < n; ++k) { total[0] += weights[n - 1 - k][0] * sum[k][0]; total[1] += weights[n - 1 - k][1] * sum[k][1]; } coefficients[i] = (total[1] - total[0]) / denominator; } } static Real GaussianQuadrature(std::vector const& roots, std::vectorconst& coefficients, Real a, Real b, std::function const& integrand) { Real const half = (Real)0.5; Real radius = half * (b - a); Real center = half * (b + a); Real result = (Real)0; for (size_t i = 0; i < roots.size(); ++i) { result += coefficients[i] * integrand(radius * roots[i] + center); } result *= radius; return result; } }; } // ----------------------------------------------------------------------- // Mathematics/RootsBiSection.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include // Compute a root of a function F(t) on an interval [t0, t1]. The caller // specifies the maximum number of iterations, in case you want limited // accuracy for the root. However, the function is designed for native types // (Real = float/double). If you specify a sufficiently large number of // iterations, the root finder bisects until either F(t) is identically zero // [a condition dependent on how you structure F(t) for evaluation] or the // midpoint (t0 + t1)/2 rounds numerically to tmin or tmax. Of course, it // is required that t0 < t1. The return value of Find is: // 0: F(t0)*F(t1) > 0, we cannot determine a root // 1: F(t0) = 0 or F(t1) = 0 // 2..maxIterations: the number of bisections plus one // maxIterations+1: the loop executed without a break (no convergence) namespace gte { template class RootsBisection { public: // Use this function when F(t0) and F(t1) are not already known. static unsigned int Find(std::function const& F, Real t0, Real t1, unsigned int maxIterations, Real& root) { // Set 'root' initially to avoid "potentially uninitialized // variable" warnings by a compiler. root = t0; if (t0 < t1) { // Test the endpoints to see whether F(t) is zero. Real f0 = F(t0); if (f0 == (Real)0) { root = t0; return 1; } Real f1 = F(t1); if (f1 == (Real)0) { root = t1; return 1; } if (f0 * f1 > (Real)0) { // It is not known whether the interval bounds a root. return 0; } unsigned int i; for (i = 2; i <= maxIterations; ++i) { root = (Real)0.5 * (t0 + t1); if (root == t0 || root == t1) { // The numbers t0 and t1 are consecutive // floating-point numbers. break; } Real fm = F(root); Real product = fm * f0; if (product < (Real)0) { t1 = root; f1 = fm; } else if (product > (Real)0) { t0 = root; f0 = fm; } else { break; } } return i; } else { // The interval endpoints are invalid. return 0; } } // If f0 = F(t0) and f1 = F(t1) are already known, pass them to the // bisector. This is useful when |f0| or |f1| is infinite, and you // can pass sign(f0) or sign(f1) rather than then infinity because // the bisector cares only about the signs of f. static unsigned int Find(std::function const& F, Real t0, Real t1, Real f0, Real f1, unsigned int maxIterations, Real& root) { // Set 'root' initially to avoid "potentially uninitialized // variable" warnings by a compiler. root = t0; if (t0 < t1) { // Test the endpoints to see whether F(t) is zero. if (f0 == (Real)0) { root = t0; return 1; } if (f1 == (Real)0) { root = t1; return 1; } if (f0 * f1 > (Real)0) { // It is not known whether the interval bounds a root. return 0; } unsigned int i; root = t0; for (i = 2; i <= maxIterations; ++i) { root = (Real)0.5 * (t0 + t1); if (root == t0 || root == t1) { // The numbers t0 and t1 are consecutive // floating-point numbers. break; } Real fm = F(root); Real product = fm * f0; if (product < (Real)0) { t1 = root; f1 = fm; } else if (product > (Real)0) { t0 = root; f0 = fm; } else { break; } } return i; } else { // The interval endpoints are invalid. return 0; } } }; } // ----------------------------------------------------------------------- // Mathematics/Vector.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once //#include #include #include #include namespace gte { template class Vector { public: // The tuple is uninitialized. Vector() = default; // The tuple is fully initialized by the inputs. Vector(std::array const& values) : mTuple(values) { } // At most N elements are copied from the initializer list, setting // any remaining elements to zero. Create the zero vector using the // syntax // Vector zero{(Real)0}; // WARNING: The C++ 11 specification states that // Vector zero{}; // will lead to a call of the default constructor, not the initializer // constructor! Vector(std::initializer_list values) { int const numValues = static_cast(values.size()); if (N == numValues) { std::copy(values.begin(), values.end(), mTuple.begin()); } else if (N > numValues) { std::copy(values.begin(), values.end(), mTuple.begin()); std::fill(mTuple.begin() + numValues, mTuple.end(), (Real)0); } else // N < numValues { std::copy(values.begin(), values.begin() + N, mTuple.begin()); } } // For 0 <= d < N, element d is 1 and all others are 0. If d is // invalid, the zero vector is created. This is a convenience for // creating the standard Euclidean basis vectors; see also // MakeUnit(int) and Unit(int). Vector(int d) { MakeUnit(d); } // The copy constructor, destructor, and assignment operator are // generated by the compiler. // Member access. The first operator[] returns a const reference // rather than a Real value. This supports writing via standard file // operations that require a const pointer to data. inline int GetSize() const { return N; } inline Real const& operator[](int i) const { return mTuple[i]; } inline Real& operator[](int i) { return mTuple[i]; } // Comparisons for sorted containers and geometric ordering. inline bool operator==(Vector const& vec) const { return mTuple == vec.mTuple; } inline bool operator!=(Vector const& vec) const { return mTuple != vec.mTuple; } inline bool operator< (Vector const& vec) const { return mTuple < vec.mTuple; } inline bool operator<=(Vector const& vec) const { return mTuple <= vec.mTuple; } inline bool operator> (Vector const& vec) const { return mTuple > vec.mTuple; } inline bool operator>=(Vector const& vec) const { return mTuple >= vec.mTuple; } // Special vectors. // All components are 0. void MakeZero() { std::fill(mTuple.begin(), mTuple.end(), (Real)0); } // All components are 1. void MakeOnes() { std::fill(mTuple.begin(), mTuple.end(), (Real)1); } // Component d is 1, all others are zero. void MakeUnit(int d) { std::fill(mTuple.begin(), mTuple.end(), (Real)0); if (0 <= d && d < N) { mTuple[d] = (Real)1; } } static Vector Zero() { Vector v; v.MakeZero(); return v; } static Vector Ones() { Vector v; v.MakeOnes(); return v; } static Vector Unit(int d) { Vector v; v.MakeUnit(d); return v; } protected: // This data structure takes advantage of the built-in operator[], // range checking, and visualizers in MSVS. std::array mTuple; }; // Unary operations. template Vector operator+(Vector const& v) { return v; } template Vector operator-(Vector const& v) { Vector result; for (int i = 0; i < N; ++i) { result[i] = -v[i]; } return result; } // Linear-algebraic operations. template Vector operator+(Vector const& v0, Vector const& v1) { Vector result = v0; return result += v1; } template Vector operator-(Vector const& v0, Vector const& v1) { Vector result = v0; return result -= v1; } template Vector operator*(Vector const& v, Real scalar) { Vector result = v; return result *= scalar; } template Vector operator*(Real scalar, Vector const& v) { Vector result = v; return result *= scalar; } template Vector operator/(Vector const& v, Real scalar) { Vector result = v; return result /= scalar; } template Vector& operator+=(Vector& v0, Vector const& v1) { for (int i = 0; i < N; ++i) { v0[i] += v1[i]; } return v0; } template Vector& operator-=(Vector& v0, Vector const& v1) { for (int i = 0; i < N; ++i) { v0[i] -= v1[i]; } return v0; } template Vector& operator*=(Vector& v, Real scalar) { for (int i = 0; i < N; ++i) { v[i] *= scalar; } return v; } template Vector& operator/=(Vector& v, Real scalar) { if (scalar != (Real)0) { Real invScalar = (Real)1 / scalar; for (int i = 0; i < N; ++i) { v[i] *= invScalar; } } else { for (int i = 0; i < N; ++i) { v[i] = (Real)0; } } return v; } // Componentwise algebraic operations. template Vector operator*(Vector const& v0, Vector const& v1) { Vector result = v0; return result *= v1; } template Vector operator/(Vector const& v0, Vector const& v1) { Vector result = v0; return result /= v1; } template Vector& operator*=(Vector& v0, Vector const& v1) { for (int i = 0; i < N; ++i) { v0[i] *= v1[i]; } return v0; } template Vector& operator/=(Vector& v0, Vector const& v1) { for (int i = 0; i < N; ++i) { v0[i] /= v1[i]; } return v0; } // Geometric operations. The functions with 'robust' set to 'false' use // the standard algorithm for normalizing a vector by computing the length // as a square root of the squared length and dividing by it. The results // can be infinite (or NaN) if the length is zero. When 'robust' is set // to 'true', the algorithm is designed to avoid floating-point overflow // and sets the normalized vector to zero when the length is zero. template Real Dot(Vector const& v0, Vector const& v1) { Real dot = v0[0] * v1[0]; for (int i = 1; i < N; ++i) { dot += v0[i] * v1[i]; } return dot; } template Real Length(Vector const& v, bool robust = false) { if (robust) { Real maxAbsComp = std::fabs(v[0]); for (int i = 1; i < N; ++i) { Real absComp = std::fabs(v[i]); if (absComp > maxAbsComp) { maxAbsComp = absComp; } } Real length; if (maxAbsComp > (Real)0) { Vector scaled = v / maxAbsComp; length = maxAbsComp * std::sqrt(Dot(scaled, scaled)); } else { length = (Real)0; } return length; } else { return std::sqrt(Dot(v, v)); } } template Real Normalize(Vector& v, bool robust = false) { if (robust) { Real maxAbsComp = std::fabs(v[0]); for (int i = 1; i < N; ++i) { Real absComp = std::fabs(v[i]); if (absComp > maxAbsComp) { maxAbsComp = absComp; } } Real length; if (maxAbsComp > (Real)0) { v /= maxAbsComp; length = std::sqrt(Dot(v, v)); v /= length; length *= maxAbsComp; } else { length = (Real)0; for (int i = 0; i < N; ++i) { v[i] = (Real)0; } } return length; } else { Real length = std::sqrt(Dot(v, v)); if (length > (Real)0) { v /= length; } else { for (int i = 0; i < N; ++i) { v[i] = (Real)0; } } return length; } } // Gram-Schmidt orthonormalization to generate orthonormal vectors from // the linearly independent inputs. The function returns the smallest // length of the unnormalized vectors computed during the process. If // this value is nearly zero, it is possible that the inputs are linearly // dependent (within numerical round-off errors). On input, // 1 <= numElements <= N and v[0] through v[numElements-1] must be // initialized. On output, the vectors v[0] through v[numElements-1] // form an orthonormal set. template Real Orthonormalize(int numInputs, Vector* v, bool robust = false) { if (v && 1 <= numInputs && numInputs <= N) { Real minLength = Normalize(v[0], robust); for (int i = 1; i < numInputs; ++i) { for (int j = 0; j < i; ++j) { Real dot = Dot(v[i], v[j]); v[i] -= v[j] * dot; } Real length = Normalize(v[i], robust); if (length < minLength) { minLength = length; } } return minLength; } return (Real)0; } // Construct a single vector orthogonal to the nonzero input vector. If // the maximum absolute component occurs at index i, then the orthogonal // vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components // zero. The index addition i+1 is computed modulo N. template Vector GetOrthogonal(Vector const& v, bool unitLength) { Real cmax = std::fabs(v[0]); int imax = 0; for (int i = 1; i < N; ++i) { Real c = std::fabs(v[i]); if (c > cmax) { cmax = c; imax = i; } } Vector result; result.MakeZero(); int inext = imax + 1; if (inext == N) { inext = 0; } result[imax] = v[inext]; result[inext] = -v[imax]; if (unitLength) { Real sqrDistance = result[imax] * result[imax] + result[inext] * result[inext]; Real invLength = ((Real)1) / std::sqrt(sqrDistance); result[imax] *= invLength; result[inext] *= invLength; } return result; } // Compute the axis-aligned bounding box of the vectors. The return value // is 'true' iff the inputs are valid, in which case vmin and vmax have // valid values. template bool ComputeExtremes(int numVectors, Vector const* v, Vector& vmin, Vector& vmax) { if (v && numVectors > 0) { vmin = v[0]; vmax = vmin; for (int j = 1; j < numVectors; ++j) { Vector const& vec = v[j]; for (int i = 0; i < N; ++i) { if (vec[i] < vmin[i]) { vmin[i] = vec[i]; } else if (vec[i] > vmax[i]) { vmax[i] = vec[i]; } } } return true; } return false; } // Lift n-tuple v to homogeneous (n+1)-tuple (v,last). template Vector HLift(Vector const& v, Real last) { Vector result; for (int i = 0; i < N; ++i) { result[i] = v[i]; } result[N] = last; return result; } // Project homogeneous n-tuple v = (u,v[n-1]) to (n-1)-tuple u. template Vector HProject(Vector const& v) { static_assert(N >= 2, "Invalid dimension."); Vector result; for (int i = 0; i < N - 1; ++i) { result[i] = v[i]; } return result; } // Lift n-tuple v = (w0,w1) to (n+1)-tuple u = (w0,u[inject],w1). By // inference, w0 is a (inject)-tuple [nonexistent when inject=0] and w1 is // a (n-inject)-tuple [nonexistent when inject=n]. template Vector Lift(Vector const& v, int inject, Real value) { Vector result; int i; for (i = 0; i < inject; ++i) { result[i] = v[i]; } result[i] = value; int j = i; for (++j; i < N; ++i, ++j) { result[j] = v[i]; } return result; } // Project n-tuple v = (w0,v[reject],w1) to (n-1)-tuple u = (w0,w1). By // inference, w0 is a (reject)-tuple [nonexistent when reject=0] and w1 is // a (n-1-reject)-tuple [nonexistent when reject=n-1]. template Vector Project(Vector const& v, int reject) { static_assert(N >= 2, "Invalid dimension."); Vector result; for (int i = 0, j = 0; i < N - 1; ++i, ++j) { if (j == reject) { ++j; } result[i] = v[j]; } return result; } } // ----------------------------------------------------------------------- // Mathematics/Vector2.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once //#include namespace gte { // Template alias for convenience. template using Vector2 = Vector<2, Real>; // Compute the perpendicular using the formal determinant, // perp = det{{e0,e1},{x0,x1}} = (x1,-x0) // where e0 = (1,0), e1 = (0,1), and v = (x0,x1). template Vector2 Perp(Vector2 const& v) { return Vector2{ v[1], -v[0] }; } // Compute the normalized perpendicular. template Vector2 UnitPerp(Vector2 const& v, bool robust = false) { Vector2 unitPerp{ v[1], -v[0] }; Normalize(unitPerp, robust); return unitPerp; } // Compute Dot((x0,x1),Perp(y0,y1)) = x0*y1 - x1*y0, where v0 = (x0,x1) // and v1 = (y0,y1). template Real DotPerp(Vector2 const& v0, Vector2 const& v1) { return Dot(v0, Perp(v1)); } // Compute a right-handed orthonormal basis for the orthogonal complement // of the input vectors. The function returns the smallest length of the // unnormalized vectors computed during the process. If this value is // nearly zero, it is possible that the inputs are linearly dependent // (within numerical round-off errors). On input, numInputs must be 1 and // v[0] must be initialized. On output, the vectors v[0] and v[1] form an // orthonormal set. template Real ComputeOrthogonalComplement(int numInputs, Vector2* v, bool robust = false) { if (numInputs == 1) { v[1] = -Perp(v[0]); return Orthonormalize<2, Real>(2, v, robust); } return (Real)0; } // Compute the barycentric coordinates of the point P with respect to the // triangle , P = b0*V0 + b1*V1 + b2*V2, where b0 + b1 + b2 = 1. // The return value is 'true' iff {V0,V1,V2} is a linearly independent // set. Numerically, this is measured by |det[V0 V1 V2]| <= epsilon. The // values bary[] are valid only when the return value is 'true' but set to // zero when the return value is 'false'. template bool ComputeBarycentrics(Vector2 const& p, Vector2 const& v0, Vector2 const& v1, Vector2 const& v2, Real bary[3], Real epsilon = (Real)0) { // Compute the vectors relative to V2 of the triangle. Vector2 diff[3] = { v0 - v2, v1 - v2, p - v2 }; Real det = DotPerp(diff[0], diff[1]); if (det < -epsilon || det > epsilon) { Real invDet = (Real)1 / det; bary[0] = DotPerp(diff[2], diff[1]) * invDet; bary[1] = DotPerp(diff[0], diff[2]) * invDet; bary[2] = (Real)1 - bary[0] - bary[1]; return true; } for (int i = 0; i < 3; ++i) { bary[i] = (Real)0; } return false; } // Get intrinsic information about the input array of vectors. The return // value is 'true' iff the inputs are valid (numVectors > 0, v is not // null, and epsilon >= 0), in which case the class members are valid. template class IntrinsicsVector2 { public: // The constructor sets the class members based on the input set. IntrinsicsVector2(int numVectors, Vector2 const* v, Real inEpsilon) : epsilon(inEpsilon), dimension(0), maxRange((Real)0), origin({ (Real)0, (Real)0 }), extremeCCW(false) { min[0] = (Real)0; min[1] = (Real)0; direction[0] = { (Real)0, (Real)0 }; direction[1] = { (Real)0, (Real)0 }; extreme[0] = 0; extreme[1] = 0; extreme[2] = 0; if (numVectors > 0 && v && epsilon >= (Real)0) { // Compute the axis-aligned bounding box for the input // vectors. Keep track of the indices into 'vectors' for the // current min and max. int j, indexMin[2], indexMax[2]; for (j = 0; j < 2; ++j) { min[j] = v[0][j]; max[j] = min[j]; indexMin[j] = 0; indexMax[j] = 0; } int i; for (i = 1; i < numVectors; ++i) { for (j = 0; j < 2; ++j) { if (v[i][j] < min[j]) { min[j] = v[i][j]; indexMin[j] = i; } else if (v[i][j] > max[j]) { max[j] = v[i][j]; indexMax[j] = i; } } } // Determine the maximum range for the bounding box. maxRange = max[0] - min[0]; extreme[0] = indexMin[0]; extreme[1] = indexMax[0]; Real range = max[1] - min[1]; if (range > maxRange) { maxRange = range; extreme[0] = indexMin[1]; extreme[1] = indexMax[1]; } // The origin is either the vector of minimum x0-value or // vector of minimum x1-value. origin = v[extreme[0]]; // Test whether the vector set is (nearly) a vector. if (maxRange <= epsilon) { dimension = 0; for (j = 0; j < 2; ++j) { extreme[j + 1] = extreme[0]; } return; } // Test whether the vector set is (nearly) a line segment. We // need direction[1] to span the orthogonal complement of // direction[0]. direction[0] = v[extreme[1]] - origin; Normalize(direction[0], false); direction[1] = -Perp(direction[0]); // Compute the maximum distance of the points from the line // origin+t*direction[0]. Real maxDistance = (Real)0; Real maxSign = (Real)0; extreme[2] = extreme[0]; for (i = 0; i < numVectors; ++i) { Vector2 diff = v[i] - origin; Real distance = Dot(direction[1], diff); Real sign = (distance > (Real)0 ? (Real)1 : (distance < (Real)0 ? (Real)-1 : (Real)0)); distance = std::fabs(distance); if (distance > maxDistance) { maxDistance = distance; maxSign = sign; extreme[2] = i; } } if (maxDistance <= epsilon * maxRange) { // The points are (nearly) on the line // origin + t * direction[0]. dimension = 1; extreme[2] = extreme[1]; return; } dimension = 2; extremeCCW = (maxSign > (Real)0); return; } } // A nonnegative tolerance that is used to determine the intrinsic // dimension of the set. Real epsilon; // The intrinsic dimension of the input set, computed based on the // nonnegative tolerance mEpsilon. int dimension; // Axis-aligned bounding box of the input set. The maximum range is // the larger of max[0]-min[0] and max[1]-min[1]. Real min[2], max[2]; Real maxRange; // Coordinate system. The origin is valid for any dimension d. The // unit-length direction vector is valid only for 0 <= i < d. The // extreme index is relative to the array of input points, and is also // valid only for 0 <= i < d. If d = 0, all points are effectively // the same, but the use of an epsilon may lead to an extreme index // that is not zero. If d = 1, all points effectively lie on a line // segment. If d = 2, the points are not collinear. Vector2 origin; Vector2 direction[2]; // The indices that define the maximum dimensional extents. The // values extreme[0] and extreme[1] are the indices for the points // that define the largest extent in one of the coordinate axis // directions. If the dimension is 2, then extreme[2] is the index // for the point that generates the largest extent in the direction // perpendicular to the line through the points corresponding to // extreme[0] and extreme[1]. The triangle formed by the points // V[extreme[0]], V[extreme[1]], and V[extreme[2]] is clockwise or // counterclockwise, the condition stored in extremeCCW. int extreme[3]; bool extremeCCW; }; } // ----------------------------------------------------------------------- // Mathematics/Vector3.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once //#include namespace gte { // Template alias for convenience. template using Vector3 = Vector<3, Real>; // Cross, UnitCross, and DotCross have a template parameter N that should // be 3 or 4. The latter case supports affine vectors in 4D (last // component w = 0) when you want to use 4-tuples and 4x4 matrices for // affine algebra. // Compute the cross product using the formal determinant: // cross = det{{e0,e1,e2},{x0,x1,x2},{y0,y1,y2}} // = (x1*y2-x2*y1, x2*y0-x0*y2, x0*y1-x1*y0) // where e0 = (1,0,0), e1 = (0,1,0), e2 = (0,0,1), v0 = (x0,x1,x2), and // v1 = (y0,y1,y2). template Vector Cross(Vector const& v0, Vector const& v1) { static_assert(N == 3 || N == 4, "Dimension must be 3 or 4."); Vector result; result.MakeZero(); result[0] = v0[1] * v1[2] - v0[2] * v1[1]; result[1] = v0[2] * v1[0] - v0[0] * v1[2]; result[2] = v0[0] * v1[1] - v0[1] * v1[0]; return result; } // Compute the normalized cross product. template Vector UnitCross(Vector const& v0, Vector const& v1, bool robust = false) { static_assert(N == 3 || N == 4, "Dimension must be 3 or 4."); Vector unitCross = Cross(v0, v1); Normalize(unitCross, robust); return unitCross; } // Compute Dot((x0,x1,x2),Cross((y0,y1,y2),(z0,z1,z2)), the triple scalar // product of three vectors, where v0 = (x0,x1,x2), v1 = (y0,y1,y2), and // v2 is (z0,z1,z2). template Real DotCross(Vector const& v0, Vector const& v1, Vector const& v2) { static_assert(N == 3 || N == 4, "Dimension must be 3 or 4."); return Dot(v0, Cross(v1, v2)); } // Compute a right-handed orthonormal basis for the orthogonal complement // of the input vectors. The function returns the smallest length of the // unnormalized vectors computed during the process. If this value is // nearly zero, it is possible that the inputs are linearly dependent // (within numerical round-off errors). On input, numInputs must be 1 or // 2 and v[0] through v[numInputs-1] must be initialized. On output, the // vectors v[0] through v[2] form an orthonormal set. template Real ComputeOrthogonalComplement(int numInputs, Vector3* v, bool robust = false) { if (numInputs == 1) { if (std::fabs(v[0][0]) > std::fabs(v[0][1])) { v[1] = { -v[0][2], (Real)0, +v[0][0] }; } else { v[1] = { (Real)0, +v[0][2], -v[0][1] }; } numInputs = 2; } if (numInputs == 2) { v[2] = Cross(v[0], v[1]); return Orthonormalize<3, Real>(3, v, robust); } return (Real)0; } // Compute the barycentric coordinates of the point P with respect to the // tetrahedron , P = b0*V0 + b1*V1 + b2*V2 + b3*V3, where // b0 + b1 + b2 + b3 = 1. The return value is 'true' iff {V0,V1,V2,V3} is // a linearly independent set. Numerically, this is measured by // |det[V0 V1 V2 V3]| <= epsilon. The values bary[] are valid only when // the return value is 'true' but set to zero when the return value is // 'false'. template bool ComputeBarycentrics(Vector3 const& p, Vector3 const& v0, Vector3 const& v1, Vector3 const& v2, Vector3 const& v3, Real bary[4], Real epsilon = (Real)0) { // Compute the vectors relative to V3 of the tetrahedron. Vector3 diff[4] = { v0 - v3, v1 - v3, v2 - v3, p - v3 }; Real det = DotCross(diff[0], diff[1], diff[2]); if (det < -epsilon || det > epsilon) { Real invDet = ((Real)1) / det; bary[0] = DotCross(diff[3], diff[1], diff[2]) * invDet; bary[1] = DotCross(diff[3], diff[2], diff[0]) * invDet; bary[2] = DotCross(diff[3], diff[0], diff[1]) * invDet; bary[3] = (Real)1 - bary[0] - bary[1] - bary[2]; return true; } for (int i = 0; i < 4; ++i) { bary[i] = (Real)0; } return false; } // Get intrinsic information about the input array of vectors. The return // value is 'true' iff the inputs are valid (numVectors > 0, v is not // null, and epsilon >= 0), in which case the class members are valid. template class IntrinsicsVector3 { public: // The constructor sets the class members based on the input set. IntrinsicsVector3(int numVectors, Vector3 const* v, Real inEpsilon) : epsilon(inEpsilon), dimension(0), maxRange((Real)0), origin({ (Real)0, (Real)0, (Real)0 }), extremeCCW(false) { min[0] = (Real)0; min[1] = (Real)0; min[2] = (Real)0; direction[0] = { (Real)0, (Real)0, (Real)0 }; direction[1] = { (Real)0, (Real)0, (Real)0 }; direction[2] = { (Real)0, (Real)0, (Real)0 }; extreme[0] = 0; extreme[1] = 0; extreme[2] = 0; extreme[3] = 0; if (numVectors > 0 && v && epsilon >= (Real)0) { // Compute the axis-aligned bounding box for the input vectors. // Keep track of the indices into 'vectors' for the current // min and max. int j, indexMin[3], indexMax[3]; for (j = 0; j < 3; ++j) { min[j] = v[0][j]; max[j] = min[j]; indexMin[j] = 0; indexMax[j] = 0; } int i; for (i = 1; i < numVectors; ++i) { for (j = 0; j < 3; ++j) { if (v[i][j] < min[j]) { min[j] = v[i][j]; indexMin[j] = i; } else if (v[i][j] > max[j]) { max[j] = v[i][j]; indexMax[j] = i; } } } // Determine the maximum range for the bounding box. maxRange = max[0] - min[0]; extreme[0] = indexMin[0]; extreme[1] = indexMax[0]; Real range = max[1] - min[1]; if (range > maxRange) { maxRange = range; extreme[0] = indexMin[1]; extreme[1] = indexMax[1]; } range = max[2] - min[2]; if (range > maxRange) { maxRange = range; extreme[0] = indexMin[2]; extreme[1] = indexMax[2]; } // The origin is either the vector of minimum x0-value, vector // of minimum x1-value, or vector of minimum x2-value. origin = v[extreme[0]]; // Test whether the vector set is (nearly) a vector. if (maxRange <= epsilon) { dimension = 0; for (j = 0; j < 3; ++j) { extreme[j + 1] = extreme[0]; } return; } // Test whether the vector set is (nearly) a line segment. We // need {direction[2],direction[3]} to span the orthogonal // complement of direction[0]. direction[0] = v[extreme[1]] - origin; Normalize(direction[0], false); if (std::fabs(direction[0][0]) > std::fabs(direction[0][1])) { direction[1][0] = -direction[0][2]; direction[1][1] = (Real)0; direction[1][2] = +direction[0][0]; } else { direction[1][0] = (Real)0; direction[1][1] = +direction[0][2]; direction[1][2] = -direction[0][1]; } Normalize(direction[1], false); direction[2] = Cross(direction[0], direction[1]); // Compute the maximum distance of the points from the line // origin + t * direction[0]. Real maxDistance = (Real)0; Real distance, dot; extreme[2] = extreme[0]; for (i = 0; i < numVectors; ++i) { Vector3 diff = v[i] - origin; dot = Dot(direction[0], diff); Vector3 proj = diff - dot * direction[0]; distance = Length(proj, false); if (distance > maxDistance) { maxDistance = distance; extreme[2] = i; } } if (maxDistance <= epsilon * maxRange) { // The points are (nearly) on the line // origin + t * direction[0]. dimension = 1; extreme[2] = extreme[1]; extreme[3] = extreme[1]; return; } // Test whether the vector set is (nearly) a planar polygon. // The point v[extreme[2]] is farthest from the line: // origin + t * direction[0]. The vector // v[extreme[2]] - origin is not necessarily perpendicular to // direction[0], so project out the direction[0] component so // that the result is perpendicular to direction[0]. direction[1] = v[extreme[2]] - origin; dot = Dot(direction[0], direction[1]); direction[1] -= dot * direction[0]; Normalize(direction[1], false); // We need direction[2] to span the orthogonal complement of // {direction[0],direction[1]}. direction[2] = Cross(direction[0], direction[1]); // Compute the maximum distance of the points from the plane // origin+t0 * direction[0] + t1 * direction[1]. maxDistance = (Real)0; Real maxSign = (Real)0; extreme[3] = extreme[0]; for (i = 0; i < numVectors; ++i) { Vector3 diff = v[i] - origin; distance = Dot(direction[2], diff); Real sign = (distance > (Real)0 ? (Real)1 : (distance < (Real)0 ? (Real)-1 : (Real)0)); distance = std::fabs(distance); if (distance > maxDistance) { maxDistance = distance; maxSign = sign; extreme[3] = i; } } if (maxDistance <= epsilon * maxRange) { // The points are (nearly) on the plane // origin + t0 * direction[0] + t1 * direction[1]. dimension = 2; extreme[3] = extreme[2]; return; } dimension = 3; extremeCCW = (maxSign > (Real)0); return; } } // A nonnegative tolerance that is used to determine the intrinsic // dimension of the set. Real epsilon; // The intrinsic dimension of the input set, computed based on the // nonnegative tolerance mEpsilon. int dimension; // Axis-aligned bounding box of the input set. The maximum range is // the larger of max[0]-min[0], max[1]-min[1], and max[2]-min[2]. Real min[3], max[3]; Real maxRange; // Coordinate system. The origin is valid for any dimension d. The // unit-length direction vector is valid only for 0 <= i < d. The // extreme index is relative to the array of input points, and is also // valid only for 0 <= i < d. If d = 0, all points are effectively // the same, but the use of an epsilon may lead to an extreme index // that is not zero. If d = 1, all points effectively lie on a line // segment. If d = 2, all points effectively line on a plane. If // d = 3, the points are not coplanar. Vector3 origin; Vector3 direction[3]; // The indices that define the maximum dimensional extents. The // values extreme[0] and extreme[1] are the indices for the points // that define the largest extent in one of the coordinate axis // directions. If the dimension is 2, then extreme[2] is the index // for the point that generates the largest extent in the direction // perpendicular to the line through the points corresponding to // extreme[0] and extreme[1]. Furthermore, if the dimension is 3, // then extreme[3] is the index for the point that generates the // largest extent in the direction perpendicular to the triangle // defined by the other extreme points. The tetrahedron formed by the // points V[extreme[0]], V[extreme[1]], V[extreme[2]], and // V[extreme[3]] is clockwise or counterclockwise, the condition // stored in extremeCCW. int extreme[4]; bool extremeCCW; }; } // ----------------------------------------------------------------------- // Mathematics/Vector4.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once //#include namespace gte { // Template alias for convenience. template using Vector4 = Vector<4, Real>; // In Vector3.h, the Vector3 Cross, UnitCross, and DotCross have a // template parameter N that should be 3 or 4. The latter case supports // affine vectors in 4D (last component w = 0) when you want to use // 4-tuples and 4x4 matrices for affine algebra. Thus, you may use those // template functions for Vector4. // Compute the hypercross product using the formal determinant: // hcross = det{{e0,e1,e2,e3},{x0,x1,x2,x3},{y0,y1,y2,y3},{z0,z1,z2,z3}} // where e0 = (1,0,0,0), e1 = (0,1,0,0), e2 = (0,0,1,0), e3 = (0,0,0,1), // v0 = (x0,x1,x2,x3), v1 = (y0,y1,y2,y3), and v2 = (z0,z1,z2,z3). template Vector4 HyperCross(Vector4 const& v0, Vector4 const& v1, Vector4 const& v2) { Real m01 = v0[0] * v1[1] - v0[1] * v1[0]; // x0*y1 - y0*x1 Real m02 = v0[0] * v1[2] - v0[2] * v1[0]; // x0*z1 - z0*x1 Real m03 = v0[0] * v1[3] - v0[3] * v1[0]; // x0*w1 - w0*x1 Real m12 = v0[1] * v1[2] - v0[2] * v1[1]; // y0*z1 - z0*y1 Real m13 = v0[1] * v1[3] - v0[3] * v1[1]; // y0*w1 - w0*y1 Real m23 = v0[2] * v1[3] - v0[3] * v1[2]; // z0*w1 - w0*z1 return Vector4 { +m23 * v2[1] - m13 * v2[2] + m12 * v2[3], // +m23*y2 - m13*z2 + m12*w2 -m23 * v2[0] + m03 * v2[2] - m02 * v2[3], // -m23*x2 + m03*z2 - m02*w2 +m13 * v2[0] - m03 * v2[1] + m01 * v2[3], // +m13*x2 - m03*y2 + m01*w2 -m12 * v2[0] + m02 * v2[1] - m01 * v2[2] // -m12*x2 + m02*y2 - m01*z2 }; } // Compute the normalized hypercross product. template Vector4 UnitHyperCross(Vector4 const& v0, Vector4 const& v1, Vector4 const& v2, bool robust = false) { Vector4 unitHyperCross = HyperCross(v0, v1, v2); Normalize(unitHyperCross, robust); return unitHyperCross; } // Compute Dot(HyperCross((x0,x1,x2,x3),(y0,y1,y2,y3),(z0,z1,z2,z3)), // (w0,w1,w2,w3)), where v0 = (x0,x1,x2,x3), v1 = (y0,y1,y2,y3), // v2 = (z0,z1,z2,z3), and v3 = (w0,w1,w2,w3). template Real DotHyperCross(Vector4 const& v0, Vector4 const& v1, Vector4 const& v2, Vector4 const& v3) { return Dot(HyperCross(v0, v1, v2), v3); } // Compute a right-handed orthonormal basis for the orthogonal complement // of the input vectors. The function returns the smallest length of the // unnormalized vectors computed during the process. If this value is // nearly zero, it is possible that the inputs are linearly dependent // (within numerical round-off errors). On input, numInputs must be 1, 2 // or 3, and v[0] through v[numInputs-1] must be initialized. On output, // the vectors v[0] through v[3] form an orthonormal set. template Real ComputeOrthogonalComplement(int numInputs, Vector4* v, bool robust = false) { if (numInputs == 1) { int maxIndex = 0; Real maxAbsValue = std::fabs(v[0][0]); for (int i = 1; i < 4; ++i) { Real absValue = std::fabs(v[0][i]); if (absValue > maxAbsValue) { maxIndex = i; maxAbsValue = absValue; } } if (maxIndex < 2) { v[1][0] = -v[0][1]; v[1][1] = +v[0][0]; v[1][2] = (Real)0; v[1][3] = (Real)0; } else if (maxIndex == 3) { // Generally, you can skip this clause and swap the last two // components. However, by swapping 2 and 3 in this case, we // allow the function to work properly when the inputs are 3D // vectors represented as 4D affine vectors (w = 0). v[1][0] = (Real)0; v[1][1] = +v[0][2]; v[1][2] = -v[0][1]; v[1][3] = (Real)0; } else { v[1][0] = (Real)0; v[1][1] = (Real)0; v[1][2] = -v[0][3]; v[1][3] = +v[0][2]; } numInputs = 2; } if (numInputs == 2) { Real det[6] = { v[0][0] * v[1][1] - v[1][0] * v[0][1], v[0][0] * v[1][2] - v[1][0] * v[0][2], v[0][0] * v[1][3] - v[1][0] * v[0][3], v[0][1] * v[1][2] - v[1][1] * v[0][2], v[0][1] * v[1][3] - v[1][1] * v[0][3], v[0][2] * v[1][3] - v[1][2] * v[0][3] }; int maxIndex = 0; Real maxAbsValue = std::fabs(det[0]); for (int i = 1; i < 6; ++i) { Real absValue = std::fabs(det[i]); if (absValue > maxAbsValue) { maxIndex = i; maxAbsValue = absValue; } } if (maxIndex == 0) { v[2] = { -det[4], +det[2], (Real)0, -det[0] }; } else if (maxIndex <= 2) { v[2] = { +det[5], (Real)0, -det[2], +det[1] }; } else { v[2] = { (Real)0, -det[5], +det[4], -det[3] }; } numInputs = 3; } if (numInputs == 3) { v[3] = HyperCross(v[0], v[1], v[2]); return Orthonormalize<4, Real>(4, v, robust); } return (Real)0; } } // ----------------------------------------------------------------------- // Mathematics/ParemetricCurve.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once //#include //#include //#include namespace gte { template class ParametricCurve { protected: // Abstract base class for a parameterized curve X(t), where t is the // parameter in [tmin,tmax] and X is an N-tuple position. The first // constructor is for single-segment curves. The second constructor is // for multiple-segment curves. The times must be strictly increasing. ParametricCurve(Real tmin, Real tmax) : mTime(2), mSegmentLength(1), mAccumulatedLength(1), mRombergOrder(DEFAULT_ROMBERG_ORDER), mMaxBisections(DEFAULT_MAX_BISECTIONS), mConstructed(false) { mTime[0] = tmin; mTime[1] = tmax; mSegmentLength[0] = (Real)0; mAccumulatedLength[0] = (Real)0; } ParametricCurve(int numSegments, Real const* times) : mTime(numSegments + 1), mSegmentLength(numSegments), mAccumulatedLength(numSegments), mRombergOrder(DEFAULT_ROMBERG_ORDER), mMaxBisections(DEFAULT_MAX_BISECTIONS), mConstructed(false) { std::copy(times, times + numSegments + 1, mTime.begin()); mSegmentLength[0] = (Real)0; mAccumulatedLength[0] = (Real)0; } public: virtual ~ParametricCurve() { } // To validate construction, create an object as shown: // DerivedClassCurve curve(parameters); // if (!curve) { ; } inline operator bool() const { return mConstructed; } // Member access. inline Real GetTMin() const { return mTime.front(); } inline Real GetTMax() const { return mTime.back(); } inline int GetNumSegments() const { return static_cast(mSegmentLength.size()); } inline Real const* GetTimes() const { return &mTime[0]; } // This function applies only when the first constructor is used (two // times rather than a sequence of three or more times). void SetTimeInterval(Real tmin, Real tmax) { if (mTime.size() == 2) { mTime[0] = tmin; mTime[1] = tmax; } } // Parameters used in GetLength(...), GetTotalLength() and // GetTime(...). // The default value is 8. inline void SetRombergOrder(int order) { mRombergOrder = std::max(order, 1); } // The default value is 1024. inline void SetMaxBisections(unsigned int maxBisections) { mMaxBisections = std::max(maxBisections, 1u); } // Evaluation of the curve. The function supports derivative // calculation through order 3; that is, order <= 3 is required. If // you want/ only the position, pass in order of 0. If you want the // position and first derivative, pass in order of 1, and so on. The // output array 'jet' must have enough storage to support the maximum // order. The values are ordered as: position, first derivative, // second derivative, third derivative. enum { SUP_ORDER = 4 }; virtual void Evaluate(Real t, unsigned int order, Vector* jet) const = 0; void Evaluate(Real t, unsigned int order, Real* values) const { Evaluate(t, order, reinterpret_cast*>(values)); } // Differential geometric quantities. Vector GetPosition(Real t) const { Vector position; Evaluate(t, 0, &position); return position; } Vector GetTangent(Real t) const { std::array, 2> jet; // (position, tangent) Evaluate(t, 1, jet.data()); Normalize(jet[1]); return jet[1]; } Real GetSpeed(Real t) const { std::array, 2> jet; // (position, tangent) Evaluate(t, 1, jet.data()); return Length(jet[1]); } Real GetLength(Real t0, Real t1) const { std::function speed = [this](Real t) { return GetSpeed(t); }; if (mSegmentLength[0] == (Real)0) { // Lazy initialization of lengths of segments. int const numSegments = static_cast(mSegmentLength.size()); Real accumulated = (Real)0; for (int i = 0; i < numSegments; ++i) { mSegmentLength[i] = Integration::Romberg(mRombergOrder, mTime[i], mTime[i + 1], speed); accumulated += mSegmentLength[i]; mAccumulatedLength[i] = accumulated; } } t0 = std::max(t0, GetTMin()); t1 = std::min(t1, GetTMax()); auto iter0 = std::lower_bound(mTime.begin(), mTime.end(), t0); int index0 = static_cast(iter0 - mTime.begin()); auto iter1 = std::lower_bound(mTime.begin(), mTime.end(), t1); int index1 = static_cast(iter1 - mTime.begin()); Real length; if (index0 < index1) { length = (Real)0; if (t0 < *iter0) { length += Integration::Romberg(mRombergOrder, t0, mTime[index0], speed); } int isup; if (t1 < *iter1) { length += Integration::Romberg(mRombergOrder, mTime[index1 - 1], t1, speed); isup = index1 - 1; } else { isup = index1; } for (int i = index0; i < isup; ++i) { length += mSegmentLength[i]; } } else { length = Integration::Romberg(mRombergOrder, t0, t1, speed); } return length; } Real GetTotalLength() const { if (mAccumulatedLength.back() == (Real)0) { // Lazy evaluation of the accumulated length array. return GetLength(mTime.front(), mTime.back()); } return mAccumulatedLength.back(); } // Inverse mapping of s = Length(t) given by t = Length^{-1}(s). The // inverse length function generally cannot be written in closed form, // in which case it is not directly computable. Instead, we can // specify s and estimate the root t for F(t) = Length(t) - s. The // derivative is F'(t) = Speed(t) >= 0, so F(t) is nondecreasing. To // be robust, we use bisection to locate the root, although it is // possible to use a hybrid of Newton's method and bisection. For // details, see the document // https://www.geometrictools.com/Documentation/MovingAlongCurveSpecifiedSpeed.pdf Real GetTime(Real length) const { if (length > (Real)0) { if (length < GetTotalLength()) { std::function F = [this, &length](Real t) { return Integration::Romberg(mRombergOrder, mTime.front(), t, [this](Real z) { return GetSpeed(z); }) - length; }; // We know that F(tmin) < 0 and F(tmax) > 0, which allows us to // use bisection. Rather than bisect the entire interval, let's // narrow it down with a reasonable initial guess. Real ratio = length / GetTotalLength(); Real omratio = (Real)1 - ratio; Real tmid = omratio * mTime.front() + ratio * mTime.back(); Real fmid = F(tmid); if (fmid > (Real)0) { RootsBisection::Find(F, mTime.front(), tmid, (Real)-1, (Real)1, mMaxBisections, tmid); } else if (fmid < (Real)0) { RootsBisection::Find(F, tmid, mTime.back(), (Real)-1, (Real)1, mMaxBisections, tmid); } return tmid; } else { return mTime.back(); } } else { return mTime.front(); } } // Compute a subset of curve points according to the specified attribute. // The input 'numPoints' must be two or larger. void SubdivideByTime(int numPoints, Vector* points) const { Real delta = (mTime.back() - mTime.front()) / (Real)(numPoints - 1); for (int i = 0; i < numPoints; ++i) { Real t = mTime.front() + delta * i; points[i] = GetPosition(t); } } void SubdivideByLength(int numPoints, Vector* points) const { Real delta = GetTotalLength() / (Real)(numPoints - 1); for (int i = 0; i < numPoints; ++i) { Real length = delta * i; Real t = GetTime(length); points[i] = GetPosition(t); } } protected: enum { DEFAULT_ROMBERG_ORDER = 8, DEFAULT_MAX_BISECTIONS = 1024 }; std::vector mTime; mutable std::vector mSegmentLength; mutable std::vector mAccumulatedLength; int mRombergOrder; unsigned int mMaxBisections; bool mConstructed; }; } // ----------------------------------------------------------------------- // Mathematics/BSplineCurve.h // // David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2019 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once //#include //#include namespace gte { template class BSplineCurve : public ParametricCurve { public: // Construction. If the input controls is non-null, a copy is made of // the controls. To defer setting the control points, pass a null // pointer and later access the control points via GetControls() or // SetControl() member functions. The domain is t in [t[d],t[n]], // where t[d] and t[n] are knots with d the degree and n the number of // control points. BSplineCurve(BasisFunctionInput const& input, Vector const* controls) : ParametricCurve((Real)0, (Real)1), mBasisFunction(input) { // The mBasisFunction stores the domain but so does // ParametricCurve. this->mTime.front() = mBasisFunction.GetMinDomain(); this->mTime.back() = mBasisFunction.GetMaxDomain(); // The replication of control points for periodic splines is // avoided by wrapping the i-loop index in Evaluate. mControls.resize(input.numControls); if (controls) { std::copy(controls, controls + input.numControls, mControls.begin()); } else { Vector zero{ (Real)0 }; std::fill(mControls.begin(), mControls.end(), zero); } this->mConstructed = true; } // Member access. inline BasisFunction const& GetBasisFunction() const { return mBasisFunction; } inline int GetNumControls() const { return static_cast(mControls.size()); } inline Vector const* GetControls() const { return mControls.data(); } inline Vector* GetControls() { return mControls.data(); } void SetControl(int i, Vector const& control) { if (0 <= i && i < GetNumControls()) { mControls[i] = control; } } Vector const& GetControl(int i) const { if (0 <= i && i < GetNumControls()) { return mControls[i]; } else { return mControls[0]; } } // Evaluation of the curve. The function supports derivative // calculation through order 3; that is, order <= 3 is required. If // you want/ only the position, pass in order of 0. If you want the // position and first derivative, pass in order of 1, and so on. The // output array 'jet' must have enough storage to support the maximum // order. The values are ordered as: position, first derivative, // second derivative, third derivative. virtual void Evaluate(Real t, unsigned int order, Vector* jet) const override { unsigned int const supOrder = ParametricCurve::SUP_ORDER; if (!this->mConstructed || order >= supOrder) { // Return a zero-valued jet for invalid state. for (unsigned int i = 0; i < supOrder; ++i) { jet[i].MakeZero(); } return; } int imin, imax; mBasisFunction.Evaluate(t, order, imin, imax); // Compute position. jet[0] = Compute(0, imin, imax); if (order >= 1) { // Compute first derivative. jet[1] = Compute(1, imin, imax); if (order >= 2) { // Compute second derivative. jet[2] = Compute(2, imin, imax); if (order == 3) { jet[3] = Compute(3, imin, imax); } } } } private: // Support for Evaluate(...). Vector Compute(unsigned int order, int imin, int imax) const { // The j-index introduces a tiny amount of overhead in order to handle // both aperiodic and periodic splines. For aperiodic splines, j = i // always. int numControls = GetNumControls(); Vector result; result.MakeZero(); for (int i = imin; i <= imax; ++i) { Real tmp = mBasisFunction.GetValue(order, i); int j = (i >= numControls ? i - numControls : i); result += tmp * mControls[j]; } return result; } BasisFunction mBasisFunction; std::vector> mControls; }; }