jk4i+@andrew.cmu.edu (John McCall Kingsley, III) (12/04/89)
Hi, I am drawing a directed graph to the screen, and would like to have arrowheads on the ends of my links. Does anyone know a way of doing this so that as the angle of the line changes, the arrow head changes with it. I have played around with several different ideas, but have not been succesful in making any of them work properly. Thanks for your help. Jack Kingsley
ba0k+@andrew.cmu.edu (Brian Patrick Arnold) (12/07/89)
Hello there, I've spent some time developing influence diagrams to represent variable dependencies of math eqns in HyperCard. The key to drawing arrow heads at any angle is to use the regular polygon tool with polySides set to 3. Redrawing links presents a bit of a speed problem. We have a solution for redrawing links when nodes are moved, but we are not satisfied with the performance and plan to write a MacApp-based compiled version. Here is an example of how we draw a directed link between two buttons. You need to have the following paint globals set: set pattern to 12 -- color arrow head black set filled to true -- for a solid arrow head set polysides to 3 -- arrow heads have 3 sides You should set these globals in a handler that saves the old values so that a complementary handler can restore the old values when through - you also need to "choose browse tool" when through. You should write SetDrawMode and RestoreDrawMode handlers as your needs require. -------------------------------------------- ON DoDrawLink fromLinkName, toLinkName -- example for two buttons named fromLinkName and toLinkName SetDrawMode DrawALink the loc of cd btn fromLinkName, <option-return> the loc of cd btn toLinkName, <option-return> the width of cd btn toLinkName && the height of cd btn toLinkName RestoreDrawMode END DoDrawLink ON DrawALink fromLoc,toLoc,toSize -- -- Draw a line from fromLoc to an arrow head flush with the rectangle -- toSize located at toLoc. Side effect: leaves you with line tool. -- -- fromLoc starting point -- toLoc end point -- toSize "width && height" of a rectangle located at toLoc set cursor to busy -- feedback when redrawing -- compute angle of the line to be drawn put (item 1 of toLoc - item 1 of fromLoc) into delx put (item 2 of toLoc - item 2 of fromLoc) into dely put atan(dely/delx) into myAngle -- fudge a little ??? am I computing something incorrectly? IF delx < 0 THEN add pi to myAngle -- scale endpoint (toLoc) to account for toSize rectangle overlap IF abs(dely/delx) < ((word 2 of toSize / word 1 of toSize)) THEN put (1-(abs(delx)-(word 1 of toSize)/2)/abs(delx)) into factor ELSE put (1-(abs(dely)-(word 2 of toSize)/2)/abs(dely)) into factor subtract round(delx*factor) from item 1 of toLoc subtract round(dely*factor) from item 2 of toLoc -- generate inset point for arrow head, "5" is arbitrary put toLoc into arrowLoc add round(5*cos(myAngle+pi)) to item 1 of arrowLoc add round(5*sin(myAngle+pi)) to item 2 of arrowLoc -- do the drawing choose regular polygon tool drag from arrowLoc to toLoc -- should draw a triangle (arrowhead) choose line tool drag from fromLoc to arrowLoc -- should draw a line END DrawALink ------------------------------------------------ We have tried to replace the trig (above) with more straightforward calculations to speed up this algorithm, but this actually slowed things down a bit. The first "choose regular polygon tool" call takes a whole 1/2 second, but subsequent choose paint tool calls take almost no time. When undrawing or erasing links, we use the same algorithm with pattern set to 1 (white). Then we use the select tool and select the arrow space (from fromLoc to toLoc), and DoMenu "Transparent" to turn white pixels into transparent pixels. This may have unfavorable side effects. If this is of any help, please let me know. I also want to hear more about anyone's work on directed graphs in HyperCard. Brian Arnold Research Programmer Department of Engineering and Public Policy Carnegie Mellon University
george@cs.qmc.ac.uk (George Coulouris) (12/07/89)
Funny you should ask that! I happen to have the answer. Actually, I did a MacApp example a few months ago that drew directed graphs, so I dug out the Pascal code, changed the :='s to 'put' and it ran (well almost, of course the Quickdraw procedures "MoveTo" and "Line" had to be defined in HyperTalk). Put the following script into a button to demonstrate it. After pressing the button, click anywhere on the card and an arrow will be drawn from 100,100 to the point you clicked at. on mouseUp set the hilite of me to true wait until the mouse is down arrow 100,100,the MouseH, the MouseV, 10, 1 set the hilite of me to false end mouseUp on arrow x1, y1, x2, y2, k, d -- k is the length of the arms of the arrow -- d is the fractional distance along the line at which arrow is placed put the tool into saveTool choose line tool MoveTo x1,y1 Line x2-x1,y2-y1 if x1 < x2 then put -1 into sign else put 1 into sign put atan((y1-y2)/(x1-x2)) into alpha -- the angle of the line put alpha+3.14/4 into beta -- plus 45 degrees put alpha-3.14/4 into gamma -- minus 45 degrees MoveTo trunc(x1+(x2-x1)*d), trunc(y1+(y2-y1)*d) line sign*trunc(k*cos(beta)), sign*trunc(k*sin(beta)) MoveTo trunc(x1+(x2-x1)*d), trunc(y1+(y2-y1)*d) line sign*trunc(k*cos(gamma)), sign*trunc(k*sin(gamma)) choose saveTool end arrow on MoveTo x, y global curpos put x&","&y into curpos end MoveTo on line x, y global curpos drag from curpos to (item 1 of curpos + x),(item 2 of curpos + y) end line -- | Computer Science Dept ARPA/Internet: george@cs.qmc.ac.uk | Queen Mary and Westfield College JANET: george@uk.ac.qmc.cs | Mile End Road | London E1 4NS England Office phone: +44 1 975 5201 (direct line) Home phone: +44 1 485 5896
pepke@loligo (Eric Pepke) (12/08/89)
Why is it that everybody wants to use angles and trigonometric functions to solve this problem and then acts surprised when it doesn't run very fast? Here is an easy solution based on elementary vector arithmetic that uses one square root, two divides (which could easily be converted into one divide and two multiplies), a few multiplies, and NO trigonometric functions. It's written in FORTRAN, and you have to convert it, but that's trivial. FRSTPT is roughly equivalent to MoveTo, VECTOR is roughly equivalent to LineTo, and SQRT is the FORTRAN square root function. SUBROUTINE ARROW(X1, Y1, X2, Y2, WIDTH, LENGTH) REAL X1, Y1, X2, Y2, WIDTH, LENGTH C C Eric Pepke, May 9, 1988 C C Draw an arrow from (X1, Y1) to (X2, Y2) C LENGTH is length of arrow head. WIDTH is width of arrow head. C Both LENGTH and WIDTH are absolute. This can easily be modified. C The arrow head is open. This, too, can be easily modified. C C X and Y components for normal vectors longitudinal and transverse C to the arrow, plus length of arrow REAL XL, YL, XT, YT, L C X and Y offsets for arrowhead based on longitudinal and C transverse contributions REAL DXL, DXT, DYL, DYT C C Draw shaft of arrow CALL FRSTPT(X1, Y1) CALL VECTOR(X2, Y2) C Calculate longitudinal and transverse vectors XL = X2 - X1 YL = Y2 - Y1 L = SQRT(XL * XL + YL * YL) IF (L .GT. 0.0) THEN C Draw arrow head C First make longitudinal and transverse vectors XL = X2 - X1 YL = Y2 - Y1 C Omit the next two lines to make relative size arrow head XL = XL / L YL = YL / L C XT = -YL YT = XL C Calculate the X and Y offsets DXL = LENGTH * XL DXT = WIDTH * XT DYL = LENGTH * YL DYT = WIDTH * YT C Draw one side of the arrow CALL VECTOR(X2 - DXL + DXT, Y2 - DYL + DYT) C Draw the other side C Change the next line to a VECTOR for a closed arrowhead CALL FRSTPT(X2 - DXL - DXT, Y2 - DYL - DYT) CALL VECTOR(X2, Y2) ENDIF END Eric Pepke INTERNET: pepke@gw.scri.fsu.edu Supercomputer Computations Research Institute MFENET: pepke@fsu Florida State University SPAN: scri::pepke Tallahassee, FL 32306-4052 BITNET: pepke@fsu Disclaimer: My employers seldom even LISTEN to my opinions. Meta-disclaimer: Any society that needs disclaimers has too many lawyers.
ba0k+@andrew.cmu.edu (Brian Patrick Arnold) (12/09/89)
Hello there, no offense to Mr. Pepke, but if you re-read my last post: >We have tried to replace the trig [see my last post] with more >straightforward calculations to speed up this algorithm, but this >actually slowed things down a bit. Avoiding trig: ---------------------------------- ON DrawALink fromLoc,toLoc,toSize --| Draw an arrow between Locs, with arrow head adjusted to touch --| an edge of the rectangle of toSize at toLoc. --| Assumes SetDrawMode has been called to set line width --| and polysides=3. --| Modified by Max to speed up and avoid trig calcs. June 18/89 put (item 1 of toLoc - item 1 of fromLoc) into delx put (item 2 of toLoc - item 2 of fromLoc) into dely -- Scale endpoint (toLoc) to bring arrow to edge of button. -- w,h is the signed vector from the center of the button -- to the corner of the button nearest to fromLoc. put Round(((word 1 of toSize)/2)*delX/Abs(delX)) into w put Round(((word 2 of toSize)/2)*delY/Abs(delY)) into h IF abs(dely/delx) < abs(h/w) THEN -- Arrow to left or right of button rectangle subtract w from item 1 of toLoc subtract Round(Abs(w)*dely/Abs(delx)) from item 2 of toLoc ELSE -- Arrow to top or bottom of button rectangle subtract h from item 2 of toLoc subtract Round(Abs(h)*delx/Abs(dely)) from item 1 of toLoc END IF -- generate center point for arrow head (an equilateral triangle) put 5 into headR -- Radius of arrow head put toLoc into arrowLoc put Sqrt(delX*delX + delY*delY) into hypotenuse subtract Round(headR*delX/hypotenuse) from item 1 of arrowLoc subtract Round(headR*delY/hypotenuse) from item 2 of arrowLoc -- do the drawing choose regular polygon tool drag from arrowLoc to toLoc -- draw a triangle choose line tool drag from fromLoc to arrowLoc END DrawALink ---------------------------------- What's different about this code compared to the Fortran example is that we scale the endpoint to be flush with a rectangle at the destination. Also, using the polygon tool saves you from drawing a couple of extra lines. We found this version to be a bit slower than our trig version. I attribute this to interpreted HyperTalk execution overhead. If you remain unconvinced, you can do your own timing tests. I recommend the trig version for arrow drawing as presented in my last post, or writing an XCMD to do the math. - Brian
pepke@loligo (Eric Pepke) (12/09/89)
In article <sZTyxfu00Uh_M1fXtU@andrew.cmu.edu> ba0k+@andrew.cmu.edu (Brian Patrick Arnold) writes: > >Hello there, > > no offense to Mr. Pepke, but if you re-read my last post: > >>We have tried to replace the trig [see my last post] with more >>straightforward calculations to speed up this algorithm, but this >>actually slowed things down a bit. None taken! I apologize for not reading your posting all the way through the first time. > [example code] >we scale the endpoint to be flush with a rectangle at the destination. >Also, using the polygon tool saves you from drawing a couple of extra >lines. > >We found this version to be a bit slower than our trig version. I >attribute this to interpreted HyperTalk execution overhead. If you >remain unconvinced, you can do your own timing tests. I recommend the >trig version for arrow drawing as presented in my last post, or writing >an XCMD to do the math. Well, I just converted my basic non-trig algorithm to HyperTalk and found it to be slightly faster than your trig algorithm (about 7%). To keep from comparing apples and oranges, I used your clipping code verbatim and also your polygon trick. (I usually use lines, because that's what most of our researchers seem to prefer.) I did the test on a Mac II with an Apple color board set to two colors, System 6.0.4, Multifinder with nothing else running except After Dark in the background. Here is the converted algorithm: ON DrawALink fromLoc,toLoc,toSize -- -- Draw a line from fromLoc to an arrow head flush with the rectangle -- toSize located at toLoc. Side effect: leaves you with polygon tool. -- -- fromLoc starting point -- toLoc end point -- toSize "width && height" of a rectangle located at toLoc set cursor to busy -- feedback when redrawing -- Calculate longitudinal unit vector put item 1 of toLoc - item 1 of fromLoc into XL put item 2 of toLoc - item 2 of fromLoc into YL -- scale endpoint (toLoc) to account for toSize rectangle overlap IF abs(YL/XL) < ((word 2 of toSize / word 1 of toSize)) THEN put (1-(abs(XL)-(word 1 of toSize)/2)/abs(XL)) into factor ELSE put (1-(abs(YL)-(word 2 of toSize)/2)/abs(YL)) into factor subtract round(XL*factor) from item 1 of toLoc subtract round(YL*factor) from item 2 of toLoc --Normalize longitudinal vector put SQRT(XL * XL + YL * YL) into L divide XL by L divide YL by L --Draw arrow choose line tool drag from fromloc to toLoc choose regular polygon tool drag from round(item 1 of toLoc - 5 * XL), <option-return> round (item 2 of toLoc - 5 * YL) to toLoc END DrawALink Eric Pepke INTERNET: pepke@gw.scri.fsu.edu Supercomputer Computations Research Institute MFENET: pepke@fsu Florida State University SPAN: scri::pepke Tallahassee, FL 32306-4052 BITNET: pepke@fsu Disclaimer: My employers seldom even LISTEN to my opinions. Meta-disclaimer: Any society that needs disclaimers has too many lawyers.
a_dent@vaxa.uwa.oz (12/14/89)
66666666666In article <QZSVM7u00WB60VEUVB@andrew.cmu.edu>, jk4i+@andrew.cmu.edu (John McCall Kingsley, III) writes: > Hi, > > I am drawing a directed graph to the screen, and would like to have > arrowheads on the ends of my links. Does anyone know a way of doing this > so that as the angle of the line changes, the arrow head changes with it. > > I have played around with several different ideas, but have not been succesful > in making any of them work properly. > > Thanks for your help. > The guys who wrote MacSurf have a library of such items in Pascal (I think). I can chase them up if you like. The problem is NOT trivial! But I do know that they sell their libraries so you may be able to get something. Andy Dent, Southern Hemisphere Mac Consultant and Programmer