Skip to content

Instantly share code, notes, and snippets.

@laiso
Created August 23, 2025 15:15
Show Gist options
  • Save laiso/5fb0d949755c9f7d6c96edd1beea45f7 to your computer and use it in GitHub Desktop.
Save laiso/5fb0d949755c9f7d6c96edd1beea45f7 to your computer and use it in GitHub Desktop.
package com.nothinglondon.sdkdemo.demos.sushi
import android.content.Context
import com.nothing.ketchum.GlyphMatrixManager
import com.nothinglondon.sdkdemo.demos.GlyphMatrixService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
/**
* High-contrast maki-sushi pixel art for a 25x25 monochrome matrix.
* Techniques: bright outline, rice dithering, inner filling dot, rim highlight shimmer.
*/
class ShushiDemoService : GlyphMatrixService("Shushi-Demo") {
private val bgScope = CoroutineScope(Dispatchers.Default)
override fun performOnServiceConnected(
context: Context,
glyphMatrixManager: GlyphMatrixManager
) {
// Subtle loop to add a glint over the fish, improving recognizability of nigiri.
bgScope.launch {
var t = 0
while (isActive) {
val frame = buildNigiriFrame(t)
glyphMatrixManager.setMatrixFrame(frame)
delay(80L)
t = (t + 1) % 32
}
}
}
override fun performOnServiceDisconnected(context: Context) {
bgScope.cancel()
}
private fun buildNigiriFrame(tick: Int): IntArray {
val w = 25
val h = 25
val grid = IntArray(w * h) { 0 }
fun set(x: Int, y: Int, v: Int) {
if (x in 0 until w && y in 0 until h) grid[y * w + x] = v.coerceIn(0, 255)
}
fun insideCapsule(
x: Int,
y: Int,
cx: Double,
cy: Double,
halfRect: Int,
r: Int
): Boolean {
val dx = x - cx
val dy = y - cy
if (abs(dx) <= halfRect && abs(dy) <= r) return true
val leftCx = cx - halfRect
val rightCx = cx + halfRect
val dl = (x - leftCx) * (x - leftCx) + (y - cy) * (y - cy)
val dr = (x - rightCx) * (x - rightCx) + (y - cy) * (y - cy)
return dl <= (r * r) || dr <= (r * r)
}
// Brightness palette (0..255)
val B_PLATE = 50
val B_RICE = 185
val B_RICE_DOT = 215
val B_FISH = 230
val B_FISH_STRIPE = 150
val B_EDGE = 90
// Plate shadow (subtle) under the rice
for (y in 21..22) for (x in 7..17) set(x, y, B_PLATE)
// Rice capsule parameters
val cxRice = w / 2.0
val cyRice = 17.0
val riceHalfRect = 5 // rectangle half-width between rounded ends
val riceR = 3 // end radius and half-height
// Rice fill with light dithering
for (y in 0 until h) {
for (x in 0 until w) {
if (insideCapsule(x, y, cxRice, cyRice, riceHalfRect, riceR)) {
var v = B_RICE
val m = (x * 3 + y * 5) and 7
if (m == 0 || m == 5) v = B_RICE_DOT
set(x, y, v)
}
}
}
// Fish (top slab) parameters
val cxFish = w / 2.0
val cyFish = 13.0
val fishHalfRect = 6
val fishR = 2
// Moving diagonal highlight band across fish
val mod = 14
val phase = tick % mod
for (y in 0 until h) {
for (x in 0 until w) {
if (insideCapsule(x, y, cxFish, cyFish, fishHalfRect, fishR)) {
var v = B_FISH
// Diagonal darker stripes to imply salmon texture
val stripe = ((x - y) and 3) == 0
if (stripe) v = min(v, B_FISH_STRIPE)
// Shimmering highlight band
val band = (x + y - phase + 1280) % mod
if (band in 0..1) v = max(v, 255)
set(x, y, v)
}
}
}
// Thin separation edge (shadow) just below fish
for (y in 1 until h) {
for (x in 0 until w) {
val above = grid[(y - 1) * w + x]
val here = grid[y * w + x]
// If above is fish-ish bright and here is rice-ish, add a darker edge
if (above >= 200 && here in (B_RICE - 20)..(B_RICE_DOT + 10)) {
grid[y * w + x] = B_EDGE
}
}
}
return grid
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment