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 "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(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
358 if (painter == NULL || view == NULL)
361 RS_Entity * e = firstEntity(RS2::ResolveNone);
367 offset += e->getLength();
368 //RS_DEBUG->print("offset: %f\nlength was: %f", offset, e->getLength());
371 for (RS_Entity * e=nextEntity(RS2::ResolveNone); e!=NULL; e = nextEntity(RS2::ResolveNone))
373 view->drawEntityPlain(e, -offset);
374 offset += e->getLength();
375 //RS_DEBUG->print("offset: %f\nlength was: %f", offset, e->getLength());
380 * Todo: draw the spline, user patterns.
383 void RS_Spline::draw(RS_Painter* painter, GraphicView* view) {
384 if (painter==NULL || view==NULL) {
389 if (data.controlPoints.count()>0) {
391 Q3ValueList<Vector>::iterator it;
392 for (it = data.controlPoints.begin(); it!=data.controlPoints.end(); ++it) {
394 painter->drawLine(view->toGui(prev),
403 int npts = data.controlPoints.count();
409 double* b = new double[npts*3+1];
410 double* h = new double[npts+1];
411 double* p = new double[p1*3+1];
413 Q3ValueList<Vector>::iterator it;
415 for (it = data.controlPoints.begin(); it!=data.controlPoints.end(); ++it) {
420 RS_DEBUG->print("RS_Spline::draw: b[%d]: %f/%f", i, b[i], b[i+1]);
424 // set all homogeneous weighting factors to 1.0
425 for (i=1; i <= npts; i++) {
430 for (i = 1; i <= 3*p1; i++) {
434 rbspline(npts,k,p1,b,h,p);
437 for (i = 1; i <= 3*p1; i=i+3) {
439 painter->drawLine(view->toGui(prev),
440 view->toGui(Vector(p[i], p[i+1])));
442 prev = Vector(p[i], p[i+1]);
448 * @return The reference points of the spline.
450 //Q3ValueList<Vector> RS_Spline::getControlPoints()
451 QList<Vector> RS_Spline::getControlPoints()
453 return data.controlPoints;
457 * Appends the given point to the control points.
459 void RS_Spline::addControlPoint(const Vector & v)
461 data.controlPoints.append(v);
465 * Removes the control point that was last added.
467 void RS_Spline::removeLastControlPoint()
469 data.controlPoints.pop_back();
473 * Generates B-Spline open knot vector with multiplicity
474 * equal to the order at the ends.
476 void RS_Spline::knot(int num, int order, int knotVector[])
480 for(int i=2; i<=num+order; i++)
482 if ((i > order) && (i < num + 2))
484 knotVector[i] = knotVector[i - 1] + 1;
488 knotVector[i] = knotVector[i - 1];
494 * Generates rational B-spline basis functions for an open knot vector.
496 void RS_Spline::rbasis(int c, double t, int npts, int x[], double h[], double r[])
506 double * temp = new double[nplusc + 1];
508 // calculate the first order nonrational basis functions n[i]
509 for(i=1; i<=nplusc-1; i++)
511 if ((t >= x[i]) && (t < x[i + 1]))
517 /* calculate the higher order nonrational basis functions */
521 for(i=1; i<=nplusc-k; i++)
523 // if the lower order basis function is zero skip the calculation
525 d = ((t - x[i]) * temp[i]) / (x[i + k - 1] - x[i]);
529 // if the lower order basis function is zero skip the calculation
530 if (temp[i + 1] != 0)
531 e = ((x[i + k] - t) * temp[i + 1]) / (x[i + k] - x[i + 1]);
539 // pick up last point
540 if (t == (double)x[nplusc])
543 // calculate sum for denominator of rational basis functions
546 for(i=1; i<=npts; i++)
547 sum = sum + temp[i] * h[i];
549 // form rational basis functions and put in r vector
550 for(i=1; i<=npts; i++)
553 r[i] = (temp[i] * h[i]) / (sum);
562 * Generates a rational B-spline curve using a uniform open knot vector.
564 void RS_Spline::rbspline(int npts, int k, int p1, double b[], double h[], double p[])
566 int i, j, icount, jcount;
568 //int x[30]; /* allows for 20 data points with basis function of order 5 */
578 int * x = new int[nplusc + 1];
579 double * nbasis = new double[npts + 1];
581 // zero and redimension the knot vector and the basis array
583 for(i = 0; i <= npts; i++)
586 for(i = 0; i <= nplusc; i++)
589 // generate the uniform open knot vector
594 // calculate the points on the rational B-spline curve
596 step = ((double)x[nplusc]) / ((double)(p1 - 1));
598 for(i1=1; i1<= p1; i1++)
600 if ((double)x[nplusc] - t < 5e-6)
601 t = (double)x[nplusc];
603 // generate the basis function for this value of t
604 rbasis(k, t, npts, x, h, nbasis);
606 // generate a point on the curve
612 // Do local matrix multiplication
613 for(i=1; i<=npts; i++)
615 temp = nbasis[i] * b[jcount];
616 p[icount + j] = p[icount + j] + temp;
629 void RS_Spline::knotu(int num, int order, int knotVector[])
631 int nplusc = num + order;
632 int nplus2 = num + 2;
635 for(int i=2; i<=nplusc; i++)
636 knotVector[i] = i - 1;
639 void RS_Spline::rbsplinu(int npts, int k, int p1, double b[], double h[], double p[])
641 int i, j, icount, jcount;
643 //int x[30]; /* allows for 20 data points with basis function of order 5 */
653 int * x = new int[nplusc + 1];
654 double * nbasis = new double[npts + 1];
656 /* zero and redimension the knot vector and the basis array */
658 for(i=0; i<=npts; i++)
661 for(i=0; i<=nplusc; i++)
664 /* generate the uniform periodic knot vector */
669 printf("The knot vector is ");
670 for (i = 1; i <= nplusc; i++){
671 printf(" %d ", x[i]);
675 printf("The usable parameter range is ");
676 for (i = k; i <= npts+1; i++){
677 printf(" %d ", x[i]);
684 /* calculate the points on the rational B-spline curve */
687 step = ((double)((npts) - (k - 1))) / ((double)(p1 - 1));
689 for(i1=1; i1<=p1; i1++)
691 if ((double)x[nplusc] - t < 5e-6)
692 t = (double)x[nplusc];
694 rbasis(k, t, npts, x, h, nbasis); /* generate the basis function for this value of t */
696 printf("t = %f \n",t);
698 for (i = 1; i <= npts; i++){
699 printf("%f ",nbasis[i]);
703 for(j=1; j<=3; j++) /* generate a point on the curve */
708 for(i=1; i<=npts; i++) /* Do local matrix multiplication */
710 temp = nbasis[i] * b[jcount];
711 p[icount + j] = p[icount + j] + temp;
713 printf("jcount,nbasis,b,nbasis*b,p = %d %f %f %f %f\n",jcount,nbasis[i],b[jcount],temp,p[icount+j]);
719 printf("icount, p %d %f %f %f \n",icount,p[icount+1],p[icount+2],p[icount+3]);
730 * Dumps the spline's data to stdout.
732 std::ostream & operator<<(std::ostream & os, const RS_Spline & l)
734 os << " Spline: " << l.getData() << "\n";