diff --git a/main.go b/main.go index 1d27a44..ef5d93e 100644 --- a/main.go +++ b/main.go @@ -1,11 +1,14 @@ package main import ( + "fmt" "log" + "math" "os" + "time" + "github.com/gen2brain/raylib-go/raylib" "github.com/ojrac/opensimplex-go" - "math" ) func clamp01(v float64) float64 { @@ -24,31 +27,6 @@ func GrayCurve(v, k float64) rl.Color { return rl.Color{R: c, G: c, B: c, A: 255} } -func sierpinskiArrow(order int, length float64) { - rl.Scalef(1, -1, 1) - //rl.Rotatef(180, 0, 0, 1) - if order == 0 { - curve(order, length, 60) - } else { - rl.Rotatef(60, 0, 0, 1) - curve(order, length, -60) - } -} - -func curve(order int, length float64, angle float64) { - if order == 0 { - len := int32(length) - rl.DrawLine(0, 0, len, 0, rl.Black) - rl.Translatef(float32(len), 0, 0) - } else { - curve(order-1, length/2, -angle) - rl.Rotatef(float32(angle), 0, 0, 1) - curve(order-1, length/2, angle) - rl.Rotatef(float32(angle), 0, 0, 1) - curve(order-1, length/2, -angle) - } -} - func main() { const ( @@ -69,6 +47,10 @@ func main() { rl.InitWindow(screenWidth, screenHeight, "sumi sierpinski arrow") + sketches := []Sketch { + SierpinskiArrow{}, + } + var camera = rl.Camera2D{ Target: rl.Vector2{X: 0, Y: 0}, Offset: rl.Vector2{X: float32(screenWidth) / 2, Y: float32(screenHeight) / 2}, @@ -78,6 +60,124 @@ func main() { rl.SetTargetFPS(60) + t0 := time.Now() + + ports := MakePorts() + ports["sierpinskiArrowLength"] = Sine { + Amp: 1000.0, + Freq: 0.001, + } + + ports["sierpinskiArrowDepth"] = Saw { + Min: 1, Max: 9, Period: 10, + } + + for !rl.WindowShouldClose() { + updateCamera(&camera) + + // begin drawing + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) + rl.BeginMode2D(camera) + rl.PushMatrix() + + t := time.Since(t0).Seconds() + + // set up RenderCtx + renderCtx := &RenderCtx { + Width: screenWidth, + Height: screenHeight, + Time: t, + Ports: ports.Eval(t), + } + + /** + MAIN DRAWING + **/ + for _, s := range sketches { + s.Draw(renderCtx) + } + + if rl.IsKeyDown(rl.KeySpace) { + if _, err := storage.Save(); err != nil { + log.Printf("Error saving snapshot: %v\n", err) + } + } + + rl.PopMatrix() + rl.EndMode2D() + + // HUD + rl.DrawText("Mouse right button drag to move, mouse wheel to zoom", 10, 10, 20, rl.White) + rl.EndDrawing() + } + + rl.CloseWindow() +} + +func updateCamera(camera *rl.Camera2D) { + if rl.IsMouseButtonDown(rl.MouseRightButton) { + delta := rl.GetMouseDelta() + delta = rl.Vector2Scale(delta, -1.0/camera.Zoom) + camera.Target = rl.Vector2Add(camera.Target, delta) + } + // Zoom based on mouse wheel + wheel := rl.GetMouseWheelMove() + if wheel != 0 { + // Get the world point that is under the mouse + mouseWorldPos := rl.GetScreenToWorld2D(rl.GetMousePosition(), *camera) + + // Set the offset to where the mouse is + camera.Offset = rl.GetMousePosition() + + // Set the target to match, so that the camera maps the world space point + // under the cursor to the screen space point under the cursor at any zoom + camera.Target = mouseWorldPos + + // Zoom increment + const zoomIncrement float32 = 0.125 + + camera.Zoom += (wheel * zoomIncrement) + if camera.Zoom < zoomIncrement { + camera.Zoom = zoomIncrement + } + } +} + + + +type SierpinskiArrow struct {} + +func (s SierpinskiArrow) Draw(ctx *RenderCtx) { + rl.PushMatrix() + sierpinskiArrow(ctx, int(ctx.Ports["sierpinskiArrowDepth"]), ctx.Ports["sierpinskiArrowLength"]) + rl.PopMatrix() +} + +func sierpinskiArrow(ctx *RenderCtx, order int, length float64) { + if order == 0 { + curve(ctx, order, length, 60) + } else { + rl.Rotatef(60, 0, 0, 1) + curve(ctx, order, length, -60) + } +} + +func curve(ctx *RenderCtx, order int, length float64, angle float64) { + if order == 0 { + len := int32(length) + rl.DrawLine(0, 0, len, 0, rl.Black) + rl.Translatef(float32(len), 0, 0) + } else { + curve(ctx, order-1, length/2, -angle) + rl.Rotatef(float32(angle), 0, 0, 1) + curve(ctx, order-1, length/2, angle) + rl.Rotatef(float32(angle), 0, 0, 1) + curve(ctx, order-1, length/2, -angle) + } +} + +func main2() { angles := make([]float32, 1000) noise := opensimplex.NewNormalized(0) for i := range len(angles) { @@ -90,100 +190,18 @@ func main() { frameNum++ - if rl.IsMouseButtonDown(rl.MouseRightButton) { - delta := rl.GetMouseDelta() - delta = rl.Vector2Scale(delta, -1.0/camera.Zoom) - camera.Target = rl.Vector2Add(camera.Target, delta) - } - - // Zoom based on mouse wheel - wheel := rl.GetMouseWheelMove() - if wheel != 0 { - // Get the world point that is under the mouse - mouseWorldPos := rl.GetScreenToWorld2D(rl.GetMousePosition(), camera) - - // Set the offset to where the mouse is - camera.Offset = rl.GetMousePosition() - - // Set the target to match, so that the camera maps the world space point - // under the cursor to the screen space point under the cursor at any zoom - camera.Target = mouseWorldPos - - // Zoom increment - const zoomIncrement float32 = 0.125 - - camera.Zoom += (wheel * zoomIncrement) - if camera.Zoom < zoomIncrement { - camera.Zoom = zoomIncrement - } - } - - rl.BeginDrawing() - rl.ClearBackground(rl.RayWhite) - - rl.BeginMode2D(camera) - - rl.PushMatrix() - - /** - for x := range screenWidth { - for y := range screenHeight { - sp := rl.Vector2 { X: float32(x), Y: float32(y) }; - wp := rl.GetScreenToWorld2D(sp, camera); - normVal := noise.Eval2(float64(wp.X), float64(wp.Y)) - rl.DrawPixelV(wp, GrayCurve(normVal, 0.8)); - } - } - **/ - // initial transform by halfway again through angle array - angleIndex := frameNum % len(angles) + angleIndex := (frameNum/10) % len(angles) angle := angles[angleIndex] initAngle := angles[(angleIndex+len(angles)/2)%len(angles)] rl.Rotatef(2500*initAngle, 0, 0, 1) rl.Translatef(100*initAngle, 100*initAngle, 0) - stepSize := int32(1) - for range 1000 { - rl.DrawLine(0, 0, stepSize, 0, rl.Black) - rl.Translatef(float32(stepSize), 0, 0) - rl.Rotatef(angle, 0, 0, 1) - angleIndex++ - angleIndex = angleIndex % len(angles) - angle += angles[angleIndex] - } - rl.PopMatrix() - - rl.PushMatrix() - - //rl.Translatef(-screenWidth/2, screenHeight/2, 0) - - sierpinskiArrow(9, 800) - - rl.PopMatrix() - - /* - rl.PushMatrix() - rl.Translatef(0, 25*50, 0) - rl.Rotatef(90, 1, 0, 0) - rl.DrawGrid(100, 50) - rl.PopMatrix() - */ + fmt.Printf("%.3f", angle) rl.EndMode2D() - - if rl.IsKeyDown(rl.KeySpace) { - if _, err := storage.Save(); err != nil { - log.Printf("Error saving snapshot: %v\n", err) - } - } - - rl.DrawText("Mouse right button drag to move, mouse wheel to zoom", 10, 10, 20, rl.White) - - rl.EndDrawing() } - rl.CloseWindow() } diff --git a/signals.go b/signals.go new file mode 100644 index 0000000..4a2cc7f --- /dev/null +++ b/signals.go @@ -0,0 +1,28 @@ +package main + +import ( + "math" +) + +type Signal interface { + Eval(t float64) float64 +} + +type Const struct{ V float64 } +func (s Const) Eval(t float64) float64 { return s.V } + +type Sine struct { + Amp, Freq, Phase, Bias float64 +} +func (s Sine) Eval(t float64) float64 { + return s.Bias + s.Amp*math.Sin(2*math.Pi*s.Freq*t+s.Phase) +} + +type Saw struct { + Min, Max, Period float64 +} +func (s Saw) Eval(t float64) float64 { + u := math.Mod(t, s.Period) / s.Period // 0..1 + return s.Min + (s.Max-s.Min)*u +} + diff --git a/sketch.go b/sketch.go new file mode 100644 index 0000000..e073e06 --- /dev/null +++ b/sketch.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/gen2brain/raylib-go/raylib" +) + +type Sketch interface { + Draw(ctx *RenderCtx) +} + +/** Ports **/ + +type Ports map[string]Signal + +func MakePorts() Ports { + return make(Ports) +} + +/** + * materialize current value for all ports + **/ +func (p Ports) Eval(t float64) map[string]float64 { + out := make(map[string]float64, len(p)) + for name, sig := range(p) { + out[name] = sig.Eval(t) + } + return out +} + +/** RenderCtx **/ + +type RenderCtx struct { + Width int32 + Height int32 + Time float64 + Ports map[string]float64 + Cam rl.Camera2D +} +