/********************************************************************** * File: quspline.cpp (Formerly qspline.c) * Description: Code for the QSPLINE class. * Author: Ray Smith * Created: Tue Oct 08 17:16:12 BST 1991 * * (C) Copyright 1991, Hewlett-Packard Ltd. ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** http://www.apache.org/licenses/LICENSE-2.0 ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. * **********************************************************************/ #include "mfcpch.h" #include "memry.h" #include "quadlsq.h" #include "quspline.h" #define QSPLINE_PRECISION 16 //no of steps to draw /********************************************************************** * QSPLINE::QSPLINE * * Constructor to build a QSPLINE given the components used in the old code. **********************************************************************/ QSPLINE::QSPLINE( //constructor inT32 count, //no of segments inT32 *xstarts, //start coords double *coeffs //coefficients ) { inT32 index; //segment index //get memory xcoords = (inT32 *) alloc_mem ((count + 1) * sizeof (inT32)); quadratics = (QUAD_COEFFS *) alloc_mem (count * sizeof (QUAD_COEFFS)); segments = count; for (index = 0; index < segments; index++) { //copy them xcoords[index] = xstarts[index]; quadratics[index] = QUAD_COEFFS (coeffs[index * 3], coeffs[index * 3 + 1], coeffs[index * 3 + 2]); } //right edge xcoords[index] = xstarts[index]; } /********************************************************************** * QSPLINE::QSPLINE * * Constructor to build a QSPLINE by appproximation of points. **********************************************************************/ QSPLINE::QSPLINE ( //constructor int xstarts[], //spline boundaries int segcount, //no of segments int xpts[], //points to fit int ypts[], int pointcount, //no of pts int degree //fit required ) { register int pointindex; /*no along text line */ register int segment; /*segment no */ inT32 *ptcounts; //no in each segment QLSQ qlsq; /*accumulator */ segments = segcount; xcoords = (inT32 *) alloc_mem ((segcount + 1) * sizeof (inT32)); ptcounts = (inT32 *) alloc_mem ((segcount + 1) * sizeof (inT32)); quadratics = (QUAD_COEFFS *) alloc_mem (segcount * sizeof (QUAD_COEFFS)); memmove (xcoords, xstarts, (segcount + 1) * sizeof (inT32)); ptcounts[0] = 0; /*none in any yet */ for (segment = 0, pointindex = 0; pointindex < pointcount; pointindex++) { while (segment < segcount && xpts[pointindex] >= xstarts[segment]) { segment++; /*try next segment */ /*cumulative counts */ ptcounts[segment] = ptcounts[segment - 1]; } ptcounts[segment]++; /*no in previous partition */ } while (segment < segcount) { segment++; /*zero the rest */ ptcounts[segment] = ptcounts[segment - 1]; } for (segment = 0; segment < segcount; segment++) { qlsq.clear (); /*first blob */ pointindex = ptcounts[segment]; if (pointindex > 0 && xpts[pointindex] != xpts[pointindex - 1] && xpts[pointindex] != xstarts[segment]) qlsq.add (xstarts[segment], ypts[pointindex - 1] + (ypts[pointindex] - ypts[pointindex - 1]) * (xstarts[segment] - xpts[pointindex - 1]) / (xpts[pointindex] - xpts[pointindex - 1])); for (; pointindex < ptcounts[segment + 1]; pointindex++) { qlsq.add (xpts[pointindex], ypts[pointindex]); } if (pointindex > 0 && pointindex < pointcount && xpts[pointindex] != xstarts[segment + 1]) qlsq.add (xstarts[segment + 1], ypts[pointindex - 1] + (ypts[pointindex] - ypts[pointindex - 1]) * (xstarts[segment + 1] - xpts[pointindex - 1]) / (xpts[pointindex] - xpts[pointindex - 1])); qlsq.fit (degree); quadratics[segment].a = qlsq.get_a (); quadratics[segment].b = qlsq.get_b (); quadratics[segment].c = qlsq.get_c (); } free_mem(ptcounts); } /********************************************************************** * QSPLINE::QSPLINE * * Constructor to build a QSPLINE from another. **********************************************************************/ QSPLINE::QSPLINE( //constructor const QSPLINE &src) { segments = 0; xcoords = NULL; quadratics = NULL; *this = src; } /********************************************************************** * QSPLINE::~QSPLINE * * Destroy a QSPLINE. **********************************************************************/ QSPLINE::~QSPLINE ( //constructor ) { if (xcoords != NULL) { free_mem(xcoords); xcoords = NULL; } if (quadratics != NULL) { free_mem(quadratics); quadratics = NULL; } } /********************************************************************** * QSPLINE::operator= * * Copy a QSPLINE **********************************************************************/ QSPLINE & QSPLINE::operator= ( //assignment const QSPLINE & source) { if (xcoords != NULL) free_mem(xcoords); if (quadratics != NULL) free_mem(quadratics); segments = source.segments; xcoords = (inT32 *) alloc_mem ((segments + 1) * sizeof (inT32)); quadratics = (QUAD_COEFFS *) alloc_mem (segments * sizeof (QUAD_COEFFS)); memmove (xcoords, source.xcoords, (segments + 1) * sizeof (inT32)); memmove (quadratics, source.quadratics, segments * sizeof (QUAD_COEFFS)); return *this; } /********************************************************************** * QSPLINE::step * * Return the total of the step functions between the given coords. **********************************************************************/ double QSPLINE::step( //find step functions double x1, //between coords double x2) { int index1, index2; //indices of coords double total; /*total steps */ index1 = spline_index (x1); index2 = spline_index (x2); total = 0; while (index1 < index2) { total += (double) quadratics[index1 + 1].y ((float) xcoords[index1 + 1]); total -= (double) quadratics[index1].y ((float) xcoords[index1 + 1]); index1++; /*next segment */ } return total; /*total steps */ } /********************************************************************** * QSPLINE::y * * Return the y value at the given x value. **********************************************************************/ double QSPLINE::y( //evaluate double x //coord to evaluate at ) const { inT32 index; //segment index index = spline_index (x); return quadratics[index].y (x);//in correct segment } /********************************************************************** * QSPLINE::spline_index * * Return the index to the largest xcoord not greater than x. **********************************************************************/ inT32 QSPLINE::spline_index( //evaluate double x //coord to evaluate at ) const { inT32 index; //segment index inT32 bottom; //bottom of range inT32 top; //top of range bottom = 0; top = segments; while (top - bottom > 1) { index = (top + bottom) / 2; //centre of range if (x >= xcoords[index]) bottom = index; //new min else top = index; //new max } return bottom; } /********************************************************************** * QSPLINE::move * * Reposition spline by vector **********************************************************************/ void QSPLINE::move( // reposition spline ICOORD vec // by vector ) { inT32 segment; //index of segment inT16 x_shift = vec.x (); for (segment = 0; segment < segments; segment++) { xcoords[segment] += x_shift; quadratics[segment].move (vec); } xcoords[segment] += x_shift; } /********************************************************************** * QSPLINE::overlap * * Return TRUE if spline2 overlaps this by no more than fraction less * than the bounds of this. **********************************************************************/ BOOL8 QSPLINE::overlap( //test overlap QSPLINE *spline2, //2 cannot be smaller double fraction //by more than this ) { int leftlimit; /*common left limit */ int rightlimit; /*common right limit */ leftlimit = xcoords[1]; rightlimit = xcoords[segments - 1]; /*or too non-overlap */ if (spline2->segments < 3 || spline2->xcoords[1] > leftlimit + fraction * (rightlimit - leftlimit) || spline2->xcoords[spline2->segments - 1] < rightlimit - fraction * (rightlimit - leftlimit)) return FALSE; else return TRUE; } /********************************************************************** * extrapolate_spline * * Extrapolates the spline linearly using the same gradient as the * quadratic has at either end. **********************************************************************/ void QSPLINE::extrapolate( //linear extrapolation double gradient, //gradient to use int xmin, //new left edge int xmax //new right edge ) { register int segment; /*current segment of spline */ int dest_segment; //dest index int *xstarts; //new boundaries QUAD_COEFFS *quads; //new ones int increment; //in size increment = xmin < xcoords[0] ? 1 : 0; if (xmax > xcoords[segments]) increment++; if (increment == 0) return; xstarts = (int *) alloc_mem ((segments + 1 + increment) * sizeof (int)); quads = (QUAD_COEFFS *) alloc_mem ((segments + increment) * sizeof (QUAD_COEFFS)); if (xmin < xcoords[0]) { xstarts[0] = xmin; quads[0].a = 0; quads[0].b = gradient; quads[0].c = y (xcoords[0]) - quads[0].b * xcoords[0]; dest_segment = 1; } else dest_segment = 0; for (segment = 0; segment < segments; segment++) { xstarts[dest_segment] = xcoords[segment]; quads[dest_segment] = quadratics[segment]; dest_segment++; } xstarts[dest_segment] = xcoords[segment]; if (xmax > xcoords[segments]) { quads[dest_segment].a = 0; quads[dest_segment].b = gradient; quads[dest_segment].c = y (xcoords[segments]) - quads[dest_segment].b * xcoords[segments]; dest_segment++; xstarts[dest_segment] = xmax + 1; } segments = dest_segment; free_mem(xcoords); free_mem(quadratics); xcoords = (inT32 *) xstarts; quadratics = quads; } /********************************************************************** * QSPLINE::plot * * Draw the QSPLINE in the given colour. **********************************************************************/ #ifndef GRAPHICS_DISABLED void QSPLINE::plot( //draw it ScrollView* window, //window to draw in ScrollView::Color colour //colour to draw in ) const { inT32 segment; //index of segment inT16 step; //index of poly piece double increment; //x increment double x; //x coord window->Pen(colour); for (segment = 0; segment < segments; segment++) { increment = (double) (xcoords[segment + 1] - xcoords[segment]) / QSPLINE_PRECISION; x = xcoords[segment]; for (step = 0; step <= QSPLINE_PRECISION; step++) { if (segment == 0 && step == 0) window->SetCursor(x, quadratics[segment].y (x)); else window->DrawTo(x, quadratics[segment].y (x)); x += increment; } } } #endif