Files
aego/tests/golden/animation_test.go
sanspie ef59ae297f Stage 11d: light theme (D345)
Theme tokens for every editor color with a literal check, System/Dark/Light preference that follows the OS, light goldens, Scene View overlays that keep viewport colors.
2026-10-25 15:31:22 +03:00

163 lines
4.8 KiB
Go

//go:build aego_golden
package golden
import (
"bytes"
"os"
"testing"
"strconv"
"aego/app/assembly"
"aego/app/assembly/builtin"
"aego/asset"
"aego/core/ids"
"aego/core/mathx"
"aego/editor/core/animedit"
"aego/editor/core/context"
"aego/editor/core/document"
"aego/editor/core/selection"
"aego/editor/core/services"
animpanel "aego/editor/panels/animation"
"aego/modules/animation"
"aego/modules/render2d"
"aego/render/texture"
"aego/scene"
"aego/script/bind"
"aego/ui/dnd"
)
const goldenClipRel = "assets/anim/hero.aeanim"
var goldenClipID = ids.AssetID{Hi: 0xa0a1, Lo: 0x51}
const goldenClip = `anim version=1 length=1 loop=loop fps=12 {
track "" prop="SpriteRenderer.tint" kind=color {
key t=0 v=[1, 1, 1, 1] interp=linear {}
key t=0.5 v=[1, 0.2, 0.2, 1] {}
key t=1 v=[1, 1, 1, 1] {}
}
track "" prop="transform.scale" kind=vec2 {
key t=0 v=[1, 1] {}
key t=0.25 v=[1.2, 0.8] {}
key t=0.75 v=[0.9, 1.1] {}
}
track "" prop="SpriteRenderer.flipX" kind=bool {
key t=0 v=false {}
key t=0.5 v=true {}
}
event t=0.25 name="Footstep" mode=presentation {}
event t=0.5 name="Hit" payload=1 {}
event t=0.75 name="Mark" mode=preview {}
}
`
type goldenFiles map[string][]byte
func (m goldenFiles) Read(rel string) ([]byte, error) {
b, ok := m[rel]
if !ok {
return nil, os.ErrNotExist
}
return bytes.Clone(b), nil
}
func (m goldenFiles) Write(rel string, data []byte) error {
m[rel] = bytes.Clone(data)
return nil
}
type animDocs struct {
m *document.Manager
d *document.SceneDocument
}
func (a animDocs) Doc() *document.SceneDocument { return a.d }
func (a animDocs) Documents() *document.Manager { return a.m }
type animSel struct{ id ids.NodeID }
func (s animSel) Select(...ids.NodeID) {}
func (s animSel) AddToSelection(...ids.NodeID) {}
func (s animSel) ToggleSelection(ids.NodeID) {}
func (s animSel) IsSelected(n ids.NodeID) bool { return n == s.id }
func (s animSel) Selected() []ids.NodeID { return []ids.NodeID{s.id} }
func (s animSel) SelectionState() *selection.Service { return nil }
type animNotices struct{}
func (animNotices) Notify(context.Level, string) {}
func (animNotices) Ask(context.Question) {}
func TestEditorAnimationPanel(t *testing.T) {
env := newEditorEnvSize(t, 960, 240)
p, reg, d, files := animationPanel(t, env)
draw := func(name string) {
t.Helper()
img, err := env.draw(func(full mathx.Rect) {
p.Draw(&services.Frame{UI: env.ui, DnD: &dnd.State{}}, full)
})
checkGolden(t, name, img, err)
}
draw("editor_animation_normal")
svc := reg.For(d)
svc.SetMode(animedit.Preview)
svc.SetTime(0.3)
draw("editor_animation_preview")
svc.SetMode(animedit.Record)
p.Select(animedit.KeyRef{Track: 0, T: 0.5}, animedit.KeyRef{Track: 1, T: 0.25})
draw("editor_animation_record")
svc.SetMode(animedit.Normal)
p.Select()
p.ShowCurves(true, 1, 1)
draw("editor_animation_curves")
p.ShowCurves(false, -1, 0)
files[goldenClipRel] = []byte(spriteClip())
if err := svc.SetClip(goldenClipRel); err != nil {
t.Fatal(err)
}
draw("editor_animation_thumbs")
}
func animationPanel(t *testing.T, env *editorEnv) (*animpanel.Panel, *animedit.Registry, *document.SceneDocument, goldenFiles) {
t.Helper()
types, err := assembly.NewTypes()
if err != nil {
t.Fatal(err)
}
if _, err := builtin.Default().Load(types, bind.New(), assembly.Options{}); err != nil {
t.Fatal(err)
}
m := document.NewManager(document.Options{Root: t.TempDir(), Types: types})
s := scene.New(types, nil, scene.Options{})
h := s.Create(s.Root(), "Player")
*scene.Add[render2d.SpriteRenderer](s, h) = render2d.DefaultSpriteRenderer()
pl := animation.DefaultPlayer()
pl.Clip = animation.ClipRef{ID: goldenClipID}
*scene.Add[animation.AnimationPlayer](s, h) = pl
s.Flush()
d := m.New("main.aescene", s)
files := goldenFiles{goldenClipRel: []byte(goldenClip)}
reg := animedit.NewRegistry(files, nil)
p := animpanel.New(animpanel.Deps{
Anim: reg, Docs: animDocs{m: m, d: d}, Selection: animSel{id: s.ID(h)}, Notices: animNotices{}, Files: files,
PathOf: func(id ids.AssetID) (string, bool) { return goldenClipRel, id == goldenClipID },
Events: func() []string { return []string{"Footstep", "Hit"} },
Thumbs: whiteThumbs{reg: env.ui.Font().White},
})
return p, reg, d, files
}
type whiteThumbs struct{ reg texture.Region }
func (w whiteThumbs) ByID(ids.AssetID) (texture.Region, bool) { return w.reg, !w.reg.IsZero() }
func spriteClip() string {
out := "anim version=1 length=1 loop=loop fps=12 {\n track \"\" prop=\"SpriteRenderer.sprite\" kind=assetRef {\n"
for i := range 4 {
out += " key t=" + strconv.FormatFloat(float64(i)*0.25, 'g', -1, 32) + " v=asset(\"" + asset.BuiltinRunner(i).String() + "\") {}\n"
}
return out + " }\n track \"\" prop=\"transform.position\" kind=vec2 {\n key t=0 v=[0, 0] {}\n key t=1 v=[1, 0] {}\n }\n}\n"
}