automated snapshot

This commit is contained in:
sumi
2025-12-22 00:36:55 -06:00
parent 63fa362060
commit 57e876e40d
4 changed files with 114 additions and 76 deletions

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
rl "github.com/gen2brain/raylib-go/raylib" rl "github.com/gen2brain/raylib-go/raylib"
"github.com/ojrac/opensimplex-go" "github.com/ojrac/opensimplex-go"
) )
@@ -46,7 +47,7 @@ func (f *SimplexNoiseField) Get(x, y float32) float32 {
type ImageField struct { type ImageField struct {
image *rl.Image image *rl.Image
pixels ImagePixels pixels ImagePixels
offsetX, offsetY float32 offsetX, offsetY int
} }
type ImagePixels struct { type ImagePixels struct {
@@ -66,14 +67,15 @@ func (p *ImagePixels) Get(x, y int) rl.Color {
func NewImageField(path string) ImageField { func NewImageField(path string) ImageField {
image := rl.LoadImage(path) image := rl.LoadImage(path)
fmt.Printf("loaded image from %s\n", path)
colors := rl.LoadImageColors(image) colors := rl.LoadImageColors(image)
pixels := ImagePixels{ pixels := ImagePixels{
w: int(image.Width), w: int(image.Width),
h: int(image.Height), h: int(image.Height),
colors: colors, colors: colors,
} }
offsetX := float32(image.Width / 2) offsetX := int(image.Width)/2
offsetY := float32(image.Height / 2) offsetY := int(image.Height)/2
return ImageField{ return ImageField{
image: image, image: image,
pixels: pixels, pixels: pixels,
@@ -82,8 +84,9 @@ func NewImageField(path string) ImageField {
} }
} }
// zero centered
func (f *ImageField) Get(x, y float32) float32 { func (f *ImageField) Get(x, y float32) float32 {
// todo : blend colors // todo : blend colors
c := f.pixels.Get(int(x+f.offsetX), int(y+f.offsetY)) c := f.pixels.Get(int(x+float32(f.offsetX)), int(y+float32(f.offsetY)))
return Brightness(c) return Brightness(c)
} }

138
main.go
View File

@@ -13,9 +13,9 @@ import (
) )
const ( const (
targetWidth = 800 targetWidth = 1000
targetHeight = 600 targetHeight = 1000
sourceScale = 10 sourceScale = 8
snapshotsDir = "snapshots" snapshotsDir = "snapshots"
) )
@@ -42,11 +42,6 @@ func main() {
rl.SetConfigFlags(rl.FlagMsaa4xHint) rl.SetConfigFlags(rl.FlagMsaa4xHint)
rl.InitWindow(targetWidth, targetHeight, "sumi sierpinski arrow") rl.InitWindow(targetWidth, targetHeight, "sumi sierpinski arrow")
log.Printf("screen=%dx%d render=%dx%d",
rl.GetScreenWidth(), rl.GetScreenHeight(),
rl.GetRenderWidth(), rl.GetRenderHeight(),
)
// point at source center // point at source center
// put source center at center of screen // put source center at center of screen
var camera = TextureCam { var camera = TextureCam {
@@ -56,36 +51,35 @@ func main() {
SourceHeight: sourceHeight, SourceHeight: sourceHeight,
} }
/*
field :=
ScaleField {
Scale: 50.0,
Field: &SimplexNoiseField {
Noise: opensimplex.NewNormalized32(0),
},
}
imgf := NewImageField("/home/d/Dropbox/art/passage/data/david.png")
imageField :=
ScaleField{
field: &imgf,
scale: 0.5,
}
*/
//rng := rand.New(rand.NewSource(0))
//contourSketch := NewContourLayer(rng, &imageField)
sketch := NewSketch()
sketch.CreateLayer("testPattern", &TestPattern{}, int32(sourceWidth), int32(sourceHeight))
rl.SetTargetFPS(60) rl.SetTargetFPS(60)
t0 := time.Now() t0 := time.Now()
ports := MakePorts() /*
*/
ports["sierpinskiArrowAngle"] = Sine{ imageField := NewImageField("/home/d/Dropbox/art/passage/data/david.png")
field :=
TranslateField{
x: -float32(sourceWidth/2.0),
y: -float32(sourceHeight/2.0),
field: &ScaleField {
scale: 5.0,
field: &imageField,
},
}
rng := rand.New(rand.NewSource(0))
contourLayer := NewContourLayer(rng, &field, sourceWidth, sourceHeight)
sketch := NewSketch()
//sketch.CreateLayer("testPattern", &TestPattern{}, int32(sourceWidth), int32(sourceHeight))
//sketch.CreateLayer("actors", &contourLayer, int32(sourceWidth), int32(sourceHeight))
sketch.CreateLayer("field", &FieldLayer { field: &field, dirty: true }, int32(sourceWidth), int32(sourceHeight))
sketch.CreateLayer("contours", &contourLayer, int32(sourceWidth), int32(sourceHeight))
ports := MakePorts()
ports["sierpinskiArrowAngle"] = Sine {
Amp: 120, Amp: 120,
Bias: 100, Bias: 100,
Freq: 0.1, Freq: 0.1,
@@ -110,20 +104,30 @@ func main() {
sketch.Update(renderCtx) sketch.Update(renderCtx)
/** /**
MAIN DRAWING * MAIN DRAWING
**/ */
rl.BeginDrawing() rl.BeginDrawing()
rl.ClearBackground(rl.Blank) rl.ClearBackground(rl.Blank)
sketch.Draw(renderCtx) sketch.Draw(renderCtx)
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.DrawText("Mouse right button drag to move, mouse wheel to zoom", 10, 10, 20, rl.White)
rl.EndDrawing() rl.EndDrawing()
if rl.IsKeyDown(rl.KeySpace) { for ch := rl.GetCharPressed(); ch != 0; ch = rl.GetCharPressed() {
if _, err := storage.Save(); err != nil { c := rune(ch)
log.Printf("Error saving snapshot: %v\n", err) if c == 'c' {
resetCamera(&camera)
} else if c >= '1' && c <= '9' {
zoom := 1 << int(ch - '0')
camera.Zoom = float32(zoom)
} }
} }
@@ -135,37 +139,49 @@ func main() {
rl.CloseWindow() rl.CloseWindow()
} }
type FieldSketch struct { func resetCamera(cam *TextureCam) {
Field Field cam.LookAt = rl.Vector2 { X: float32(cam.SourceWidth) / 2.0, Y: float32(cam.SourceHeight) / 2.0 }
cam.Zoom = 1.0
} }
/* type FieldLayer struct {
func (s *FieldSketch) Draw(ctx *RenderCtx) { field Field
fmt.Printf("drawing field") dirty bool
for x := range ctx.TargetWidth { }
for y := range ctx.TargetHeight {
screen := rl.Vector2{X: float32(x), Y: float32(y)} func (s *FieldLayer) Update(ctx *RenderCtx) {
world := rl.GetScreenToWorld2D(screen, ctx.Cam) ;
v := s.Field.Get(world.X, world.Y) }
clr := GrayCurve(v, 1.0)
rl.DrawPixelV(world, clr) func (s *FieldLayer) Draw(ctx *RenderCtx) {
} for x := range ctx.SourceWidth {
} for y := range ctx.SourceHeight {
v := s.field.Get(float32(x), float32(y))
clr := GrayCurve(v, 1.0)
rl.DrawPixel(x, y, clr)
}
}
s.dirty = false
}
func (s *FieldLayer) IsDirty() bool {
return s.dirty
} }
*/
type ContourLayer struct { type ContourLayer struct {
field Field field Field
actors []*Actor actors []*Actor
} }
func NewContourLayer(rng *rand.Rand, field Field) ContourLayer { func NewContourLayer(rng *rand.Rand, field Field, sourceWidth int, sourceHeight int) ContourLayer {
actors := make([]*Actor, 20000) actors := make([]*Actor, 20000)
for i := range len(actors) { for i := range len(actors) {
x := rng.Int() % sourceWidth
y := rng.Int() % sourceHeight
actors[i] = actors[i] =
&Actor{ &Actor{
position: RandRadialVec(rng, 0, 500, 0, 360), position: rl.Vector2 { X: float32(x), Y: float32(y) },
field: field, field: field,
stepSize: 1, stepSize: 1,
color: rl.NewColor(11, 35, 176, 100), color: rl.NewColor(11, 35, 176, 100),
@@ -177,10 +193,20 @@ func NewContourLayer(rng *rand.Rand, field Field) ContourLayer {
} }
} }
func (s *ContourLayer) Update(ctx *RenderCtx) {
;
}
func (s *ContourLayer) Draw(ctx *RenderCtx) { func (s *ContourLayer) Draw(ctx *RenderCtx) {
rl.BeginBlendMode(rl.BlendAdditive)
for _, actor := range s.actors { for _, actor := range s.actors {
actor.Draw() actor.Draw()
} }
rl.EndBlendMode()
}
func (s *ContourLayer) IsDirty() bool {
return true
} }
type Actor struct { type Actor struct {

View File

@@ -7,32 +7,38 @@ import (
type Sketch struct { type Sketch struct {
layerTools map[string]LayerTools layerTools map[string]LayerTools
layerToolsOrdered []LayerTools
} }
func NewSketch() Sketch { func NewSketch() Sketch {
return Sketch{ return Sketch{
layerTools: make(map[string]LayerTools), layerTools: make(map[string]LayerTools),
layerToolsOrdered: []LayerTools {},
} }
} }
func (s *Sketch) CreateLayer(name string, layer Layer, sourceWidth int32, sourceHeight int32) { func (s *Sketch) CreateLayer(name string, layer Layer, sourceWidth int32, sourceHeight int32) {
texture := rl.LoadRenderTexture(sourceWidth, sourceHeight) texture := rl.LoadRenderTexture(sourceWidth, sourceHeight)
s.layerTools[name] = LayerTools{ layerTools := LayerTools {
name: name, name: name,
texture: &texture, texture: &texture,
layer: layer, layer: layer,
} }
s.layerToolsOrdered = append(s.layerToolsOrdered, layerTools)
s.layerTools[name] = layerTools
} }
func (s *Sketch) Draw(ctx *RenderCtx) { func (s *Sketch) Draw(ctx *RenderCtx) {
// render onto all layer textures // render onto all layer textures
for _, instance := range s.layerTools { for _, instance := range s.layerToolsOrdered {
instance.layer.Update(ctx)
if instance.layer.IsDirty() {
layer := instance.layer layer := instance.layer
rl.BeginTextureMode(*instance.texture) rl.BeginTextureMode(*instance.texture)
rl.ClearBackground(rl.Blank)
layer.Draw(ctx) layer.Draw(ctx)
rl.EndTextureMode() rl.EndTextureMode()
} }
}
// composite all layers to screen // composite all layers to screen
@@ -45,7 +51,7 @@ func (s *Sketch) Draw(ctx *RenderCtx) {
viewport := s.CalcViewport(ctx) viewport := s.CalcViewport(ctx)
for _, instance := range s.layerTools { for _, instance := range s.layerToolsOrdered {
rl.DrawTexturePro(instance.texture.Texture, viewport, screen, rl.Vector2{}, 0, rl.White) rl.DrawTexturePro(instance.texture.Texture, viewport, screen, rl.Vector2{}, 0, rl.White)
} }
@@ -101,20 +107,17 @@ type LayerTools struct {
/** Layer **/ /** Layer **/
type Layer interface { type Layer interface {
Update(ctx *RenderCtx)
Draw(ctx *RenderCtx) Draw(ctx *RenderCtx)
IsDirty() bool
} }
type TestPattern struct{} type TestPattern struct{
dirty bool
}
func DrawGrid(spacing int32, halfExtent int32) { func (tp *TestPattern) Update(ctx *RenderCtx) {
col := rl.Color{R: 220, G: 220, B: 220, A: 255} ;
for x := -halfExtent; x <= halfExtent; x += spacing {
rl.DrawLine(x, -halfExtent, x, halfExtent, col)
}
for y := -halfExtent; y <= halfExtent; y += spacing {
rl.DrawLine(-halfExtent, y, halfExtent, y, col)
}
} }
func (tp *TestPattern) Draw(ctx *RenderCtx) { func (tp *TestPattern) Draw(ctx *RenderCtx) {
@@ -137,6 +140,12 @@ func (tp *TestPattern) Draw(ctx *RenderCtx) {
rl.DrawLine(0, -10000, 0, 10000, rl.Green) rl.DrawLine(0, -10000, 0, 10000, rl.Green)
rl.DrawRectangleLines(-50, -50, 100, 100, rl.Magenta) rl.DrawRectangleLines(-50, -50, 100, 100, rl.Magenta)
rl.PopMatrix() rl.PopMatrix()
tp.dirty = false
}
func (tp *TestPattern) IsDirty() bool {
return tp.dirty
} }
/** Ports **/ /** Ports **/

BIN
sumi Executable file

Binary file not shown.