curvedraw
diff src/curve.cc @ 15:37ab3a4c02f8
merged
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 20 Dec 2015 09:06:04 +0200 |
parents | 84a647283237 |
children | 7f795f7fecd6 |
line diff
1.1 --- a/src/curve.cc Thu Dec 17 16:41:42 2015 +0200 1.2 +++ b/src/curve.cc Sun Dec 20 09:06:04 2015 +0200 1.3 @@ -1,9 +1,39 @@ 1.4 #include <float.h> 1.5 +#include <assert.h> 1.6 +#include <algorithm> 1.7 #include "curve.h" 1.8 1.9 Curve::Curve(CurveType type) 1.10 { 1.11 this->type = type; 1.12 + bbvalid = true; 1.13 +} 1.14 + 1.15 +Curve::Curve(const Vector4 *cp, int numcp, CurveType type) 1.16 + : Curve(type) 1.17 +{ 1.18 + this->cp.resize(numcp); 1.19 + for(int i=0; i<numcp; i++) { 1.20 + this->cp[i] = cp[i]; 1.21 + } 1.22 +} 1.23 + 1.24 +Curve::Curve(const Vector3 *cp, int numcp, CurveType type) 1.25 + : Curve(type) 1.26 +{ 1.27 + this->cp.resize(numcp); 1.28 + for(int i=0; i<numcp; i++) { 1.29 + this->cp[i] = Vector4(cp[i].x, cp[i].y, cp[i].z, 1.0f); 1.30 + } 1.31 +} 1.32 + 1.33 +Curve::Curve(const Vector2 *cp, int numcp, CurveType type) 1.34 + : Curve(type) 1.35 +{ 1.36 + this->cp.resize(numcp); 1.37 + for(int i=0; i<numcp; i++) { 1.38 + this->cp[i] = Vector4(cp[i].x, cp[i].y, 0.0f, 1.0f); 1.39 + } 1.40 } 1.41 1.42 void Curve::set_type(CurveType type) 1.43 @@ -16,9 +46,20 @@ 1.44 return type; 1.45 } 1.46 1.47 +void Curve::add_point(const Vector4 &p) 1.48 +{ 1.49 + cp.push_back(p); 1.50 + inval_bounds(); 1.51 +} 1.52 + 1.53 +void Curve::add_point(const Vector3 &p, float weight) 1.54 +{ 1.55 + add_point(Vector4(p.x, p.y, p.z, weight)); 1.56 +} 1.57 + 1.58 void Curve::add_point(const Vector2 &p, float weight) 1.59 { 1.60 - cp.push_back(Vector3(p.x, p.y, weight)); 1.61 + add_point(Vector4(p.x, p.y, 0.0f, weight)); 1.62 } 1.63 1.64 bool Curve::remove_point(int idx) 1.65 @@ -27,23 +68,14 @@ 1.66 return false; 1.67 } 1.68 cp.erase(cp.begin() + idx); 1.69 + inval_bounds(); 1.70 return true; 1.71 } 1.72 1.73 -int Curve::nearest_point(const Vector2 &p) 1.74 +void Curve::clear() 1.75 { 1.76 - int res = -1; 1.77 - float bestsq = FLT_MAX; 1.78 - 1.79 - for(size_t i=0; i<cp.size(); i++) { 1.80 - float d = (get_point(i) - p).length_sq(); 1.81 - if(d < bestsq) { 1.82 - bestsq = d; 1.83 - res = i; 1.84 - } 1.85 - } 1.86 - 1.87 - return res; 1.88 + cp.clear(); 1.89 + inval_bounds(); 1.90 } 1.91 1.92 bool Curve::empty() const 1.93 @@ -56,29 +88,45 @@ 1.94 return (int)cp.size(); 1.95 } 1.96 1.97 -Vector3 &Curve::operator [](int idx) 1.98 +Vector4 &Curve::operator [](int idx) 1.99 +{ 1.100 + inval_bounds(); 1.101 + return cp[idx]; 1.102 +} 1.103 + 1.104 +const Vector4 &Curve::operator [](int idx) const 1.105 { 1.106 return cp[idx]; 1.107 } 1.108 1.109 -const Vector3 &Curve::operator [](int idx) const 1.110 +const Vector4 &Curve::get_point(int idx) const 1.111 { 1.112 return cp[idx]; 1.113 } 1.114 1.115 -const Vector3 &Curve::get_homo_point(int idx) const 1.116 +Vector3 Curve::get_point3(int idx) const 1.117 { 1.118 - return cp[idx]; 1.119 + return Vector3(cp[idx].x, cp[idx].y, cp[idx].z); 1.120 } 1.121 1.122 -Vector2 Curve::get_point(int idx) const 1.123 +Vector2 Curve::get_point2(int idx) const 1.124 { 1.125 return Vector2(cp[idx].x, cp[idx].y); 1.126 } 1.127 1.128 float Curve::get_weight(int idx) const 1.129 { 1.130 - return cp[idx].z; 1.131 + return cp[idx].w; 1.132 +} 1.133 + 1.134 +bool Curve::set_point(int idx, const Vector3 &p, float weight) 1.135 +{ 1.136 + if(idx < 0 || idx >= (int)cp.size()) { 1.137 + return false; 1.138 + } 1.139 + cp[idx] = Vector4(p.x, p.y, p.z, weight); 1.140 + inval_bounds(); 1.141 + return true; 1.142 } 1.143 1.144 bool Curve::set_point(int idx, const Vector2 &p, float weight) 1.145 @@ -86,7 +134,8 @@ 1.146 if(idx < 0 || idx >= (int)cp.size()) { 1.147 return false; 1.148 } 1.149 - cp[idx] = Vector3(p.x, p.y, weight); 1.150 + cp[idx] = Vector4(p.x, p.y, 0.0, weight); 1.151 + inval_bounds(); 1.152 return true; 1.153 } 1.154 1.155 @@ -95,7 +144,17 @@ 1.156 if(idx < 0 || idx >= (int)cp.size()) { 1.157 return false; 1.158 } 1.159 - cp[idx].z = weight; 1.160 + cp[idx].w = weight; 1.161 + return true; 1.162 +} 1.163 + 1.164 +bool Curve::move_point(int idx, const Vector3 &p) 1.165 +{ 1.166 + if(idx < 0 || idx >= (int)cp.size()) { 1.167 + return false; 1.168 + } 1.169 + cp[idx] = Vector4(p.x, p.y, p.z, cp[idx].w); 1.170 + inval_bounds(); 1.171 return true; 1.172 } 1.173 1.174 @@ -104,22 +163,211 @@ 1.175 if(idx < 0 || idx >= (int)cp.size()) { 1.176 return false; 1.177 } 1.178 - cp[idx] = Vector3(p.x, p.y, cp[idx].z); 1.179 + cp[idx] = Vector4(p.x, p.y, 0.0f, cp[idx].w); 1.180 + inval_bounds(); 1.181 return true; 1.182 } 1.183 1.184 -Vector2 Curve::interpolate(float t, CurveType type) const 1.185 + 1.186 +int Curve::nearest_point(const Vector3 &p) const 1.187 { 1.188 - if(cp.empty()) { 1.189 - return Vector2(0, 0); 1.190 + int res = -1; 1.191 + float bestsq = FLT_MAX; 1.192 + 1.193 + for(size_t i=0; i<cp.size(); i++) { 1.194 + float d = (get_point3(i) - p).length_sq(); 1.195 + if(d < bestsq) { 1.196 + bestsq = d; 1.197 + res = i; 1.198 + } 1.199 + } 1.200 + return res; 1.201 +} 1.202 + 1.203 +int Curve::nearest_point(const Vector2 &p) const 1.204 +{ 1.205 + int res = -1; 1.206 + float bestsq = FLT_MAX; 1.207 + 1.208 + for(size_t i=0; i<cp.size(); i++) { 1.209 + float d = (get_point2(i) - p).length_sq(); 1.210 + if(d < bestsq) { 1.211 + bestsq = d; 1.212 + res = i; 1.213 + } 1.214 + } 1.215 + return res; 1.216 +} 1.217 + 1.218 + 1.219 +void Curve::inval_bounds() const 1.220 +{ 1.221 + bbvalid = false; 1.222 +} 1.223 + 1.224 +void Curve::calc_bounds() const 1.225 +{ 1.226 + calc_bbox(&bbmin, &bbmax); 1.227 + bbvalid = true; 1.228 +} 1.229 + 1.230 +void Curve::get_bbox(Vector3 *bbmin, Vector3 *bbmax) const 1.231 +{ 1.232 + if(!bbvalid) { 1.233 + calc_bounds(); 1.234 + } 1.235 + *bbmin = this->bbmin; 1.236 + *bbmax = this->bbmax; 1.237 +} 1.238 + 1.239 +void Curve::calc_bbox(Vector3 *bbmin, Vector3 *bbmax) const 1.240 +{ 1.241 + if(empty()) { 1.242 + *bbmin = *bbmax = Vector3(0, 0, 0); 1.243 + return; 1.244 + } 1.245 + 1.246 + Vector3 bmin = cp[0]; 1.247 + Vector3 bmax = bmin; 1.248 + for(size_t i=1; i<cp.size(); i++) { 1.249 + const Vector4 &v = cp[i]; 1.250 + for(int j=0; j<3; j++) { 1.251 + if(v[j] < bmin[j]) bmin[j] = v[j]; 1.252 + if(v[j] > bmax[j]) bmax[j] = v[j]; 1.253 + } 1.254 + } 1.255 + *bbmin = bmin; 1.256 + *bbmax = bmax; 1.257 +} 1.258 + 1.259 +void Curve::normalize() 1.260 +{ 1.261 + if(!bbvalid) { 1.262 + calc_bounds(); 1.263 + } 1.264 + 1.265 + Vector3 bsize = bbmax - bbmin; 1.266 + Vector3 boffs = (bbmin + bbmax) * 0.5; 1.267 + 1.268 + Vector3 bscale; 1.269 + bscale.x = bsize.x == 0.0f ? 1.0f : 1.0f / bsize.x; 1.270 + bscale.y = bsize.y == 0.0f ? 1.0f : 1.0f / bsize.y; 1.271 + bscale.z = bsize.z == 0.0f ? 1.0f : 1.0f / bsize.z; 1.272 + 1.273 + for(size_t i=0; i<cp.size(); i++) { 1.274 + cp[i].x = (cp[i].x - boffs.x) * bscale.x; 1.275 + cp[i].y = (cp[i].y - boffs.y) * bscale.y; 1.276 + cp[i].z = (cp[i].z - boffs.z) * bscale.z; 1.277 + } 1.278 + inval_bounds(); 1.279 +} 1.280 + 1.281 +Vector3 Curve::proj_point(const Vector3 &p, float refine_thres) const 1.282 +{ 1.283 + // first step through the curve a few times and find the nearest of them 1.284 + int num_cp = size(); 1.285 + int num_steps = num_cp * 5; // arbitrary number; sounds ok 1.286 + float dt = 1.0f / (float)(num_steps - 1); 1.287 + 1.288 + float best_distsq = FLT_MAX; 1.289 + float best_t = 0.0f; 1.290 + Vector3 best_pp; 1.291 + 1.292 + float t = 0.0f; 1.293 + for(int i=0; i<num_steps; i++) { 1.294 + Vector3 pp = interpolate(t); 1.295 + float distsq = (pp - p).length_sq(); 1.296 + if(distsq < best_distsq) { 1.297 + best_distsq = distsq; 1.298 + best_pp = pp; 1.299 + best_t = t; 1.300 + } 1.301 + t += dt; 1.302 + } 1.303 + 1.304 + // refine by gradient descent 1.305 + float dist = best_distsq; 1.306 + t = best_t; 1.307 + dt *= 0.05; 1.308 + for(;;) { 1.309 + float tn = t + dt; 1.310 + float tp = t - dt; 1.311 + 1.312 + float dn = (interpolate(tn) - p).length_sq(); 1.313 + float dp = (interpolate(tp) - p).length_sq(); 1.314 + 1.315 + if(fabs(dn - dp) < refine_thres * refine_thres) { 1.316 + break; 1.317 + } 1.318 + 1.319 + if(dn < dist) { 1.320 + t = tn; 1.321 + dist = dn; 1.322 + } else if(dp < dist) { 1.323 + t = tp; 1.324 + dist = dp; 1.325 + } else { 1.326 + break; // found the minimum 1.327 + } 1.328 + } 1.329 + 1.330 + return interpolate(t); 1.331 +} 1.332 + 1.333 +float Curve::distance(const Vector3 &p) const 1.334 +{ 1.335 + return (proj_point(p) - p).length(); 1.336 +} 1.337 + 1.338 +float Curve::distance_sq(const Vector3 &p) const 1.339 +{ 1.340 + return fabs((proj_point(p) - p).length_sq()); 1.341 +} 1.342 + 1.343 + 1.344 +Vector3 Curve::interpolate_segment(int a, int b, float t) const 1.345 +{ 1.346 + int num_cp = size(); 1.347 + 1.348 + if(t < 0.0) t = 0.0; 1.349 + if(t > 1.0) t = 1.0; 1.350 + 1.351 + Vector4 res; 1.352 + if(type == CURVE_LINEAR || num_cp == 2) { 1.353 + res = lerp(cp[a], cp[b], t); 1.354 + } else { 1.355 + int prev = a <= 0 ? a : a - 1; 1.356 + int next = b >= num_cp - 1 ? b : b + 1; 1.357 + 1.358 + if(type == CURVE_HERMITE) { 1.359 + res = catmull_rom_spline(cp[prev], cp[a], cp[b], cp[next], t); 1.360 + } else { 1.361 + res = bspline(cp[prev], cp[a], cp[b], cp[next], t); 1.362 + if(res.w != 0.0f) { 1.363 + res.x /= res.w; 1.364 + res.y /= res.w; 1.365 + res.z /= res.w; 1.366 + } 1.367 + } 1.368 + } 1.369 + 1.370 + return Vector3(res.x, res.y, res.z); 1.371 +} 1.372 + 1.373 +Vector3 Curve::interpolate(float t) const 1.374 +{ 1.375 + if(empty()) { 1.376 + return Vector3(0, 0, 0); 1.377 } 1.378 1.379 int num_cp = (int)cp.size(); 1.380 if(num_cp == 1) { 1.381 - return Vector2(cp[0].x, cp[0].y); 1.382 + return Vector3(cp[0].x, cp[0].y, cp[0].z); 1.383 } 1.384 1.385 - Vector3 res; 1.386 + if(t < 0.0) t = 0.0; 1.387 + if(t > 1.0) t = 1.0; 1.388 + 1.389 int idx0 = std::min((int)floor(t * (num_cp - 1)), num_cp - 2); 1.390 int idx1 = idx0 + 1; 1.391 1.392 @@ -128,35 +376,17 @@ 1.393 float t1 = (float)idx1 * dt; 1.394 1.395 t = (t - t0) / (t1 - t0); 1.396 - if(t < 0.0) t = 0.0; 1.397 - if(t > 1.0) t = 1.0; 1.398 1.399 - if(type == CURVE_LINEAR || num_cp <= 2) { 1.400 - res = lerp(cp[idx0], cp[idx1], t); 1.401 - } else { 1.402 - int idx_prev = idx0 <= 0 ? idx0 : idx0 - 1; 1.403 - int idx_next = idx1 >= num_cp - 1 ? idx1 : idx1 + 1; 1.404 + return interpolate_segment(idx0, idx1, t); 1.405 +} 1.406 1.407 - if(type == CURVE_HERMITE) { 1.408 - res = catmull_rom_spline(cp[idx_prev], cp[idx0], cp[idx1], cp[idx_next], t); 1.409 - } else { 1.410 - res = bspline(cp[idx_prev], cp[idx0], cp[idx1], cp[idx_next], t); 1.411 - if(res.z != 0.0f) { 1.412 - res.x /= res.z; 1.413 - res.y /= res.z; 1.414 - } 1.415 - } 1.416 - } 1.417 - 1.418 +Vector2 Curve::interpolate2(float t) const 1.419 +{ 1.420 + Vector3 res = interpolate(t); 1.421 return Vector2(res.x, res.y); 1.422 } 1.423 1.424 -Vector2 Curve::interpolate(float t) const 1.425 -{ 1.426 - return interpolate(t, type); 1.427 -} 1.428 - 1.429 -Vector2 Curve::operator ()(float t) const 1.430 +Vector3 Curve::operator ()(float t) const 1.431 { 1.432 return interpolate(t); 1.433 }