;Coded by *Kronos* the Wizard       4 - Mar - 1996

;Instructions:
;Left mouse button & drag = rotate
;Right mouse button & drag = scale

;caveat: the first line of the window remains dirty because of a bug in the
;WCls instruction (not mine; ask Acid Software).

#AXIS=0               ;1=On (if you wanna view the axis)
#WIDTH=320            ;width of the screen
#HEIGHT=256           ;height of the screen
#NUMPTS=8             ;number of points of the poligon
#NUMEDGES=12          ;number of edges of the poligon

NEWTYPE.pt    ;a point (wow!) is obviously made of 3 coords (x,y,z)
  x.f
  y.f
  z.f
End NEWTYPE

NEWTYPE.ed    ;an edge is (start_vertex, end_vertex)
  stver.w
  enver.w
End NEWTYPE

Dim vertex.pt(#NUMPTS)        ;here go points
Dim edge.ed(#NUMEDGES)        ;this is filled with edges

Restore ptdat                 ;initialize arrays
For k.w=0 To #NUMPTS-1
  Read vertex(k)\x
  Read vertex(k)\y
  Read vertex(k)\z
Next k

Restore edgedat
For k=0 To #NUMEDGES-1
  Read edge(k)\stver
  Read edge(k)\enver
Next k

;This Statement gets three parameters: the two angles a and b and the vertex
;number. It rotates the selected vertex around axis Z and X with angles a and
;b respectively.

Statement RotZX{a.f,b.f,n.w}
SHARED vertex.pt()

  x.f=vertex(n)\x
  y.f=vertex(n)\y
  z.f=vertex(n)\z
  vertex(n)\x=x*Cos(a)+y*Sin(a)*Cos(b)+z*Sin(a)*Sin(b)
  vertex(n)\y=-x*Sin(a)+y*Cos(a)*Cos(b)+z*Cos(a)*Sin(b)
  vertex(n)\z=-y*Sin(b)+z*Cos(b)
End Statement

;The following routine has two points (x,y,z) as parameters: it projects
;them on the 2-dimensional screen and draws the line connecting them.
;Because Blitz2 doesn't allow more than 6 parameters per Statement I had
;to Share the line pattern linpat.w. The dashed lines are obtained by
;assigning the 16-bit value $F0F0 to the LinePtrn field of the RastPort
;and by using the graphics.library routines Move and Draw instead of the
;built-in Blitz2 Line instruction.
;I use the variables cospi6 and sinpi6 loaded respectively with the cosine
;and the sine of 30 deg (instead of calculating them every time the Statement
;is called) to speed up computing.

Statement ProjLine{x1.f,y1.f,z1.f,x2.f,y2.f,z2.f}
SHARED vertex(),edge(),cospi6.f,sinpi6.f,*rp.RastPort,linpat.w

  stpx.f=y1*cospi6-x1*cospi6+#WIDTH/2
  stpy.f=y1*sinpi6+x1*sinpi6-z1+#HEIGHT/2
  enpx.f=y2*cospi6-x2*cospi6+#WIDTH/2
  enpy.f=y2*sinpi6+x2*sinpi6-z2+#HEIGHT/2

  *rp\LinePtrn=linpat
  *rp\linpatcnt=15
  *rp\Flags=*rp\Flags|#FRST_DOT
  Move_ *rp,stpx,stpy
  Draw_ *rp,enpx,enpy
End Statement

;This Statement cycles through edges and draws the poligon.

Statement DrawAll{}
SHARED vertex(),edge.ed(),cospi6.f,sinpi6.f,linpat.w,*rp

;This is a trick to recognize the hidden vertex (only if you have one hidden
;vertex at a time): it's the one with the most negative sum of its three
;coords.

  totmin.f=0
  For k.w=0 To #NUMPTS-1
    If totmin>vertex(k)\x+vertex(k)\y+vertex(k)\z
      totmin=vertex(k)\x+vertex(k)\y+vertex(k)\z
      totver.w=k
    EndIf
  Next k
  WCls
  cospi6.f=Cos(Pi/6)
  sinpi6.f=Sin(Pi/6)

CNIF #AXIS=1
  linpat.w=$FFFF
  SetAPen_ *rp,2
  ProjLine{0,0,0,80,0,0}    ;X axis
  SetAPen_ *rp,3
  ProjLine{0,0,0,0,80,0}    ;Y axis
  SetAPen_ *rp,4
  ProjLine{0,0,0,0,0,80}    ;Z axis
CEND

  SetAPen_ *rp,1
  For k=0 To #NUMEDGES-1
    linpat.w=$FFFF

;If the current edge departs from the hidden vertex then the line is dashed

    If edge(k)\stver=totver OR edge(k)\enver=totver Then linpat=$F0F0
    vx1.f=vertex(edge(k)\stver)\x
    vy1.f=vertex(edge(k)\stver)\y
    vz1.f=vertex(edge(k)\stver)\z
    vx2.f=vertex(edge(k)\enver)\x
    vy2.f=vertex(edge(k)\enver)\y
    vz2.f=vertex(edge(k)\enver)\z
    ProjLine{vx1,vy1,vz1,vx2,vy2,vz2}   ;project and draw
  Next k
End Statement


;Begin
flags.l=#WFLG_CLOSEGADGET|#WFLG_ACTIVATE|#WFLG_SMART_REFRESH|#WFLG_RMBTRAP|#WFLG_GIMMEZEROZERO
Screen 0,0,0,#WIDTH,#HEIGHT,3,0,"3DRot",0,1
Window 0,0,0,#WIDTH,#HEIGHT,flags,"3DRotation by *Kronos* the Wizard",0,1
*rp.RastPort=RastPort(0)

restart:
ev.l=WaitEvent
If ev=$200 OR EventWindow<>0 Then Goto bye
omx.w=WMouseX
omy.w=WMouseY

;This is the main loop.

Repeat
  nmx.w=WMouseX
  nmy.w=WMouseY
  If nmx<>omx OR nmy<>omy      ;if mouse position has changed...
    Select Joyb(0)

      Case 1    ;if left mouse button

;To make the rotation relative to the mouse starting point (when the user
;pressed the left button) and not to its absolute position on the screen,
;I subtract the starting position omx from the new one nmx and then I
;multiply the resulting quantity with the fraction of 360 deg represented
;by one pixel.

        anglez.f=(nmx-omx)*((2*Pi)/#WIDTH)     ;rot around third coord (z)
        anglex.f=(nmy-omy)*((2*Pi)/#HEIGHT)    ;rot around first coord (x)
        For k.w=0 To #NUMPTS-1
          RotZX{anglez,anglex,k}    ;rotates all vertices
        Next k
        DrawAll{}   ;and then draw the entire poligon

      Case 2    ;if right mouse button

;The scaling factor scalefac depends on the mouse distance from the origin:
;this is why I compute the radius (squared) r2 of a circle centered in the
;origin of the axis (the center of the screen). Here also, as I did before
;for the rotation part, I use the mouse position relative to the starting
;point.

        r2.w=((nmx-#WIDTH/2)^2+(nmy-#HEIGHT/2)^2)-((omx-#WIDTH/2)^2+(omy-#HEIGHT/2)^2)
        If r2>0
          scalefac.f=1.1
        Else
          scalefac.f=0.9
        EndIf
        For k.w=0 To #NUMPTS-1                ;scale all vertices
          vertex(k)\x=vertex(k)\x*scalefac
          vertex(k)\y=vertex(k)\y*scalefac
          vertex(k)\z=vertex(k)\z*scalefac
        Next k
        DrawAll{}    ;and draw the entire poligon
    End Select
    omx=nmx
    omy=nmy
  EndIf
Until Joyb(0)=0   ;until user releases the mouse button
Goto restart

bye:
Free Window 0
Free Screen 0
End

ptdat:                      ;vertex coords (x, y, z)
Data.f -100,-100,-100
Data.f -100,100,-100
Data.f 100,100,-100
Data.f 100,-100,-100
Data.f -100,-100,100
Data.f -100,100,100
Data.f 100,100,100
Data.f 100,-100,100

edgedat:                    ;edge=(start_vertex, end_vertex)

Data.w 0,1,1,2,2,3,3,0,4,5,5,6,6,7,7,4,0,4,1,5,2,6,3,7