curvedraw

diff src/curve.cc @ 12:84a647283237

added all the extra functionality to the curve class first pass at a project-to-curve function (needs more work)
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 20 Dec 2015 07:21:32 +0200
parents 2b7ae76c173f
children 4da693339d99
line diff
     1.1 --- a/src/curve.cc	Sat Dec 19 22:39:18 2015 +0200
     1.2 +++ b/src/curve.cc	Sun Dec 20 07:21:32 2015 +0200
     1.3 @@ -1,10 +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 @@ -17,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 @@ -28,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 @@ -57,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 @@ -87,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 @@ -96,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 @@ -105,22 +163,207 @@
   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 +/* Projection to the curve is not correct, but should be good enough for
   1.282 + * most purposes.
   1.283 + *
   1.284 + * First we find the nearest segment (pair of control points), and then
   1.285 + * we subdivide between them to find the nearest interpolated point in that
   1.286 + * segment.
   1.287 + * The incorrect assumption here is that the nearest segment as defined by
   1.288 + * the distance of its control points to p, will contain the nearest point
   1.289 + * on the curve. This only holds for polylines, and *possibly* bsplines, but
   1.290 + * certainly not hermite splines.
   1.291 + */
   1.292 +Vector3 Curve::proj_point(const Vector3 &p) const
   1.293 +{
   1.294 +	// TODO fix: select nearest segment based on point-line distance, not distance from the CPs
   1.295 +	int num_cp = size();
   1.296 +	if(num_cp <= 0) return p;
   1.297 +	if(num_cp == 1) return cp[0];
   1.298 +
   1.299 +	int idx0 = nearest_point(p);
   1.300 +	int next_idx = idx0 + 1;
   1.301 +	int prev_idx = idx0 - 1;
   1.302 +
   1.303 +	float next_distsq = next_idx >= num_cp ? FLT_MAX : (get_point3(next_idx) - p).length_sq();
   1.304 +	float prev_distsq = prev_idx < 0 ? FLT_MAX : (get_point3(prev_idx) - p).length_sq();
   1.305 +	int idx1 = next_distsq < prev_distsq ? next_idx : prev_idx;
   1.306 +	assert(idx1 >= 0 && idx1 < num_cp - 1);
   1.307 +	if(idx0 > idx1) std::swap(idx0, idx1);
   1.308 +
   1.309 +	float t0 = 0.0f, t1 = 1.0f;
   1.310 +	Vector3 pp0 = interpolate_segment(idx0, idx1, 0.0f);
   1.311 +	Vector3 pp1 = interpolate_segment(idx0, idx1, 1.0f);
   1.312 +	float dist0 = (pp0 - p).length_sq();
   1.313 +	float dist1 = (pp1 - p).length_sq();
   1.314 +	Vector3 pp;
   1.315 +
   1.316 +	for(int i=0; i<32; i++) {	// max iterations
   1.317 +		float t = (t0 + t1) / 2.0;
   1.318 +		pp = interpolate_segment(idx0, idx1, t);
   1.319 +		float dist = (pp - p).length_sq();
   1.320 +
   1.321 +		// mid point more distant than both control points, nearest cp is closest
   1.322 +		if(dist > dist0 && dist > dist1) {
   1.323 +			pp = dist0 < dist1 ? pp0 : pp1;
   1.324 +			break;
   1.325 +		}
   1.326 +
   1.327 +		if(dist0 < dist1) {
   1.328 +			t1 = t;
   1.329 +			dist1 = dist;
   1.330 +			pp1 = pp;
   1.331 +		} else {
   1.332 +			t0 = t;
   1.333 +			dist0 = dist;
   1.334 +			pp0 = pp;
   1.335 +		}
   1.336 +
   1.337 +		if(fabs(dist0 - dist1) < 1e-4) {
   1.338 +			break;
   1.339 +		}
   1.340 +	}
   1.341 +	return pp;
   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 +			}
   1.366 +		}
   1.367 +	}
   1.368 +
   1.369 +	return Vector3(res.x, res.y, res.z);
   1.370 +}
   1.371 +
   1.372 +Vector3 Curve::interpolate(float t) const
   1.373 +{
   1.374 +	if(empty()) {
   1.375 +		return Vector3(0, 0, 0);
   1.376  	}
   1.377  
   1.378  	int num_cp = (int)cp.size();
   1.379  	if(num_cp == 1) {
   1.380 -		return Vector2(cp[0].x, cp[0].y);
   1.381 +		return Vector3(cp[0].x, cp[0].y, cp[0].z);
   1.382  	}
   1.383  
   1.384 -	Vector3 res;
   1.385  	int idx0 = std::min((int)floor(t * (num_cp - 1)), num_cp - 2);
   1.386  	int idx1 = idx0 + 1;
   1.387  
   1.388 @@ -129,35 +372,17 @@
   1.389  	float t1 = (float)idx1 * dt;
   1.390  
   1.391  	t = (t - t0) / (t1 - t0);
   1.392 -	if(t < 0.0) t = 0.0;
   1.393 -	if(t > 1.0) t = 1.0;
   1.394  
   1.395 -	if(type == CURVE_LINEAR || num_cp <= 2) {
   1.396 -		res = lerp(cp[idx0], cp[idx1], t);
   1.397 -	} else {
   1.398 -		int idx_prev = idx0 <= 0 ? idx0 : idx0 - 1;
   1.399 -		int idx_next = idx1 >= num_cp - 1 ? idx1 : idx1 + 1;
   1.400 +	return interpolate_segment(idx0, idx1, t);
   1.401 +}
   1.402  
   1.403 -		if(type == CURVE_HERMITE) {
   1.404 -			res = catmull_rom_spline(cp[idx_prev], cp[idx0], cp[idx1], cp[idx_next], t);
   1.405 -		} else {
   1.406 -			res = bspline(cp[idx_prev], cp[idx0], cp[idx1], cp[idx_next], t);
   1.407 -			if(res.z != 0.0f) {
   1.408 -				res.x /= res.z;
   1.409 -				res.y /= res.z;
   1.410 -			}
   1.411 -		}
   1.412 -	}
   1.413 -
   1.414 +Vector2 Curve::interpolate2(float t) const
   1.415 +{
   1.416 +	Vector3 res = interpolate(t);
   1.417  	return Vector2(res.x, res.y);
   1.418  }
   1.419  
   1.420 -Vector2 Curve::interpolate(float t) const
   1.421 -{
   1.422 -	return interpolate(t, type);
   1.423 -}
   1.424 -
   1.425 -Vector2 Curve::operator ()(float t) const
   1.426 +Vector3 Curve::operator ()(float t) const
   1.427  {
   1.428  	return interpolate(t);
   1.429  }