Revisão | 52 (tree) |
---|---|
Hora | 2019-08-11 14:39:09 |
Autor | mocchi_2012 |
曲線長計算の追加
@@ -0,0 +1,60 @@ | ||
1 | +#define NOMINMAX | |
2 | +#include "opennurbs.h" | |
3 | +#include "ONGEO.h" | |
4 | +#include <cmath> | |
5 | +#include <cstdio> | |
6 | + | |
7 | +#include "ONGEO_NumericalIntegration.hpp" | |
8 | + | |
9 | +// Reference : B. Guenter, R. Parent, | |
10 | +// "Computing the arc length of parametric curves". | |
11 | +// IEEE Computer Graphics and Applications, Vol 10, No.3, May. 1990, pp.72-78. | |
12 | + | |
13 | +namespace{ | |
14 | + struct IntegralFunc{ | |
15 | + ON_BezierCurve bc; | |
16 | + double operator()(double t) const{ | |
17 | + ON_3dPoint pt; | |
18 | + ON_3dVector dt; | |
19 | + bc.Ev1Der(t, pt, dt); | |
20 | + return dt.Length(); | |
21 | + } | |
22 | + }; | |
23 | +} | |
24 | + | |
25 | +double ONGEO_LengthBezierCurve_NumericalIntegration(const ON_BezierCurve &bc, const double *subdivided_prms, int cnt, double *estimated_deviation){ | |
26 | + ONGEO_NumericalIntegration_GaussKronrod<5> integ(0, 0); | |
27 | + | |
28 | + IntegralFunc func; | |
29 | + func.bc = bc; | |
30 | + | |
31 | + double t_prv = *subdivided_prms; | |
32 | + double sum_l = 0, sum_err = 0; | |
33 | + for (int i = 1; i < cnt; ++i){ | |
34 | + double t_cur = subdivided_prms[i]; | |
35 | + integ.SetRange(t_prv, t_cur); | |
36 | + double err, l = integ.Calc(func, err); | |
37 | + sum_l += l; | |
38 | + sum_err += err; | |
39 | + t_prv = t_cur; | |
40 | + } | |
41 | + if (estimated_deviation) *estimated_deviation = sum_err; | |
42 | + return sum_l; | |
43 | +} | |
44 | + | |
45 | +double ONGEO_LengthBezierCurve_AdaptiveQuadrate(const ON_BezierCurve &bc, const double *subdivided_prms, int cnt, double tolerance, double *estimated_deviation){ | |
46 | + IntegralFunc func; | |
47 | + func.bc = bc; | |
48 | + | |
49 | + double t_prv = *subdivided_prms; | |
50 | + double sum_l = 0, sum_err = 0; | |
51 | + for (int i = 1; i < cnt; ++i){ | |
52 | + double t_cur = subdivided_prms[i]; | |
53 | + double err, l = ONGEO_AdaptiveQuadrature<ONGEO_NumericalIntegration_GaussKronrod<5>, IntegralFunc>(func, t_prv, t_cur, tolerance * (t_cur - t_prv), err); | |
54 | + sum_l += l; | |
55 | + sum_err += err; | |
56 | + t_prv = t_cur; | |
57 | + } | |
58 | + if (estimated_deviation) *estimated_deviation = sum_err; | |
59 | + return sum_l; | |
60 | +} |
@@ -0,0 +1,52 @@ | ||
1 | +#define NOMINMAX | |
2 | +#include "opennurbs.h" | |
3 | +#include "ONGEO.h" | |
4 | +#include <cmath> | |
5 | +#include <cstdio> | |
6 | + | |
7 | +#include "ONGEO_CurveSubdivision.hpp" | |
8 | + | |
9 | +// Reference : Maharavo Randrianarivony, | |
10 | +// "Length Estimation of Rational Bezier Curves and Application to CAD Parametrization". | |
11 | + | |
12 | +inline void GetLengthBounds(const ON_BezierCurve &bc_cur, double &lb, double &ub){ | |
13 | + ON_3dPoint pt_start, pt_end; | |
14 | + bc_cur.GetCV(0, pt_start); | |
15 | + bc_cur.GetCV(bc_cur.CVCount()-1, pt_end); | |
16 | + lb = pt_end.DistanceTo(pt_start); | |
17 | + ub = bc_cur.ControlPolygonLength(); | |
18 | +} | |
19 | + | |
20 | +struct LengthOperator{ | |
21 | + // input | |
22 | + double tolerance; | |
23 | + | |
24 | + // output | |
25 | + double sum_length; | |
26 | + LengthOperator(double tolerance_) : tolerance(tolerance_), sum_length(0){ | |
27 | + } | |
28 | + bool TestConverge(const ON_BezierCurve &bc_cur, const ON_Interval &t_int){ | |
29 | + double lb, ub; | |
30 | + GetLengthBounds(bc_cur, lb, ub); | |
31 | + bool converged = ((ub - lb) < tolerance * t_int.Length()); | |
32 | + if (converged){ | |
33 | + sum_length += 0.5 * (ub + lb); | |
34 | + } | |
35 | + return converged; | |
36 | + } | |
37 | +}; | |
38 | + | |
39 | +double ONGEO_LengthBezierCurve_SimpleSubdivision(const ON_BezierCurve &bc, double tolerance){ | |
40 | + double lb_0, ub_0; | |
41 | + GetLengthBounds(bc, lb_0, ub_0); | |
42 | + | |
43 | + // ほぼ直線 | |
44 | + if ((ub_0 - lb_0) < tolerance){ | |
45 | + return 0.5 * (ub_0 + lb_0); | |
46 | + } | |
47 | + | |
48 | + LengthOperator lo(tolerance); | |
49 | + ONGEO_CurveSubdivision(lo, bc); | |
50 | + return lo.sum_length; | |
51 | +} | |
52 | + |
@@ -0,0 +1,150 @@ | ||
1 | +#define NOMINMAX | |
2 | +#include "opennurbs.h" | |
3 | +#include "ONGEO.h" | |
4 | +#include <cmath> | |
5 | +#include <cstdio> | |
6 | +#include <algorithm> | |
7 | + | |
8 | +#include "ONGEO_NumericalIntegration.hpp" | |
9 | +#include "ONGEO_RootFinding.hpp" | |
10 | + | |
11 | +// Reference : B. Guenter, R. Parent, | |
12 | +// "Computing the arc length of parametric curves". | |
13 | +// IEEE Computer Graphics and Applications, Vol 10, No.3, May. 1990, pp.72-78. | |
14 | + | |
15 | +namespace{ | |
16 | + struct IntegralFunc{ | |
17 | + ON_NurbsCurve nc; | |
18 | + double operator()(double t) const{ | |
19 | + ON_3dPoint pt; | |
20 | + ON_3dVector dt; | |
21 | + nc.Ev1Der(t, pt, dt); | |
22 | + return dt.Length(); | |
23 | + } | |
24 | + }; | |
25 | +} | |
26 | + | |
27 | +ONGEO_LengthParamNurbsCurve_AdaptiveQuadrate::ONGEO_LengthParamNurbsCurve_AdaptiveQuadrate(const ON_NurbsCurve &nc, double tolerance){ | |
28 | + IntegralFunc func; | |
29 | + func.nc = nc; | |
30 | + this->nc = nc; | |
31 | + this->tolerance = tolerance; | |
32 | + | |
33 | + if (!nc.IsValid()) return; | |
34 | + | |
35 | + // Nurbs 曲線を区間分割 | |
36 | + { | |
37 | + ON_SimpleArray<double> span_vect(nc.SpanCount()+1); | |
38 | + if (span_vect.Capacity() == 1) return; | |
39 | + | |
40 | + span_vect.SetCount(span_vect.Capacity()); | |
41 | + nc.GetSpanVector(span_vect.First()); | |
42 | + | |
43 | + double cpl = nc.ControlPolygonLength(); | |
44 | + | |
45 | + nc_prms.AppendNew(); | |
46 | + nc.GetDomain(&nc_prms[0], 0); | |
47 | + | |
48 | + for (int j = 0; j <= nc.CVCount() - nc.Order(); ++j){ | |
49 | + ON_BezierCurve bc; | |
50 | + if (!nc.ConvertSpanToBezier(j, bc)) continue; | |
51 | + ON_Interval p_int(*nc_prms.Last(), nc.Knot(j+nc.Order()-1)); | |
52 | + | |
53 | + ON_SimpleArray<double> tess_prm; | |
54 | + ONGEO_TessellateBezierCurve_QuasiInterpolating(bc, cpl / 1000.0, tess_prm); | |
55 | + | |
56 | + for (int i = 1; i < tess_prm.Count(); ++i){ | |
57 | + nc_prms.Append(p_int.ParameterAt(tess_prm[i])); | |
58 | + } | |
59 | + } | |
60 | + } | |
61 | + | |
62 | + double prm_range = *nc_prms.Last() - *nc_prms.First(); | |
63 | + | |
64 | + // 適応求積法で区間ごとの曲線長を算出し、区間ごとの距離の積算値を nc_lengths に保持 | |
65 | + double t_prv = nc_prms[0]; | |
66 | + double sum_l = 0, sum_err = 0; | |
67 | + nc_lengths.Append(0); | |
68 | + for (int i = 1; i < nc_prms.Count(); ++i){ | |
69 | + double t_cur = nc_prms[i]; | |
70 | + double err, l = ONGEO_AdaptiveQuadrature<ONGEO_NumericalIntegration_GaussKronrod<5>, IntegralFunc>(func, t_prv, t_cur, tolerance * (t_cur - t_prv) / prm_range, err); | |
71 | + sum_l += l; | |
72 | + sum_err += err; | |
73 | + t_prv = t_cur; | |
74 | + | |
75 | + nc_lengths.Append(sum_l); | |
76 | + } | |
77 | + estimated_deviation = sum_err; | |
78 | +} | |
79 | + | |
80 | +double ONGEO_LengthParamNurbsCurve_AdaptiveQuadrate::Length() const{ | |
81 | + if (nc_lengths.Count() == 0) return 0; | |
82 | + return *nc_lengths.Last(); | |
83 | +} | |
84 | + | |
85 | +double ONGEO_LengthParamNurbsCurve_AdaptiveQuadrate::ParamToLength(double nc_prm) const{ | |
86 | + IntegralFunc func; | |
87 | + func.nc = nc; | |
88 | + double prm_range = *nc_prms.Last() - *nc_prms.First(); | |
89 | + | |
90 | + // 指定されたパラメータ値を超えない最も大きなパラメータ値を保持した配列から探し、 | |
91 | + // そのインデックスを取得する。 | |
92 | + const double *p = std::upper_bound(nc_prms.First(), nc_prms.Last()+1, nc_prm); | |
93 | + int p_idx = p - nc_prms.First(); | |
94 | + if (p_idx == 0) return -1; | |
95 | + | |
96 | + // 指定されたパラメータ値と同じ値が配列に入っている場合は、対応する距離そのままを返す。 | |
97 | + if (*(p-1) == nc_prm) return nc_lengths[p_idx-1]; | |
98 | + | |
99 | + // 配列から探したパラメータ値と指定されたパラメータ値で決まる区間の曲線長を算出し、足して返す。 | |
100 | + double err; | |
101 | + double l = ONGEO_AdaptiveQuadrature<ONGEO_NumericalIntegration_GaussKronrod<5>, IntegralFunc>(func, *(p-1), nc_prm, tolerance * (nc_prm - *(p-1)) / prm_range, err); | |
102 | + | |
103 | + return nc_lengths[p_idx-1] + l; | |
104 | +} | |
105 | +double ONGEO_LengthParamNurbsCurve_AdaptiveQuadrate::LengthToParam(double nc_length) const{ | |
106 | + IntegralFunc func; | |
107 | + func.nc = nc; | |
108 | + double prm_range = *nc_prms.Last() - *nc_prms.First(); | |
109 | + | |
110 | + const double *l = std::upper_bound(nc_lengths.First(), nc_lengths.Last()+1, nc_length); | |
111 | + int l_idx = l - nc_lengths.First(); | |
112 | + if (l_idx == 0) return -1; | |
113 | + | |
114 | + // 指定された距離と同じ値が配列に入っている場合は、対応するパラメータそのままを返す。 | |
115 | + if (*(l-1) == nc_length) return nc_prms[l_idx-1]; | |
116 | + | |
117 | + | |
118 | + ON_Interval p_range(nc_prms[l_idx-1], nc_prms[l_idx]); | |
119 | + ON_Interval l_range(nc_lengths[l_idx-1], nc_lengths[l_idx]); | |
120 | + double diff_l = nc_length - *(l-1); | |
121 | +#if 0 | |
122 | + // 配列から見つけた距離から区間を特定し、区間内の距離からパラメータをはさみうち法で算出し、返す。 | |
123 | + for(;;){ | |
124 | + double prm_estimate = p_range.ParameterAt(l_range.NormalizedParameterAt(nc_length)); | |
125 | + double err; | |
126 | + double itg_l = ONGEO_AdaptiveQuadrature<ONGEO_NumericalIntegration_GaussKronrod<5>, IntegralFunc>(func, p_range.Min(), prm_estimate, tolerance * (prm_estimate - p_range.Min()) / prm_range, err); | |
127 | + if (diff_l >= itg_l){ | |
128 | + if (diff_l - itg_l <= tolerance) return prm_estimate; | |
129 | + l_range.m_t[0] += itg_l; | |
130 | + p_range.m_t[0] = prm_estimate; | |
131 | + diff_l = nc_length - l_range.m_t[0]; | |
132 | + }else{ | |
133 | + if (itg_l - diff_l < tolerance) return prm_estimate; | |
134 | + l_range.m_t[1] = l_range.m_t[0] + itg_l; | |
135 | + p_range.m_t[1] = prm_estimate; | |
136 | + } | |
137 | + } | |
138 | +#else | |
139 | + return ONGEO_FindRootByBrentMethod(p_range.m_t[0], p_range.m_t[1], | |
140 | + [&] (double t) { | |
141 | + double err, itg_l = 0; | |
142 | + if (std::abs(t - p_range.m_t[0]) > tolerance){ | |
143 | + itg_l = ONGEO_AdaptiveQuadrature<ONGEO_NumericalIntegration_GaussKronrod<5>, IntegralFunc>(func, p_range.m_t[0], t, tolerance * (t - p_range.m_t[0]) / prm_range, err); | |
144 | + } | |
145 | + return l_range.m_t[0] +itg_l - nc_length; | |
146 | + } | |
147 | + , tolerance); | |
148 | +#endif | |
149 | +} | |
150 | + |
@@ -0,0 +1,33 @@ | ||
1 | +#include "ONGEO.h" | |
2 | +#include "ONGEO_NumericalIntegration.hpp" | |
3 | + | |
4 | +// 数値積分用の係数列 | |
5 | + | |
6 | +// Gauss-Kronrod 公式用の係数 | |
7 | +// 参考 | |
8 | +// http://www.ktech.biz/jp/archives/1009 | |
9 | +// http://www.ktech.biz/jp/archives/1045 | |
10 | + | |
11 | +template<> | |
12 | +ONGEO_DECL void ONGEO_GaussKronrod_Coefs<3>(const double *&t_i_, const double *&wg_i_, const double *&wk_i_){ | |
13 | + static const double t_i[] = { 0, 0.434243749346802, 0.774596669241483, 0.96049126870802 }; | |
14 | + static const double wg_i[] = { 0.555555555555556, 0.888888888888889, 0.555555555555556 }; | |
15 | + static const double wk_i[] = { 0.450916538658474, 0.401397414775962, 0.268488089868333, 0.104656226026467 }; | |
16 | + t_i_ = t_i, wg_i_ = wg_i, wk_i_ = wk_i; | |
17 | +} | |
18 | + | |
19 | +template<> | |
20 | +ONGEO_DECL void ONGEO_GaussKronrod_Coefs<5>(const double *&t_i_, const double *&wg_i_, const double *&wk_i_){ | |
21 | + static const double t_i[] = { 0, 0.279630413161783, 0.538469310105683, 0.754166726570849, 0.906179845938664, 0.984085360094842 }; | |
22 | + static const double wg_i[] = { 0.236926885056189, 0.478628670499367, 0.568888888888889, 0.478628670499367, 0.236926885056189 }; | |
23 | + static const double wk_i[] = { 0.282987417857491, 0.272849801912559, 0.241040339228648, 0.186800796556493, 0.115233316622474, 0.0425820367510818 }; | |
24 | + t_i_ = t_i, wg_i_ = wg_i, wk_i_ = wk_i; | |
25 | +} | |
26 | + | |
27 | +template<> | |
28 | +ONGEO_DECL void ONGEO_GaussKronrod_Coefs<7>(const double *&t_i_, const double *&wg_i_, const double *&wk_i_){ | |
29 | + static const double t_i[] = { 0, 0.207784955007898, 0.405845151377397, 0.586087235467691, 0.741531185599394, 0.864864423359769, 0.949107912342758, 0.991455371120813 }; | |
30 | + static const double wg_i[] = { 0.129484966168870, 0.279705391489277, 0.381830050505119, 0.417959183673469, 0.381830050505119, 0.279705391489277, 0.129484966168870 }; | |
31 | + static const double wk_i[] = { 0.209482141084728, 0.204432940075299, 0.190350578064785, 0.169004726639268, 0.140653259715526, 0.104790010322250, 0.0630920926299790, 0.0229353220105292 }; | |
32 | + t_i_ = t_i, wg_i_ = wg_i, wk_i_ = wk_i; | |
33 | +} |
@@ -0,0 +1,27 @@ | ||
1 | +#define NOMINMAX | |
2 | +#include "opennurbs.h" | |
3 | + | |
4 | +template <typename Operator> | |
5 | +void ONGEO_CurveSubdivision(Operator &op, const ON_BezierCurve &bc){ | |
6 | + ON_SimpleArray<double> stack; | |
7 | + | |
8 | + stack.Append(0); | |
9 | + | |
10 | + while(stack.Count()){ | |
11 | + double t_s = *stack.Last(); | |
12 | + stack.SetCount(stack.Count()-1); | |
13 | + | |
14 | + ON_Interval t_int; | |
15 | + t_int.m_t[0] = t_s; | |
16 | + t_int.m_t[1] = (stack.Count()) ? *stack.Last() : 1.0; | |
17 | + | |
18 | + ON_BezierCurve bc_cur = bc; | |
19 | + bc_cur.Trim(t_int); | |
20 | + if (!op.TestConverge(bc_cur, t_int)){ | |
21 | + double t_m = t_int.Mid(); | |
22 | + stack.Append(t_m); | |
23 | + stack.Append(t_s); | |
24 | + } | |
25 | + } | |
26 | +} | |
27 | + |
@@ -0,0 +1,73 @@ | ||
1 | +#define NOMINMAX | |
2 | +#include "opennurbs.h" | |
3 | + | |
4 | +template <int N> void ONGEO_GaussKronrod_Coefs(const double *&t_i_, const double *&wg_i_, const double *&wk_i_); | |
5 | + | |
6 | +template <int N> struct ONGEO_NumericalIntegration_GaussKronrod{ | |
7 | + double t[N*2+1], wg[N], wk[N*2+1]; | |
8 | + ONGEO_NumericalIntegration_GaussKronrod(double a, double b){ | |
9 | + SetRange(a, b); | |
10 | + } | |
11 | + void SetRange(double a, double b){ | |
12 | + if (a == b) return; | |
13 | + const double *t_i, *wg_i, *wk_i; | |
14 | + ONGEO_GaussKronrod_Coefs<N>(t_i, wg_i, wk_i); | |
15 | + | |
16 | + // 区間変更 | |
17 | + double ba_diff = (b - a) * 0.5, ba_mean = (a + b) * 0.5; | |
18 | + for (int i = 1; i <= N; ++i) t[N+i] = ba_diff * t_i[i] + ba_mean, t[N-i] = - ba_diff * t_i[i] + ba_mean; | |
19 | + t[N] = ba_diff * t_i[0] + ba_mean; | |
20 | + for (int i = 0; i < N; ++i) wg[i] = ba_diff * wg_i[i]; | |
21 | + for (int i = 1; i <= N; ++i) wk[N+i] = wk[N-i] = ba_diff * wk_i[i]; | |
22 | + wk[N] = ba_diff * wk_i[0]; | |
23 | + } | |
24 | + | |
25 | + template <typename Func> | |
26 | + double Calc(const Func &f, double &err){ | |
27 | + double fv[N*2+1]; | |
28 | + for (int i = 0; i < N*2+1; ++i) fv[i] = f(t[i]); | |
29 | + double g = 0; | |
30 | + for (int i = 0; i < N; ++i){ | |
31 | + g += fv[i*2+1] * wg[i]; | |
32 | + } | |
33 | + double k = 0; | |
34 | + for (int i = 0; i < N*2+1; ++i) k += fv[i] * wk[i]; | |
35 | + err = std::pow(200.0 * std::abs(k - g), 1.5); | |
36 | + return k; | |
37 | + } | |
38 | +}; | |
39 | + | |
40 | +template <typename Integrator, typename Func> double ONGEO_AdaptiveQuadrature(const Func &f, double a, double b, double tolerance, double &err_result){ | |
41 | + double range = b - a; | |
42 | + Integrator integ(0, 0); | |
43 | + ON_SimpleArray<double> stack; | |
44 | + | |
45 | + double sum = 0; | |
46 | + err_result = 0; | |
47 | + | |
48 | + stack.Append(b); | |
49 | + stack.Append(a); | |
50 | + | |
51 | + while(stack.Count() > 1){ | |
52 | + double t_s = *stack.Last(); | |
53 | + stack.SetCount(stack.Count()-1); | |
54 | + | |
55 | + double s_a = t_s, s_b = *stack.Last(); | |
56 | + | |
57 | + integ.SetRange(s_a, s_b); | |
58 | + | |
59 | + double sect_tolerance = tolerance * ((s_b - s_a) / range); | |
60 | + | |
61 | + double err; | |
62 | + double r = integ.Calc(f, err); | |
63 | + if (err < sect_tolerance){ | |
64 | + sum += r; | |
65 | + err_result += err; | |
66 | + }else{ | |
67 | + double t_m = (s_a + s_b) * 0.5; | |
68 | + stack.Append(t_m); | |
69 | + stack.Append(t_s); | |
70 | + } | |
71 | + } | |
72 | + return sum; | |
73 | +}; |
@@ -0,0 +1,188 @@ | ||
1 | +#define NOMINMAX | |
2 | +#include "ONGEO.h" | |
3 | +#include <cmath> | |
4 | +#include <stack> | |
5 | +#include <map> | |
6 | +#include <algorithm> | |
7 | +#include <vector> | |
8 | +#include <string> | |
9 | +#include <cstdio> | |
10 | + | |
11 | + | |
12 | +// Reference : Yohan D.Fougerolle, Sandrine Lanquetin, Marc Neveu, and Thierry Lauthelier, | |
13 | +// "A Geometric Algorithm for Ray/Bezier Surfaces Intersection using Quasi-interpolating Control Net". | |
14 | +// Signal Image Technology and Internet Based Systems, 2008. | |
15 | + | |
16 | + | |
17 | +// ====== | |
18 | +// === 曲線、曲面に共通な処理 === | |
19 | +// ====== | |
20 | +inline double ONGEO_QI_GetZInf(int dim){ | |
21 | + if (dim == 1) return 0; | |
22 | + if (dim == 2) return 0.0625; // 1/16 | |
23 | + return static_cast<double>(((dim + 1) / 2) * (dim / 2))/(2.0*static_cast<double>(dim)) - 0.25; | |
24 | +} | |
25 | + | |
26 | +inline void ONGEO_QI_GetQuasiInterpCP(const ON_3dPoint &pp, const ON_3dPoint &pc, const ON_3dPoint &pn, ON_3dPoint &po){ | |
27 | + po.x = (pp.x + pc.x * 2.0 + pn.x) * 0.25; | |
28 | + po.y = (pp.y + pc.y * 2.0 + pn.y) * 0.25; | |
29 | + po.z = (pp.z + pc.z * 2.0 + pn.z) * 0.25; | |
30 | +} | |
31 | + | |
32 | +inline void ONGEO_QI_GetQuasiInterpCP(const ON_4dPoint &pp, const ON_4dPoint &pc, const ON_4dPoint &pn, ON_4dPoint &po){ | |
33 | + po.x = (pp.x + pc.x * 2.0 + pn.x) * 0.25; | |
34 | + po.y = (pp.y + pc.y * 2.0 + pn.y) * 0.25; | |
35 | + po.z = (pp.z + pc.z * 2.0 + pn.z) * 0.25; | |
36 | + po.w = (pp.w + pc.w * 2.0 + pn.w) * 0.25; | |
37 | +} | |
38 | + | |
39 | +inline double ONGEO_QI_GetDelta2LengthSquared(const ON_3dPoint &pp, const ON_3dPoint &pc, const ON_3dPoint &pn){ | |
40 | + double d, dd = 0; | |
41 | + d = pp.x - pc.x * 2.0 + pn.x, d *= d, dd += d; | |
42 | + d = pp.y - pc.y * 2.0 + pn.y, d *= d, dd += d; | |
43 | + d = pp.z - pc.z * 2.0 + pn.z, d *= d, dd += d; | |
44 | + return dd; | |
45 | +// return ON_3dVector(pp - pc * 2.0 + pn).LengthSquared(); | |
46 | +} | |
47 | + | |
48 | +inline double ONGEO_QI_GetDelta2LengthSquared(const ON_4dPoint &pp, const ON_4dPoint &pc, const ON_4dPoint &pn){ | |
49 | + double d, dd = 0; | |
50 | + d = pp.x - pc.x * 2.0 + pn.x, d *= d, dd += d; | |
51 | + d = pp.y - pc.y * 2.0 + pn.y, d *= d, dd += d; | |
52 | + d = pp.z - pc.z * 2.0 + pn.z, d *= d, dd += d; | |
53 | + d = pp.w - pc.w * 2.0 + pn.w, d *= d, dd += d; | |
54 | + return dd; | |
55 | +} | |
56 | + | |
57 | +template <typename T, typename S> struct ONGEO_QI_PointDimTraits{ | |
58 | +}; | |
59 | +// ====== | |
60 | +// === 曲線用の処理 === | |
61 | +// ====== | |
62 | +template <> struct ONGEO_QI_PointDimTraits<ON_3dPoint, ON_BezierCurve>{ | |
63 | + static const int DIM = 3; | |
64 | + static double GetErrFromErr2(const ON_BezierCurve &bez, double errmax2){ | |
65 | + return ONGEO_QI_GetZInf(bez.m_order-1) * std::sqrt(errmax2); | |
66 | + } | |
67 | +}; | |
68 | + | |
69 | +template <> struct ONGEO_QI_PointDimTraits<ON_4dPoint, ON_BezierCurve>{ | |
70 | + static const int DIM = 4; | |
71 | + static double GetErrFromErr2(const ON_BezierCurve &bez, double errmax2){ | |
72 | + double wmin = bez.Weight(0); | |
73 | + for (int i = 0; i < bez.m_order; ++i){ | |
74 | + if (wmin > bez.Weight(i)) wmin = bez.Weight(i); | |
75 | + } | |
76 | + return ONGEO_QI_PointDimTraits<ON_3dPoint, ON_BezierCurve>::GetErrFromErr2(bez, errmax2) / wmin; | |
77 | + } | |
78 | +}; | |
79 | + | |
80 | +template <typename T> void ONGEO_QI_GetQuasiInterpControlPoints(const ON_BezierCurve &bez, ON_SimpleArray<T> &qicp){ | |
81 | + ON_SimpleArray<T> work_cp; | |
82 | + work_cp.SetCapacity(bez.m_order); | |
83 | + work_cp.SetCount(work_cp.Capacity()); | |
84 | + | |
85 | + qicp.SetCapacity(work_cp.Count()); | |
86 | + qicp.SetCount(qicp.Capacity()); | |
87 | + for (int i = 0; i < bez.m_order; ++i){ | |
88 | + bez.GetCV(i, j, work_cp[i]); | |
89 | + } | |
90 | + for (int i = 1; i < bez.m_order-1; ++i){ | |
91 | + ONGEO_QI_GetQuasiInterpCP(work_cp[i-1], work_cp[i], work_cp[i+1], qicp[i]); | |
92 | + } | |
93 | + qicp[0] = work_cp[0]; | |
94 | + qicp[bez.m_order-1] = work_cp[bez.m_order-1]; | |
95 | +} | |
96 | + | |
97 | +template <typename T> double ONGEO_QI_GetError(const ON_BezierCurve &bez){ | |
98 | + double errmax2 = 0; | |
99 | + const int ord = bez.m_order; | |
100 | + ON_SimpleArray<T> cp(ord); | |
101 | + for (int i = 0; i < ord; ++i){ | |
102 | + ONGEO_GetCVHomogeneous(bez, i, cp[i]); | |
103 | + } | |
104 | + for (int i = 1; i < ord-1; ++i){ | |
105 | + double d = ONGEO_QI_GetDelta2LengthSquared(cp[i-1], cp[i], cp[i+1]); | |
106 | + if (errmax2 < d) errmax2 = d; | |
107 | + } | |
108 | + | |
109 | + return ONGEO_QI_PointDimTraits<T, ON_BezierCurve>::GetErrFromErr2(bez, errmax2); | |
110 | +} | |
111 | + | |
112 | +// ====== | |
113 | +// === 曲面用の処理 === | |
114 | +// ====== | |
115 | +template <> struct ONGEO_QI_PointDimTraits<ON_3dPoint, ON_BezierSurface>{ | |
116 | + static const int DIM = 3; | |
117 | + static double GetErrFromErr2(const ON_BezierSurface &bez, double errmax2u, double errmax2v){ | |
118 | + return ONGEO_QI_GetZInf(bez.m_order[0]-1) * std::sqrt(errmax2u) + ONGEO_QI_GetZInf(bez.m_order[1]-1) * std::sqrt(errmax2v); | |
119 | + } | |
120 | +}; | |
121 | + | |
122 | +template <> struct ONGEO_QI_PointDimTraits<ON_4dPoint, ON_BezierSurface>{ | |
123 | + static const int DIM = 4; | |
124 | + static double GetErrFromErr2(const ON_BezierSurface &bez, double errmax2u, double errmax2v){ | |
125 | + double wmin = bez.Weight(0, 0); | |
126 | + for (int j = 0; j < bez.m_order[1]; ++j){ | |
127 | + for (int i = 0; i < bez.m_order[0]; ++i){ | |
128 | + if (wmin > bez.Weight(i, j)) wmin = bez.Weight(i, j); | |
129 | + } | |
130 | + } | |
131 | + return ONGEO_QI_PointDimTraits<ON_3dPoint, ON_BezierSurface>::GetErrFromErr2(bez, errmax2u, errmax2v) / wmin; | |
132 | + } | |
133 | +}; | |
134 | + | |
135 | +template <typename T> void ONGEO_QI_GetQuasiInterpControlPoints(const ON_BezierSurface &bez, ON_SimpleArray<T> &qicp){ | |
136 | + ON_SimpleArray<T> work_cp; | |
137 | + work_cp.SetCapacity(bez.m_order[0] * bez.m_order[1]); | |
138 | + work_cp.SetCount(work_cp.Capacity()); | |
139 | + // U方向 | |
140 | + ON_SimpleArray<T> work_u(bez.m_order[0]); | |
141 | + for (int j = 0, ji = 0; j < bez.m_order[1]; ++j, ji += bez.m_order[0]){ | |
142 | + for (int i = 0; i < bez.m_order[0]; ++i){ | |
143 | + bez.GetCV(i, j, work_u[i]); | |
144 | + } | |
145 | + for (int i = 1; i < bez.m_order[0]-1; ++i){ | |
146 | + ONGEO_QI_GetQuasiInterpCP(work_u[i-1], work_u[i], work_u[i+1], work_cp[ji+i]); | |
147 | + } | |
148 | + work_cp[ji] = work_u[0], work_cp[ji+bez.m_order[0]-1] = work_u[bez.m_order[0]-1]; | |
149 | + } | |
150 | +// ON_SimpleArray<T> work_v(bez.m_order[1]); | |
151 | + | |
152 | + qicp.SetCapacity(work_cp.Count()); | |
153 | + qicp.SetCount(qicp.Capacity()); | |
154 | + // V方向 | |
155 | + for (int i = 0; i < bez.m_order[0]; ++i){ | |
156 | + int j, ji; | |
157 | + for (j = 1, ji = bez.m_order[0]; j < bez.m_order[1]-1; ++j, ji += bez.m_order[0]){ | |
158 | + ONGEO_QI_GetQuasiInterpCP(work_cp[ji+i-bez.m_order[0]], work_cp[ji+i], work_cp[ji+i+bez.m_order[0]], qicp[ji+i]); | |
159 | + } | |
160 | + qicp[i] = work_cp[i], qicp[ji+i] = work_cp[ji+i]; | |
161 | + } | |
162 | +} | |
163 | + | |
164 | +template <typename T> double ONGEO_QI_GetError(const ON_BezierSurface &bez){ | |
165 | + // U方向 | |
166 | + double errmax2u = 0; | |
167 | + const int ord_u = bez.m_order[0], ord_v = bez.m_order[1]; | |
168 | + ON_SimpleArray<T> cp(ord_u * ord_v); | |
169 | + for (int j = 0, ji = 0; j < ord_v; ++j, ji += ord_u){ | |
170 | + for (int i = 0; i < ord_u; ++i){ | |
171 | + ONGEO_GetCVHomogeneous(bez, i, j, cp[ji+i]); | |
172 | + } | |
173 | + for (int i = 1; i < ord_u-1; ++i){ | |
174 | + double d = ONGEO_QI_GetDelta2LengthSquared(cp[ji+i-1], cp[ji+i], cp[ji+i+1]); | |
175 | + if (errmax2u < d) errmax2u = d; | |
176 | + } | |
177 | + } | |
178 | + // V方向 | |
179 | + double errmax2v = 0; | |
180 | + for (int i = 0; i < ord_u; ++i){ | |
181 | + for (int j = 1, ji = ord_u; j < ord_v-1; ++j, ji += ord_u){ | |
182 | + double d = ONGEO_QI_GetDelta2LengthSquared(cp[ji-ord_u], cp[ji], cp[ji+ord_u]); | |
183 | + if (errmax2v < d) errmax2v = d; | |
184 | + } | |
185 | + } | |
186 | + | |
187 | + return ONGEO_QI_PointDimTraits<T, ON_BezierSurface>::GetErrFromErr2(bez, errmax2u, errmax2v); | |
188 | +} |
@@ -0,0 +1,66 @@ | ||
1 | +#define NOMINMAX | |
2 | +#include "ONGEO.h" | |
3 | +#include <cmath> | |
4 | + | |
5 | +// Brent法を用いて根を求める。区間 ti1 - ti2 の中で単調増加、または単調減少であること。 | |
6 | +// @param ti1 [in] 狭めたい区間の下限 | |
7 | +// @param ti2 [in] 狭めたい区間の上限 ti1 < ti2であること。 | |
8 | +// @return 根となる値 | |
9 | +// Reference : R.P.Brent, | |
10 | +// "An algorithm with guaranteed convergence for finding a zero of a function". | |
11 | +// The Computer Journal, 14(1971), pp.422-425. | |
12 | +template <typename EvalFunc> | |
13 | +double ONGEO_FindRootByBrentMethod(double ti1, double ti2, EvalFunc f, double tolerance) { | |
14 | + double a = ti1, b = ti2; | |
15 | + double fa = f(a); | |
16 | + double fb = f(b); | |
17 | + double c = a, fc = fa, d, dprev; | |
18 | + d = dprev = b - a; | |
19 | + // d : 収束処理でbを動かす量 | |
20 | + for(;;){ | |
21 | + // 根に近い方をb,fb、遠い方をa=c,fa=fcとする。 | |
22 | + if (std::abs(fc) < std::abs(fb)){ | |
23 | + a = b, b = c, c = a; | |
24 | + fa = fb, fb = fc, fc = fa; | |
25 | + } | |
26 | + double tol = 2.0 * DBL_EPSILON * std::abs(b) + tolerance; | |
27 | + double c_b = c - b; | |
28 | + double m = 0.5 * c_b; | |
29 | + if (std::abs(m) <= tol || fb == 0) break; | |
30 | + | |
31 | + // bisectionの場合のd、dprev | |
32 | + d = dprev = m; | |
33 | + if (std::abs(dprev) >= tol && std::abs(fa) > std::abs(fb)){ | |
34 | + double r3 = fb / fa; | |
35 | + double p, q; | |
36 | + if (a == c){ | |
37 | + // linear interpolation | |
38 | + p = c_b * r3; | |
39 | + q = 1.0 - r3; | |
40 | + }else{ | |
41 | + // inverse quadratic interpolation | |
42 | + double ifc = 1.0 / fc; | |
43 | + double r1 = fa * ifc, r2 = fb * ifc; | |
44 | + p = r3 * (c_b * r1 * (r1 - r2) - (b - a) * (r2 - 1)); | |
45 | + q = (r1 - 1) * (r2 - 1) * (r3 - 1); | |
46 | + } | |
47 | + if (p > 0) q = -q; | |
48 | + else p = -p; | |
49 | + if (2.0 * p < 1.5 * c_b * q - std::abs(tol * q) && p < std::abs(0.5 * dprev * q)){ | |
50 | + // 上記条件に合う場合は、dとdprevをlinear interpolation、 | |
51 | + // またはinverse quadratic interpolation由来のものに書き換える。 | |
52 | + dprev = d; | |
53 | + d = p / q; | |
54 | + } | |
55 | + } | |
56 | + // a, fa, b, fbを更新 | |
57 | + a = b, fa = fb; | |
58 | + b += (std::abs(d) > tol) ? d : ((m > 0) ? tol : -tol); | |
59 | + fb = f(b); | |
60 | + if (fb * fc > 0){ | |
61 | + c = a, fc = fa; | |
62 | + dprev = b - a; | |
63 | + } | |
64 | + } | |
65 | + return b; | |
66 | +} |
@@ -0,0 +1,108 @@ | ||
1 | +#define NOMINMAX | |
2 | +#include "opennurbs.h" | |
3 | +#include "ONGEO.h" | |
4 | +#include <cmath> | |
5 | +#include <cstdio> | |
6 | + | |
7 | +#include "ONGEO_CurveSubdivision.hpp" | |
8 | +#include "ONGEO_QuasiInterpolating.hpp" | |
9 | + | |
10 | +inline double GetRoughSag(const ON_BezierCurve &bc_cur){ | |
11 | + ON_Line l; | |
12 | + bc_cur.GetCV(0, l.from); | |
13 | + bc_cur.GetCV(bc_cur.CVCount()-1, l.to); | |
14 | + double sag_max = 0; | |
15 | + for (int i = 1; i < bc_cur.CVCount() - 1; ++i){ | |
16 | + ON_3dPoint pt; | |
17 | + bc_cur.GetCV(i, pt); | |
18 | + double dist = l.MinimumDistanceTo(pt); | |
19 | + if (sag_max < dist) sag_max = dist; | |
20 | + } | |
21 | + return sag_max; | |
22 | +} | |
23 | + | |
24 | +struct SimpleTessellateOperator{ | |
25 | + // input | |
26 | + double tolerance; | |
27 | + | |
28 | + // output | |
29 | + ON_SimpleArray<double> prms; | |
30 | + SimpleTessellateOperator(double tolerance_) : tolerance(tolerance_){} | |
31 | + void Initialize(double tolerance_){ | |
32 | + tolerance = tolerance_; | |
33 | + prms.Empty(); | |
34 | + } | |
35 | + bool TestConverge(const ON_BezierCurve &bc_cur, const ON_Interval &t_int){ | |
36 | + double rsag = GetRoughSag(bc_cur); | |
37 | + bool converged = rsag < tolerance; | |
38 | + if (converged){ | |
39 | + prms.Append(t_int.m_t[0]); | |
40 | + } | |
41 | + return converged; | |
42 | + } | |
43 | + void EndConverge(){ | |
44 | + prms.Append(1.0); | |
45 | + } | |
46 | +}; | |
47 | + | |
48 | +void ONGEO_TessellateBezierCurve_Simple(const ON_BezierCurve &bc, double tolerance, ON_SimpleArray<double> &prms){ | |
49 | + SimpleTessellateOperator to(tolerance); | |
50 | + ONGEO_CurveSubdivision(to, bc); | |
51 | + to.EndConverge(); | |
52 | + | |
53 | + ONGEO_Swap(prms, to.prms); | |
54 | +} | |
55 | + | |
56 | +template<typename T> struct QuasiInterpolatingTessellateOperator{ | |
57 | + // input | |
58 | + double tolerance; | |
59 | + | |
60 | + // output | |
61 | + ON_SimpleArray<double> prms; | |
62 | + QuasiInterpolatingTessellateOperator(double tolerance_) : tolerance(tolerance_){} | |
63 | + void Initialize(double tolerance_){ | |
64 | + tolerance = tolerance_; | |
65 | + prms.Empty(); | |
66 | + } | |
67 | + bool TestConverge(const ON_BezierCurve &bc_cur, const ON_Interval &t_int){ | |
68 | + double qi_sag = ONGEO_QI_GetError<T>(bc_cur); | |
69 | + bool qi_converged = qi_sag < tolerance; | |
70 | + if (qi_converged){ | |
71 | +#if 1 | |
72 | + int idx_c = bc_cur.CVCount() / 2; | |
73 | + double t0 = static_cast<double>(idx_c) / (bc_cur.CVCount()-1); | |
74 | + double t1 = static_cast<double>(idx_c+1) / (bc_cur.CVCount()-1); | |
75 | + ON_3dPoint ptcrv = bc_cur.PointAt((t0+t1)*0.5); | |
76 | + ON_3dPoint ptlin = (bc_cur.PointAt(t0) + bc_cur.PointAt(t1)) * 0.5; | |
77 | + if (ptcrv.DistanceTo(ptlin) > tolerance) return false; | |
78 | +#endif | |
79 | + | |
80 | + for (int i = 0; i < bc_cur.CVCount() - 1; ++i){ | |
81 | + double t = static_cast<double>(i) / (bc_cur.CVCount() - 1); | |
82 | + prms.Append(t_int.ParameterAt(t)); | |
83 | + } | |
84 | + return true; | |
85 | + } | |
86 | + return false; | |
87 | + } | |
88 | + void EndConverge(){ | |
89 | + prms.Append(1.0); | |
90 | + } | |
91 | +}; | |
92 | + | |
93 | +void ONGEO_TessellateBezierCurve_QuasiInterpolating(const ON_BezierCurve &bc, double tolerance, ON_SimpleArray<double> &prms){ | |
94 | + if (bc.IsRational()){ | |
95 | + QuasiInterpolatingTessellateOperator<ON_4dPoint> qo(tolerance); | |
96 | + ONGEO_CurveSubdivision(qo, bc); | |
97 | + qo.EndConverge(); | |
98 | + | |
99 | + ONGEO_Swap(prms, qo.prms); | |
100 | + }else{ | |
101 | + QuasiInterpolatingTessellateOperator<ON_3dPoint> qo(tolerance); | |
102 | + ONGEO_CurveSubdivision(qo, bc); | |
103 | + qo.EndConverge(); | |
104 | + | |
105 | + ONGEO_Swap(prms, qo.prms); | |
106 | + } | |
107 | +} | |
108 | + |