this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

ellipsis

alice 549905c0 70d6aa7b

+239 -1
+9 -1
tic80_rust/assets/default.lua
··· 29 29 30 30 -- Title and legend 31 31 print("TIC-80 Rust Demo", 8, 6, 15) 32 - print("cls pix line rect rectb circ circb clip print", 8, 16, 14) 32 + print("cls pix line rect rectb circ circb elli ellib tri trib clip print", 8, 16, 14) 33 33 34 34 -- Static card: rect + rectb 35 35 rectb(20, 28, 48, 22, 12) -- border ··· 44 44 local rc = 12 + ((t // 60) % 4) -- slow pulse 12..15 45 45 circb(180, 56, 14, 14) 46 46 circ(180, 56, rc, 10) 47 + 48 + -- Ellipse: border + filled 49 + ellib(200, 92, 10, 6, 12) 50 + elli(200, 92, 8, 4, 11) 51 + 52 + -- Triangles: filled + border 53 + tri(40, 90, 55, 110, 25, 110, 8) 54 + trib(70, 90, 85, 110, 55, 110, 2) 47 55 48 56 -- Clip demo toggles every ~2.5 seconds (150 frames @60 FPS) 49 57 local clipped = (t // 150) % 2 == 0
+129
tic80_rust/src/gfx/framebuffer.rs
··· 292 292 } 293 293 } 294 294 295 + // Ellipse border using midpoint algorithm 296 + pub fn ellib(&mut self, cx: i32, cy: i32, a: i32, b: i32, color: u8) { 297 + if a < 0 || b < 0 { return; } 298 + let c = color & 0x0F; 299 + if a == 0 && b == 0 { let _ = self.set_pixel(cx, cy, c); return; } 300 + 301 + let a2 = (a as i64) * (a as i64); 302 + let b2 = (b as i64) * (b as i64); 303 + 304 + let mut x: i64 = 0; 305 + let mut y: i64 = b as i64; 306 + let mut d = b2 - a2 * (b as i64) + a2 / 4; 307 + while b2 * x <= a2 * y { 308 + let xx = x as i32; let yy = y as i32; 309 + let _ = self.set_pixel(cx + xx, cy + yy, c); 310 + let _ = self.set_pixel(cx - xx, cy + yy, c); 311 + let _ = self.set_pixel(cx + xx, cy - yy, c); 312 + let _ = self.set_pixel(cx - xx, cy - yy, c); 313 + if d < 0 { 314 + d += b2 * (2 * x + 3); 315 + } else { 316 + d += b2 * (2 * x + 3) + a2 * (-2 * y + 2); 317 + y -= 1; 318 + } 319 + x += 1; 320 + } 321 + 322 + x = a as i64; y = 0; d = a2 - b2 * (a as i64) + b2 / 4; 323 + while a2 * y <= b2 * x { 324 + let xx = x as i32; let yy = y as i32; 325 + let _ = self.set_pixel(cx + xx, cy + yy, c); 326 + let _ = self.set_pixel(cx - xx, cy + yy, c); 327 + let _ = self.set_pixel(cx + xx, cy - yy, c); 328 + let _ = self.set_pixel(cx - xx, cy - yy, c); 329 + if d < 0 { 330 + d += a2 * (2 * y + 3); 331 + } else { 332 + d += a2 * (2 * y + 3) + b2 * (-2 * x + 2); 333 + x -= 1; 334 + } 335 + y += 1; 336 + } 337 + } 338 + 339 + // Filled ellipse using horizontal spans 340 + pub fn elli(&mut self, cx: i32, cy: i32, a: i32, b: i32, color: u8) { 341 + if a < 0 || b < 0 { return; } 342 + let c = color & 0x0F; 343 + if a == 0 && b == 0 { let _ = self.set_pixel(cx, cy, c); return; } 344 + if a == 0 { for yy in (cy - b)..=(cy + b) { let _ = self.set_pixel(cx, yy, c); } return; } 345 + if b == 0 { for xx in (cx - a)..=(cx + a) { let _ = self.set_pixel(xx, cy, c); } return; } 346 + let af = a as f32; let bf = b as f32; let bf2 = bf * bf; 347 + for dy in -b..=b { 348 + let yf = dy as f32; 349 + let t = 1.0 - (yf * yf) / bf2; 350 + if t < 0.0 { continue; } 351 + let xf = af * t.sqrt(); 352 + let x = xf.floor() as i32; 353 + self.hspan(cx - x, cx + x, cy + dy, c); 354 + } 355 + } 356 + 357 + // Triangle border via lines 358 + #[allow(clippy::too_many_arguments)] 359 + pub fn trib(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, x3: i32, y3: i32, color: u8) { 360 + let c = color & 0x0F; 361 + self.line(x1, y1, x2, y2, c); 362 + self.line(x2, y2, x3, y3, c); 363 + self.line(x3, y3, x1, y1, c); 364 + } 365 + 366 + // Filled triangle using scanline rasterization 367 + #[allow(clippy::too_many_arguments)] 368 + pub fn tri(&mut self, mut x1: i32, mut y1: i32, mut x2: i32, mut y2: i32, mut x3: i32, mut y3: i32, color: u8) { 369 + let c = color & 0x0F; 370 + // Sort by y, then x 371 + if y2 < y1 || (y2 == y1 && x2 < x1) { std::mem::swap(&mut x1, &mut x2); std::mem::swap(&mut y1, &mut y2); } 372 + if y3 < y1 || (y3 == y1 && x3 < x1) { std::mem::swap(&mut x1, &mut x3); std::mem::swap(&mut y1, &mut y3); } 373 + if y3 < y2 || (y3 == y2 && x3 < x2) { std::mem::swap(&mut x2, &mut x3); std::mem::swap(&mut y2, &mut y3); } 374 + if y1 == y3 { return; } 375 + 376 + if y2 == y3 { 377 + // flat-bottom 378 + let inv1 = (x2 - x1) as f32 / (y2 - y1) as f32; 379 + let inv2 = (x3 - x1) as f32 / (y2 - y1) as f32; 380 + let mut cx1 = x1 as f32; 381 + let mut cx2 = x1 as f32; 382 + for y in y1..=y2 { 383 + self.hspan(cx1.floor() as i32, cx2.floor() as i32, y, c); 384 + cx1 += inv1; 385 + cx2 += inv2; 386 + } 387 + } else if y1 == y2 { 388 + // flat-top 389 + let inv1 = (x3 - x1) as f32 / (y3 - y1) as f32; 390 + let inv2 = (x3 - x2) as f32 / (y3 - y2) as f32; 391 + let mut cx1 = x1 as f32; 392 + let mut cx2 = x2 as f32; 393 + for y in y1..=y3 { 394 + self.hspan(cx1.floor() as i32, cx2.floor() as i32, y, c); 395 + cx1 += inv1; 396 + cx2 += inv2; 397 + } 398 + } else { 399 + // general: split at y2 400 + let x4 = x1 + (((y2 - y1) as f32) * ((x3 - x1) as f32) / ((y3 - y1) as f32)).floor() as i32; 401 + // flat-bottom part 402 + let inv1 = (x2 - x1) as f32 / (y2 - y1) as f32; 403 + let inv2 = (x4 - x1) as f32 / (y2 - y1) as f32; 404 + let mut cx1 = x1 as f32; 405 + let mut cx2 = x1 as f32; 406 + for y in y1..=y2 { 407 + self.hspan(cx1.floor() as i32, cx2.floor() as i32, y, c); 408 + cx1 += inv1; 409 + cx2 += inv2; 410 + } 411 + // flat-top part 412 + let inv1b = (x3 - x2) as f32 / (y3 - y2) as f32; 413 + let inv2b = (x3 - x4) as f32 / (y3 - y2) as f32; 414 + let mut cx1b = x2 as f32; 415 + let mut cx2b = x4 as f32; 416 + for y in y2..=y3 { 417 + self.hspan(cx1b.floor() as i32, cx2b.floor() as i32, y, c); 418 + cx1b += inv1b; 419 + cx2b += inv2b; 420 + } 421 + } 422 + } 423 + 295 424 // Print text using TIC-80 default font (5x8 glyphs, 1px spacing) 296 425 #[allow(clippy::too_many_arguments)] 297 426 pub fn print_text(
+32
tic80_rust/src/script/lua_runner.rs
··· 140 140 })?; 141 141 globals.set("print", print_fn)?; 142 142 143 + // elli(x, y, a, b, color) 144 + let fb_elli = fb.clone(); 145 + let elli_fn = lua.create_function(move |_, (x, y, a, b, color): (i32, i32, i32, i32, u8)| { 146 + fb_elli.borrow_mut().elli(x, y, a, b, color); 147 + Ok(()) 148 + })?; 149 + globals.set("elli", elli_fn)?; 150 + 151 + // ellib(x, y, a, b, color) 152 + let fb_ellib = fb.clone(); 153 + let ellib_fn = lua.create_function(move |_, (x, y, a, b, color): (i32, i32, i32, i32, u8)| { 154 + fb_ellib.borrow_mut().ellib(x, y, a, b, color); 155 + Ok(()) 156 + })?; 157 + globals.set("ellib", ellib_fn)?; 158 + 159 + // tri(x1,y1,x2,y2,x3,y3,color) 160 + let fb_tri = fb.clone(); 161 + let tri_fn = lua.create_function(move |_, (x1,y1,x2,y2,x3,y3,color): (i32,i32,i32,i32,i32,i32,u8)| { 162 + fb_tri.borrow_mut().tri(x1,y1,x2,y2,x3,y3,color); 163 + Ok(()) 164 + })?; 165 + globals.set("tri", tri_fn)?; 166 + 167 + // trib(x1,y1,x2,y2,x3,y3,color) 168 + let fb_trib = fb.clone(); 169 + let trib_fn = lua.create_function(move |_, (x1,y1,x2,y2,x3,y3,color): (i32,i32,i32,i32,i32,i32,u8)| { 170 + fb_trib.borrow_mut().trib(x1,y1,x2,y2,x3,y3,color); 171 + Ok(()) 172 + })?; 173 + globals.set("trib", trib_fn)?; 174 + 143 175 // Load script 144 176 lua.load(script_src).set_name("cart").exec()?; 145 177
+41
tic80_rust/tests/gfx_framebuffer_tests.rs
··· 302 302 fb2.circb(10, 10, 0, 4); 303 303 assert_eq!(fb2.pix(10, 10, None), Some(4)); 304 304 } 305 + 306 + #[test] 307 + fn ellib_cardinals_and_fill_center_row() { 308 + let mut fb = Framebuffer::new(); 309 + fb.cls(0); 310 + let (cx, cy, a, b) = (40, 30, 6, 4); 311 + fb.ellib(cx, cy, a, b, 12); 312 + // Cardinal border points 313 + assert_eq!(fb.pix(cx + a, cy, None), Some(12)); 314 + assert_eq!(fb.pix(cx - a, cy, None), Some(12)); 315 + assert_eq!(fb.pix(cx, cy + b, None), Some(12)); 316 + assert_eq!(fb.pix(cx, cy - b, None), Some(12)); 317 + 318 + // Filled ellipse center row spans fully 319 + let mut fb2 = Framebuffer::new(); 320 + fb2.cls(0); 321 + fb2.elli(cx, cy, a, b, 5); 322 + for x in (cx - a)..=(cx + a) { 323 + assert_eq!(fb2.pix(x, cy, None), Some(5)); 324 + } 325 + assert_eq!(fb2.pix(cx - a - 1, cy, None), Some(0)); 326 + assert_eq!(fb2.pix(cx + a + 1, cy, None), Some(0)); 327 + } 328 + 329 + #[test] 330 + fn tri_fill_and_border() { 331 + let mut fb = Framebuffer::new(); 332 + fb.cls(0); 333 + // Simple triangle 334 + fb.tri(10, 10, 20, 10, 15, 15, 3); 335 + // Interior pixel 336 + assert_eq!(fb.pix(15, 12, None), Some(3)); 337 + // Outside pixel 338 + assert_eq!(fb.pix(9, 9, None), Some(0)); 339 + 340 + // Border triangle over it 341 + fb.trib(10, 10, 20, 10, 15, 15, 7); 342 + assert_eq!(fb.pix(10, 10, None), Some(7)); 343 + assert_eq!(fb.pix(20, 10, None), Some(7)); 344 + assert_eq!(fb.pix(15, 15, None), Some(7)); 345 + }
+28
tic80_rust/tests/lua_api_tests.rs
··· 244 244 assert_eq!(fbm.pix(30, 23, None), Some(9)); 245 245 assert_eq!(fbm.pix(30, 17, None), Some(9)); 246 246 } 247 + 248 + #[test] 249 + fn lua_elli_ellib_and_tri_trib() { 250 + let script = r#" 251 + function BOOT() cls(0) end 252 + function TIC() 253 + elli(60, 20, 5, 3, 4) 254 + ellib(60, 20, 5, 3, 12) 255 + tri(80, 10, 90, 20, 70, 20, 6) 256 + trib(100, 10, 110, 20, 90, 20, 9) 257 + end 258 + "#; 259 + let fb = run_lua(script, 1); 260 + let mut fbm = fb.borrow_mut(); 261 + // Ellipse border cardinals 262 + assert_eq!(fbm.pix(65, 20, None), Some(12)); 263 + assert_eq!(fbm.pix(55, 20, None), Some(12)); 264 + assert_eq!(fbm.pix(60, 23, None), Some(12)); 265 + assert_eq!(fbm.pix(60, 17, None), Some(12)); 266 + // Filled ellipse center row (interior only; endpoints are border color) 267 + for x in 56..=64 { assert_eq!(fbm.pix(x, 20, None), Some(4)); } 268 + // Triangle interior 269 + assert_eq!(fbm.pix(80, 18, None), Some(6)); 270 + // Border triangle vertices 271 + assert_eq!(fbm.pix(100, 10, None), Some(9)); 272 + assert_eq!(fbm.pix(110, 20, None), Some(9)); 273 + assert_eq!(fbm.pix(90, 20, None), Some(9)); 274 + }