Subdividing a Cubic Spline
By Don Lancaster                                                                   
Version 1.2 February 14, 1998
Copyright c. 1998 by Don Lancaster and Synergetics, Box 809, Thatcher AZ, 85552
(520) 428-4073. synergetics@tinaja.com All commercial rights and all electronic media
rights are *fully* reserved. Linking welcome. Reposting is expressly forbidden.

Consulting services available.
Further support on www.tinaja.com/info01.html  


(The following is believed correct. Please report any errors or differing experiences.)

% SUMMARY: Guru Don Lancaster gives us three new PostScript methods to subdivide
% a cubic spline into individual chord segments.
%
% These include the CONSTANT ERROR method useful for vector
% conversion, the CONSTANT T PARAMETER method useful for
% various art applications, and Don's new and nonobvious
% CONSTANT CHORD LENGTH method for placing glyphs along an
% arbitrary path.
%
%*****************************************************************
% There are lots of intriguing PostScript opportunities that emerge
% whenever you subdivide a cubic spline into smaller pieces.

% There are three methods that yield different results for different
% purposes:

% (A) The CONSTANT ERROR method uses the flattenpath operator
% to break the spline up into chords of usually uneven
% length. Any and all chords will always be within a
% given error distance to the real curve.
%
% One use is to convert PostScript curves into vectors
% for use on a plotter, Santa Claus machine, engraver,
% embroidery or other non-PostScript device.

% (B) The CONSTANT "t" PARAMETER method uses the equations
% behind cubic splines to break the spline up into chords
% of usually uneven length. The chords will be somewhat
% longer along the "more bent" portions of the curve.
%
% Two uses are for avuncular sleezoids or similar "artsy
% craftsy" stuff.

% (C) The CONSTANT CHORD method uses successive approximations
% of method (B) to break the spline up into chords of
% virtually identical length. This method is non obvious
% and computationally intensive. But the results are
% easily distilled and compiled.
%
% One major use is to nonlinear map border elements or
% other glyphs along a serpentine or otherwise arbitrary
% path. More on this in a followup file.

% Let's look at each method in turn, generating some working utilities
% along the way. We will use my gonzo utilities to quickly and conveniently
% show the results. Most of the actual tools do not necessarily require
% gonzo for their final use.

%%%%%%%%%%%% (A) INSTALL AND RUN GONZO UTILITIES %%%%%%%%%%%%%%%%%%

% The demo uses my gonzo utilities from
%
http://www.tinaja.com/psutils/gonzo20.ps

% (C:\\windows\\desktop\\gonzo\\gonzo.ps) run % run the gonzo utilities

% gonzo begin               % activate the gonzo utilities
% ps.util.1 begin
% % printerror
% nuisance begin

% Build a graph space...

100 100 10 setgrid          % set up a working grid
30 40 showgrid              % and show part of it

%%%%%%%%%%%%%%% (B) CONSTANT ERROR METHOD %%%%%%%%%%%%%%%%%%%%%%

% The CONSTANT ERROR method uses the flattenpath operator to break
% the spline up into chords of usually uneven length. Any and all
% chords will always be within a given error distance to the real
% curve. The chords will typically be different lengths.

gsave 0 5 translate                   % position on graph
/maxpixelerror 5 def                  % set maximum error in pixels
line1                                 % set drawing width
beige 0.25 setgray                    % nice color

gsave 5 0 mt 10 10 20 0 25 10 curveto % define the curve

gsave stroke grestore                 % draw the curve

gsave maxpixelerror setflat           % replace curve with chords
flattenpath

mark                                  % create xy array for use
{}{}{}{} pathforall ]
/xyvalues exch def grestore

% showflatchords plots the endpoints of your chords

/showflatchordends {0 2 xyvalues length 1 sub
 {/posn exch def xyvalues posn get xyvalues posn
 1 add get mt dot} for} def

showflatchordends                       % show chord ends as dots

/cstretch 0.04 def                     

% and Gonzo label the curve

/sstretch 0.2 def
/font1 /Helvetica 0.85 gonzofont font1
black

12 3 ((A) Constant error method) cl grestore

%%%%%%%%%%%%%%% (C) CONSTANT "t" PARAMETER METHOD %%%%%%%%%%%%%%%%%%%

% The CONSTANT "t" PARAMETER method uses the equations behind cubic
% splines to break the spline up into chords of usually uneven length.
% The chords will be somewhat longer along the "more bent" portions
% of the curve.

% Let's start with three Bezier general curve utilities

% grabcoeffs takes the eight spline xy values and changes them into
% some intermediate variables handy to calculate x and y as a function
% of t...

