// drawFrequonsFourier draws the frequency-domain representation of Frequons. func drawFrequonsFourier(pm nimble.PixMap) { c := universe.Zoo h := harmonicStorage[:len(c)] var ampScale float32 if autoGain.Value { // Compute L1 norm of amplitudes norm := float32(0) for i := range c { norm += math32.Abs(c[i].Amplitude) } ampScale = 1 / norm } else { ampScale = 1 / float32(len(c)) } fracX, fracY := universe.BoxFraction() fracX *= zoomCompression fracY *= zoomCompression sizeX, sizeY := pm.Size() // Set up harmonics // (cx,cy) is center of fourier view cx, cy := 0.5*float32(sizeX)*fracX, 0.5*float32(sizeY)*fracY α, β := -0.5*cx, -0.5*cy ωScale := 200. / float32(sizeX*sizeY) for i := range h { ωx := (c[i].Sx - cx) * ωScale ωy := (c[i].Sy - cy) * ωScale h[i].Ωx = ωx h[i].Ωy = ωy h[i].Phase = α*ωx + β*ωy + phaseRoll // Scale amplitude so that DFT values fit within domain of color lookup table. h[i].Amplitude = c[i].Amplitude * ampScale } marginX := int32(math32.Round(0.5 * float32(sizeX) * (1 - fracX))) marginY := int32(math32.Round(0.5 * float32(sizeY) * (1 - fracY))) fourier.Draw(pm.Intersect(nimble.Rect{ Left: marginX, Right: sizeX - marginX, Top: marginY, Bottom: sizeY - marginY, }), h, universe.Scheme()) if marginX != 0 || marginY != 0 { pm.DrawRect(nimble.Rect{Left: 0, Right: sizeX, Top: 0, Bottom: marginY}, nimble.Black) pm.DrawRect(nimble.Rect{Left: 0, Right: sizeX, Top: sizeY - marginY, Bottom: sizeY}, nimble.Black) pm.DrawRect(nimble.Rect{Left: 0, Right: marginX, Top: marginY, Bottom: sizeY - marginY}, nimble.Black) pm.DrawRect(nimble.Rect{Left: sizeX - marginX, Right: sizeX, Top: marginY, Bottom: sizeY - marginY}, nimble.Black) } }
func (context) Render(pm nimble.PixMap) { dt := updateClock() // Advance the boot sequence advanceBootSequence(dt) // Draw dividers for i, r := range divider { if i < dividerCount { pm.DrawRect(r, nimble.White) } else { pm.DrawRect(r, nimble.Black) } } if fourierIsVisible { // Update universe xf, yf := fourierPort.RelativeToLeftTop(mouseX, mouseY) switch universe.Update(dt, xf, yf) { case universe.GameWin: youWin() case universe.GameLose: youLose() } updateZoom(dt) // Fourier view if rollPhase.Value { updatePhaseRoll(dt) } drawFrequonsFourier(pm.Intersect(fourierPort)) drawFrequonsSpatial(pm.Intersect(fourierPort), xf, yf) tallyFourierFrame() } else { // Teletype view teletype.Draw(pm.Intersect(fourierPort)) } // Fall view if fallIsVisible { inv := invStorage[:len(universe.Zoo)-1] for k := range inv { c := &universe.Zoo[k+1] inv[k] = fall.Invader{ Progress: c.Progress, Amplitude: c.Amplitude, Color: pastels.Pixel(0, int32(c.Id)), } } fall.Draw(pm.Intersect(fallPort), inv) } else { pm.DrawRect(fallPort, nimble.Black) } // Radar view if radarIsVisible { radar.Draw(pm.Intersect(radarPort), universe.Scheme(), radarIsRunning) } else { pm.DrawRect(radarPort, nimble.Black) } // Score if scoreIsVisible { score.Draw(pm.Intersect(scorePort), universe.NKill()) } else { pm.DrawRect(scorePort, nimble.Black) } // Menu bar if len(menuBar) > 0 { menu.DrawMenuBar(pm, menuBar) } // Cursor showCursor := true switch currentMode { case modeGame: showCursor = false case modeTraining: showCursor = !fourierPort.Contains(mouseX, mouseY) } if showCursor != cursorIsVisible { nimble.ShowCursor(showCursor) cursorIsVisible = showCursor } }