111 lines
2.6 KiB
Nim
111 lines
2.6 KiB
Nim
import std/[math, random]
|
|
|
|
import gtk2 except update
|
|
import gdk2, glib2, cairo
|
|
|
|
const
|
|
Width = 500
|
|
Height = 500
|
|
DrawIters = 72
|
|
Red = (1.0, 0.0, 0.0)
|
|
Green = (0.0, 1.0, 0.0)
|
|
Blue = (0.0, 0.0, 1.0)
|
|
Black = (0.0, 0.0, 0.0)
|
|
White = (1.0, 1.0, 1.0)
|
|
Gold = (1.0, 215 / 255, 0.0)
|
|
Colors = [Blue, Red, Green, White, Gold]
|
|
Angles = [75, 100, 135, 160]
|
|
|
|
type
|
|
|
|
Vec2 = tuple[x, y: float]
|
|
Point = Vec2
|
|
|
|
# Description of the simulation.
|
|
Simulation = object
|
|
area: PDrawingArea
|
|
xmax, ymax: float
|
|
center: Point
|
|
itercount: int
|
|
|
|
|
|
proc initSimulation(area: PDrawingArea; width, height: int): Simulation {.noInit.} =
|
|
## Initialize a simulation object.
|
|
result = Simulation(area: area,
|
|
xmax: float(width - 1),
|
|
ymax: float(height - 1),
|
|
center: (result.xmax * 0.5, result.ymax * 0.5))
|
|
|
|
|
|
func δ(r, θ: float): Vec2 =
|
|
## Return the delta in cartesian coordinates.
|
|
(r * cos(degToRad(θ)), r * sin(degToRad(θ)))
|
|
|
|
|
|
func nextPoint(p: Point; r, θ: float): Point =
|
|
## Return the next point for given polar coordinates.
|
|
let dp = δ(r, θ)
|
|
result = (p.x + dp.x, p.y + dp.y)
|
|
|
|
|
|
template setColor(ctx: ptr Context; color: (float, float, float)) =
|
|
## Set the color for next drawing in given context.
|
|
ctx.setSourceRgb(color[0], color[1], color[2])
|
|
|
|
|
|
proc draw(sim: var Simulation; context: ptr Context) =
|
|
## Draw the spiral.
|
|
|
|
context.setColor(Black)
|
|
context.rectangle(0, 0, sim.xmax, sim.ymax)
|
|
context.fill()
|
|
|
|
let colorIndex = sim.itercount mod Colors.len
|
|
let color = Colors[colorIndex]
|
|
|
|
var p1 = sim.center
|
|
let δθ = Angles[sim.itercount mod Angles.len].toFloat
|
|
var θ = δθ * rand(1.0) * 3
|
|
var r = 5.0
|
|
let δr = 3.0
|
|
|
|
for _ in 1..DrawIters:
|
|
let p2 = p1.nextPoint(r, θ)
|
|
context.moveTo(p1.x, p1.y)
|
|
context.setColor(color)
|
|
context.lineTo(p2.x, p2.y)
|
|
context.setLineWidth(2)
|
|
context.stroke()
|
|
θ += δθ
|
|
r += δr
|
|
p1 = p2
|
|
|
|
|
|
proc update(sim: var Simulation): gboolean =
|
|
## Update the simulation state.
|
|
sim.draw(sim.area.window.cairoCreate())
|
|
inc sim.itercount
|
|
result = gboolean(1)
|
|
|
|
|
|
proc onDestroyEvent(widget: PWidget; data: pointer): gboolean {.cdecl.} =
|
|
## Quit the application.
|
|
mainQuit()
|
|
|
|
|
|
nimInit()
|
|
|
|
let window = windowNew(WINDOW_TOPLEVEL)
|
|
window.setSizeRequest(Width, Height)
|
|
window.setTitle("Polyspiral")
|
|
discard window.signalConnect("destroy", SIGNAL_FUNC(onDestroyEvent), nil)
|
|
|
|
let area = drawingAreaNew()
|
|
window.add area
|
|
|
|
let sim = initSimulation(area, Width, Height)
|
|
|
|
discard timeoutAdd(500, cast[gtk2.TFunction](update), sim.addr)
|
|
|
|
window.showAll()
|
|
main()
|