1 /****************************************************************************
2 ** $Id: rs_spline.cpp 2367 2005-04-04 16:57:36Z andrew $
4 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
6 ** This file is part of the qcadlib Library project.
8 ** This file may be distributed and/or modified under the terms of the
9 ** GNU General Public License version 2 as published by the Free Software
10 ** Foundation and appearing in the file LICENSE.GPL included in the
11 ** packaging of this file.
13 ** Licensees holding valid qcadlib Professional Edition licenses may use
14 ** this file in accordance with the qcadlib Commercial License
15 ** Agreement provided with the Software.
17 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 ** See http://www.ribbonsoft.com for further details.
22 ** Contact info@ribbonsoft.com if any conditions of this licensing are
25 **********************************************************************/
27 #include "rs_spline.h"
30 #include "rs_graphicview.h"
31 #include "rs_graphic.h"
32 #include "paintintf.h"
37 RS_Spline::RS_Spline(RS_EntityContainer * parent, const RS_SplineData & d):
38 RS_EntityContainer(parent), data(d)
46 RS_Spline::~RS_Spline()
50 RS_Entity * RS_Spline::clone()
52 RS_Spline * l = new RS_Spline(*this);
53 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
54 // l->entities.setAutoDelete(entities.autoDelete());
60 /** @return RS2::EntitySpline */
61 /*virtual*/ RS2::EntityType RS_Spline::rtti() const
63 return RS2::EntitySpline;
67 /*virtual*/ bool RS_Spline::isEdge() const
72 /** @return Copy of data that defines the spline. */
73 RS_SplineData RS_Spline::getData() const
78 /** Sets the splines degree (1-3). */
79 void RS_Spline::setDegree(int deg)
81 if (deg >= 1 && deg <= 3)
85 /** @return Degree of this spline curve (1-3).*/
86 int RS_Spline::getDegree()
92 int RS_Spline::getNumberOfKnots()
97 /** @return Number of control points. */
98 int RS_Spline::getNumberOfControlPoints()
100 return data.controlPoints.count();
104 * @retval true if the spline is closed.
105 * @retval false otherwise.
107 bool RS_Spline::isClosed()
113 * Sets the closed falg of this spline.
115 void RS_Spline::setClosed(bool c)
121 void RS_Spline::calculateBorders()
123 /*minV = Vector::minimum(data.startpoint, data.endpoint);
124 maxV = Vector::maximum(data.startpoint, data.endpoint);
126 Q3ValueList<Vector>::iterator it;
127 for (it = data.controlPoints.begin();
128 it!=data.controlPoints.end(); ++it) {
130 minV = Vector::minimum(*it, minV);
131 maxV = Vector::maximum(*it, maxV);
136 VectorSolutions RS_Spline::getRefPoints()
138 VectorSolutions ret(data.controlPoints.count());
141 QList<Vector>::iterator it;
143 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it, ++i)
151 Vector RS_Spline::getNearestRef(const Vector & coord, double * dist)
153 //return getRefPoints().getClosest(coord, dist);
154 return RS_Entity::getNearestRef(coord, dist);
157 Vector RS_Spline::getNearestSelectedRef(const Vector & coord, double * dist)
159 //return getRefPoints().getClosest(coord, dist);
160 return RS_Entity::getNearestSelectedRef(coord, dist);
164 * Updates the internal polygon of this spline. Called when the
165 * spline or it's data, position, .. changes.
167 void RS_Spline::update()
169 RS_DEBUG->print("RS_Spline::update");
176 if (data.degree < 1 || data.degree > 3)
178 RS_DEBUG->print("RS_Spline::update: invalid degree: %d", data.degree);
182 if (data.controlPoints.count() < (uint)data.degree + 1)
184 RS_DEBUG->print("RS_Spline::update: not enough control points");
190 // Q3ValueList<Vector> tControlPoints = data.controlPoints;
191 QList<Vector> tControlPoints = data.controlPoints;
195 for(int i=0; i<data.degree; ++i)
196 tControlPoints.append(data.controlPoints[i]);
200 int npts = tControlPoints.count();
202 int k = data.degree + 1;
204 int p1 = getGraphicVariableInt("$SPLINESEGS", 8) * npts;
206 double * b = new double[npts * 3 + 1];
207 double * h = new double[npts + 1];
208 double * p = new double[p1 * 3 + 1];
210 // Q3ValueList<Vector>::iterator it;
211 QList<Vector>::iterator it;
214 for(it=tControlPoints.begin(); it!=tControlPoints.end(); ++it)
220 RS_DEBUG->print("RS_Spline::update: b[%d]: %f/%f", i, b[i], b[i + 1]);
224 // set all homogeneous weighting factors to 1.0
225 for(i=1; i<=npts; i++)
228 for(i=1; i<=3*p1; i++)
232 rbsplinu(npts, k, p1, b, h, p);
234 rbspline(npts, k, p1, b, h, p);
238 for(i=1; i<=3*p1; i=i+3)
242 RS_Line * line = new RS_Line(this, RS_LineData(prev, Vector(p[i], p[i + 1])));
243 line->setLayer(NULL);
244 line->setPen(RS_Pen(RS2::FlagInvalid));
248 prev = Vector(p[i], p[i+1]);
249 minV = Vector::minimum(prev, minV);
250 maxV = Vector::maximum(prev, maxV);
258 Vector RS_Spline::getNearestEndpoint(const Vector & coord, double * dist)
260 double minDist = RS_MAXDOUBLE;
264 for (uint i=0; i<data.controlPoints.count(); i++) {
265 d = data.controlPoints[i].distanceTo(coord);
269 ret = data.controlPoints[i];
279 // The default implementation of RS_EntityContainer is inaccurate but
280 // has to do for now..
281 Vector RS_Spline::getNearestPointOnEntity(const Vector& coord,
282 bool onEntity, double* dist, RS_Entity** entity) {
286 Vector RS_Spline::getNearestCenter(const Vector & /*coord*/, double * dist)
289 *dist = RS_MAXDOUBLE;
291 return Vector(false);
294 Vector RS_Spline::getNearestMiddle(const Vector & /*coord*/, double * dist)
297 *dist = RS_MAXDOUBLE;
299 return Vector(false);
302 Vector RS_Spline::getNearestDist(double /*distance*/, const Vector & /*coord*/, double * dist)
305 *dist = RS_MAXDOUBLE;
307 return Vector(false);
310 void RS_Spline::move(Vector offset)
312 // Q3ValueList<Vector>::iterator it;
313 QList<Vector>::iterator it;
315 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it)
321 void RS_Spline::rotate(Vector center, double angle)
323 // Q3ValueList<Vector>::iterator it;
324 QList<Vector>::iterator it;
326 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it)
327 (*it).rotate(center, angle);
332 void RS_Spline::scale(Vector center, Vector factor)
334 // Q3ValueList<Vector>::iterator it;
335 QList<Vector>::iterator it;
337 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it)
338 (*it).scale(center, factor);
343 void RS_Spline::mirror(Vector axisPoint1, Vector axisPoint2)
345 // Q3ValueList<Vector>::iterator it;
346 QList<Vector>::iterator it;
348 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it)
349 (*it).mirror(axisPoint1, axisPoint2);
354 void RS_Spline::moveRef(const Vector & ref, const Vector & offset)
356 // Q3ValueList<Vector>::iterator it;
357 QList<Vector>::iterator it;
359 for(it=data.controlPoints.begin(); it!=data.controlPoints.end(); ++it)
361 if (ref.distanceTo(*it) < 1.0e-4)
368 //void RS_Spline::draw(RS_Painter* painter, RS_GraphicView* view, double /*patternOffset*/)
369 void RS_Spline::draw(PaintInterface * painter, RS_GraphicView * view, double /*patternOffset*/)
371 if (painter == NULL || view == NULL)
374 RS_Entity * e = firstEntity(RS2::ResolveNone);
380 offset += e->getLength();
381 //RS_DEBUG->print("offset: %f\nlength was: %f", offset, e->getLength());
384 for (RS_Entity * e=nextEntity(RS2::ResolveNone); e!=NULL; e = nextEntity(RS2::ResolveNone))
386 view->drawEntityPlain(e, -offset);
387 offset += e->getLength();
388 //RS_DEBUG->print("offset: %f\nlength was: %f", offset, e->getLength());
393 * Todo: draw the spline, user patterns.
396 void RS_Spline::draw(RS_Painter* painter, RS_GraphicView* view) {
397 if (painter==NULL || view==NULL) {
402 if (data.controlPoints.count()>0) {
404 Q3ValueList<Vector>::iterator it;
405 for (it = data.controlPoints.begin(); it!=data.controlPoints.end(); ++it) {
407 painter->drawLine(view->toGui(prev),
416 int npts = data.controlPoints.count();
422 double* b = new double[npts*3+1];
423 double* h = new double[npts+1];
424 double* p = new double[p1*3+1];
426 Q3ValueList<Vector>::iterator it;
428 for (it = data.controlPoints.begin(); it!=data.controlPoints.end(); ++it) {
433 RS_DEBUG->print("RS_Spline::draw: b[%d]: %f/%f", i, b[i], b[i+1]);
437 // set all homogeneous weighting factors to 1.0
438 for (i=1; i <= npts; i++) {
443 for (i = 1; i <= 3*p1; i++) {
447 rbspline(npts,k,p1,b,h,p);
450 for (i = 1; i <= 3*p1; i=i+3) {
452 painter->drawLine(view->toGui(prev),
453 view->toGui(Vector(p[i], p[i+1])));
455 prev = Vector(p[i], p[i+1]);
461 * @return The reference points of the spline.
463 //Q3ValueList<Vector> RS_Spline::getControlPoints()
464 QList<Vector> RS_Spline::getControlPoints()
466 return data.controlPoints;
470 * Appends the given point to the control points.
472 void RS_Spline::addControlPoint(const Vector & v)
474 data.controlPoints.append(v);
478 * Removes the control point that was last added.
480 void RS_Spline::removeLastControlPoint()
482 data.controlPoints.pop_back();
486 * Generates B-Spline open knot vector with multiplicity
487 * equal to the order at the ends.
489 void RS_Spline::knot(int num, int order, int knotVector[])
493 for(int i=2; i<=num+order; i++)
495 if ((i > order) && (i < num + 2))
497 knotVector[i] = knotVector[i - 1] + 1;
501 knotVector[i] = knotVector[i - 1];
507 * Generates rational B-spline basis functions for an open knot vector.
509 void RS_Spline::rbasis(int c, double t, int npts, int x[], double h[], double r[])
519 double * temp = new double[nplusc + 1];
521 // calculate the first order nonrational basis functions n[i]
522 for(i=1; i<=nplusc-1; i++)
524 if ((t >= x[i]) && (t < x[i + 1]))
530 /* calculate the higher order nonrational basis functions */
534 for(i=1; i<=nplusc-k; i++)
536 // if the lower order basis function is zero skip the calculation
538 d = ((t - x[i]) * temp[i]) / (x[i + k - 1] - x[i]);
542 // if the lower order basis function is zero skip the calculation
543 if (temp[i + 1] != 0)
544 e = ((x[i + k] - t) * temp[i + 1]) / (x[i + k] - x[i + 1]);
552 // pick up last point
553 if (t == (double)x[nplusc])
556 // calculate sum for denominator of rational basis functions
559 for(i=1; i<=npts; i++)
560 sum = sum + temp[i] * h[i];
562 // form rational basis functions and put in r vector
563 for(i=1; i<=npts; i++)
566 r[i] = (temp[i] * h[i]) / (sum);
575 * Generates a rational B-spline curve using a uniform open knot vector.
577 void RS_Spline::rbspline(int npts, int k, int p1, double b[], double h[], double p[])
579 int i, j, icount, jcount;
581 //int x[30]; /* allows for 20 data points with basis function of order 5 */
591 int * x = new int[nplusc + 1];
592 double * nbasis = new double[npts + 1];
594 // zero and redimension the knot vector and the basis array
596 for(i = 0; i <= npts; i++)
599 for(i = 0; i <= nplusc; i++)
602 // generate the uniform open knot vector
607 // calculate the points on the rational B-spline curve
609 step = ((double)x[nplusc]) / ((double)(p1 - 1));
611 for(i1=1; i1<= p1; i1++)
613 if ((double)x[nplusc] - t < 5e-6)
614 t = (double)x[nplusc];
616 // generate the basis function for this value of t
617 rbasis(k, t, npts, x, h, nbasis);
619 // generate a point on the curve
625 // Do local matrix multiplication
626 for(i=1; i<=npts; i++)
628 temp = nbasis[i] * b[jcount];
629 p[icount + j] = p[icount + j] + temp;
642 void RS_Spline::knotu(int num, int order, int knotVector[])
644 int nplusc = num + order;
645 int nplus2 = num + 2;
648 for(int i=2; i<=nplusc; i++)
649 knotVector[i] = i - 1;
652 void RS_Spline::rbsplinu(int npts, int k, int p1, double b[], double h[], double p[])
654 int i, j, icount, jcount;
656 //int x[30]; /* allows for 20 data points with basis function of order 5 */
666 int * x = new int[nplusc + 1];
667 double * nbasis = new double[npts + 1];
669 /* zero and redimension the knot vector and the basis array */
671 for(i=0; i<=npts; i++)
674 for(i=0; i<=nplusc; i++)
677 /* generate the uniform periodic knot vector */
682 printf("The knot vector is ");
683 for (i = 1; i <= nplusc; i++){
684 printf(" %d ", x[i]);
688 printf("The usable parameter range is ");
689 for (i = k; i <= npts+1; i++){
690 printf(" %d ", x[i]);
697 /* calculate the points on the rational B-spline curve */
700 step = ((double)((npts) - (k - 1))) / ((double)(p1 - 1));
702 for(i1=1; i1<=p1; i1++)
704 if ((double)x[nplusc] - t < 5e-6)
705 t = (double)x[nplusc];
707 rbasis(k, t, npts, x, h, nbasis); /* generate the basis function for this value of t */
709 printf("t = %f \n",t);
711 for (i = 1; i <= npts; i++){
712 printf("%f ",nbasis[i]);
716 for(j=1; j<=3; j++) /* generate a point on the curve */
721 for(i=1; i<=npts; i++) /* Do local matrix multiplication */
723 temp = nbasis[i] * b[jcount];
724 p[icount + j] = p[icount + j] + temp;
726 printf("jcount,nbasis,b,nbasis*b,p = %d %f %f %f %f\n",jcount,nbasis[i],b[jcount],temp,p[icount+j]);
732 printf("icount, p %d %f %f %f \n",icount,p[icount+1],p[icount+2],p[icount+3]);
743 * Dumps the spline's data to stdout.
745 std::ostream & operator<<(std::ostream & os, const RS_Spline & l)
747 os << " Spline: " << l.getData() << "\n";