[comp.lang.postscript] SOLUTION! to Spline data/Bezier Curve problem

cse0080@desire.wright.edu (06/11/91)

In article <1991Jun3.095901.3793@desire.wright.edu>, I wrote:
> Help!
> 
> I need to know how to generate spline curves in PostScript when
> all the data I have on hand is an array of data points that lie on 
> the curve itself.  (IE: no control points for the curveto operator)
> 
> Anyone know a good way to do this?
> 
> Thanks for your assistance.
> 
> John Hansen
> cse0080@wsu.bitnet
> cse0080@desire.wright.edu
> 
I received two C program fragments, and a copy of the posted PostScript
code that attempted to help me solve my problem.  Unfortunately, none of 
them seemed to do what I needed.  The PostScript code didn't draw through
every point so it seemed pointless (pun intended) to use that.  The other
two C programs produced weird results (and I'm no C expert so I didn't 
try too hard to fix/modify them to my needs.  So... I decided to attempt a
solution myself.  Written in my native tongue: Fortran.  (Sorry about that...)

I have included at the end a code fragment which should be easily translated
into C or Pascal for you CS types.  My program appears to work for all the
test datasets which I tried.  It works like this:

1. Calculate angle to horizontal of a line "tangent" to the desired curve
at each point.  (tangle(i) in the code)
2. Calculate two control points in between each set of two data points
using the 1/4 (magic number 8-) the distance between the two data points.
formula: new_x = 0.25*len*sind(tangle(i)) + point(i).x, where len is dist.
between point_i & point_i+1.

I set the tangent angle of the first and last points to be equal to the
straight line angle between each of those points and the next/previous point.

Anyway, the code shouldn't be too obtuse (heh, heh, heh...)   I have also
included a /SPline word in PostScript which I use to eat all the numbers I
write out.  Hopefully this will save someone out there some time and effort.
It took me the better part of three days to get this to actually work in all
the cases that I threw at it.

Thanks for your help
John Hansen
cse0080@wsu.bitnet
cse0080@desire.wright.edu
hansen@mdss1.hq.aflc.af.mil (work)

--------------------------- cut here --------------------------------

C
C	Sample do loop for processing my spline data.
C
	do i=1,numsp
	  clr = splines(i).clr/10.0
	  wid = max(mod(splines(i).width,10)*1.0,0.5)
	  write(unit=pat,fmt='(I1)') mod(splines(i).style,10)
	  if (pat .eq. '0') pat = ' '
	  write(7,'(F5.2,A)') clr,' ['
	  do j=1,splines(i).points
	    pts(j).x = int(splines(i).x(j)*10)
	    pts(j).y = int(splines(i).y(j)*10)
	  end do
	  call dospline(splines(i).points)
	  write(7,'(3A,F5.2,A)') '] [',pat,'] 0',wid,' SPline'
	end do

C
C	The Fortran code which writes /SPline to my prolog file
C
	write(7,'(A)') ' '
	write(7,'(A)') '/SPline { setlinewidth setdash /polyarray exch def'
	write(7,'(A)') '/pcount polyarray length 2 sub 6 idiv def'
	write(7,'(A)') 'polyarray aload pop moveto pcount {curveto}'
	write(7,'(A)') 'repeat setgray stroke} def'
	write(7,'(A)') ' '


C
C	The pts point array is COMMON but could be passed as a parameter
C	to DOSPLINE.  My splines will never be more than 30 points long
C	so I hard-coded the array sizes.
C
	subroutine dospline(npts)
	structure /point/
	  real x,y
	end structure
	record /point/ pts(50), cp1(50), cp2(50)
	real dx1,dx2,dy1,dy2,angle1(50),angle2(50),distp(50)
	real jarct2d, tangle(50)
	integer npts
	common /ptdata/ pts
	if (npts .lt. 3) goto 1000
	do i=2,npts-1
	  dx1 = pts(i).x - pts(i-1).x
	  dy1 = pts(i).y - pts(i-1).y
	  dx2 = pts(i+1).x - pts(i).x
	  dy2 = pts(i+1).y - pts(i).y
	  distp(i) = sqrt(dx1*dx1+dy1*dy1)
	  angle1(i) = jarct2d(dy1,dx1)
	  angle2(i) = jarct2d(dy2,dx2)
	  tangle(i) = (angle2(i) + angle1(i))/2.0
	end do
	dx2 = pts(npts-1).x - pts(npts).x
	dy2 = pts(npts-1).y - pts(npts).y
	distp(npts) = sqrt(dx2*dx2+dy2*dy2)
	angle1(npts) = jarct2d(dy2,dx2)
C
C	The commented change slightly the look of the curve at start & end
C
C	tangle(1) = angle1(2) + tangle(2) - angle2(2)
C	tangle(npts)=angle1(npts)+(angle1(npts-1)-angle2(npt-1))/2.0
C
C	Here they are set == to straight-line angle to next/previous points
C
	tangle(1) = angle1(2)
	tangle(npts)=angle1(npts)
	do i=1,npts-1
	  call compcp(distp(i+1),tangle(i),i,1,cp1(i).x,cp1(i).y)
	  if (i .le. npts-2) then
	   call compcp(distp(i+1),tangle(i+1),i+1,2,cp2(i).x,cp2(i).y)
	  endif
	end do
	call compcp(distp(npts),tangle(npts),npts,2,
	1	cp2(npts-1).x,cp2(npts-1).y)
	do i=npts-1,1,-1
	  write(7,'(6F10.4)') cp1(i).x,cp1(i).y,
	1	cp2(i).x,cp2(i).y,pts(i+1).x,pts(i+1).y
	end do
	write(7,'(2F10.4)') pts(1).x,pts(1).y
 1000	return
	end

	real function compcp(len,ang,ptnum,cp,cpx,cpy)
	structure /point/
	  real x, y
	end structure
	record /point/ pts(50)
	real len, ang, tx, ty, dd, dx, dy, temp, cpx, cpy
	real computecpx, computecpy
	integer ptnum,cp
	common /ptdata/ pts
	tx = computecpx(len,ang,ptnum)
	ty = computecpy(len,ang,ptnum)
	if (cp .eq. 1) then
	  dx = pts(ptnum+1).x - tx
	  dy = pts(ptnum+1).y - ty
	else
	  dx = pts(ptnum-1).x - tx
	  dy = pts(ptnum-1).y - ty
	endif
	dd = sqrt(dx*dx+dy*dy)
	if (dd .gt. len) then
	  tx = computecpx(len,180+ang,ptnum)
	  ty = computecpy(len,180+ang,ptnum)
	endif
	cpx = tx
	cpy = ty
	return
	end


	real function computecpx(len,ang,ptnum)
	structure /point/
	  real x, y
	end structure
	record /point/ pts(50)
	real len, ang
	integer ptnum
	common /ptdata/ pts
C
C	The number 0.25 could be used as a variable for "shaping"
C	the curve between each data point.
C
	temp = 0.25*len*cosd(ang) + pts(ptnum).x
	computecpx = temp
	return
	end

	real function computecpy(len,ang,ptnum)
	structure /point/
	  real x, y
	end structure
	record /point/ pts(50)
	real len, ang
	integer ptnum
	common /ptdata/ pts
	temp = 0.25*len*sind(ang) + pts(ptnum).y
	computecpy = temp
	return
	end

C
C	The atan2d VMS fortran intrinsic funtion returns angles between
C	-180 & 180.  I remove the undefined case of dx=dy=0.
C

	real function jarct2d(dumy,dumx)
	real dumx,dumy
	if (dumx .eq. 0 .and. dumy .eq. 0) then
	  jarct2d = 0
	else
	  jarct2d = atan2d(dumy,dumx)
	endif
	return
	end

larry@csccat.cs.com (Larry Spence) (06/12/91)

In article <1991Jun10.162302.3900@desire.wright.edu> cse0080@desire.wright.edu writes:
>In article <1991Jun3.095901.3793@desire.wright.edu>, I wrote:
>> Help!
>> 
>> I need to know how to generate spline curves in PostScript when
>> all the data I have on hand is an array of data points that lie on 
>> the curve itself.  (IE: no control points for the curveto operator)
>> 
>> Anyone know a good way to do this?
>> 
> [FORTRAN code (wince,shudder!) for interpolation deleted]

I still suggest that anyone who wants to implement this go look at Farin's
book.  There are a number of well-known schemes for determining tangents and
end conditions, no need to reinvent them!

Also, drop those arctangents and just use the direction from the previous
point to the next point.

Will someone please whack his knuckles for posting FORTRAN here?  %) %)

-- 
Larry Spence
larry@csccat.cs.com
....{uunet,texsun,cs.utexas.edu,decwrl}!csccat!larry