home *** CD-ROM | disk | FTP | other *** search
/ Dream 57 / Amiga_Dream_57.iso / Amiga / Programmation / e / Exemples / Bezier.lha / bezier.e next >
Encoding:
Text File  |  1998-04-10  |  10.5 KB  |  350 lines

  1. /****** teaching/bezier *******************************************
  2. *   NAME
  3. *       bezier -- draw moving BΘzier spline.
  4. *
  5. *   SYNOPSIS
  6. *       bezier
  7. *
  8. *   FUNCTION
  9. *       Draws an animated BΘzier spline on the screen.
  10. *       Demonstrates with 4 rebounding points on screen, A B C and D.
  11. *       You should see that the spline runs from A to D, heading to B
  12. *       from point A, and ending facing away from C at point D. If that
  13. *       didn't make sense, just run it :)
  14. *
  15. *       Working example to accompany tutorial in Defy 4 from Cydonia :)
  16. *
  17. *   RESULT
  18. *       Demonstrates the following topics:
  19. *       - ASL ScreenMode requester
  20. *       - Intuition double-buffering
  21. *       - Use of fixed-point math. (fixed.m)
  22. *       - Basic 680x0 instructions
  23. *       - Recursive subdivision
  24. *
  25. *   EXAMPLES
  26. *       As an exercise for the reader, possible extensions include:
  27. *       - Multiple splines - trails, joined shapes, screen blankers
  28. *       - Automated decisions on how many subdivisions to make, and whether
  29. *         to use points or lines.
  30. *       - User influence on movement
  31. *
  32. *   NOTE
  33. *       You should be able to see two levels of abstraction here. The main
  34. *       loop is concerned with the screen buffering, timing and user input,
  35. *       whereas init()/done() and draw() only know of a 'virtual' screen
  36. *       whose boundaries are given in init, and they may only write using
  37. *       stdrast and graphics functions.
  38. *
  39. *       We use 'fixed point' arithmetic - basically, add 16 bits of extra
  40. *       significance, work with it, then take it away when finished
  41. *       calculating.
  42. *
  43. *       We use a very complicated line of code in the double-buffering code:
  44. *       IF sig THEN
  45. *         WHILE m:=GetMsg(port) DO status[1-Long(m+SIZEOF mn)]:=OK_REDRAW
  46. *
  47. *       Basically, on the first run we are allowed to draw into either buffer
  48. *       then 'swap it in'. When we swap it in, we ask the system to send us
  49. *       a message to say it has been swapped in, therefore the other buffer
  50. *       has been swapped out. We know that it will use the message in the
  51. *       DBufInfo structure, and right after this is the UserData1 long.
  52. *       So we can access the long directly after the message we get from
  53. *       GetMsg(), 'Long(m + SIZEOF mn)', and say that the other buffer
  54. *       '1-', is ok to redraw into: 'status[...]:=OK_REDRAW'
  55. *
  56. *   SEE ALSO
  57. *       bezier.txt
  58. *
  59. ****************************************************************************
  60. *
  61. *
  62. */
  63.  
  64. OPT PREPROCESS
  65. OPT OSVERSION=39
  66.  
  67. MODULE 'asl', 'exec/ports', 'graphics/displayinfo', 'graphics/rastport',
  68.        'graphics/view', 'intuition/intuition', 'intuition/screens',
  69.        'libraries/asl', 'utility/tagitem', '*fixed'
  70.  
  71. -> the left/top/right/bottom edges of our screen
  72. DEF lft, top, rgt, bot
  73.  
  74. ->--------------------------------------------------------------------------
  75.  
  76. -> A B C and D are vectors - points on the screen with a direction.
  77. OBJECT vector
  78.    x,  y
  79.    dx, dy
  80. ENDOBJECT
  81.  
  82. DEF a:PTR TO vector,
  83.     b:PTR TO vector,
  84.     c:PTR TO vector,
  85.     d:PTR TO vector
  86.  
  87. PROC rand() OF vector
  88.   -> create new vector with random position/movement (within screen limits)
  89.  
  90.   self.x  := Rnd(rgt-lft) + lft
  91.   self.y  := Rnd(bot-top) + top
  92.   self.dx := (Rnd(10)-5) OR 1  -> between -5 and 5 but not 0
  93.   self.dy := (Rnd(10)-5) OR 1
  94. ENDPROC
  95.  
  96. PROC move() OF vector
  97.   -> move and bounce around the limits of the screen (l/r/t/b)
  98.  
  99.   IF self.x <= lft THEN self.dx :=  (Rnd(5) OR 1)
  100.   IF self.x >= rgt THEN self.dx := -(Rnd(5) OR 1)
  101.   IF self.y <= top THEN self.dy :=  (Rnd(5) OR 1)
  102.   IF self.y >= bot THEN self.dy := -(Rnd(5) OR 1)
  103.  
  104.   self.x:=Bounds(self.x+self.dx, lft, rgt)
  105.   self.y:=Bounds(self.y+self.dy, top, bot)
  106. ENDPROC
  107.  
  108. ->--------------------------------------------------------------------------
  109.  
  110. PROC init(leftedge, topedge, width, height)
  111.   -> This is called once at the start from the main program, with
  112.   -> leftedge/topedge and width/height of the screen we'll be rendering on
  113.  
  114.   -> make a quite (!!) random seed
  115.   DEF bla; CurrentTime({bla},{bla}); Rnd(-Abs(Mul({arg},bla)))
  116.  
  117.   -> we set up the edges of our 'screen' appropriately
  118.   lft := leftedge
  119.   rgt := leftedge + width  - 1
  120.   top := topedge
  121.   bot := topedge  + height - 1
  122.  
  123.   -> create and give random positions and directions to our vectors
  124.   NEW a.rand()
  125.   NEW b.rand()
  126.   NEW c.rand()
  127.   NEW d.rand()
  128. ENDPROC
  129.  
  130. PROC done()
  131.   END a; END b; END c; END d
  132. ENDPROC
  133.  
  134. PROC draw()
  135.   -> called once per frame from the main loop. We should draw whatever to
  136.   -> stdrast.
  137.  
  138.   -> clear screen
  139.   SetAPen(stdrast, 0)
  140.   RectFill(stdrast, lft, top, rgt, bot)
  141.  
  142.   -> move vectors a, b, c and d.
  143.   a.move(); b.move(); c.move(); d.move()
  144.  
  145.   -> draw b to a to d to c in white
  146.   SetAPen(stdrast, 2)
  147.   Move(stdrast, b.x, b.y)
  148.   Draw(stdrast, a.x, a.y)
  149.   Draw(stdrast, d.x, d.y)
  150.   Draw(stdrast, c.x, c.y)
  151.  
  152.   -> draw bΘzier curve in black
  153.   SetAPen(stdrast, 1)
  154.   bezier(
  155.    inttofixed(a.x), inttofixed(a.y),
  156.    inttofixed(b.x), inttofixed(b.y),
  157.    inttofixed(c.x), inttofixed(c.y),
  158.    inttofixed(d.x), inttofixed(d.y)
  159.   )
  160. ENDPROC
  161.  
  162. ->--------------------------------------------------------------------------
  163.  
  164. PROC bezier(p1_x,p1_y, p2_x,p2_y, p3_x,p3_y, p4_x,p4_y, level=0)
  165.   ->  with thanks to Storm/Cydonia
  166.  
  167.   -> All  we  do  is  divide the curve into two seperate curves, and repeat
  168.   -> this  division  until we have many curves in which case we have enough
  169.   -> accuracy to get away with drawing these tiny curves as straight lines.
  170.  
  171.   -> P1  is  the start point of the curve (or curve segment). P4 is the end
  172.   -> point. P2 and P3 are the control points of it. We divide the 'P' curve
  173.   -> into  two  curves,  the  'L'  curve  (L1/L2/L3/L4)  and  the 'R' curve
  174.   -> (R1/R2/R3/R4).  The  L curve goes from the start point to the midpoint
  175.   -> of the P curve. The R curve goes from the midpoint to the end point of
  176.   -> the P curve.
  177.  
  178.   -> we do this 5 times, and end up with 2^5 parts = 32 line segments
  179.  
  180.   -> note that we take in and calculate with 'fixed point' coordinates
  181.   -> and turn them back into normal integers to draw the line segments.
  182.  
  183.   DEF l1_x,l1_y, l2_x,l2_y, l3_x,l3_y, l4_x,l4_y,
  184.       r1_x,r1_y, r2_x,r2_y, r3_x,r3_y, r4_x,r4_y,
  185.       h_x, h_y
  186.  
  187.   IF level>4
  188.     Move(stdrast, fixedtoint(p1_x), fixedtoint(p1_y))
  189.     Draw(stdrast, fixedtoint(p4_x), fixedtoint(p4_y))
  190.     RETURN
  191.   ENDIF
  192.  
  193.   -> register D1 = constant 1 (to speed up the LSR command)
  194.   MOVEQ   #1,D1
  195.  
  196.   -> L1 = P1
  197.   MOVE.L  p1_x,l1_x
  198.   MOVE.L  p1_y,l1_y
  199.  
  200.   -> R4 = P4
  201.   MOVE.L  p4_x,r4_x
  202.   MOVE.L  p4_y,r4_y
  203.  
  204.   -> L2 = average(P1, P2)
  205.   MOVE.L p1_x,D0; ADD.L p2_x,D0; LSR.L D1,D0; MOVE.L D0,l2_x
  206.   MOVE.L p1_y,D0; ADD.L p2_y,D0; LSR.L D1,D0; MOVE.L D0,l2_y
  207.  
  208.   -> R3 = average(P3, P4)
  209.   MOVE.L p3_x,D0; ADD.L p4_x,D0; LSR.L D1,D0; MOVE.L D0,r3_x
  210.   MOVE.L p3_y,D0; ADD.L p4_y,D0; LSR.L D1,D0; MOVE.L D0,r3_y
  211.  
  212.   -> H = average(P2, P3)
  213.   MOVE.L p2_x,D0; ADD.L p3_x,D0; LSR.L D1,D0; MOVE.L D0,h_x
  214.   MOVE.L p2_y,D0; ADD.L p3_y,D0; LSR.L D1,D0; MOVE.L D0,h_y
  215.  
  216.   -> L3 = average(L2, H)
  217.   MOVE.L l2_x,D0; ADD.L h_x,D0; LSR.L D1,D0; MOVE.L D0,l3_x
  218.   MOVE.L l2_y,D0; ADD.L h_y,D0; LSR.L D1,D0; MOVE.L D0,l3_y
  219.  
  220.   -> R2 = average(R3, H)
  221.   MOVE.L r3_x,D0; ADD.L h_x,D0; LSR.L D1,D0; MOVE.L D0,r2_x
  222.   MOVE.L r3_y,D0; ADD.L h_y,D0; LSR.L D1,D0; MOVE.L D0,r2_y
  223.  
  224.   -> L4 = average(L3, R2)
  225.   MOVE.L l3_x,D0; ADD.L r2_x,D0; LSR.L D1,D0; MOVE.L D0,l4_x
  226.   MOVE.L l3_y,D0; ADD.L r2_y,D0; LSR.L D1,D0; MOVE.L D0,l4_y
  227.  
  228.   -> R1 = L4
  229.   MOVE.L  l4_x,r1_x
  230.   MOVE.L  l4_y,r1_y
  231.  
  232.   -> and subdivide again...
  233.   bezier(l1_x,l1_y, l2_x,l2_y, l3_x,l3_y, l4_x,l4_y, level+1)
  234.   bezier(r1_x,r1_y, r2_x,r2_y, r3_x,r3_y, r4_x,r4_y, level+1)
  235. ENDPROC
  236.  
  237. ->--------------------------------------------------------------------------
  238.  
  239. ENUM OK_NONE,OK_REDRAW,OK_SWAPIN
  240.  
  241. PROC main() HANDLE
  242.   -> the main program - handle allocations, buffers, input, etc
  243.  
  244.   DEF s=NIL:PTR TO screen, w=NIL:PTR TO window, m:PTR TO intuimessage,
  245.  
  246.       sb:PTR TO screenbuffer, rp[2]:ARRAY OF rastport,
  247.       status[2]:ARRAY OF LONG, sbuf[2]:ARRAY OF LONG,
  248.       port=NIL:PTR TO mp,
  249.       held_off=FALSE, sigs=0, buf_current=0, buf_nextdraw=1, buf_nextswap=1
  250.  
  251.   sbuf:=[NIL, NIL]
  252.   status:=[OK_REDRAW, OK_REDRAW]
  253.   InitRastPort(rp[0])
  254.   InitRastPort(rp[1])
  255.  
  256.   IF (port:=CreateMsgPort())=NIL THEN Raise()
  257.  
  258.   -> open the screen
  259.   s := OpenScreenTagList(NIL, [
  260.     SA_DEPTH,     2,
  261.     SA_DISPLAYID, asl_modereq(),
  262.     SA_TITLE,     'BΘzier spline demo',
  263.     SA_PENS,      [-1]:INT,
  264.     TAG_DONE
  265.   ])
  266.   IF s=NIL THEN Raise()
  267.  
  268.   sbuf[0]:=AllocScreenBuffer(s, NIL, SB_SCREEN_BITMAP)
  269.   sbuf[1]:=AllocScreenBuffer(s, NIL, SB_COPY_BITMAP)
  270.  
  271.   IF (sbuf[0] AND sbuf[1])=NIL THEN Raise()
  272.  
  273.   sb:=sbuf[0]; sb.dbufinfo.userdata1:=0; rp[0].bitmap:=sb.bitmap
  274.   sb:=sbuf[1]; sb.dbufinfo.userdata1:=1; rp[1].bitmap:=sb.bitmap
  275.  
  276.   -> open a window to recieve input on the screen
  277.   w := OpenWindowTagList(NIL, [
  278.     WA_CUSTOMSCREEN, s,
  279.     WA_FLAGS,        WFLG_BORDERLESS OR WFLG_RMBTRAP,
  280.     WA_IDCMP,        IDCMP_VANILLAKEY OR IDCMP_MOUSEBUTTONS,
  281.     WA_BACKDROP,     TRUE,
  282.     WA_BORDERLESS,   TRUE,
  283.     WA_ACTIVATE,     TRUE,
  284.     TAG_DONE
  285.   ])
  286.   IF w=NIL THEN Raise()
  287.  
  288.   init(0, s.barheight+1, s.width, s.height-s.barheight-1)
  289.  
  290.   REPEAT
  291.     IF sigs THEN WHILE m:=GetMsg(port) DO status[1-Long(m+SIZEOF mn)]:=OK_REDRAW
  292.  
  293.     held_off:=FALSE
  294.  
  295.     IF status[buf_nextdraw]=OK_REDRAW
  296.       stdrast:=rp[buf_nextdraw]
  297.       draw()
  298.       WaitBlit()
  299.       status[buf_nextdraw]:=OK_SWAPIN
  300.       buf_nextdraw:=1-buf_nextdraw
  301.     ENDIF
  302.  
  303.     IF status[buf_nextswap]=OK_SWAPIN
  304.       sb:=sbuf[buf_nextswap]
  305.       sb.dbufinfo.safemessage.replyport:=port
  306.       IF ChangeScreenBuffer(s, sb)
  307.         status[buf_nextswap]:=OK_NONE
  308.         buf_current:=buf_nextswap
  309.         buf_nextswap:=1-buf_nextswap
  310.       ELSE
  311.         held_off:=TRUE
  312.       ENDIF
  313.     ENDIF
  314.  
  315.     IF held_off THEN WaitTOF() ELSE sigs:=Wait(Shl(1,port.sigbit))
  316.   UNTIL CtrlC() OR (IF m:=GetMsg(w.userport) THEN ReplyMsg(m) BUT 1 ELSE 0)
  317.  
  318.   done()
  319.  
  320. EXCEPT DO
  321.   IF w       THEN CloseWindow(w)
  322.   IF sbuf[1] THEN FreeScreenBuffer(s, sbuf[1])
  323.   IF sbuf[0] THEN FreeScreenBuffer(s, sbuf[0])
  324.   IF s       THEN CloseScreen(s)
  325.   IF port    THEN DeleteMsgPort(port)
  326. ENDPROC
  327.  
  328. ->--------------------------------------------------------------------------
  329.  
  330. PROC asl_modereq()
  331.   -> a little moderequester
  332.   -> will return modeid of chosen mode if user successfully
  333.   -> chose a screenmode, otherwise will Raise()
  334.  
  335.   DEF req:PTR TO screenmoderequester, modeid, result=0
  336.  
  337.   IF aslbase := OpenLibrary('asl.library',37)
  338.     IF req := AllocAslRequest(ASL_SCREENMODEREQUEST, NIL)
  339.       result := AslRequest(req, NIL)
  340.       modeid := req.displayid
  341.       FreeAslRequest(req)
  342.     ENDIF
  343.     CloseLibrary(aslbase)
  344.   ENDIF
  345.  
  346.   IF result=0 THEN Raise() -> if user actually chose 'Cancel'
  347.  
  348. ENDPROC modeid
  349.