summaryrefslogtreecommitdiff
path: root/ipl/gprogs/sier.icn
blob: a0b48cf39b3d0b68052e9aac2f603e460faf2065 (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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
############################################################################
#
#	File:     sier.icn
#
#	Subject:  Program for generalized Sierpinski's triangle
#
#	Author:   Gregg M. Townsend
#
#	Date:     June 10, 2004
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#     Originally inspired by the Nova television show on chaos.
#     Colorization suggested by Kenneth Walker.
#
############################################################################
#
#     This program constructs Sierpinski's triangle using an iterative
#  method.  An initial point is chosen (by clicking the mouse inside the
#  triangle) and marked.  Then, the program repeatedly moves half way to
#  a randomly chosen vertex and plots a point in the color corresponding
#  to the vertex.
#
#     The polygon need not be a triangle.  The number of sides may be given
#  as a command line argument, or a digit 3 through 9 or 0 through 2 may be
#  pressed to establish a new polygon of 3 to 12 sides.
#
#     The S, G, E, and Q keys function identically to the Stop, Go, Erase,
#  Quit pushbuttons.
#
############################################################################
#
#  Requires:  Version 9 graphics
#
############################################################################
#
#  Links: button, evmux, random, graphics
#
############################################################################

link button
link evmux
link random
link graphics

$define BevelWidth 2
$define WindowMargin 10

global win, bwin, vwin, vcolors
global m, w, h
global nsides, xpos, ypos, outline
global running, xcur, ycur

procedure main(args)
   local i, vcolors

   win := Window("size=400,400", "font=Helvetica,bold,14", "bg=pale gray", args)
   nsides := integer(args[1]) | 3
   if nsides < 3 then stop("sierpinski: need at least 3 sides!")

   m := WindowMargin
   h := WAttrib("height") - 2 * m		# usable height
   w := WAttrib("width") - 2 * m		# usable width

   # make a window (g.c.) for drawing in background color
   bwin := Clone(win)
   Fg(bwin, Bg(win))

   # make a color for each vertex
   vcolors := [ 
      "deep green",
      "dark red",
      "dark blue",
      "deep red-magenta",
      "dark cyanish blue",
      "dark red-orange",
      "deep purple",
      "deep cyan",
      "deep brown",
      "deep orangish red",
      "deep purple",
      "dark cyanish blue"
      ]
   vwin := []
   if WAttrib(win, "depth") > 2 then
      every put(vwin, Clone(win, "fg=" || !vcolors))
   else
      put(vwin, win)

   # configure and draw the polygon
   configure()
   erase()

   # set up buttons and character handlers
   button(win, "Go", setfill, 0, m, m, 50, 20)
   button(win, "Stop", setfill, -1, m, m + 30, 50, 20)
   button(win, "Erase", argless, erase, m + w - 50, m, 50, 20)
   button(win, "Quit", argless, exit, m + w - 50, m + 30, 50, 20)
   sensor(win, 'Gg', setfill, 0)
   sensor(win, 'Ss', setfill, -1)
   sensor(win, 'Ee', argless, erase)
   quitsensor(win)				# enable Q-for-quit etc.
   sensor(win, '3456789012', setsides)

   # set up sensor for drawing the curve
   sensor(win, &lrelease, setfill, 1, m, m, w, h)

   # process events
   randomize()
   i := 1
   repeat {
      while *Pending(win) > 0 | running < 0 do
         evhandle(win)
      every 1 to 100 do {
         DrawPoint(vwin [i | 1], xcur, ycur)
         i := ?nsides
         xcur := (xcur + xpos[i]) / 2
         ycur := (ycur + ypos[i]) / 2
         }
      }
end



#  configure() -- set vertex points

procedure configure()
   local a, da, i
   local xmin, xmax, xscale, ymin, ymax, yscale

   # ensure we have enough windows for the vertices
   while *vwin < nsides do
      vwin |||:= vwin

   # get coordinates for vertices as points on a radius-1 circle
   da := 2 * &pi / nsides
   a := 1.5 * &pi - da / 2
   if nsides = 4 then
      a +:= &pi / 12
   xpos := list(nsides)
   ypos := list(nsides)
   every i := 1 to nsides do {
      xpos[i] := cos(a)
      ypos[i] := sin(a)
      a -:= da
      }

   # now scale to available window size
   # also make coord list for drawing outline
   xmin := xmax := ymin := ymax := 0.0
   every xmin >:= !xpos
   every xmax <:= !xpos
   every ymin >:= !ypos
   every ymax <:= !ypos
   xscale := w / (xmax - xmin)
   yscale := h / (ymax - ymin)
   outline := [win]
   every i := 1 to nsides do {
      put(outline, m + xscale * (1.01 * xpos[i] - xmin))
      put(outline, m + h - yscale * (1.01 * ypos[i] - ymin))
      xpos[i] := m + xscale * (xpos[i] - xmin)
      ypos[i] := m + h - yscale * (ypos[i] - ymin)
      }
   put(outline, outline[2])
   put(outline, outline[3])
end



#  erase(gc) -- erase the polygon and draw its outline

procedure erase(gc)
   outline[1] := bwin
   FillPolygon ! outline
   outline[1] := \gc | win
   DrawLine ! outline
   running := -1
   xcur := m + w / 2
   ycur := m + h / 2
   return
end



#  setfill(win, n, x, y) -- start/stop filling points according to n
#
#  n<0  stop
#  n=0  start, from current point
#  n>0  start, from (x,y)

procedure setfill(win, n, x, y)
   if n > 0 then {
      xcur := x
      ycur := y
      }
   if n >= 0 then {
      outline[1] := bwin
      DrawLine ! outline		# erase outline
      }
   running := n
   return
end



#  setsides(win, dummy, x, y, event) - reset the number of sides

procedure setsides(win, dummy, x, y, event)
   nsides := integer(event)
   if nsides < 3 then nsides +:= 10
   erase(bwin)
   configure()
   erase()
end