mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-09-04 10:16:57 +00:00
fix(misc): label wheel beta slices
This commit is contained in:
@@ -3,7 +3,9 @@ package misc
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"image"
|
||||
"image/gif"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -290,6 +292,51 @@ func TestWheelOfNamesBeta_CurrentOptionTracksPointer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWheelOfNamesBeta_PointerPointsIntoWheel(t *testing.T) {
|
||||
img := renderWheelBetaFrame([]string{"Alice", "Bob"}, 0, finalWheelRotation(2, 0), false)
|
||||
cx := wheelBetaSize / 2
|
||||
tipY := wheelBetaSize/2 - wheelBetaRadius
|
||||
if got := img.ColorIndexAt(cx, tipY); got != 1 {
|
||||
t.Fatalf("pointer tip color = %d, want 1", got)
|
||||
}
|
||||
if got := img.ColorIndexAt(cx+5, tipY-10); got != 1 {
|
||||
t.Fatalf("pointer shoulder color = %d, want 1", got)
|
||||
}
|
||||
if got := img.ColorIndexAt(cx+5, tipY); got == 1 {
|
||||
t.Fatalf("pointer tip is too wide at color index %d", got)
|
||||
}
|
||||
if got := img.ColorIndexAt(cx+12, tipY+20); got == 1 {
|
||||
t.Fatalf("pointer widens below wheel edge at color index %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWheelOfNamesBeta_DrawsOptionLabelsInsideSlices(t *testing.T) {
|
||||
options := []string{"Student", "Teacher", "Parent", "Staff"}
|
||||
rotation := 0.0
|
||||
img := renderWheelBetaFrame(options, 0, rotation, false)
|
||||
segment := 2 * math.Pi / float64(len(options))
|
||||
angle := rotation + segment/2
|
||||
centerX := wheelBetaSize/2 + int(math.Round(math.Cos(angle)*float64(wheelBetaSliceLabelRadius)))
|
||||
baselineY := wheelBetaSize/2 + int(math.Round(math.Sin(angle)*float64(wheelBetaSliceLabelRadius))) + wheelBetaSliceLabelBaselineOffset
|
||||
bounds := image.Rect(centerX-28, baselineY-14, centerX+28, baselineY+2)
|
||||
if got := countColorIndex(img, bounds, 1); got == 0 {
|
||||
t.Fatalf("slice label dark pixels = %d, want > 0", got)
|
||||
}
|
||||
}
|
||||
|
||||
func countColorIndex(img *image.Paletted, bounds image.Rectangle, colorIndex byte) int {
|
||||
bounds = bounds.Intersect(img.Bounds())
|
||||
count := 0
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
if img.ColorIndexAt(x, y) == colorIndex {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func TestWheelOfNamesBeta_SendsAnimationWithoutSpoilingCaption(t *testing.T) {
|
||||
rb, _ := installMisc(t, 999)
|
||||
rb.Bot.ProcessUpdate(context.Background(), testutil.NewPrivateMessage(7, "/wheelofnamesbeta Alice"))
|
||||
|
||||
@@ -8,11 +8,6 @@ import (
|
||||
"image/draw"
|
||||
"image/gif"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/basicfont"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -102,14 +97,15 @@ func renderWheelBetaFrame(options []string, winner int, rotation float64, reveal
|
||||
}
|
||||
}
|
||||
|
||||
drawWheelSliceLabels(img, options, rotation)
|
||||
drawCircle(img, cx, cy, 24, 2)
|
||||
drawCircle(img, cx, cy, 18, 1)
|
||||
drawPointer(img, cx, cy-wheelBetaRadius-10)
|
||||
drawPointer(img, cx, cy-wheelBetaRadius)
|
||||
drawCenteredText(img, "WHEELOFNAMES BETA", cy+wheelBetaRadius+34, 1)
|
||||
label := "CURRENT"
|
||||
value := asciiWheelText(options[currentWheelBetaIndex(len(options), rotation)], 28)
|
||||
if reveal {
|
||||
label = "WINNER"
|
||||
label = "RESULT"
|
||||
value = asciiWheelText(options[winner], 28)
|
||||
}
|
||||
drawStatusBand(img, label, value)
|
||||
@@ -133,66 +129,3 @@ func normalizeAngle(theta float64) float64 {
|
||||
}
|
||||
return theta
|
||||
}
|
||||
|
||||
func drawCircle(img *image.Paletted, cx, cy, radius int, colorIndex byte) {
|
||||
r2 := radius * radius
|
||||
for y := cy - radius; y <= cy+radius; y++ {
|
||||
for x := cx - radius; x <= cx+radius; x++ {
|
||||
dx, dy := x-cx, y-cy
|
||||
if dx*dx+dy*dy <= r2 {
|
||||
img.SetColorIndex(x, y, colorIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func drawPointer(img *image.Paletted, cx, tipY int) {
|
||||
for y := 0; y < 34; y++ {
|
||||
half := y / 2
|
||||
for x := cx - half; x <= cx+half; x++ {
|
||||
img.SetColorIndex(x, tipY+y, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func drawStatusBand(img *image.Paletted, label, value string) {
|
||||
for y := 230; y < 282; y++ {
|
||||
for x := 28; x < wheelBetaSize-28; x++ {
|
||||
img.SetColorIndex(x, y, 2)
|
||||
}
|
||||
}
|
||||
drawCenteredText(img, label, 250, 1)
|
||||
drawCenteredText(img, value, 270, 1)
|
||||
}
|
||||
|
||||
func drawCenteredText(img *image.Paletted, text string, baselineY int, colorIndex byte) {
|
||||
face := basicfont.Face7x13
|
||||
width := font.MeasureString(face, text).Ceil()
|
||||
x := (wheelBetaSize - width) / 2
|
||||
drawer := font.Drawer{
|
||||
Dst: img,
|
||||
Src: image.NewUniform(wheelBetaPalette[colorIndex]),
|
||||
Face: face,
|
||||
Dot: fixed.P(x, baselineY),
|
||||
}
|
||||
drawer.DrawString(text)
|
||||
}
|
||||
|
||||
func asciiWheelText(s string, limit int) string {
|
||||
var b strings.Builder
|
||||
for _, r := range s {
|
||||
if b.Len() >= limit {
|
||||
break
|
||||
}
|
||||
if r < 32 || r > 126 {
|
||||
b.WriteByte('?')
|
||||
continue
|
||||
}
|
||||
b.WriteRune(r)
|
||||
}
|
||||
out := strings.TrimSpace(b.String())
|
||||
if out == "" {
|
||||
return "winner"
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"image"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/basicfont"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
const (
|
||||
wheelBetaSliceLabelRadius = 64
|
||||
wheelBetaSliceLabelBaselineOffset = 5
|
||||
)
|
||||
|
||||
func drawWheelSliceLabels(img *image.Paletted, options []string, rotation float64) {
|
||||
if len(options) == 0 {
|
||||
return
|
||||
}
|
||||
cx, cy := wheelBetaSize/2, wheelBetaSize/2
|
||||
segment := 2 * math.Pi / float64(len(options))
|
||||
limit := wheelBetaSliceLabelLimit(len(options))
|
||||
for idx, option := range options {
|
||||
angle := rotation + (float64(idx)+0.5)*segment
|
||||
centerX := cx + int(math.Round(math.Cos(angle)*float64(wheelBetaSliceLabelRadius)))
|
||||
baselineY := cy + int(math.Round(math.Sin(angle)*float64(wheelBetaSliceLabelRadius))) + wheelBetaSliceLabelBaselineOffset
|
||||
text := asciiWheelText(option, limit)
|
||||
drawCenteredTextAt(img, text, centerX+1, baselineY+1, 2)
|
||||
drawCenteredTextAt(img, text, centerX, baselineY, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func wheelBetaSliceLabelLimit(optionCount int) int {
|
||||
switch {
|
||||
case optionCount <= 2:
|
||||
return 14
|
||||
case optionCount <= 4:
|
||||
return 11
|
||||
case optionCount <= 6:
|
||||
return 8
|
||||
default:
|
||||
return 6
|
||||
}
|
||||
}
|
||||
|
||||
func drawCircle(img *image.Paletted, cx, cy, radius int, colorIndex byte) {
|
||||
r2 := radius * radius
|
||||
for y := cy - radius; y <= cy+radius; y++ {
|
||||
for x := cx - radius; x <= cx+radius; x++ {
|
||||
dx, dy := x-cx, y-cy
|
||||
if dx*dx+dy*dy <= r2 {
|
||||
img.SetColorIndex(x, y, colorIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func drawPointer(img *image.Paletted, cx, tipY int) {
|
||||
for y := 0; y < 34; y++ {
|
||||
half := y / 2
|
||||
rowY := tipY - y
|
||||
for x := cx - half; x <= cx+half; x++ {
|
||||
img.SetColorIndex(x, rowY, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func drawStatusBand(img *image.Paletted, label, value string) {
|
||||
for y := 230; y < 282; y++ {
|
||||
for x := 28; x < wheelBetaSize-28; x++ {
|
||||
img.SetColorIndex(x, y, 2)
|
||||
}
|
||||
}
|
||||
drawCenteredText(img, label, 250, 1)
|
||||
drawCenteredText(img, value, 270, 1)
|
||||
}
|
||||
|
||||
func drawCenteredText(img *image.Paletted, text string, baselineY int, colorIndex byte) {
|
||||
drawCenteredTextAt(img, text, wheelBetaSize/2, baselineY, colorIndex)
|
||||
}
|
||||
|
||||
func drawCenteredTextAt(img *image.Paletted, text string, centerX, baselineY int, colorIndex byte) {
|
||||
face := basicfont.Face7x13
|
||||
width := font.MeasureString(face, text).Ceil()
|
||||
x := centerX - width/2
|
||||
drawer := font.Drawer{
|
||||
Dst: img,
|
||||
Src: image.NewUniform(wheelBetaPalette[colorIndex]),
|
||||
Face: face,
|
||||
Dot: fixed.P(x, baselineY),
|
||||
}
|
||||
drawer.DrawString(text)
|
||||
}
|
||||
|
||||
func asciiWheelText(s string, limit int) string {
|
||||
var b strings.Builder
|
||||
for _, r := range s {
|
||||
if b.Len() >= limit {
|
||||
break
|
||||
}
|
||||
if r < 32 || r > 126 {
|
||||
b.WriteByte('?')
|
||||
continue
|
||||
}
|
||||
b.WriteRune(r)
|
||||
}
|
||||
out := strings.TrimSpace(b.String())
|
||||
if out == "" {
|
||||
return "winner"
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user