/grabcoeffs {

/y3a exch def          % final y
/x3a exch def          % final x
/y2a exch def          % 2nd influence y
/x2a exch def          % 2nd influence x
/y1a exch def          % 1st influence y
/x1a exch def          % 1st influence x
/y0a exch def          % initial y
/x0a exch def          % initial x

/A x3a x1a x2a sub 3 mul add x0a sub store      % as in A(x)t^3
/B x2a x1a dup add sub x0a add 3 mul store      % as in B(x)t^2
/C x1a x0a sub 3 mul store                      % as in C(x)t
/D y3a y1a y2a sub 3 mul add y0a sub store      % as in D(y)t^3
/E y2a y1a dup add sub y0a add 3 mul store      % as in E(y)t^2
/F y1a y0a sub 3 mul store                      % as in F(y)t

} def

% And this is the "magic" code to find x and y given tt from 0 to 1..

/ytt { dup dup D mul E add mul F add mul y0a add} def /xtt { dup dup A mul B add mul C add mul x0a add} def

gsave 0 10 translate               % set position on graph
line1                              % set the linewidth
beige 0.25 setgray                 % nice color

5 0 10 10 20 0 25 10 grabcoeffs    % grab curveto values

5 0 moveto 10 10 20 0 25 10        % draw the curve curveto stroke

/numsegs 14 def                    % select number of segments

x0a y0a mt                         % plot the dots

0 1 numsegs div 1.0001 {dup xtt
exch ytt lineto currentpoint dot mt} for

% Note that the tt values are 0, 1 numsegs div, 2 numsegs div, ... 1
% An explicit custom array is probably not required

/cstretch 0.04 def                 % and Gonzo label the curve
/sstretch 0.2 def
/font1 /Helvetica 0.85 gonzofont
font1 black

12 3 ((B) Constant "t" parameter method) cl grestore

grestore

%%%%%%%%%%%%%%% (D) CONSTANT CHORD METHOD %%%%%%%%%%%%%%%%%%%

% The CONSTANT CHORD method uses successive approximations of method
% (B) to break the spline up into chords of virtually identical length.
% This method is non obvious and computationally intensive. But the
% results are easily distilled and compiled.

% bezierlength finds the length of a Bezier curve and leaves the length
% on the stack. It works by breaking the curve up into straight line
% approximate segments.

/bezierlength {/oldx x0a store /oldy y0a store /blength 0 store 0 1 numlenpts 1 sub div 1.0001 {dup xtt exch ytt /newy exch store /newx exch store newx oldx sub dup mul newy oldy sub dup mul add sqrt blength add /blength exch store /oldy newy store /oldx newx store} for }def

gsave 0 20 translate              % set position on graph
line1                             % set the linewidth beige
0.25 setgray                      % nice color

/numlenpts 400 def                % # of samples for length

5 0 10 10 20 0 25 10 grabcoeffs   % grab curveto values

bezierlength                      % get 23.9937 in this example

/segments 14 def                  % number of equal length chords
/chord blength segments div def   % length along curve between repeats
/numtrials 100 def                % needed accuracy of chording

% grabprevxy saves the previous x and y values

/grabprevxy { dup dup xtt /prevx exch store ytt /prevy exch store} def

% findchordguess measures the current chord

/findchordguess {dup dup xtt exch ytt prevy sub dup mul exch prevx sub dup mul add sqrt} def

% guessnextttt estimates the chord length by assuming it is equal to
% the change in t. This greatly reduces the number of trials needed...

/guessnexttt {1 copy 1 segments div add findchordguess chord lt} def

% findtvalues creates an array of the t values needed for virtually
% equal length chords

/findtvalues {mark 0 grabprevxy segments {guessnexttt /guessdir exch store {1 segments numtrials mul div guessdir {add}{sub} ifelse findchordguess chord guessdir {gt}{lt} ifelse {exit} if dup dup 0 lt exch 1 gt or {exit} if} loop grabprevxy} repeat 1 gt {1} if ] /ttvalues exch def} def

findtvalues                     % creates an array of tt values

% plotchordvalues puts a dot at each tt location

/plotchordvalues { ttvalues { dup xtt exch ytt mt dot} forall } def

gsave 0 5 translate

line1 x0a y0a moveto x1a y1a x2a y2a x3a y3a curveto stroke plotchordvalues

/cstretch 0.04 def              % and Gonzo label the curve
/sstretch 0.2 def
/font1 /Helvetica 0.85 gonzofont
font1 black

12 3 ((C) Constant chord length method) cl grestore

showpage



Copyright c. 1998 by Don Lancaster and Synergetics, Box 809, Thatcher AZ, 85552
(520) 428-4073. synergetics@tinaja.com All commercial rights and all electronic media
rights are *fully* reserved. Linking welcome. Reposting is expressly forbidden.

Consulting services available.
Further support on www.tinaja.com/info01.html  


Please click here to...