/*POPRACER MODULE*/ /* Algorithm to give points along a Bezier curve Takes in a list of control points and number of interpolations Returns a list of actual co-ordinates to be plotted */ /* Curve is plotted with straight lines between points. Accuracy is improved by increasing number of interpolations Adding extra control points does not affect accuracy! */ /* To create a closed loop (eg. a circle) ensure the first and last control points in the list are equal. */ /* Mark Rowan - based on lecture notes by Volker Sorge/Iain Styles In turn based on an original document by Ela Claridge */ define factorial(value)->result; if value<=1 then 1->result; else; factorial(round(value)-1)*round(value)->result; /* rounding just in case someone was stupid enough to try finding factorial of a non-integer value */ endif; enddefine; define blending(k,n,u)->result; /* calculates the blending function result for given k and n at step u */ lvars temp; /* using temp vars to break up calculation for simplicity */ factorial(n)/(factorial(k)*factorial(n-k)) -> temp; temp*u**k->temp; temp*((1-u)**(n-k))->result; enddefine; define findBezierPoints(controls,interpolations)->coords; lvars controlpoints,k,u,delta_u; lvars x,y; /* x and y are lists of x and y co-ordinates */ lvars xval, yval; lvars sumx,sumy; if interpolations<2 then interpolations=2; endif; /* min. 2 interps */ if length(controls)<2 then return; endif; /* disallow fewer than 2 controls */ length(controls) -> controlpoints; /* number of control points */ /* controls is a list of control points in the form [ [x1 y1] [x2 y2] ] stages is an integer describing how many interpolations of the curve to perform coords is the output list of co-ordinates to be plotted by a line-drawing procedure. It should be of length interpolations. k counts from 0 to the number of control points u counts from 0 to 1 in steps of delta_u */ 1/(interpolations-1)->delta_u; [% for u from 0 by delta_u to 1 do; 0->sumx; /* reinitialise sumx */ for k from 0 to controlpoints do; if k>0 then /* can't access list(0) */ (controls(k))(1)->xval; (xval*blending(k,controlpoints,u))+sumx->sumx; endif; endfor; round(sumx); /* put result on stack */ endfor; %]->x; [% for u from 0 by delta_u to 1 do; 0->sumy; /* reinitialise sumy */ for k from 0 to controlpoints do; if k>0 then /* can't access list(0) */ (controls(k))(2)->yval; (yval*blending(k,controlpoints,u))+sumy->sumy; endif; endfor; round(sumy); /* put result on stack */ endfor; %]->y; [^x ^y]->coords; enddefine; define get_x_for_given_y(y)->x; /* see below */ enddefine; define get_y_for_given_x(x,coords)->y; /* for n from 1 to length (x_list) if required value of x is at x_list(n) then say 'yippee' and just return corresponding y value do a return; here to avoid confusing the poor dears at the for-loop factory -- no, we really DON'T want any more iterations, and the ones you sent last time were all broken anyway! else say 'oh bugger' and get out the pen and paper if required x is between x_list(n) and x_list(n+1) find corresponding y pair divide difference of x pair by difference of y pair -> gradient divide (distance of the required value from lowest of x_pair) by difference between x pairs -> this is fraction of how far between x pair values the required value is multiply this fraction by difference of y pair values -> THE RESULT! add this result to a LIST cos there may be many more values that match... -- this is only good for checking collisions! when plotting, just pass coords in from the list and draw straight lines between them endif endif endfor; wibble; */ enddefine; /* some nice tests */ ;;;findBezierPoints([ [100 200] [100 200] [300 300] [400 0] ],10)=> /* hill */ ;;;findBezierPoints([[0 0] [0 1000] [1000 1000] [1000 0] [0 0]],20)=> /* circle! */ /* end niceness */ ;;;lvars coords=findBezierPoints([ [0 0] [100 200] [300 300] [400 0] ],10); ;;;get_y_for_given_x(x,coords)=> npr('* Bezier Engine OK.');