1 /* === S Y N F I G ========================================================= */
3 ** \brief Curve Set Implementation File
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
10 ** This package is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU General Public License as
12 ** published by the Free Software Foundation; either version 2 of
13 ** the License, or (at your option) any later version.
15 ** This package is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ** General Public License for more details.
21 /* ========================================================================= */
23 /* === H E A D E R S ======================================================= */
32 #include "curve_helper.h"
34 #include "blinepoint.h"
42 /* === U S I N G =========================================================== */
46 using namespace synfig;
48 /* === M A C R O S ========================================================= */
50 /* === G L O B A L S ======================================================= */
51 const Real ERROR = 1e-10;
53 /* === P R O C E D U R E S ================================================= */
55 /* === M E T H O D S ======================================================= */
57 /* === E N T R Y P O I N T ================================================= */
58 template < typename T >
59 inline bool Zero(const T &a, const T &tol = (T)ERROR)
61 return a < tol && a > -tol;
64 CurvePoint::CurvePoint(const Point &pin, const Vector &left, const Vector &right)
65 :p(pin),l(left),r(right)
69 CurvePoint::CurvePoint(const BLinePoint &bpoint)
71 p = bpoint.get_vertex();
73 l = p + bpoint.get_tangent1()*(1/3.0f);
74 r = p + bpoint.get_tangent2()*(1/3.0f);
87 int go_in; //going in = 1, coming out = -1
96 bool operator<(const ipoint &rhs) const
98 if(curveindex == rhs.curveindex)
100 if(vertindex == rhs.vertindex)
102 return tvalue < rhs.tvalue;
103 }else return vertindex < rhs.vertindex;
104 }else return curveindex < rhs.curveindex;
107 bool operator >(const ipoint &rhs) const
112 void insert_after(ipoint *i)
114 //from: next - next.prev
115 //to: next* - i - next.prev*
120 //assuming the input point is not connected to anything, we don't have to do anything with it...
127 void insert_before(ipoint *i)
129 //from: prev.next - prev
130 //to: prev.next* - i - prev*
135 //assuming the input point is not connected to anything, we don't have to do anything with it...
142 void insert_sorted(ipoint *i)
144 ipoint *search = this;
152 search = search->next;
153 }while(*i < *search && search != this); //ending conditions...
155 //now we insert previously...
156 search->insert_before(i);
163 search = search->prev;
164 }while(*i > *search && search != this); //ending conditions...
166 //now we insert previously...
167 search->insert_after(i);
184 typedef vector<ipoint *> CurveInts; //in no particular order
186 vector<CurveInts> c1ints;
187 vector<CurveInts> c2ints;
189 //get the intersections
190 void GetIntersections(const CurveSet &lhs, const CurveSet &rhs)
198 //clear out so everyone's happy
202 c1ints.resize(lhs.set.size());
203 c2ints.resize(rhs.set.size());
205 //loop through everyone and be happy...
207 //intersect each curve with each other curve, and we're good
208 for(ci1=0;ci1 < (int)lhs.set.size(); ++ci1)
210 const CurveSet::region &cur1 = lhs.set[ci1];
212 for(j1 = s1-1, i1=0; i1 < s1; j1 = i1++)
216 b1[1] = b1[0] + cur1[j1].r/3;
217 b1[2] = b1[3] - cur1[i1].l/3;
219 for(ci2=0;ci2 < (int)rhs.set.size(); ++ci2)
221 const CurveSet::region &cur2 = rhs.set[ci2];
223 for(j2 = s2-1, i2=0; i2 < s2; j2 = i2++)
227 b2[1] = b2[0] + cur2[j2].r/3;
228 b2[2] = b2[3] - cur2[i2].l/3;
232 for(int index=0; index < (int)isect.times.size(); ++index)
234 //prepare basic intersection information
235 ipoint *ip1 = new ipoint, *ip2 = new ipoint;
238 ip1->curveindex = ci1; ip1->vertindex = j1; ip1->tvalue = isect.times[index].first;
239 ip2->curveindex = ci2; ip2->vertindex = j2; ip2->tvalue = isect.times[index].second;
245 //first one just goes on end of list
246 c1ints[ci1].back()->insert_sorted(ip1);
247 c1ints[ci1].push_back(ip1);
249 //second one must go in order
250 c2ints[ci2].back()->insert_sorted(ip2);
251 c2ints[ci2].push_back(ip2);
260 //Now figure out the containment properties of each int point
263 for(int i = 0; i < (int)c1ints.size(); ++i)
265 if(c1ints[i].size() == 0) continue;
267 //must test insideness for the edges
268 ipoint *start, *iter;
269 start = iter = c1ints[i].front();
271 //i == iter->curveindex == the index of the current curve we're looking at
273 //set the initial insideness on the other curve...
274 p = lhs.set[i][iter->vertindex].p;
275 inside = rhs.intersect(p)%2; //if it's inside by the even odd rule
279 iter->go_in = inside? -1 : 1; //leaving if inside, or coming in if not
282 }while(iter != start); //I hope this isn't an infinite loop!
286 for(int i = 0; i < (int)c2ints.size(); ++i)
288 if(c2ints[i].size() == 0) continue;
290 //must test insideness for the edges
291 ipoint *start, *iter;
292 start = iter = c1ints[i].front();
294 //set the initial insideness on the other curve...
295 p = rhs.set[i][iter->vertindex].p;
296 inside = lhs.intersect(p)%2; //if it's inside by the even odd rule
300 iter->go_in = inside? -1 : 1; //leaving if inside, or coming in if not
303 }while(iter != start); //I hope this isn't an infinite loop!
307 bool ConstructSet(CurveSet &/*c*/, const CurveSet &lhs, const CurveSet &rhs, int type)
313 case INTERSECT: //1&2
315 in1 = true; in2 = true;
321 in1 = false; in2 = false;
327 in1 = true; in2 = false;
331 case INVSUBTRACT: //2-1
333 in1 = false; in2 = true;
343 //traverse path based on inside flags
345 //fill all the paths of native stuff
347 for(int ci=0; ci<(int)c1ints.size(); ++ci)
349 for(int i=0; i < (int)c1ints[ci].size(); ++i)
351 ipset.insert(c1ints[ci][i]);
356 while(ipset.size() > 0)
358 //start from one point (always on curveset 1) and traverse until we find it again
359 ipoint *start, *iter;
360 start = iter = *ipset.begin();
362 //All the info to swap when we transition curves...
363 const CurveSet *cur, *other;
367 set<ipoint *>::iterator deliter;
372 CurveSet::region current;
375 cur = &lhs; other = &rhs;
376 curin = in1; otherin = in2;
381 //remove the current iter from the set
384 deliter = ipset.find(iter);
385 if(deliter != ipset.end()) ipset.erase(deliter);
388 //go to next and accumulate information
389 ci = iter->curveindex;
390 i1 = iter->vertindex;
392 iter = iter->next; //move to next and get its info
394 i2 = iter->vertindex;
397 size = cur->set[ci].size();
399 //record all the stuff between us...
400 //start on an intersection - get the curve point...
403 //transition curves...
404 iter = iter->neighbor;
408 }while(iter != start); //I hope THIS isn't an infinite loop
415 void CurveSet::SetClamp(int &i, int &si)
417 if(si > 0 && si < (int)set.size())
419 if(i >= (int)set[si].size())
431 void CurveSet::CleanUp(int /*curve*/)
435 /* Detect intersections that are crazy happy good
437 Performance annoyances:
438 1) Recursing down to find an intersection at the end points that doesn't actually exist
439 (can be helped a bit by not including the edges of bounding rectangles)
440 2) Intersecting curves is slow... oh well
443 1) Inside out scheme, track when edges go into and come out of various objects etc.
445 + doesn't require initial conditions
446 - only works with odd-even rule
449 CurveSet CurveSet::operator &(const CurveSet &/*rhs*/) const
454 CurveSet CurveSet::operator |(const CurveSet &/*rhs*/) const
459 CurveSet CurveSet::operator -(const CurveSet &/*rhs*/) const
464 int CurveSet::intersect(const Point &p) const
466 int inter = 0, ci,i,j,s;
469 for(ci=0; ci < (int)set.size(); ++ci)
471 const vector<CurvePoint> &curve = set[ci];
473 for(j=s-1,i=0; i < s; j = i++)
475 b[0] = curve[j].p; b[3] = curve[i].p;
476 b[1] = b[0] + curve[j].r/3; b[2] = b[3] - curve[i].l/3;
478 inter += synfig::intersect(b,p);