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  }