refactoring progress

This commit is contained in:
2026-01-08 11:54:35 -06:00
parent bbad63c60a
commit b3d9d0340a
7 changed files with 312 additions and 155 deletions

View File

@@ -4,6 +4,7 @@ import (
"github.com/gen2brain/raylib-go/raylib" "github.com/gen2brain/raylib-go/raylib"
"math" "math"
"math/rand" "math/rand"
sg "github.com/d2fn/sumi/internal/graphics"
) )
type ContourLayer struct { type ContourLayer struct {
@@ -12,12 +13,12 @@ type ContourLayer struct {
maxActors uint32 maxActors uint32
actors []*Actor actors []*Actor
actorIndex uint32 actorIndex uint32
actorColor rl.Color actorColor sg.Color
loActorAngle float32 loActorAngle float32
hiActorAngle float32 hiActorAngle float32
} }
func NewContourLayer(sketch *Sketch, rng *rand.Rand, field Field, color rl.Color, loActorAngle float32, hiActorAngle float32) *ContourLayer { func NewContourLayer(sketch *Sketch, rng *rand.Rand, field Field, color sg.Color, loActorAngle float32, hiActorAngle float32) *ContourLayer {
maxActors := 200000 maxActors := 200000
@@ -37,13 +38,13 @@ func NewContourLayer(sketch *Sketch, rng *rand.Rand, field Field, color rl.Color
return &layer return &layer
} }
func (s *ContourLayer) AddActors(color rl.Color, n, sourceWidth, sourceHeight int32) { func (s *ContourLayer) AddActors(color sg.Color, n, sourceWidth, sourceHeight int32) {
for range n { for range n {
x := s.rng.Int31() % sourceWidth x := s.rng.Int31() % sourceWidth
y := s.rng.Int31() % sourceHeight y := s.rng.Int31() % sourceHeight
newActor := newActor :=
&Actor{ &Actor{
position: rl.Vector2{X: float32(x), Y: float32(y)}, position: sg.Point { X: float32(x), Y: float32(y) },
field: s.field, field: s.field,
stepSize: 1, stepSize: 1,
color: s.actorColor, color: s.actorColor,
@@ -55,18 +56,19 @@ func (s *ContourLayer) AddActors(color rl.Color, n, sourceWidth, sourceHeight in
} }
} }
func (s *ContourLayer) Update(ctx *RenderCtx) { func (s *ContourLayer) Update(ctx *Env) {
s.AddActors(s.actorColor, 100, ctx.SourceWidth, ctx.SourceHeight) s.AddActors(s.actorColor, 100, int32(ctx.Graphics.Layout.Graphics.Width), int32(ctx.Graphics.Layout.Graphics.Height))
} }
func (s *ContourLayer) Draw(ctx *RenderCtx) { func (s *ContourLayer) Draw(ctx *Env) {
rl.BeginBlendMode(rl.BlendAdditive) g := ctx.Graphics
g.BeginAdditiveBlend()
for _, actor := range s.actors { for _, actor := range s.actors {
if actor != nil { if actor != nil {
actor.Draw() actor.Draw(ctx)
} }
} }
rl.EndBlendMode() g.EndBlend()
} }
func (s *ContourLayer) IsDirty() bool { func (s *ContourLayer) IsDirty() bool {
@@ -74,40 +76,43 @@ func (s *ContourLayer) IsDirty() bool {
} }
type Actor struct { type Actor struct {
position rl.Vector2 position sg.Point
field Field field Field
stepSize float32 stepSize float32
color rl.Color color sg.Color
loAngle float32 loAngle float32
hiAngle float32 hiAngle float32
} }
func (a *Actor) Draw() { func (a *Actor) Draw(ctx *Env) {
v := a.field.Get(a.position.X, a.position.Y) v := a.field.Get(a.position.X, a.position.Y)
rad := rl.Remap(v, 0, 1, a.loAngle, a.hiAngle) rad := rl.Remap(v, 0, 1, a.loAngle, a.hiAngle)
nextPosition := rl.Vector2{X: a.position.X + a.stepSize*float32(math.Cos(float64(rad))), Y: a.position.Y + a.stepSize*float32(math.Sin(float64(rad)))} nextPosition := sg.Point {X: a.position.X + a.stepSize*float32(math.Cos(float64(rad))), Y: a.position.Y + a.stepSize*float32(math.Sin(float64(rad)))}
rl.DrawLineV(a.position, nextPosition, a.color) //nextPosition := rl.Vector2{X: a.position.X + a.stepSize*float32(math.Cos(float64(rad))), Y: a.position.Y + a.stepSize*float32(math.Sin(float64(rad)))}
g := ctx.Graphics
g.SetStrokeWeight(1.0)
g.SetStroke(true)
g.SetStrokeColor(a.color)
g.DrawLine(a.position, nextPosition)
//rl.DrawLineV(a.position, nextPosition, a.color)
a.position = nextPosition a.position = nextPosition
} }
func RandRadialVec(rng *rand.Rand, minRadius float32, maxRadius float32, loAngle float32, hiAngle float32) rl.Vector2 {
r := float64(rl.Remap(rng.Float32(), 0, 1, minRadius, maxRadius))
deg := float64(rl.Remap(rng.Float32(), 0, 1, loAngle, hiAngle))
rad := rl.Deg2rad * deg
return rl.Vector2{X: float32(r * math.Cos(rad)), Y: float32(r * math.Sin(rad))}
}
type Worm struct { type Worm struct {
position rl.Vector2 position sg.Point
angles []float32 angles []float32
angleIndex int angleIndex int
stepSize int stepSize int
renderPct float32 renderPct float32
} }
func (w *Worm) Draw(ctx *RenderCtx) { func (w *Worm) Draw(ctx *Env) {
rl.PushMatrix() g := ctx.Graphics
rl.Translatef(w.position.X, w.position.Y, 0) g.PushMatrix()
g.Translate(w.position)
//rl.PushMatrix()
//rl.Translatef(w.position.X, w.position.Y, 0)
lastAngle := float32(0.0) lastAngle := float32(0.0)
stepCount := 0 stepCount := 0
nudged := false nudged := false
@@ -117,8 +122,8 @@ func (w *Worm) Draw(ctx *RenderCtx) {
deltaAngle := angle - lastAngle deltaAngle := angle - lastAngle
if !nudged { if !nudged {
rad := float64(deltaAngle * math.Pi / 180.0) rad := float64(deltaAngle * math.Pi / 180.0)
nudge := rl.Vector2{X: float32(w.stepSize) * float32(math.Cos(rad)), Y: float32(w.stepSize) * float32(math.Sin(rad))} nudge := sg.Vec {X: float32(w.stepSize) * float32(math.Cos(rad)), Y: float32(w.stepSize) * float32(math.Sin(rad))}
w.position = rl.Vector2Add(w.position, nudge) w.position = w.position.Add(nudge)//rl.Vector2Add(w.position, nudge)
nudged = true nudged = true
} }
rl.Rotatef(deltaAngle, 0, 0, 1) rl.Rotatef(deltaAngle, 0, 0, 1)
@@ -130,6 +135,6 @@ func (w *Worm) Draw(ctx *RenderCtx) {
break break
} }
} }
rl.PopMatrix() g.PopMatrix()
w.angleIndex = (w.angleIndex + 1) % len(w.angles) w.angleIndex = (w.angleIndex + 1) % len(w.angles)
} }

37
env.go Normal file
View File

@@ -0,0 +1,37 @@
package main
import (
sg "github.com/d2fn/sumi/internal/graphics"
)
/** Env **/
type Env struct {
Layout Layout
Time float64
Ports map[string]float64
Graphics sg.Graphics
}
type Layout struct {
// total monitor bounds
Monitor sg.Rect
// bounds of the application window
Window sg.Rect
// bounds of the ui controls area
Controls sg.Rect
// bounds of the region of the app window reserved for rendering the graphics buffer
Viewport sg.Rect
// bounds of the off screen graphics buffer where rendering happens
Graphics sg.Rect
}
func (ctx *Env) GetGraphicsWidth() float32 {
return ctx.Graphics.Bounds.Width
}
func (ctx *Env) GetGraphicsHeight() float32 {
return ctx.Graphics.Bounds.Height
}

View File

@@ -101,15 +101,15 @@ type FieldLayer struct {
dirty bool dirty bool
} }
func (fl *FieldLayer) Update(ctx *RenderCtx) { func (fl *FieldLayer) Update(env *Env) {
} }
func (fl *FieldLayer) Draw(ctx *RenderCtx) { func (fl *FieldLayer) Draw(env *Env) {
rl.ClearBackground(rl.Blank) rl.ClearBackground(rl.Blank)
rl.BeginBlendMode(rl.BlendAlphaPremultiply) rl.BeginBlendMode(rl.BlendAlphaPremultiply)
for x := range ctx.SourceWidth { for x := range int32(env.GetGraphicsHeight()) {
for y := range ctx.SourceHeight { for y := range int32(env.GetGraphicsHeight()) {
v := fl.field.Get(float32(x), float32(y)) v := fl.field.Get(float32(x), float32(y))
clr := LerpCurve(v, 1.3, fl.loColor, fl.hiColor) clr := LerpCurve(v, 1.3, fl.loColor, fl.hiColor)
rl.DrawPixel(x, y, clr) rl.DrawPixel(x, y, clr)

View File

@@ -8,45 +8,152 @@ import (
) )
type Graphics struct { type Graphics struct {
layout Layout Style Style
style Style Bounds Rect
} }
type Layout struct { type Style struct {
// total monitor bounds StrokeColor, FillColor color.RGBA
Monitor Rect StrokeWeight float32
// bounds of the application window Stroke, Fill bool
Window Rect }
// bounds of the ui controls area
Controls Rect func (g *Graphics) GetGraphicsWidth() int32 {
// bounds of the region of the app window reserved for rendering the graphics buffer return int32(g.Bounds.Width)
Viewport Rect }
// bounds of the off screen graphics buffer where rendering happens
Graphics Rect func (g *Graphics) GetGraphicsHeight() int32 {
return int32(g.Bounds.Height)
}
func (g *Graphics) PushStyle() {
}
func (g *Graphics) PopStyle() {
}
func (g *Graphics) SetStrokeColor(c Color) {
g.Style.StrokeColor = color.RGBA { R: c.R, G: c.G, B: c.B, A: c.A }
}
func (g *Graphics) SetFillColor(c Color) {
g.Style.FillColor = color.RGBA { R: c.R, G: c.G, B: c.B, A: c.A }
}
func (g *Graphics) SetStrokeWeight(w float32) {
g.Style.StrokeWeight = w
}
func (g *Graphics) SetStroke(b bool) {
g.Style.Stroke = b
}
func (g *Graphics) SetFill(b bool) {
g.Style.Fill = b
}
func (g *Graphics) PushMatrix() {
rl.PushMatrix()
}
func (g *Graphics) Translate(p Point) {
rl.Translatef(p.X, p.Y, 0)
}
func (g *Graphics) PopMatrix() {
rl.PopMatrix()
}
func (g *Graphics) BeginClip(r Rect) {
rlRect := r.ToRL()
rint := (&rlRect).ToInt32()
rl.BeginScissorMode(rint.X, rint.Y, rint.Width, rint.Height)
}
func (g *Graphics) EndClip() {
rl.EndScissorMode()
}
func (g *Graphics) BeginAdditiveBlend() {
rl.BeginBlendMode(rl.BlendAdditive)
}
func (g *Graphics) EndBlend() {
rl.EndBlendMode()
}
func (g *Graphics) BeginTexture(t rl.RenderTexture2D) {
rl.BeginTextureMode(t)
}
func (g *Graphics) EndTexture() {
rl.EndTextureMode()
}
func (g *Graphics) DrawRect(r Rect) {
if g.Style.Fill {
rl.DrawRectangleRec(r.ToRL(), g.Style.FillColor)
}
if g.Style.Stroke {
saveLineWidth := rl.GetLineWidth()
rl.SetLineWidth(g.Style.StrokeWeight)
rl.DrawRectangleLines(
int32(r.X), int32(r.Y),
int32(r.Width), int32(r.Height),
g.Style.StrokeColor,
)
rl.SetLineWidth(saveLineWidth)
}
}
func (g *Graphics) DrawLine(a, b Point) {
saveLineWidth := rl.GetLineWidth()
rl.SetLineWidth(g.Style.StrokeWeight)
rl.DrawLineV(a.ToRL(), b.ToRL(), g.Style.StrokeColor)
rl.SetLineWidth(saveLineWidth)
} }
type Color color.RGBA type Color color.RGBA
func RGBA(r, g, b, a uint8) Color {
return Color { R: r, G: g, B: b, A: a }
}
type HSBA struct { type HSBA struct {
H uint H uint
S, B float32 S, B float32
A uint8 A uint8
} }
type Style struct { type Point rl.Vector3
StrokeColor Color type Vec rl.Vector3
StrokeWeight float32 type Rect rl.Rectangle
FillColor Color
func (p *Point) Add(v Vec) Point {
return Point { X: p.X + v.X, Y: p.Y + v.Y, Z: p.Z + v.Z }
} }
type Rect rl.Rectangle func (p Point) ToRL() rl.Vector2 {
return rl.Vector2 { X: p.X, Y: p.Y }
}
func (p Point) ToRL3() rl.Vector3 {
return rl.Vector3 { X: p.X, Y: p.Y, Z: p.Z }
}
/** /**
* scale the given rect down to the target rect * scale the given rect down to the target rect
* maintaining the aspect ratio of the original rect * maintaining the aspect ratio of the original rect
*/ */
func (r Rect) ToRL() *rl.Rectangle {
return &rl.Rectangle { X: r.X, Y: r.Y, Width: r.Width, Height: r.Height } func (r Rect) UL() Point {
return Point { X: r.X, Y: r.Y }
}
func (r Rect) ToRL() rl.Rectangle {
return rl.Rectangle { X: r.X, Y: r.Y, Width: r.Width, Height: r.Height }
} }
func (r Rect) ScaleTo(tgt Rect) Rect { func (r Rect) ScaleTo(tgt Rect) Rect {

67
main.go
View File

@@ -9,7 +9,7 @@ import (
"os" "os"
"time" "time"
g "github.com/d2fn/sumi/internal/graphics" sg "github.com/d2fn/sumi/internal/graphics"
"github.com/ojrac/opensimplex-go" "github.com/ojrac/opensimplex-go"
gui "github.com/gen2brain/raylib-go/raygui" gui "github.com/gen2brain/raylib-go/raygui"
@@ -17,13 +17,14 @@ import (
//"github.com/ojrac/opensimplex-go" //"github.com/ojrac/opensimplex-go"
) )
func Bootstrap() sg.Graphics {
var ( var (
snapshotsPath string snapshotsPath string
storage *Storage storage *Storage
) )
func Bootstrap() g.Layout {
rl.InitWindow(800, 600, "bootstrap") rl.InitWindow(800, 600, "bootstrap")
monitor := rl.GetCurrentMonitor() monitor := rl.GetCurrentMonitor()
@@ -74,12 +75,27 @@ func Bootstrap() g.Layout {
os.Exit(1) os.Exit(1)
} }
return g.Layout { layout := sg.Layout {
Monitor: g.Rect{X: 0, Y: 0, Width: float32(monitorWidth), Height: float32(monitorHeight)}, Monitor: sg.Rect{X: 0, Y: 0, Width: float32(monitorWidth), Height: float32(monitorHeight)},
Window: g.Rect{X: 0, Y: 0, Width: float32(windowWidth), Height: float32(windowHeight)}, Window: sg.Rect{X: 0, Y: 0, Width: float32(windowWidth), Height: float32(windowHeight)},
Controls: g.Rect{X: 0, Y: 0, Width: float32(controlsWidth), Height: float32(windowHeight)}, Controls: sg.Rect{X: 0, Y: 0, Width: float32(controlsWidth), Height: float32(windowHeight)},
Viewport: g.Rect{X: float32(controlsWidth), Y: 0, Width: float32(viewportWidth), Height: float32(windowHeight)}, Viewport: sg.Rect{X: float32(controlsWidth), Y: 0, Width: float32(viewportWidth), Height: float32(windowHeight)},
Graphics: g.Rect{X: 0, Y: 0, Width: float32(graphicsWidth), Height: float32(graphicsHeight)}, Graphics: sg.Rect{X: 0, Y: 0, Width: float32(graphicsWidth), Height: float32(graphicsHeight)},
}
//rl.SetConfigFlags(rl.FlagMsaa4xHint)
rl.InitWindow(int32(layout.Window.Width), int32(layout.Window.Height), "sumi sierpinski arrow")
rl.SetTargetFPS(60)
return sg.Graphics {
Layout: layout,
Style: sg.Style {
Fill: false,
FillColor: rl.RayWhite,
Stroke: true,
StrokeColor: rl.Black,
StrokeWeight: 1.0,
},
} }
} }
@@ -87,15 +103,11 @@ func main() {
log := log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) log := log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)
layout := Bootstrap() g := Bootstrap()
//rl.SetConfigFlags(rl.FlagMsaa4xHint)
rl.InitWindow(int32(layout.Window.Width), int32(layout.Window.Height), "sumi sierpinski arrow")
// reproducable flourescent color cycle // reproducable flourescent color cycle
colorCycle := g.NewFixedColorCycle(g.FlourescentColors).Shuffle(0) colorCycle := sg.NewFixedColorCycle(sg.FlourescentColors).Shuffle(0)
rl.SetTargetFPS(60)
t0 := time.Now() t0 := time.Now()
rng := rand.New(rand.NewSource(0)) rng := rand.New(rand.NewSource(0))
@@ -108,8 +120,8 @@ func main() {
//imageField := NewImageField("/home/d/Dropbox/art/data/moses_statue.jpg") //imageField := NewImageField("/home/d/Dropbox/art/data/moses_statue.jpg")
field := field :=
&TranslateField{ &TranslateField{
x: -float32(layout.Graphics.Width / 2.0), x: -float32(g.Layout.Graphics.Width / 2.0),
y: -float32(layout.Graphics.Height / 2.0), y: -float32(g.Layout.Graphics.Height / 2.0),
field: &ScaleField{ field: &ScaleField{
scale: 100.0, scale: 100.0,
field: &AdderField{ field: &AdderField{
@@ -123,7 +135,7 @@ func main() {
//sierpinskiLayer := &SierpinskiArrow { dirty: true } //sierpinskiLayer := &SierpinskiArrow { dirty: true }
sketch := NewSketch(int32(layout.Graphics.Width), int32(layout.Graphics.Height)) sketch := NewSketch(&g)
fieldColor := colorCycle.Next() fieldColor := colorCycle.Next()
fmt.Printf("field color = %v\n", fieldColor) fmt.Printf("field color = %v\n", fieldColor)
@@ -138,12 +150,14 @@ func main() {
hsv := rl.ColorToHSV(actorColor) hsv := rl.ColorToHSV(actorColor)
hsv.Z *= 0.7 hsv.Z *= 0.7
actorColor = rl.ColorFromHSV(hsv.X, hsv.Y, hsv.Z) actorColor = rl.ColorFromHSV(hsv.X, hsv.Y, hsv.Z)
actorColor = g.Clamp(actorColor, 10, 255) actorColor = sg.Clamp(actorColor, 10, 255)
actorColor.A = 10 actorColor.A = 10
actorSGColor := sg.Color { R: actorColor.R, G: actorColor.G, B: actorColor.B, A: actorColor.A }
//NewColor(11, 35, 176, 50), //NewColor(11, 35, 176, 50),
//r //r
contourLayer := NewContourLayer(&sketch, rng, field, actorColor, -25*math.Pi, 25*math.Pi) contourLayer := NewContourLayer(&sketch, rng, field, actorSGColor, -25*math.Pi, 25*math.Pi)
sketch.AddLayer("contours", contourLayer) sketch.AddLayer("contours", contourLayer)
//sketch.AddLayer("sierpinski-arrowhead", sierpinskiLayer) //sketch.AddLayer("sierpinski-arrowhead", sierpinskiLayer)
// aurora := NewImageLayer("/home/d/Dropbox/photos/Events/2025/Aurora/Photo Nov 11 2025, 9 52 03 PM.jpg") // aurora := NewImageLayer("/home/d/Dropbox/photos/Events/2025/Aurora/Photo Nov 11 2025, 9 52 03 PM.jpg")
@@ -175,15 +189,13 @@ func main() {
t := time.Since(t0).Seconds() t := time.Since(t0).Seconds()
// set up RenderCtx // set up RenderCtx
renderCtx := &RenderCtx { env := &Env {
TargetBounds: layout.Viewport, Graphics: g,
SourceWidth: int32(layout.Graphics.Width),
SourceHeight: int32(layout.Graphics.Height),
Time: t, Time: t,
Ports: ports.Eval(t), Ports: ports.Eval(t),
} }
sketch.Update(renderCtx) sketch.Update(env)
/** /**
* MAIN DRAWING * MAIN DRAWING
@@ -191,7 +203,7 @@ func main() {
rl.BeginDrawing() rl.BeginDrawing()
rl.ClearBackground(rl.GetColor(uint(gui.GetStyle(gui.DEFAULT, gui.BACKGROUND_COLOR)))) rl.ClearBackground(rl.GetColor(uint(gui.GetStyle(gui.DEFAULT, gui.BACKGROUND_COLOR))))
sketch.Draw(renderCtx) sketch.Draw(env)
gui.SetStyle(gui.DEFAULT, gui.BACKGROUND_COLOR, 0x181818FF) gui.SetStyle(gui.DEFAULT, gui.BACKGROUND_COLOR, 0x181818FF)
gui.SetStyle(gui.DEFAULT, gui.BASE_COLOR_NORMAL, 0x2A2A2AFF) gui.SetStyle(gui.DEFAULT, gui.BASE_COLOR_NORMAL, 0x2A2A2AFF)
@@ -204,7 +216,7 @@ func main() {
y := float32(10) y := float32(10)
minX := float32(60) minX := float32(60)
maxX := float32(layout.Controls.X + layout.Controls.Width - 20) maxX := float32(g.Layout.Controls.X + g.Layout.Controls.Width - 20)
sliderWidth := maxX - minX - 20 sliderWidth := maxX - minX - 20
controlRowHeight := 20 controlRowHeight := 20
for _, layerTools := range sketch.layerToolsOrdered { for _, layerTools := range sketch.layerToolsOrdered {
@@ -246,7 +258,6 @@ func main() {
*/ */
y += float32(controlRowHeight + 10) y += float32(controlRowHeight + 10)
} }
rl.EndDrawing() rl.EndDrawing()

View File

@@ -8,13 +8,13 @@ type SierpinskiArrow struct {
dirty bool dirty bool
} }
func (s *SierpinskiArrow) Draw(ctx *RenderCtx) { func (s *SierpinskiArrow) Draw(env *Env) {
rl.Translatef(float32(ctx.SourceWidth)/2.0, float32(ctx.SourceHeight)/2.0, 0) rl.Translatef(float32(env.GetGraphicsWidth())/2.0, float32(env.GetGraphicsHeight())/2.0, 0)
rl.ClearBackground(rl.NewColor(0, 0, 0, 0)) rl.ClearBackground(rl.NewColor(0, 0, 0, 0))
sierpinskiArrow(ctx, int(ctx.Ports["sierpinskiArrowDepth"]), ctx.Ports["sierpinskiArrowLength"]) sierpinskiArrow(env, int(env.Ports["sierpinskiArrowDepth"]), env.Ports["sierpinskiArrowLength"])
} }
func (s *SierpinskiArrow) Update(ctx *RenderCtx) { func (s *SierpinskiArrow) Update(_ *Env) {
s.dirty = true s.dirty = true
} }
@@ -22,26 +22,26 @@ func (s *SierpinskiArrow) IsDirty() bool {
return s.dirty return s.dirty
} }
func sierpinskiArrow(ctx *RenderCtx, order int, length float64) { func sierpinskiArrow(env *Env, order int, length float64) {
if order == 0 { if order == 0 {
curve(ctx, order, length, ctx.Ports["sierpinskiArrowAngle"]) curve(env, order, length, env.Ports["sierpinskiArrowAngle"])
} else { } else {
rl.Rotatef(float32(ctx.Ports["sierpinskiArrowAngle"]), 0, 0, 1) rl.Rotatef(float32(env.Ports["sierpinskiArrowAngle"]), 0, 0, 1)
curve(ctx, order, length, -ctx.Ports["sierpinskiArrowAngle"]) curve(env, order, length, -env.Ports["sierpinskiArrowAngle"])
} }
} }
func curve(ctx *RenderCtx, order int, length float64, angle float64) { func curve(env *Env, order int, length float64, angle float64) {
if order == 0 { if order == 0 {
len := int32(length) len := int32(length)
rl.SetLineWidth(4) rl.SetLineWidth(4)
rl.DrawLine(0, 0, len, 0, rl.RayWhite) rl.DrawLine(0, 0, len, 0, rl.RayWhite)
rl.Translatef(float32(length), 0, 0) rl.Translatef(float32(length), 0, 0)
} else { } else {
curve(ctx, order-1, length/2, -angle) curve(env, order-1, length/2, -angle)
rl.Rotatef(float32(angle), 0, 0, 1) rl.Rotatef(float32(angle), 0, 0, 1)
curve(ctx, order-1, length/2, angle) curve(env, order-1, length/2, angle)
rl.Rotatef(float32(angle), 0, 0, 1) rl.Rotatef(float32(angle), 0, 0, 1)
curve(ctx, order-1, length/2, -angle) curve(env, order-1, length/2, -angle)
} }
} }

121
sketch.go
View File

@@ -2,14 +2,13 @@ package main
import ( import (
"fmt" "fmt"
g "github.com/d2fn/sumi/internal/graphics" sg "github.com/d2fn/sumi/internal/graphics"
"github.com/gen2brain/raylib-go/raylib" "github.com/gen2brain/raylib-go/raylib"
"math" "math"
) )
type Sketch struct { type Sketch struct {
sourceWidth int32 graphics *sg.Graphics
sourceHeight int32
cam *TextureCam cam *TextureCam
composite rl.RenderTexture2D composite rl.RenderTexture2D
layerTools map[string]*LayerTools layerTools map[string]*LayerTools
@@ -21,15 +20,6 @@ type TextureCam struct {
Zoom float32 Zoom float32
} }
/** RenderCtx **/
type RenderCtx struct {
TargetBounds g.Rect
SourceWidth int32
SourceHeight int32
Time float64
Ports map[string]float64
}
type LayerTools struct { type LayerTools struct {
name string name string
layer Layer layer Layer
@@ -52,27 +42,26 @@ type LayerConfig struct {
kValue float32 kValue float32
} }
func NewSketch(sourceWidth, sourceHeight int32) Sketch { func NewSketch(g *sg.Graphics) Sketch {
// 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{
LookAt: rl.Vector2{X: float32(sourceWidth) / 2.0, Y: float32(sourceHeight) / 2.0}, LookAt: rl.Vector2{X: float32(g.Layout.Graphics.Width) / 2.0, Y: float32(g.Layout.Graphics.Height) / 2.0},
Zoom: 1.0, Zoom: 1.0,
} }
return Sketch{ return Sketch{
sourceWidth: sourceWidth, graphics: g,
sourceHeight: sourceHeight,
layerTools: make(map[string]*LayerTools), layerTools: make(map[string]*LayerTools),
layerToolsOrdered: []*LayerTools{}, layerToolsOrdered: []*LayerTools{},
composite: rl.LoadRenderTexture(sourceWidth, sourceHeight), composite: rl.LoadRenderTexture(int32(g.Layout.Graphics.Width), int32(g.Layout.Graphics.Height)),
cam: &camera, cam: &camera,
} }
} }
func (s *Sketch) AddLayer(name string, layer Layer) { func (s *Sketch) AddLayer(name string, layer Layer) {
texture := rl.LoadRenderTexture(s.sourceWidth, s.sourceHeight) texture := rl.LoadRenderTexture(int32(s.graphics.Layout.Graphics.Width), int32(s.graphics.Layout.Graphics.Height))
config := NewLayerConfig() config := NewLayerConfig()
layerTools := layerTools :=
LayerTools{ LayerTools{
@@ -93,7 +82,8 @@ func (s *Sketch) AddColorLayer(name string, c rl.Color) {
s.AddLayer(name, colorLayer) s.AddLayer(name, colorLayer)
} }
func (s *Sketch) Redraw(ctx *RenderCtx) { func (s *Sketch) Redraw(ctx *Env) {
g := ctx.Graphics
// render onto all layer textures // render onto all layer textures
for _, instance := range s.layerToolsOrdered { for _, instance := range s.layerToolsOrdered {
layer := instance.layer layer := instance.layer
@@ -102,46 +92,51 @@ func (s *Sketch) Redraw(ctx *RenderCtx) {
layer.Update(ctx) layer.Update(ctx)
// re-render to texture if dirty // re-render to texture if dirty
if instance.layer.IsDirty() { if instance.layer.IsDirty() {
rl.BeginTextureMode(instance.texture) g.PushMatrix()
rl.PushMatrix() g.BeginTexture(instance.texture)
layer.Draw(ctx) layer.Draw(ctx)
rl.PopMatrix() g.PopMatrix()
rl.EndTextureMode() g.EndTexture()
} }
} }
} }
} }
func (s *Sketch) Draw(ctx *RenderCtx) { func (s *Sketch) Draw(ctx *Env) {
g := ctx.Graphics
s.Redraw(ctx) s.Redraw(ctx)
// copy from full texture for compositing, with vertical flipping // copy from full texture for compositing, with vertical flipping
src := g.Layout.Graphics
src.Height = -src.Height
dst := g.Layout.Graphics
/*
src := g.Rect { src := g.Rect {
X: 0, Y: 0, X: 0, Y: 0,
Width: float32(ctx.SourceWidth), Width: float32(ctx.SourceWidth),
Height: -float32(ctx.SourceHeight), Height: -float32(ctx.SourceHeight),
} }
dst := g.Rect { dst := g.Rect{
X: 0, Y: 0, X: 0, Y: 0,
Width: float32(ctx.SourceWidth), Width: float32(ctx.SourceWidth),
Height: float32(ctx.SourceHeight), Height: float32(ctx.SourceHeight),
} }
*/
viewport := s.CalcViewport(ctx) viewport := s.CalcViewport(ctx)
sourceRect := g.Rect{X: 0, Y: 0, Width: float32(ctx.SourceWidth), Height: float32(ctx.SourceHeight)} outputRect := g.Layout.Graphics.ScaleTo(g.Layout.Viewport)
targetRect := g.Rect{X: float32(ctx.TargetBounds.X), Y: float32(ctx.TargetBounds.Y), Width: float32(ctx.TargetBounds.Width), Height: float32(ctx.TargetBounds.Height)}
outputRect := sourceRect.ScaleTo(targetRect)
fmt.Printf("outputRect = %v\n", outputRect) fmt.Printf("outputRect = %v\n", outputRect)
x := float32(0) x := float32(0)
y := float32(0) y := float32(0)
w := outputRect.Width w := outputRect.Width
h := outputRect.Height h := outputRect.Height
rl.PushMatrix() g.PushMatrix()
rl.Translatef(outputRect.X, outputRect.Y, 0) g.Translate(outputRect.UL())
rl.BeginScissorMode(int32(outputRect.X), int32(outputRect.Y), int32(outputRect.Width), int32(outputRect.Height)) g.BeginClip(outputRect)
checkSize := float32(25.0) checkSize := float32(25.0)
grey := rl.NewColor(220, 220, 220, 255) grey := rl.NewColor(220, 220, 220, 255)
cellX := 0 cellX := 0
@@ -161,8 +156,8 @@ func (s *Sketch) Draw(ctx *RenderCtx) {
y += checkSize y += checkSize
cellY++ cellY++
} }
rl.EndScissorMode() g.EndClip()
rl.PopMatrix() g.PopMatrix()
rl.BeginBlendMode(rl.BlendAlphaPremultiply) rl.BeginBlendMode(rl.BlendAlphaPremultiply)
//rl.BeginBlendMode(rl.BlendAlpha) //rl.BeginBlendMode(rl.BlendAlpha)
@@ -188,7 +183,7 @@ func (s *Sketch) Draw(ctx *RenderCtx) {
g = uint8(float32(g) * (float32(config.a) / 255.0)) g = uint8(float32(g) * (float32(config.a) / 255.0))
b = uint8(float32(b) * (float32(config.a) / 255.0)) b = uint8(float32(b) * (float32(config.a) / 255.0))
tint := rl.NewColor(r, g, b, config.a) tint := rl.NewColor(r, g, b, config.a)
rl.DrawTexturePro(instance.texture.Texture, src, dst, rl.Vector2{}, 0, tint) rl.DrawTexturePro(instance.texture.Texture, src.ToRL(), dst.ToRL(), rl.Vector2{}, 0, tint)
} }
} }
rl.EndTextureMode() rl.EndTextureMode()
@@ -197,29 +192,30 @@ func (s *Sketch) Draw(ctx *RenderCtx) {
rl.GenTextureMipmaps(&s.composite.Texture) rl.GenTextureMipmaps(&s.composite.Texture)
rl.SetTextureFilter(s.composite.Texture, rl.FilterTrilinear) rl.SetTextureFilter(s.composite.Texture, rl.FilterTrilinear)
rl.DrawTexturePro(s.composite.Texture, viewport, *outputRect.ToRL(), rl.Vector2{}, 0, rl.White) rl.DrawTexturePro(s.composite.Texture, viewport.ToRL(), outputRect.ToRL(), rl.Vector2{}, 0, rl.White)
outlineRect := outputRect.ToRL().ToInt32() outputRectRL := outputRect.ToRL()
outlineRect := (&outputRectRL).ToInt32()
rl.DrawRectangleLines(outlineRect.X, outlineRect.Y, outlineRect.Width, outlineRect.Height, rl.Gray) rl.DrawRectangleLines(outlineRect.X, outlineRect.Y, outlineRect.Width, outlineRect.Height, rl.Gray)
} }
func (s *Sketch) CalcViewport(ctx *RenderCtx) g.Rect { func (s *Sketch) CalcViewport(ctx *Env) sg.Rect {
viewportWidth := rl.Clamp(float32(ctx.SourceWidth)/s.cam.Zoom, 0, float32(ctx.SourceWidth)) viewportWidth := rl.Clamp(float32(ctx.Graphics.Layout.Graphics.Width)/s.cam.Zoom, 0, float32(ctx.Graphics.Layout.Graphics.Width))
viewportHeight := rl.Clamp(float32(ctx.SourceHeight)/s.cam.Zoom, 0, float32(ctx.SourceHeight)) viewportHeight := rl.Clamp(float32(ctx.Graphics.Layout.Graphics.Height)/s.cam.Zoom, 0, float32(ctx.Graphics.Layout.Graphics.Height))
return g.Rect { return sg.Rect{
X: rl.Clamp(s.cam.LookAt.X-viewportWidth/2.0, 0, float32(ctx.SourceWidth)-viewportWidth), X: rl.Clamp(s.cam.LookAt.X-viewportWidth/2.0, 0, float32(ctx.Graphics.Layout.Graphics.Width)-viewportWidth),
Y: rl.Clamp(s.cam.LookAt.Y-viewportHeight/2.0, 0, float32(ctx.SourceHeight)-viewportHeight), Y: rl.Clamp(s.cam.LookAt.Y-viewportHeight/2.0, 0, float32(ctx.Graphics.Layout.Graphics.Height)-viewportHeight),
Width: float32(viewportWidth), Width: float32(viewportWidth),
Height: -float32(viewportHeight), Height: -float32(viewportHeight),
} }
} }
func (s *Sketch) Update(ctx *RenderCtx) { func (s *Sketch) Update(ctx *Env) {
if rl.IsMouseButtonDown(rl.MouseRightButton) { if rl.IsMouseButtonDown(rl.MouseRightButton) {
// get mouse delta from last frame // get mouse delta from last frame
delta := rl.GetMouseDelta() delta := rl.GetMouseDelta()
sourceScale := float32(ctx.SourceWidth) / float32(ctx.TargetBounds.Width) sourceScale := float32(ctx.Graphics.Layout.Graphics.Width) / float32(ctx.Graphics.Layout.Viewport.Width)
// compute the amount to move scaled by the camera zoom // compute the amount to move scaled by the camera zoom
delta = rl.Vector2Scale(delta, -sourceScale/s.cam.Zoom) delta = rl.Vector2Scale(delta, -sourceScale/s.cam.Zoom)
delta.Y = -delta.Y delta.Y = -delta.Y
@@ -227,8 +223,8 @@ func (s *Sketch) Update(ctx *RenderCtx) {
} }
// clamp LookAt to be somewhere on the texture // clamp LookAt to be somewhere on the texture
s.cam.LookAt.X = rl.Clamp(s.cam.LookAt.X, 0, float32(ctx.SourceWidth-1)) s.cam.LookAt.X = rl.Clamp(s.cam.LookAt.X, 0, float32(ctx.Graphics.Layout.Graphics.Width))
s.cam.LookAt.Y = rl.Clamp(s.cam.LookAt.Y, 0, float32(ctx.SourceHeight-1)) s.cam.LookAt.Y = rl.Clamp(s.cam.LookAt.Y, 0, float32(ctx.Graphics.Layout.Graphics.Height))
// Zoom based on mouse wheel // Zoom based on mouse wheel
wheel := rl.GetMouseWheelMove() wheel := rl.GetMouseWheelMove()
@@ -246,12 +242,12 @@ func (s *Sketch) Update(ctx *RenderCtx) {
} }
func (s *Sketch) ResetCamera() { func (s *Sketch) ResetCamera() {
s.cam.LookAt = rl.Vector2{X: float32(s.sourceWidth) / 2.0, Y: float32(s.sourceHeight) / 2.0} s.cam.LookAt = rl.Vector2{X: float32(s.graphics.Layout.Graphics.Width) / 2.0, Y: float32(s.graphics.Layout.Graphics.Height) / 2.0}
s.cam.Zoom = 1.0 s.cam.Zoom = 1.0
} }
type SketchCapture struct { type SketchCapture struct {
width, height uint32 width, height int32
compositeImage *rl.Image compositeImage *rl.Image
layerTools map[string]*LayerTools layerTools map[string]*LayerTools
layerToolsOrdered []*LayerTools layerToolsOrdered []*LayerTools
@@ -265,7 +261,8 @@ func (s *Sketch) Capture() *SketchCapture {
rl.ImageFlipVertical(layerTool.capture) rl.ImageFlipVertical(layerTool.capture)
} }
return &SketchCapture{ return &SketchCapture{
width: uint32(s.sourceWidth), height: uint32(s.sourceHeight), width: s.graphics.GetGraphicsWidth(),
height: s.graphics.GetGraphicsHeight(),
compositeImage: composite, compositeImage: composite,
layerTools: s.layerTools, layerTools: s.layerTools,
layerToolsOrdered: s.layerToolsOrdered, layerToolsOrdered: s.layerToolsOrdered,
@@ -290,8 +287,8 @@ func NewLayerConfig() LayerConfig {
/** Layer **/ /** Layer **/
type Layer interface { type Layer interface {
Update(ctx *RenderCtx) Update(ctx *Env)
Draw(ctx *RenderCtx) Draw(ctx *Env)
IsDirty() bool IsDirty() bool
} }
@@ -300,11 +297,11 @@ type ColorLayer struct {
dirty bool dirty bool
} }
func (cl *ColorLayer) Update(ctx *RenderCtx) { func (cl *ColorLayer) Update(ctx *Env) {
} }
func (cl *ColorLayer) Draw(ctx *RenderCtx) { func (cl *ColorLayer) Draw(ctx *Env) {
rl.ClearBackground(cl.color) rl.ClearBackground(cl.color)
cl.dirty = false cl.dirty = false
} }
@@ -327,12 +324,12 @@ func NewImageLayer(path string) *ImageLayer {
} }
} }
func (il *ImageLayer) Update(ctx *RenderCtx) { func (il *ImageLayer) Update(ctx *Env) {
} }
func (il *ImageLayer) Draw(ctx *RenderCtx) { func (il *ImageLayer) Draw(ctx *Env) {
rl.Translatef(float32(ctx.SourceWidth)/2.0-float32(il.texture.Width)/2.0, float32(ctx.SourceHeight)/2.0-float32(il.texture.Height)/2.0, 0) rl.Translatef(float32(ctx.GetGraphicsWidth())/2.0-float32(il.texture.Width)/2.0, float32(ctx.GetGraphicsHeight())/2.0-float32(il.texture.Height)/2.0, 0)
rl.DrawTexture(il.texture, 0, 0, rl.White) rl.DrawTexture(il.texture, 0, 0, rl.White)
} }
@@ -344,22 +341,22 @@ type TestPattern struct {
dirty bool dirty bool
} }
func (tp *TestPattern) Update(ctx *RenderCtx) { func (tp *TestPattern) Update(ctx *Env) {
} }
func (tp *TestPattern) Draw(ctx *RenderCtx) { func (tp *TestPattern) Draw(ctx *Env) {
rl.ClearBackground(rl.Black) rl.ClearBackground(rl.Black)
centerX := float32(ctx.SourceWidth) / 2 centerX := float32(ctx.GetGraphicsWidth()) / 2
centerY := float32(ctx.SourceHeight) / 2 centerY := float32(ctx.GetGraphicsHeight()) / 2
rl.DrawRectangleRec(*g.Rect{X: 0, Y: 0, Width: centerX, Height: centerY}.ToRL(), rl.Red) rl.DrawRectangleRec(rl.Rectangle{X: 0, Y: 0, Width: centerX, Height: centerY}, rl.Red)
rl.DrawRectangleRec(rl.Rectangle{X: centerX, Y: 0, Width: centerX, Height: centerY}, rl.Green) rl.DrawRectangleRec(rl.Rectangle{X: centerX, Y: 0, Width: centerX, Height: centerY}, rl.Green)
rl.DrawRectangleRec(rl.Rectangle{X: 0, Y: centerY, Width: centerX, Height: centerY}, rl.Blue) rl.DrawRectangleRec(rl.Rectangle{X: 0, Y: centerY, Width: centerX, Height: centerY}, rl.Blue)
rl.DrawRectangleRec(rl.Rectangle{X: centerX, Y: centerY, Width: centerX, Height: centerY}, rl.White) rl.DrawRectangleRec(rl.Rectangle{X: centerX, Y: centerY, Width: centerX, Height: centerY}, rl.White)
rl.DrawLine(0, 0, ctx.SourceWidth, ctx.SourceHeight, rl.Black) rl.DrawLine(0, 0, ctx.Graphics.GetGraphicsWidth(), ctx.Graphics.GetGraphicsHeight(), rl.Black)
rl.PushMatrix() rl.PushMatrix()
rl.Translatef(centerX, centerY, 0) rl.Translatef(centerX, centerY, 0)