-- helpers.lua — shared palette + pixel primitives for asset generation -- Usage: local H = dofile(ROOT .. "tools/aseprite/helpers.lua") -- All draw helpers operate on an Aseprite Image via a "pen" table bound to an origin. local H = {} H.ROOT = "/Users/leidianyayi/Documents/GitHub/ModelTest20260714/Kimi-K3/" -- ---------------------------------------------------------------------------- -- Candy palette (CONTRACT §1) + white-layer grays + a few flat utility shades. -- Stored as {r,g,b}. No gradients, no AA — every pixel is one of these. -- ---------------------------------------------------------------------------- H.P = { -- grass greens grass = {125, 201, 94}, -- #7DC95E grass_d = { 95, 179, 74}, -- #5FB34A grass_dd = { 74, 148, 58}, grass_l = {158, 216, 128}, -- cream cream = {255, 243, 214}, -- #FFF3D6 cream_d = {238, 220, 178}, cream_dd = {216, 196, 150}, -- honey orange honey = {245, 166, 35}, -- #F5A623 honey_d = {224, 143, 18}, honey_dd = {196, 120, 10}, honey_l = {250, 193, 96}, -- sky blue sky = {143, 211, 255}, -- #8FD3FF sky_d = {110, 186, 240}, -- stone road stone = {232, 220, 200}, -- #E8DCC8 stone_d = {206, 190, 162}, stone_dd = {178, 160, 130}, stone_l = {245, 237, 224}, -- lake water water = {110, 193, 228}, -- #6EC1E4 water_d = { 79, 168, 216}, -- #4FA8D8 water_dd = { 56, 140, 190}, water_l = {158, 216, 242}, -- pink pink = {247, 183, 203}, -- #F7B7CB pink_d = {232, 150, 176}, pink_dd = {208, 120, 150}, pink_l = {252, 208, 222}, -- brown (trunk / wood dark) brown = {139, 90, 43}, -- #8B5A2B brown_d = {108, 68, 30}, brown_dd = { 84, 52, 22}, brown_l = {172, 122, 72}, -- dirt road dirt = {205, 172, 122}, dirt_d = {182, 148, 98}, dirt_l = {220, 190, 146}, -- white-layer grays (CONTRACT §1) w_hi = {245, 245, 245}, -- #F5F5F5 w_mid = {218, 218, 218}, -- #DADADA w_lo = {189, 189, 189}, -- #BDBDBD w_dk = {150, 150, 150}, w_dd = {110, 110, 110}, -- reds red = {226, 87, 77}, red_d = {198, 66, 56}, red_l = {240, 130, 120}, -- blues blue = { 91, 155, 213}, blue_d = { 62, 125, 190}, blue_l = {140, 190, 232}, -- yellows yellow = {247, 214, 92}, yellow_d = {224, 186, 55}, yellow_l = {252, 228, 140}, -- foliage leaf = { 86, 168, 78}, leaf_d = { 64, 138, 60}, leaf_dd = { 46, 108, 44}, leaf_l = {120, 192, 110}, -- wood wood = {201, 152, 96}, wood_d = {176, 126, 74}, wood_dd = {150, 104, 58}, wood_l = {220, 178, 124}, -- stone wall (cool gray) swall = {196, 190, 178}, swall_d = {164, 158, 146}, swall_dd = {134, 128, 116}, swall_l = {214, 209, 200}, -- berry berry = {206, 62, 96}, berry_d = {172, 44, 76}, -- misc skin = {255, 224, 190}, skin_d = {238, 198, 156}, hair = {120, 84, 52}, hair_d = { 96, 66, 40}, hair_l = {152, 112, 74}, black = { 54, 44, 40}, ink = { 40, 34, 30}, white = {255, 255, 255}, grey = {150, 150, 150}, grey_d = {108, 108, 108}, grey_l = {196, 196, 196}, screen = { 44, 58, 54}, screen_l = {120, 220, 160}, metal = {178, 184, 190}, metal_d = {140, 146, 152}, flame = {255, 214, 92}, flame_d = {245, 140, 40}, leaf2 = { 60, 150, 70}, } -- make an Aseprite color from a palette name (or {r,g,b} table) + optional alpha function H.col(name, a) local t = name if type(name) == "string" then t = H.P[name] end return app.pixelColor.rgba(t[1], t[2], t[3], a or 255) end -- ---------------------------------------------------------------------------- -- Pen: a table of draw methods bound to an image + origin (ox, oy). -- All coordinates passed to pen methods are LOCAL to the origin. -- ---------------------------------------------------------------------------- function H.at(img, ox, oy) local t = { img = img, ox = ox, oy = oy } function t.p(x, y, c) img:drawPixel(ox + x, oy + y, c) end function t.pn(x, y, name, a) img:drawPixel(ox + x, oy + y, H.col(name, a)) end function t.rect(x, y, w, h, c) for j = 0, h - 1 do for i = 0, w - 1 do img:drawPixel(ox + x + i, oy + y + j, c) end end end function t.rectn(x, y, w, h, name, a) local c = H.col(name, a) for j = 0, h - 1 do for i = 0, w - 1 do img:drawPixel(ox + x + i, oy + y + j, c) end end end function t.hl(x, y, len, c) for i = 0, len - 1 do img:drawPixel(ox + x + i, oy + y, c) end end function t.hln(x, y, len, name, a) local c = H.col(name, a); for i = 0, len - 1 do img:drawPixel(ox + x + i, oy + y, c) end end function t.vl(x, y, len, c) for j = 0, len - 1 do img:drawPixel(ox + x, oy + y + j, c) end end function t.vln(x, y, len, name, a) local c = H.col(name, a); for j = 0, len - 1 do img:drawPixel(ox + x, oy + y + j, c) end end -- outline rectangle function t.frame(x, y, w, h, c) t.hl(x, y, w, c); t.hl(x, y + h - 1, w, c) t.vl(x, y, h, c); t.vl(x + w - 1, y, h, c) end function t.framen(x, y, w, h, name, a) t.frame(x, y, w, h, H.col(name, a)) end -- filled disc (filled circle), integer radius function t.disc(cx, cy, r, c) for yy = -r, r do for xx = -r, r do if xx * xx + yy * yy <= r * r then t.p(cx + xx, cy + yy, c) end end end end function t.discn(cx, cy, r, name, a) t.disc(cx, cy, r, H.col(name, a)) end -- filled ellipse function t.ellipse(cx, cy, rx, ry, c) for yy = -ry, ry do for xx = -rx, rx do if (xx * xx) * (ry * ry) + (yy * yy) * (rx * rx) <= (rx * rx) * (ry * ry) then t.p(cx + xx, cy + yy, c) end end end end function t.ellipsen(cx, cy, rx, ry, name, a) t.ellipse(cx, cy, rx, ry, H.col(name, a)) end return t end -- pen for a 16x16 tile by id (tileset is 16 columns) function H.tile(img, id) local ox = (id % 16) * 16 local oy = math.floor(id / 16) * 16 return H.at(img, ox, oy) end -- ---------------------------------------------------------------------------- -- Motif helpers (drawn through a pen `t`, local coords) -- ---------------------------------------------------------------------------- -- a small 5-petal flower centered at (cx,cy) function H.flower(t, cx, cy, petal, center) local pc = H.col(petal); local cc = H.col(center or "yellow") t.p(cx, cy - 1, pc); t.p(cx, cy + 1, pc) t.p(cx - 1, cy, pc); t.p(cx + 1, cy, pc) t.p(cx, cy, cc) end -- a window with brown frame + sky panes, top-left (x,y), size w x h function H.window(t, x, y, w, h, glass) glass = glass or "sky" t.rectn(x, y, w, h, "brown_d") t.rectn(x + 1, y + 1, w - 2, h - 2, glass) -- mullions local mx = x + math.floor(w / 2) local my = y + math.floor(h / 2) t.vln(mx, y + 1, h - 2, "brown_d") t.hln(x + 1, my, w - 2, "brown_d") -- glass highlight t.pn(x + 2, y + 2, "sky_l" == glass and "white" or "water_l") end -- ---------------------------------------------------------------------------- -- Mirror pen: wraps a pen `t`, flipping x within a `w`-wide frame. -- Lets one directional drawing serve both left and right facings. -- ---------------------------------------------------------------------------- function H.mirrorX(t, w) local m = {} function m.p(x, y, c) t.p(w - 1 - x, y, c) end function m.pn(x, y, name, a) t.pn(w - 1 - x, y, name, a) end function m.rect(x, y, ww, h, c) t.rect(w - x - ww, y, ww, h, c) end function m.rectn(x, y, ww, h, name, a) t.rectn(w - x - ww, y, ww, h, name, a) end function m.hl(x, y, len, c) t.hl(w - x - len, y, len, c) end function m.hln(x, y, len, name, a) t.hln(w - x - len, y, len, name, a) end function m.vl(x, y, len, c) t.vl(w - 1 - x, y, len, c) end function m.vln(x, y, len, name, a) t.vln(w - 1 - x, y, len, name, a) end function m.framen(x, y, ww, h, name, a) t.framen(w - x - ww, y, ww, h, name, a) end function m.discn(cx, cy, r, name, a) t.discn(w - 1 - cx, cy, r, name, a) end function m.ellipsen(cx, cy, rx, ry, name, a) t.ellipsen(w - 1 - cx, cy, rx, ry, name, a) end return m end -- create a fresh RGB sprite + return (sprite, image) function H.new(w, h) local spr = Sprite(w, h, ColorMode.RGB) local img = app.activeImage return spr, img end return H