summaryrefslogtreecommitdiff
path: root/cylindermod.ml
blob: f93ae784beef6c724a5e7eee2cd117e825435eff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

let pi2 = 6.2831853


(* draws the specified cylinder along the z axis.
 * explanation of how this works can be found at the bottom of this file *)
let cylinder ~numl ~numa ~length ~rmod ~rmodd ~xmod ~xmodd ~ymod ~ymodd =
  (* the formula for the normal and the vertex *)
  let point a r dr x dx y dy o =
    let nx = sin a *. r in
    let ny = cos a *. r in
    let nz = (sin a *. dr +. dx) *. -1.0 *. sin a *. r
           -. cos a *. r *. (cos a *. dr +. dy) in
    let len = sqrt (nx*.nx +. ny*.ny +. nz*.nz) in
    GlDraw.normal3 (nx/.len, ny/.len, nz/.len);
    GlDraw.vertex3 (
      sin a *. r +. x,
      cos a *. r +. y,
      o
    )
  in

  (* cache previous values *)
  let pl  = ref        0.0  and
      pr  = ref (rmod  0.0) and
      prd = ref (rmodd 0.0) and
      px  = ref (xmod  0.0) and
      pxd = ref (xmodd 0.0) and
      py  = ref (ymod  0.0) and
      pyd = ref (ymodd 0.0) in

  (* generate stuff *)
  GlDraw.begins `quads;
  for li = 1 to numl do
    let l = (float li) /. (float numl) *. length in
    let cr = rmod l and crd = rmodd l in
    let cx = xmod l and cxd = xmodd l in
    let cy = ymod l and cyd = ymodd l in
    let p = ref 0.0 in
    for ai = 1 to numa do
      let a = pi2 *. (float (ai mod numa)) /. (float numa) in
      point  a  cr  crd  cx  cxd  cy  cyd   l;
      point  a !pr !prd !px !pxd !py !pyd !pl;
      point !p !pr !prd !px !pxd !py !pyd !pl;
      point !p  cr  crd  cx  cxd  cy  cyd   l;
      p := a;
    done;
    pl := l;
    pr := cr; prd := crd;
    px := cx; pxd := cxd;
    py := cy; pyd := cyd;
  done;
  GlDraw.ends ()



let draw t =
  Gl.enable `lighting;
  GlMat.push ();
  GlMat.translate3 (-4.5, 0.0, -4.0);
  GlMat.rotate ~angle:90.0 ~y:1.0 ();

  GlDraw.color (0.2, 0.2, 0.6);
  GlLight.material ~face:`both (`specular (0.1, 0.1, 0.1, 1.0));

  cylinder ~numl:45 ~numa:20 ~length:9.0
    ~rmod: (fun l -> 0.75 +. 0.5*.sin (l *. pi2 *. 0.2 +. t))
    ~rmodd:(fun l -> 0.5 *. cos (l *. pi2 *. 0.2 +. t) *. pi2 *. 0.2)
    ~xmod: (fun l -> 0.0)
    ~xmodd:(fun l -> 0.0)
    ~ymod: (fun l -> 0.5 *. sin (l *. pi2 *. 0.15 +. t))
    ~ymodd:(fun l -> 0.5 *. cos (l *. pi2 *. 0.15 +. t) *. pi2 *. 0.15);

  GlMat.pop ()


(*
  a = angle of the circle
  l = length of the cylinder

  rmod(l) = function returning the radius of the cylinder at the given length
  xmod(l)
  ymod(l) = functions returning the x and y positions of the center of the circle
            at a given length in the cylinder

  Every point on the cylinder can be described with the following formula:
  V(a,l) = [
    x = sin(a) * rmod(l) + xmod(l)
    y = cos(a) * rmod(l) + ymod(l)
          |        |          \--  translating the circle
          |        \-- scaling the circle
          \-- formula for a normal circle
    z = l
  ]

  (z could also be modulated to create more complex models, but let's keep
  things simple for now)

  This is an "Analytic Surface", as explained in http://glprogramming.com/red/appendixe.html
  So the normal can be calculated with:
  dV/dl -> l = [
    x = sin(a) * rmod'(l) + xmod'(l)
    y = cos(a) * rmod'(l) + ymod'(l)
    z = 1
  ]
  dV/da -> a = [
    x = cos(a) * rmod(l),
    y = -sin(a) * rmod(l),
    z = 0
  ]
  normal = [
    x = l.y * a.z - a.y * l.z = sin(a) * rmod(l)
    y = a.x * l.z - l.x * a.z = cos(a) * rmod(l)
    z = l.x * a.y - a.x * l.y
      = (sin(a) * rmod'(l) + xmod'(l)) * -sin(a) * rmod(l)
      - cos(a) * rmod(l) * (cos(a) * rmod'(l) + ymod'(l))
  ]
*)