1
/****************************************************************************
3
** This file is part of the LibreCAD project, a 2D CAD program
5
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
9
** This file may be distributed and/or modified under the terms of the
10
** GNU General Public License version 2 as published by the Free Software
11
** Foundation and appearing in the file gpl-2.0.txt included in the
12
** packaging of this file.
14
** This program is distributed in the hope that it will be useful,
15
** but WITHOUT ANY WARRANTY; without even the implied warranty of
16
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
** GNU General Public License for more details.
19
** You should have received a copy of the GNU General Public License
20
** along with this program; if not, write to the Free Software
21
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
** This copyright notice MUST APPEAR in all copies of the script!
25
**********************************************************************/
28
#include "rs_spline.h"
31
#include "rs_graphicview.h"
32
#include "rs_painter.h"
33
#include "rs_graphic.h"
38
RS_Spline::RS_Spline(RS_EntityContainer* parent,
39
const RS_SplineData& d)
40
:RS_EntityContainer(parent), data(d) {
49
RS_Spline::~RS_Spline() {}
54
RS_Entity* RS_Spline::clone() {
55
RS_Spline* l = new RS_Spline(*this);
56
l->setOwner(isOwner());
64
void RS_Spline::calculateBorders() {
65
/*minV = RS_Vector::minimum(data.startpoint, data.endpoint);
66
maxV = RS_Vector::maximum(data.startpoint, data.endpoint);
68
QList<RS_Vector>::iterator it;
69
for (it = data.controlPoints.begin();
70
it!=data.controlPoints.end(); ++it) {
72
minV = RS_Vector::minimum(*it, minV);
73
maxV = RS_Vector::maximum(*it, maxV);
80
RS_VectorSolutions RS_Spline::getRefPoints() {
82
RS_VectorSolutions ret(data.controlPoints.size());
84
for (int i = 0; i < data.controlPoints.size(); ++i) {
85
ret.set(i, data.controlPoints.at(i));
91
RS_Vector RS_Spline::getNearestRef(const RS_Vector& coord,
94
//return getRefPoints().getClosest(coord, dist);
95
return RS_Entity::getNearestRef(coord, dist);
98
RS_Vector RS_Spline::getNearestSelectedRef(const RS_Vector& coord,
101
//return getRefPoints().getClosest(coord, dist);
102
return RS_Entity::getNearestSelectedRef(coord, dist);
107
* Updates the internal polygon of this spline. Called when the
108
* spline or it's data, position, .. changes.
110
void RS_Spline::update() {
112
RS_DEBUG->print("RS_Spline::update");
120
if (data.degree<1 || data.degree>3) {
121
RS_DEBUG->print("RS_Spline::update: invalid degree: %d", data.degree);
125
if (data.controlPoints.size() < data.degree+1) {
126
RS_DEBUG->print("RS_Spline::update: not enough control points");
132
QList<RS_Vector> tControlPoints = data.controlPoints;
135
for (int i=0; i<data.degree; ++i) {
136
tControlPoints.append(data.controlPoints.at(i));
141
int npts = tControlPoints.count();
143
int k = data.degree+1;
145
int p1 = getGraphicVariableInt("$SPLINESEGS", 8) * npts;
147
double* b = new double[npts*3+1];
148
double* h = new double[npts+1];
149
double* p = new double[p1*3+1];
152
for (int it = 0; it < tControlPoints.size(); ++it) {
153
b[i] = tControlPoints.at(it).x;
154
b[i+1] = tControlPoints.at(it).y;
157
RS_DEBUG->print("RS_Spline::update: b[%d]: %f/%f", i, b[i], b[i+1]);
161
// set all homogeneous weighting factors to 1.0
162
for (i=1; i <= npts; i++) {
166
for (i = 1; i <= 3*p1; i++) {
171
rbsplinu(npts,k,p1,b,h,p);
173
rbspline(npts,k,p1,b,h,p);
176
RS_Vector prev(false);
177
for (i = 1; i <= 3*p1; i=i+3) {
179
RS_Line* line = new RS_Line(this,
180
RS_LineData(prev, RS_Vector(p[i], p[i+1])));
181
line->setLayer(NULL);
182
line->setPen(RS_Pen(RS2::FlagInvalid));
185
prev = RS_Vector(p[i], p[i+1]);
187
minV = RS_Vector::minimum(prev, minV);
188
maxV = RS_Vector::maximum(prev, maxV);
196
RS_Vector RS_Spline::getStartpoint() const {
197
if (data.closed) return RS_Vector(false);
198
return static_cast<RS_Line*>(const_cast<RS_Spline*>(this)->firstEntity())->getStartpoint();
200
RS_Vector RS_Spline::getEndpoint() const {
201
if (data.closed) return RS_Vector(false);
202
return static_cast<RS_Line*>(const_cast<RS_Spline*>(this)->lastEntity())->getEndpoint();
206
RS_Vector RS_Spline::getNearestEndpoint(const RS_Vector& coord,
208
double minDist = RS_MAXDOUBLE;
209
RS_Vector ret(false);
210
if(! data.closed) { // no endpoint for closed spline
211
RS_Vector vp1(getStartpoint());
212
RS_Vector vp2(getEndpoint());
213
double d1( (coord-vp1).squared());
214
double d2( (coord-vp2).squared());
222
// for (int i=0; i<data.controlPoints.count(); i++) {
223
// d = (data.controlPoints.at(i)).distanceTo(coord);
227
// ret = data.controlPoints.at(i);
240
// The default implementation of RS_EntityContainer is inaccurate but
241
// has to do for now..
242
RS_Vector RS_Spline::getNearestPointOnEntity(const RS_Vector& coord,
243
bool onEntity, double* dist, RS_Entity** entity) {
249
RS_Vector RS_Spline::getNearestCenter(const RS_Vector& /*coord*/,
253
*dist = RS_MAXDOUBLE;
256
return RS_Vector(false);
261
RS_Vector RS_Spline::getNearestMiddle(const RS_Vector& /*coord*/,
263
int /*middlePoints*/)const {
265
*dist = RS_MAXDOUBLE;
268
return RS_Vector(false);
273
RS_Vector RS_Spline::getNearestDist(double /*distance*/,
274
const RS_Vector& /*coord*/,
277
*dist = RS_MAXDOUBLE;
280
return RS_Vector(false);
285
void RS_Spline::move(const RS_Vector& offset) {
286
RS_EntityContainer::move(offset);
287
for (int i = 0; i < data.controlPoints.size(); ++i) {
288
data.controlPoints[i].move(offset);
295
void RS_Spline::rotate(const RS_Vector& center, const double& angle) {
296
rotate(center,RS_Vector(angle));
301
void RS_Spline::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
302
RS_EntityContainer::rotate(center, angleVector);
303
for (int i = 0; i < data.controlPoints.size(); ++i) {
304
(data.controlPoints[i] ).rotate(center, angleVector);
309
void RS_Spline::scale(const RS_Vector& center, const RS_Vector& factor) {
310
for (int i = 0; i < data.controlPoints.size(); ++i) {
311
(data.controlPoints[i] ).scale(center, factor);
319
void RS_Spline::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
320
RS_EntityContainer::mirror(axisPoint1, axisPoint2);
321
for (int i = 0; i < data.controlPoints.size(); ++i) {
322
(data.controlPoints[i] ).mirror(axisPoint1, axisPoint2);
330
void RS_Spline::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
331
for (int i = 0; i < data.controlPoints.size(); ++i) {
333
if (ref.distanceTo(data.controlPoints.at(i))<1.0e-4) {
334
data.controlPoints[i].move(offset);
341
void RS_Spline::revertDirection() {
342
for(int k = 0; k < data.controlPoints.size() / 2; k++) {
343
data.controlPoints.swap(k, data.controlPoints.size() - 1 - k);
350
void RS_Spline::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/) {
352
if (painter==NULL || view==NULL) {
357
RS_Entity* e=firstEntity(RS2::ResolveNone);
359
RS_Pen p=this->getPen(true);
361
double patternOffset(0.0);
362
view->drawEntity(painter, e, patternOffset);
363
//RS_DEBUG->print("offset: %f\nlength was: %f", offset, e->getLength());
365
e = nextEntity(RS2::ResolveNone);
367
view->drawEntityPlain(painter, e, patternOffset);
368
e = nextEntity(RS2::ResolveNone);
369
//RS_DEBUG->print("offset: %f\nlength was: %f", offset, e->getLength());
377
* Todo: draw the spline, user patterns.
380
void RS_Spline::draw(RS_Painter* painter, RS_GraphicView* view) {
381
if (painter==NULL || view==NULL) {
386
if (data.controlPoints.count()>0) {
387
RS_Vector prev(false);
388
QList<RS_Vector>::iterator it;
389
for (it = data.controlPoints.begin(); it!=data.controlPoints.end(); ++it) {
391
painter->drawLine(view->toGui(prev),
400
int npts = data.controlPoints.count();
406
double* b = new double[npts*3+1];
407
double* h = new double[npts+1];
408
double* p = new double[p1*3+1];
410
QList<RS_Vector>::iterator it;
412
for (it = data.controlPoints.begin(); it!=data.controlPoints.end(); ++it) {
417
RS_DEBUG->print("RS_Spline::draw: b[%d]: %f/%f", i, b[i], b[i+1]);
421
// set all homogeneous weighting factors to 1.0
422
for (i=1; i <= npts; i++) {
427
for (i = 1; i <= 3*p1; i++) {
431
rbspline(npts,k,p1,b,h,p);
433
RS_Vector prev(false);
434
for (i = 1; i <= 3*p1; i=i+3) {
436
painter->drawLine(view->toGui(prev),
437
view->toGui(RS_Vector(p[i], p[i+1])));
439
prev = RS_Vector(p[i], p[i+1]);
447
* @return The reference points of the spline.
449
QList<RS_Vector> RS_Spline::getControlPoints() {
450
return data.controlPoints;
456
* Appends the given point to the control points.
458
void RS_Spline::addControlPoint(const RS_Vector& v) {
459
data.controlPoints.append(v);
465
* Removes the control point that was last added.
467
void RS_Spline::removeLastControlPoint() {
468
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[]) {
478
for (int i = 2; i <= num + order; i++) {
479
if ( (i > order) && (i < num + 2) ) {
480
knotVector[i] = knotVector[i-1] + 1;
482
knotVector[i] = knotVector[i-1];
490
* Generates rational B-spline basis functions for an open knot vector.
492
void RS_Spline::rbasis(int c, double t, int npts,
493
int x[], double h[], double r[]) {
503
double* temp = new double[nplusc+1];
505
// calculate the first order nonrational basis functions n[i]
506
for (i = 1; i<= nplusc-1; i++) {
507
if (( t >= x[i]) && (t < x[i+1]))
513
/* calculate the higher order nonrational basis functions */
515
for (k = 2; k <= c; k++) {
516
for (i = 1; i <= nplusc-k; i++) {
517
// if the lower order basis function is zero skip the calculation
519
d = ((t-x[i])*temp[i])/(x[i+k-1]-x[i]);
522
// if the lower order basis function is zero skip the calculation
524
e = ((x[i+k]-t)*temp[i+1])/(x[i+k]-x[i+1]);
532
// pick up last point
533
if (t == (double)x[nplusc]) {
537
// calculate sum for denominator of rational basis functions
539
for (i = 1; i <= npts; i++) {
540
sum = sum + temp[i]*h[i];
543
// form rational basis functions and put in r vector
544
for (i = 1; i <= npts; i++) {
546
r[i] = (temp[i]*h[i])/(sum);
556
* Generates a rational B-spline curve using a uniform open knot vector.
558
void RS_Spline::rbspline(int npts, int k, int p1,
559
double b[], double h[], double p[]) {
561
int i,j,icount,jcount;
563
//int x[30]; /* allows for 20 data points with basis function of order 5 */
573
int* x = new int[nplusc+1];
574
double* nbasis = new double[npts+1];
576
// zero and redimension the knot vector and the basis array
578
for(i = 0; i <= npts; i++) {
582
for(i = 0; i <= nplusc; i++) {
586
// generate the uniform open knot vector
591
// calculate the points on the rational B-spline curve
593
step = ((double)x[nplusc])/((double)(p1-1));
595
for (i1 = 1; i1<= p1; i1++) {
597
if ((double)x[nplusc] - t < 5e-6) {
598
t = (double)x[nplusc];
601
// generate the basis function for this value of t
602
rbasis(k,t,npts,x,h,nbasis);
604
// generate a point on the curve
605
for (j = 1; j <= 3; j++) {
609
// Do local matrix multiplication
610
for (i = 1; i <= npts; i++) {
611
// temp = nbasis[i]*b[jcount];
612
// p[icount + j] = p[icount + j] + temp;
613
p[icount + j] += nbasis[i]*b[jcount];
626
void RS_Spline::knotu(int num, int order, int knotVector[]) {
627
int nplusc,/*nplus2,*/i;
629
nplusc = num + order;
633
for (i = 2; i <= nplusc; i++) {
640
void RS_Spline::rbsplinu(int npts, int k, int p1,
641
double b[], double h[], double p[]) {
643
int i,j,icount,jcount;
645
//int x[30]; /* allows for 20 data points with basis function of order 5 */
656
int* x = new int[nplusc+1];
657
double* nbasis = new double[npts+1];
659
/* zero and redimension the knot vector and the basis array */
661
for(i = 0; i <= npts; i++) {
665
for(i = 0; i <= nplusc; i++) {
669
/* generate the uniform periodic knot vector */
674
printf("The knot vector is ");
675
for (i = 1; i <= nplusc; i++){
676
printf(" %d ", x[i]);
680
printf("The usable parameter range is ");
681
for (i = k; i <= npts+1; i++){
682
printf(" %d ", x[i]);
689
/* calculate the points on the rational B-spline curve */
692
step = ((double)((npts)-(k-1)))/((double)(p1-1));
694
for (i1 = 1; i1<= p1; i1++) {
696
if ((double)x[nplusc] - t < 5e-6) {
697
t = (double)x[nplusc];
700
rbasis(k,t,npts,x,h,nbasis); /* generate the basis function for this value of t */
702
printf("t = %f \n",t);
704
for (i = 1; i <= npts; i++){
705
printf("%f ",nbasis[i]);
709
for (j = 1; j <= 3; j++) { /* generate a point on the curve */
713
for (i = 1; i <= npts; i++) { /* Do local matrix multiplication */
714
// temp = nbasis[i]*b[jcount];
715
// p[icount + j] = p[icount + j] + temp;
716
p[icount + j] += nbasis[i]*b[jcount];
718
printf("jcount,nbasis,b,nbasis*b,p = %d %f %f %f %f\n",jcount,nbasis[i],b[jcount],temp,p[icount+j]);
724
printf("icount, p %d %f %f %f \n",icount,p[icount+1],p[icount+2],p[icount+3]);
736
* Dumps the spline's data to stdout.
738
std::ostream& operator << (std::ostream& os, const RS_Spline& l) {
739
os << " Spline: " << l.getData() << "\n";