diff --git a/cmd/tins2021/appcontext.go b/cmd/tins2021/appcontext.go index 392bef4..2038739 100644 --- a/cmd/tins2021/appcontext.go +++ b/cmd/tins2021/appcontext.go @@ -15,9 +15,11 @@ type appContext struct { Score *tins2021.ScoreState Debug bool - StarTexture tins2021.AnimatedTexture - HeartTexture tins2021.AnimatedTexture - MonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture + StarTexture tins2021.AnimatedTexture + HeartTexture tins2021.AnimatedTexture + MonsterTextureNames map[tins2021.MonsterType]string + MonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture + DyingMonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture Audio *AudioPlayer MenuMusic *Music @@ -28,17 +30,27 @@ type appContext struct { func newAppContext(ctx ui.Context, settings *tins2021.Settings, score *tins2021.ScoreState, setView func(ui.Control)) *appContext { textures := textureGenerator{} app := &appContext{ - setView: setView, - Settings: settings, - Audio: NewAudioPlayer(ctx.Resources(), "resources/sounds/"), - Score: score, - StarTexture: newAnimatedTexture(ctx, "star", defaultAnimationFrames, textures.Star), - HeartTexture: newAnimatedTexture(ctx, "heart", defaultAnimationFrames, textures.Heart), - MonsterTextures: map[tins2021.MonsterType]tins2021.AnimatedTexture{ - tins2021.MonsterTypeStraight: newAnimatedTexture(ctx, "straight-walking-monster", defaultAnimationFrames, textures.StraightWalkingMonster), - tins2021.MonsterTypeRandom: newAnimatedTexture(ctx, "random-walking-monster", defaultAnimationFrames, textures.RandomWalkingMonster), - tins2021.MonsterTypeChaser: newAnimatedTexture(ctx, "chasing-monster", defaultAnimationFrames, textures.ChasingMonster), - }, + setView: setView, + Settings: settings, + Audio: NewAudioPlayer(ctx.Resources(), "resources/sounds/"), + Score: score, + StarTexture: newAnimatedTexture(ctx, "star", defaultAnimationFrames, textures.Star), + HeartTexture: newAnimatedTexture(ctx, "heart", defaultAnimationFrames, textures.Heart), + MonsterTextureNames: map[tins2021.MonsterType]string{}, + MonsterTextures: map[tins2021.MonsterType]tins2021.AnimatedTexture{}, + DyingMonsterTextures: map[tins2021.MonsterType]tins2021.AnimatedTexture{}, + } + + monsterNames := map[tins2021.MonsterType]string{ + tins2021.MonsterTypeStraight: "straight-walking", + tins2021.MonsterTypeRandom: "random-walking", + tins2021.MonsterTypeChaser: "chasing", + } + for typ, name := range monsterNames { + textureName := fmt.Sprintf("%s-monster", name) + app.MonsterTextureNames[typ] = textureName + app.MonsterTextures[typ] = newAnimatedTexture(ctx, textureName, defaultAnimationFrames, textures.Monster(typ)) + app.DyingMonsterTextures[typ] = newAnimatedTexture(ctx, fmt.Sprintf("%s-dying-monster", name), defaultAnimationFrames, textures.DyingMonster(typ)) } return app diff --git a/cmd/tins2021/info.go b/cmd/tins2021/info.go index 36b87bb..2c0ab4e 100644 --- a/cmd/tins2021/info.go +++ b/cmd/tins2021/info.go @@ -44,6 +44,7 @@ func (l *infoLegend) Render(ctx ui.Context) { } func newInfo(app *appContext, ctx ui.Context) ui.Control { + monsterName := func(typ tins2021.MonsterType) string { return app.MonsterTextureNames[typ] } legend := ui.BuildStackPanel(ui.OrientationVertical, func(p *ui.StackPanel) { p.AddChild(&infoLegend{ Icon: ctx.Textures().ScaledByName("star", infoLegendIconSize), @@ -54,15 +55,15 @@ func newInfo(app *appContext, ctx ui.Context) ui.Control { Description: "Gives (back) a life.", }) p.AddChild(&infoLegend{ - Icon: ctx.Textures().ScaledByName("straight-walking-monster", infoLegendIconSize), + Icon: ctx.Textures().ScaledByName(monsterName(tins2021.MonsterTypeStraight), infoLegendIconSize), Description: "Monster that walks over a fixed diagonal.", }) p.AddChild(&infoLegend{ - Icon: ctx.Textures().ScaledByName("random-walking-monster", infoLegendIconSize), + Icon: ctx.Textures().ScaledByName(monsterName(tins2021.MonsterTypeRandom), infoLegendIconSize), Description: "Monster that walks randomly.", }) p.AddChild(&infoLegend{ - Icon: ctx.Textures().ScaledByName("chasing-monster", infoLegendIconSize), + Icon: ctx.Textures().ScaledByName(monsterName(tins2021.MonsterTypeChaser), infoLegendIconSize), Description: "Monster that walks towards you.", }) }) diff --git a/cmd/tins2021/levelcontroller.go b/cmd/tins2021/levelcontroller.go index fa5d05a..844aa7c 100644 --- a/cmd/tins2021/levelcontroller.go +++ b/cmd/tins2021/levelcontroller.go @@ -3,6 +3,7 @@ package main import ( "fmt" "image/color" + "log" "math/rand" "strconv" "time" @@ -24,8 +25,10 @@ type levelController struct { Inverted tins2021.NamedTexture Animations map[string]*tins2021.Animations - IdleMonsters *tins2021.Animations - MovingMonsters *tins2021.Animations + IdleMonsters *tins2021.Animations + MovingMonsters *tins2021.Animations + DyingMonsters *tins2021.Animations + DyingMonsterTypes map[geom.Point]tins2021.MonsterType SmallFont *tins2021.BitmapFont @@ -134,17 +137,26 @@ func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool { } } + monsterHit := func(hit *tins2021.MonsterHit) { + r.app.Audio.PlaySample("player_hurt.mp3") + if hit == nil { + log.Printf("player was hit by monster but we don't know exactly where?\n") + } + r.DyingMonsters.Frame(hit.Position) + r.DyingMonsterTypes[hit.Position] = hit.Type + } + switch e := e.(type) { case *ui.KeyDownEvent: dir, ok := r.Controls[e.Key] if ok { stars, lives := r.Level.StarsCollected, r.Level.Lives - r.Level.MovePlayer(dir) + _, hit := r.Level.MovePlayer(dir) switch { case r.Level.StarsCollected > stars: r.app.Audio.PlaySample("player_collect_star.mp3") case r.Level.Lives < lives: - r.app.Audio.PlaySample("player_hurt.mp3") + monsterHit(hit) case r.Level.Lives > lives: r.app.Audio.PlaySample("player_collect_heart.mp3") } @@ -161,6 +173,7 @@ func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool { } r.IdleMonsters.Update() r.MovingMonsters.Update() + r.DyingMonsters.Update() var jumped []geom.Point for pos, animation := range r.MovingMonsters.Values { if animation.Frame < 40 { // after 40 frames the player hit is checked @@ -168,10 +181,10 @@ func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool { } target := r.Level.MonsterTargets[pos] if target == r.Level.Player { // player is hit + monsterHit(&tins2021.MonsterHit{Position: target, Type: r.Level.Monsters[pos].Type()}) r.Level.DestroyMonster(pos) jumped = append(jumped, pos) r.Level.DecrementLive() - r.app.Audio.PlaySample("player_hurt.mp3") checkGameOver() continue } @@ -224,6 +237,8 @@ func (r *levelController) Play(level *tins2021.Level) { } r.IdleMonsters = tins2021.NewAnimations(40*time.Millisecond, 100, false, false) r.MovingMonsters = tins2021.NewAnimations(16*time.Millisecond, 50, false, false) + r.DyingMonsters = tins2021.NewAnimations(16*time.Millisecond, 20, false, false) + r.DyingMonsterTypes = map[geom.Point]tins2021.MonsterType{} for monster := range level.Monsters { r.IdleMonsters.Frame(monster) } @@ -272,6 +287,10 @@ func (r levelController) Render(ctx ui.Context) { for typ, animation := range r.app.MonsterTextures { monsterTextures[typ] = animation.Scale(scale * .4) } + dyingMonsterTextures := map[tins2021.MonsterType]tins2021.AnimatedTexture{} + for typ, animation := range r.app.DyingMonsterTextures { + dyingMonsterTextures[typ] = animation.Scale(scale * .4) + } propHeight := star.FrameSize(0).Y propOffset := geom.PtF32(-.5*float32(propHeight), -.8*float32(propHeight)) @@ -338,6 +357,22 @@ func (r levelController) Render(ctx ui.Context) { } } + var died []geom.Point + for pos, monster := range r.DyingMonsterTypes { + frame := r.DyingMonsters.Frame(pos) + if frame == 20 { + died = append(died, pos) + continue + } + texture := dyingMonsterTextures[monster] + _, platformPos := positionOfTile(pos) + texture.Draw(renderer, platformPos.Add(propOffset), frame) + } + for _, pos := range died { + delete(r.DyingMonsters.Values, pos) + delete(r.DyingMonsterTypes, pos) + } + textColor := ctx.Style().Palette.Text scoreFont := ctx.Fonts().Font("score") fontOffsetY := .5 * (float32(propHeight) - scoreFont.Height()) diff --git a/cmd/tins2021/resources/textures/chasing-dying-monster.png b/cmd/tins2021/resources/textures/chasing-dying-monster.png new file mode 100644 index 0000000..2bbe948 Binary files /dev/null and b/cmd/tins2021/resources/textures/chasing-dying-monster.png differ diff --git a/cmd/tins2021/resources/textures/random-walking-dying-monster.png b/cmd/tins2021/resources/textures/random-walking-dying-monster.png new file mode 100644 index 0000000..7d0dba0 Binary files /dev/null and b/cmd/tins2021/resources/textures/random-walking-dying-monster.png differ diff --git a/cmd/tins2021/resources/textures/straight-walking-dying-monster.png b/cmd/tins2021/resources/textures/straight-walking-dying-monster.png new file mode 100644 index 0000000..5e6bdef Binary files /dev/null and b/cmd/tins2021/resources/textures/straight-walking-dying-monster.png differ diff --git a/cmd/tins2021/texturegenerator.go b/cmd/tins2021/texturegenerator.go index 9d375af..715eba8 100644 --- a/cmd/tins2021/texturegenerator.go +++ b/cmd/tins2021/texturegenerator.go @@ -16,12 +16,29 @@ func (textureGenerator) Heart() image.Image { return tins2021.AnimatePolygon(tins2021.CreateHeart(), tins2021.Red, defaultAnimationFrames, tins2021.MeshRotateAnimation{}) } -func (textureGenerator) monster(color string) image.Image { - return tins2021.AnimatePolygon(tins2021.CreateHexagon(), color, defaultAnimationFrames, tins2021.MeshWobbleTransformation{Wobble: 30}) +func (g textureGenerator) MonsterTypeColor(typ tins2021.MonsterType) string { + switch typ { + case tins2021.MonsterTypeStraight: + return tins2021.Green + case tins2021.MonsterTypeRandom: + return tins2021.Blue + case tins2021.MonsterTypeChaser: + return tins2021.Purple + default: + panic("monster does not have a color") + } } -func (g textureGenerator) ChasingMonster() image.Image { return g.monster(tins2021.Purple) } +func (g textureGenerator) Monster(typ tins2021.MonsterType) func() image.Image { + color := g.MonsterTypeColor(typ) + return func() image.Image { + return tins2021.AnimatePolygon(tins2021.CreateHexagon(), color, defaultAnimationFrames, tins2021.MeshWobbleTransformation{Wobble: 30}) + } +} -func (g textureGenerator) RandomWalkingMonster() image.Image { return g.monster(tins2021.Blue) } - -func (g textureGenerator) StraightWalkingMonster() image.Image { return g.monster(tins2021.Green) } +func (g textureGenerator) DyingMonster(typ tins2021.MonsterType) func() image.Image { + color := g.MonsterTypeColor(typ) + return func() image.Image { + return tins2021.Animate(color, defaultAnimationFrames, &tins2021.ExplodingHexagonAnimation{}) + } +} diff --git a/level.go b/level.go index ab80919..d02b2a7 100644 --- a/level.go +++ b/level.go @@ -92,10 +92,15 @@ func (l *Level) MoveMonster(target, source geom.Point) { l.DestroyMonster(source) } -func (l *Level) MovePlayer(dir Direction) bool { +type MonsterHit struct { + Position geom.Point + Type MonsterType +} + +func (l *Level) MovePlayer(dir Direction) (bool, *MonsterHit) { towards, allowed := l.CanPlayerMove(dir) if !allowed { - return false + return false, nil } l.Player = towards tile := l.Tiles[towards] @@ -109,13 +114,18 @@ func (l *Level) MovePlayer(dir Direction) bool { tile.Star = false l.Score += 25 } + var hit *MonsterHit if l.Monsters[towards] != nil { + hit = &MonsterHit{ + Position: towards, + Type: l.Monsters[towards].Type(), + } l.DecrementLive() l.DestroyMonster(towards) l.Score -= 5 } l.Score -= 1 // for every move - return true + return true, hit } func (l *Level) Randomize(difficulty int, stars int) { diff --git a/math.go b/math.go new file mode 100644 index 0000000..d4fb69e --- /dev/null +++ b/math.go @@ -0,0 +1,15 @@ +package tins2021 + +import "opslag.de/schobers/geom" + +func Polar(a, r float64) geom.PointF { + return geom.PtF(r*geom.Cos(a), r*geom.Sin(a)) +} + +func PolarUnity(a float64) geom.PointF { + return geom.PtF(geom.Cos(a), geom.Sin(a)) +} + +func Percentage(i, n int) float64 { + return float64(i) / float64(n) +} diff --git a/meshanimation.go b/meshanimation.go index 6a6c89a..0e59482 100644 --- a/meshanimation.go +++ b/meshanimation.go @@ -28,19 +28,17 @@ var ( light = fauxgl.V(.5, 1, .75).Normalize() // light direction ) -func animateMesh(mesh *fauxgl.Mesh, hexColor string, frames int, transform MeshAnimationTransformer) image.Image { +func Animate(hexColor string, frames int, animator MeshAnimator) image.Image { const scale = 4 const s = 1.1 - mesh.BiUnitCube() - matrix := fauxgl.Orthographic(-s, s, -s, s, near, far).Mul(fauxgl.LookAt(eye, center, up)) animation := image.NewNRGBA(image.Rect(0, 0, TextureSize*frames, TextureSize)) threads := ints.Max(1, runtime.NumCPU()) framesC := make(chan int, threads) - wait := parallel(threads, func() { + wait := parallel(1, func() { context := fauxgl.NewContext(TextureSize*scale, TextureSize*scale) color := fauxgl.HexColor(hexColor) @@ -53,9 +51,8 @@ func animateMesh(mesh *fauxgl.Mesh, hexColor string, frames int, transform MeshA shader.AmbientColor = fauxgl.MakeColor(mustHexColor(`#7F7F7F`)) context.Shader = shader - copy := mesh.Copy() - transform.transform(copy, FrameState{Current: i, TotalFrames: frames}) - context.DrawMesh(copy) + mesh := animator.animate(FrameState{Current: i, TotalFrames: frames}) + context.DrawMesh(mesh) frame := resize.Resize(TextureSize, TextureSize, context.Image(), resize.Bilinear) draw.Copy(animation, image.Pt(i*TextureSize, 0), frame, frame.Bounds(), draw.Src, nil) @@ -70,12 +67,12 @@ func animateMesh(mesh *fauxgl.Mesh, hexColor string, frames int, transform MeshA return animation } -func AnimatePolygon(polygon geom.PolygonF, hexColor string, frames int, transform MeshAnimationTransformer) image.Image { - mesh := generateMeshFromPolygon(polygon, .2) - return animateMesh(mesh, hexColor, frames, transform) +func AnimatePolygon(polygon geom.PolygonF, hexColor string, frames int, transformer MeshTransformer) image.Image { + animation := newMeshAnimation(generateMeshFromPolygon(polygon, .2), transformer) + return Animate(hexColor, frames, animation) } -func AnimateSTL(resources ui.PhysicalResources, name, hexColor string, frames int, transform MeshAnimationTransformer) image.Image { +func AnimateSTL(resources ui.PhysicalResources, name, hexColor string, frames int, transformer MeshTransformer) image.Image { path, err := resources.FetchResource(name) if err != nil { panic(err) @@ -84,7 +81,53 @@ func AnimateSTL(resources ui.PhysicalResources, name, hexColor string, frames in if err != nil { panic(err) } - return animateMesh(mesh, hexColor, frames, transform) + return Animate(hexColor, frames, newMeshAnimation(mesh, transformer)) +} + +type ExplodingHexagonAnimation struct { + Biunit fauxgl.Matrix +} + +func rotateMeshAround(mesh *fauxgl.Mesh, around fauxgl.Vector, angle float64) { + mesh.Transform(fauxgl.Translate(fauxgl.V(-around.X, -around.Y, -around.Z))) + mesh.Transform(fauxgl.Rotate(fauxgl.V(0, 1, 0), angle)) + mesh.Transform(fauxgl.Translate(around)) +} + +func (a *ExplodingHexagonAnimation) animate(s FrameState) *fauxgl.Mesh { + ani := s.Animation() + + mesh := fauxgl.NewEmptyMesh() + const parts = 6 + + const oneThird = float64(1) / 3 + const twoThirds = float64(2) / 3 + const da = twoThirds * geom.Pi + partHeight := geom.Sqrt(3) / 2 + for part := 0; part < parts; part++ { + a := 2 * geom.Pi * float64(part) / float64(parts) + + closeLength := (float64(.25) + .75*(1-ani)) + + center := Polar(a, twoThirds*partHeight) + aa := a + ani*geom.Pi + far := center.Add(Polar(aa, oneThird*partHeight)) + farWidth := float64(.5) * (1 - ani) + right := far.Add(Polar(aa-.5*geom.Pi, farWidth)) + left := far.Add(Polar(aa+.5*geom.Pi, farWidth)) + close := center.Add(Polar(aa+geom.Pi, closeLength*twoThirds*partHeight)) + + partMesh := generateMeshFromPolygon(geom.PolF(right, left, close), .2) + rotateMeshAround(partMesh, fauxgl.V(-center.X, -center.Y, 0), ani*geom.Pi) + mesh.Add(partMesh) + } + + if s.Current == 0 { + a.Biunit = mesh.BiUnitCube() + } else { + mesh.Transform(a.Biunit) + } + return mesh } type FrameState struct { @@ -94,7 +137,7 @@ type FrameState struct { func (s FrameState) Animation() float64 { return float64(s.Current) / float64(s.TotalFrames) } -func generateMeshFromPolygon(polygon geom.PolygonF, thickness float64) *fauxgl.Mesh { +func generateTrianglesForPolygon(polygon geom.PolygonF, thickness float64) []*fauxgl.Triangle { vec := func(p geom.PointF, z float64) fauxgl.Vector { return fauxgl.V(p.X, p.Y, z) } tri := fauxgl.NewTriangleForPoints face := func(q, r, s geom.PointF, n float64) *fauxgl.Triangle { @@ -115,7 +158,11 @@ func generateMeshFromPolygon(polygon geom.PolygonF, thickness float64) *fauxgl.M q, r, s, t := vec(p, back), vec(next, back), vec(next, front), vec(p, front) triangles = append(triangles, tri(q, r, s), tri(q, s, t)) } + return triangles +} +func generateMeshFromPolygon(polygon geom.PolygonF, thickness float64) *fauxgl.Mesh { + triangles := generateTrianglesForPolygon(polygon, thickness) mesh := fauxgl.NewTriangleMesh(triangles) return mesh } @@ -130,6 +177,54 @@ func iterate(n int, threads int) <-chan int { return iterator } +type MeshAnimation struct { + MeshTransformer + + mesh *fauxgl.Mesh +} + +func newMeshAnimation(mesh *fauxgl.Mesh, transformer MeshTransformer) *MeshAnimation { + mesh.BiUnitCube() + return &MeshAnimation{transformer, mesh} +} + +func (a *MeshAnimation) animate(s FrameState) *fauxgl.Mesh { + mesh := a.mesh.Copy() + a.MeshTransformer.transform(mesh, s) + return mesh +} + +type MeshAnimator interface { + animate(FrameState) *fauxgl.Mesh +} + +type MeshRotateAnimation struct{} + +func (MeshRotateAnimation) transform(mesh *fauxgl.Mesh, s FrameState) { + mesh.Transform(fauxgl.Rotate(up, 2*geom.Pi*s.Animation())) +} + +type MeshTransformer interface { + transform(*fauxgl.Mesh, FrameState) +} + +type MeshWobbleTransformation struct { + Wobble float64 +} + +func (a MeshWobbleTransformation) wobble(s FrameState) float64 { + animation := float64(s.Current) / float64(s.TotalFrames) + animation += .25 + if animation >= 1 { + animation -= 1 + } + return geom.Abs(animation*4-2) - 1 +} + +func (a MeshWobbleTransformation) transform(mesh *fauxgl.Mesh, s FrameState) { + mesh.Transform(fauxgl.Rotate(up, a.wobble(s)*a.Wobble*geom.Pi/180)) +} + func parallel(n int, action func()) *sync.WaitGroup { wait := &sync.WaitGroup{} wait.Add(n) @@ -142,33 +237,6 @@ func parallel(n int, action func()) *sync.WaitGroup { return wait } -type MeshAnimationTransformer interface { - transform(*fauxgl.Mesh, FrameState) -} - -type MeshRotateAnimation struct{} - -func (MeshRotateAnimation) transform(mesh *fauxgl.Mesh, s FrameState) { - mesh.Transform(fauxgl.Rotate(up, 2*geom.Pi*s.Animation())) -} - -type MeshWobbleTransformation struct { - Wobble float64 -} - -func (a MeshWobbleTransformation) animate(s FrameState) float64 { - animation := float64(s.Current) / float64(s.TotalFrames) - animation += .25 - if animation >= 1 { - animation -= 1 - } - return geom.Abs(animation*4-2) - 1 -} - -func (a MeshWobbleTransformation) transform(mesh *fauxgl.Mesh, s FrameState) { - mesh.Transform(fauxgl.Rotate(up, a.animate(s)*a.Wobble*geom.Pi/180)) -} - func saveMeshSTL(path, name string, mesh *fauxgl.Mesh) error { stl, err := os.Create(path) if err != nil { diff --git a/monsters.go b/monsters.go index a2e672c..b631398 100644 --- a/monsters.go +++ b/monsters.go @@ -4,6 +4,8 @@ import ( "opslag.de/schobers/geom" ) +var AllMonsterTypes = []MonsterType{MonsterTypeStraight, MonsterTypeRandom, MonsterTypeChaser} + type ChasingMonster struct{} func (m ChasingMonster) Type() MonsterType { return MonsterTypeChaser } diff --git a/polygons.go b/polygons.go index 25ad7bd..44316c3 100644 --- a/polygons.go +++ b/polygons.go @@ -13,7 +13,7 @@ func CreateHeart() geom.PolygonF { var polygon geom.PolygonF const segments = 100 for segment := 0; segment < 100; segment++ { - t := 2 * geom.Pi * float64(segment) / segments + t := 2 * geom.Pi * Percentage(segment, segments) st := geom.Sin(t) polygon.Points = append(polygon.Points, geom.PtF( 16*st*st*st, @@ -23,15 +23,15 @@ func CreateHeart() geom.PolygonF { } func CreateHexagon() geom.PolygonF { - var polygon geom.PolygonF - pt := func(rotation float64) geom.PointF { - a := .5*geom.Pi + 2*geom.Pi*rotation - return geom.PtF(geom.Cos(a), geom.Sin(a)) - } const sides = 6 - for side := 0; side < 6; side++ { + return CreateRegularPolygon(6) +} + +func CreateRegularPolygon(sides int) geom.PolygonF { + var polygon geom.PolygonF + for side := 0; side < sides; side++ { polygon.Points = append(polygon.Points, - pt(float64(side)/float64(sides)), + PolarUnity(float64(side)/float64(sides)), ) } return polygon @@ -39,14 +39,10 @@ func CreateHexagon() geom.PolygonF { func CreateStar(sides int) geom.PolygonF { var polygon geom.PolygonF - pt := func(rotation float64) geom.PointF { - a := .5*geom.Pi + 2*geom.Pi*rotation - return geom.PtF(geom.Cos(a), geom.Sin(a)) - } for side := 0; side < sides; side++ { polygon.Points = append(polygon.Points, - pt(float64(side)/float64(sides)), - pt((float64(side)+0.5)/float64(sides)).Mul(.5), + PolarUnity(float64(side)/float64(sides)), + PolarUnity((float64(side)+0.5)/float64(sides)).Mul(.5), ) } return polygon