3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // (C) 2010 Underground Software
8 // JLH = James L. Hammons <jlhamm@acm.org>
11 // --- ---------- -----------------------------------------------------------
12 // JLH 06/02/2010 Added this text. :-)
15 #include "rs_spline.h"
18 #include "rs_graphicview.h"
20 #include "paintintf.h"
25 RS_Spline::RS_Spline(RS_EntityContainer * parent, const RS_SplineData & d):
26 RS_EntityContainer(parent), data(d)
34 RS_Spline::~RS_Spline()
38 RS_Entity * RS_Spline::clone()
40 RS_Spline * l = new RS_Spline(*this);
41 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
42 // l->entities.setAutoDelete(entities.autoDelete());
48 /** @return RS2::EntitySpline */
49 /*virtual*/ RS2::EntityType RS_Spline::rtti() const
51 return RS2::EntitySpline;
55 /*virtual*/ bool RS_Spline::isEdge() const
60 /** @return Copy of data that defines the spline. */
61 RS_SplineData RS_Spline::getData() const
66 /** Sets the splines degree (1-3). */
67 void RS_Spline::setDegree(int deg)
69 if (deg >= 1 && deg <= 3)
73 /** @return Degree of this spline curve (1-3).*/
74 int RS_Spline::getDegree()
80 int RS_Spline::getNumberOfKnots()
85 /** @return Number of control points. */
86 int RS_Spline::getNumberOfControlPoints()
88 return data.controlPoints.count();
92 * @retval true if the spline is closed.
93 * @retval false otherwise.
95 bool RS_Spline::isClosed()
101 * Sets the closed falg of this spline.
103 void RS_Spline::setClosed(bool c)
109 void RS_Spline::calculateBorders()
111 /*minV = Vector::minimum(data.startpoint, data.endpoint);
112 maxV = Vector::maximum(data.startpoint, data.endpoint);
114 Q3ValueList<Vector>::iterator it;
115 for (it = data.controlPoints.begin();
116 it!=data.controlPoints.end(); ++it) {
118 minV = Vector::minimum(*it, minV);
119 maxV = Vector::maximum(*it, maxV);
124 VectorSolutions RS_Spline::getRefPoints()
126 VectorSolutions ret(data.controlPoints.count());
129 QList<Vector>::iterator it;
131 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it, ++i)
139 Vector RS_Spline::getNearestRef(const Vector & coord, double * dist)
141 //return getRefPoints().getClosest(coord, dist);
142 return RS_Entity::getNearestRef(coord, dist);
145 Vector RS_Spline::getNearestSelectedRef(const Vector & coord, double * dist)
147 //return getRefPoints().getClosest(coord, dist);
148 return RS_Entity::getNearestSelectedRef(coord, dist);
152 * Updates the internal polygon of this spline. Called when the
153 * spline or it's data, position, .. changes.
155 void RS_Spline::update()
157 RS_DEBUG->print("RS_Spline::update");
164 if (data.degree < 1 || data.degree > 3)
166 RS_DEBUG->print("RS_Spline::update: invalid degree: %d", data.degree);
170 if (data.controlPoints.count() < (uint)data.degree + 1)
172 RS_DEBUG->print("RS_Spline::update: not enough control points");
178 // Q3ValueList<Vector> tControlPoints = data.controlPoints;
179 QList<Vector> tControlPoints = data.controlPoints;
183 for(int i=0; i<data.degree; ++i)
184 tControlPoints.append(data.controlPoints[i]);
188 int npts = tControlPoints.count();
190 int k = data.degree + 1;
192 int p1 = getGraphicVariableInt("$SPLINESEGS", 8) * npts;
194 double * b = new double[npts * 3 + 1];
195 double * h = new double[npts + 1];
196 double * p = new double[p1 * 3 + 1];
198 // Q3ValueList<Vector>::iterator it;
199 QList<Vector>::iterator it;
202 for(it=tControlPoints.begin(); it!=tControlPoints.end(); ++it)
208 RS_DEBUG->print("RS_Spline::update: b[%d]: %f/%f", i, b[i], b[i + 1]);
212 // set all homogeneous weighting factors to 1.0
213 for(i=1; i<=npts; i++)
216 for(i=1; i<=3*p1; i++)
220 rbsplinu(npts, k, p1, b, h, p);
222 rbspline(npts, k, p1, b, h, p);
226 for(i=1; i<=3*p1; i=i+3)
230 RS_Line * line = new RS_Line(this, RS_LineData(prev, Vector(p[i], p[i + 1])));
231 line->setLayer(NULL);
232 line->setPen(RS_Pen(RS2::FlagInvalid));
236 prev = Vector(p[i], p[i+1]);
237 minV = Vector::minimum(prev, minV);
238 maxV = Vector::maximum(prev, maxV);
246 Vector RS_Spline::getNearestEndpoint(const Vector & coord, double * dist)
248 double minDist = RS_MAXDOUBLE;
252 for (uint i=0; i<data.controlPoints.count(); i++) {
253 d = data.controlPoints[i].distanceTo(coord);
257 ret = data.controlPoints[i];
267 // The default implementation of RS_EntityContainer is inaccurate but
268 // has to do for now..
269 Vector RS_Spline::getNearestPointOnEntity(const Vector& coord,
270 bool onEntity, double* dist, RS_Entity** entity) {
274 Vector RS_Spline::getNearestCenter(const Vector & /*coord*/, double * dist)
277 *dist = RS_MAXDOUBLE;
279 return Vector(false);
282 Vector RS_Spline::getNearestMiddle(const Vector & /*coord*/, double * dist)
285 *dist = RS_MAXDOUBLE;
287 return Vector(false);
290 Vector RS_Spline::getNearestDist(double /*distance*/, const Vector & /*coord*/, double * dist)
293 *dist = RS_MAXDOUBLE;
295 return Vector(false);
298 void RS_Spline::move(Vector offset)
300 // Q3ValueList<Vector>::iterator it;
301 QList<Vector>::iterator it;
303 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it)
309 void RS_Spline::rotate(Vector center, double angle)
311 // Q3ValueList<Vector>::iterator it;
312 QList<Vector>::iterator it;
314 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it)
315 (*it).rotate(center, angle);
320 void RS_Spline::scale(Vector center, Vector factor)
322 // Q3ValueList<Vector>::iterator it;
323 QList<Vector>::iterator it;
325 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it)
326 (*it).scale(center, factor);
331 void RS_Spline::mirror(Vector axisPoint1, Vector axisPoint2)
333 // Q3ValueList<Vector>::iterator it;
334 QList<Vector>::iterator it;
336 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it)
337 (*it).mirror(axisPoint1, axisPoint2);
342 void RS_Spline::moveRef(const Vector & ref, const Vector & offset)
344 // Q3ValueList<Vector>::iterator it;
345 QList<Vector>::iterator it;
347 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it)
349 if (ref.distanceTo(*it) < 1.0e-4)
356 //void RS_Spline::draw(RS_Painter* painter, RS_GraphicView* view, double /*patternOffset*/)
357 void RS_Spline::draw(PaintInterface * painter, RS_GraphicView * view, double /*patternOffset*/)
359 if (painter == NULL || view == NULL)
362 RS_Entity * e = firstEntity(RS2::ResolveNone);
368 offset += e->getLength();
369 //RS_DEBUG->print("offset: %f\nlength was: %f", offset, e->getLength());
372 for (RS_Entity * e=nextEntity(RS2::ResolveNone); e!=NULL; e = nextEntity(RS2::ResolveNone))
374 view->drawEntityPlain(e, -offset);
375 offset += e->getLength();
376 //RS_DEBUG->print("offset: %f\nlength was: %f", offset, e->getLength());
381 * Todo: draw the spline, user patterns.
384 void RS_Spline::draw(RS_Painter* painter, RS_GraphicView* view) {
385 if (painter==NULL || view==NULL) {
390 if (data.controlPoints.count()>0) {
392 Q3ValueList<Vector>::iterator it;
393 for (it = data.controlPoints.begin(); it!=data.controlPoints.end(); ++it) {
395 painter->drawLine(view->toGui(prev),
404 int npts = data.controlPoints.count();
410 double* b = new double[npts*3+1];
411 double* h = new double[npts+1];
412 double* p = new double[p1*3+1];
414 Q3ValueList<Vector>::iterator it;
416 for (it = data.controlPoints.begin(); it!=data.controlPoints.end(); ++it) {
421 RS_DEBUG->print("RS_Spline::draw: b[%d]: %f/%f", i, b[i], b[i+1]);
425 // set all homogeneous weighting factors to 1.0
426 for (i=1; i <= npts; i++) {
431 for (i = 1; i <= 3*p1; i++) {
435 rbspline(npts,k,p1,b,h,p);
438 for (i = 1; i <= 3*p1; i=i+3) {
440 painter->drawLine(view->toGui(prev),
441 view->toGui(Vector(p[i], p[i+1])));
443 prev = Vector(p[i], p[i+1]);
449 * @return The reference points of the spline.
451 //Q3ValueList<Vector> RS_Spline::getControlPoints()
452 QList<Vector> RS_Spline::getControlPoints()
454 return data.controlPoints;
458 * Appends the given point to the control points.
460 void RS_Spline::addControlPoint(const Vector & v)
462 data.controlPoints.append(v);
466 * Removes the control point that was last added.
468 void RS_Spline::removeLastControlPoint()
470 data.controlPoints.pop_back();
474 * Generates B-Spline open knot vector with multiplicity
475 * equal to the order at the ends.
477 void RS_Spline::knot(int num, int order, int knotVector[])
481 for(int i=2; i<=num+order; i++)
483 if ((i > order) && (i < num + 2))
485 knotVector[i] = knotVector[i - 1] + 1;
489 knotVector[i] = knotVector[i - 1];
495 * Generates rational B-spline basis functions for an open knot vector.
497 void RS_Spline::rbasis(int c, double t, int npts, int x[], double h[], double r[])
507 double * temp = new double[nplusc + 1];
509 // calculate the first order nonrational basis functions n[i]
510 for(i=1; i<=nplusc-1; i++)
512 if ((t >= x[i]) && (t < x[i + 1]))
518 /* calculate the higher order nonrational basis functions */
522 for(i=1; i<=nplusc-k; i++)
524 // if the lower order basis function is zero skip the calculation
526 d = ((t - x[i]) * temp[i]) / (x[i + k - 1] - x[i]);
530 // if the lower order basis function is zero skip the calculation
531 if (temp[i + 1] != 0)
532 e = ((x[i + k] - t) * temp[i + 1]) / (x[i + k] - x[i + 1]);
540 // pick up last point
541 if (t == (double)x[nplusc])
544 // calculate sum for denominator of rational basis functions
547 for(i=1; i<=npts; i++)
548 sum = sum + temp[i] * h[i];
550 // form rational basis functions and put in r vector
551 for(i=1; i<=npts; i++)
554 r[i] = (temp[i] * h[i]) / (sum);
563 * Generates a rational B-spline curve using a uniform open knot vector.
565 void RS_Spline::rbspline(int npts, int k, int p1, double b[], double h[], double p[])
567 int i, j, icount, jcount;
569 //int x[30]; /* allows for 20 data points with basis function of order 5 */
579 int * x = new int[nplusc + 1];
580 double * nbasis = new double[npts + 1];
582 // zero and redimension the knot vector and the basis array
584 for(i = 0; i <= npts; i++)
587 for(i = 0; i <= nplusc; i++)
590 // generate the uniform open knot vector
595 // calculate the points on the rational B-spline curve
597 step = ((double)x[nplusc]) / ((double)(p1 - 1));
599 for(i1=1; i1<= p1; i1++)
601 if ((double)x[nplusc] - t < 5e-6)
602 t = (double)x[nplusc];
604 // generate the basis function for this value of t
605 rbasis(k, t, npts, x, h, nbasis);
607 // generate a point on the curve
613 // Do local matrix multiplication
614 for(i=1; i<=npts; i++)
616 temp = nbasis[i] * b[jcount];
617 p[icount + j] = p[icount + j] + temp;
630 void RS_Spline::knotu(int num, int order, int knotVector[])
632 int nplusc = num + order;
633 int nplus2 = num + 2;
636 for(int i=2; i<=nplusc; i++)
637 knotVector[i] = i - 1;
640 void RS_Spline::rbsplinu(int npts, int k, int p1, double b[], double h[], double p[])
642 int i, j, icount, jcount;
644 //int x[30]; /* allows for 20 data points with basis function of order 5 */
654 int * x = new int[nplusc + 1];
655 double * nbasis = new double[npts + 1];
657 /* zero and redimension the knot vector and the basis array */
659 for(i=0; i<=npts; i++)
662 for(i=0; i<=nplusc; i++)
665 /* generate the uniform periodic knot vector */
670 printf("The knot vector is ");
671 for (i = 1; i <= nplusc; i++){
672 printf(" %d ", x[i]);
676 printf("The usable parameter range is ");
677 for (i = k; i <= npts+1; i++){
678 printf(" %d ", x[i]);
685 /* calculate the points on the rational B-spline curve */
688 step = ((double)((npts) - (k - 1))) / ((double)(p1 - 1));
690 for(i1=1; i1<=p1; i1++)
692 if ((double)x[nplusc] - t < 5e-6)
693 t = (double)x[nplusc];
695 rbasis(k, t, npts, x, h, nbasis); /* generate the basis function for this value of t */
697 printf("t = %f \n",t);
699 for (i = 1; i <= npts; i++){
700 printf("%f ",nbasis[i]);
704 for(j=1; j<=3; j++) /* generate a point on the curve */
709 for(i=1; i<=npts; i++) /* Do local matrix multiplication */
711 temp = nbasis[i] * b[jcount];
712 p[icount + j] = p[icount + j] + temp;
714 printf("jcount,nbasis,b,nbasis*b,p = %d %f %f %f %f\n",jcount,nbasis[i],b[jcount],temp,p[icount+j]);
720 printf("icount, p %d %f %f %f \n",icount,p[icount+1],p[icount+2],p[icount+3]);
731 * Dumps the spline's data to stdout.
733 std::ostream & operator<<(std::ostream & os, const RS_Spline & l)
735 os << " Spline: " << l.getData() << "\n";