1#[cfg(not(target_arch = "wasm32"))]
3mod ai;
4#[cfg(not(target_arch = "wasm32"))]
5mod gamepad;
6pub(crate) mod jit_abi;
7
8pub fn init_aot_runtime() {
11 let interp = Interpreter::new();
12 jit_abi::init(interp);
13}
14#[cfg(not(target_arch = "wasm32"))]
15mod net;
16use crate::gfx::{GfxState, Light};
17use crate::parser::ast::*;
18use std::cell::RefCell;
19use std::collections::HashMap;
20use crate::gfx::raster::draw_line;
23#[cfg(not(target_arch = "wasm32"))]
24use crate::gfx::raster::fill_triangle;
25#[cfg(not(target_arch = "wasm32"))]
26use ling_audio::{AudioEngine, ToneParams, Wave};
27
28#[cfg(not(target_arch = "wasm32"))]
29use ling_audio::FftAnalyzer;
30
31#[cfg(not(target_arch = "wasm32"))]
32use ling_mic;
33
34#[derive(Debug, Clone)]
37pub enum Value {
38 Str(String),
39 Number(f64),
40 Bool(bool),
41 Unit,
42 List(Vec<Value>),
43 Ok(Box<Value>),
44 Err(Box<Value>),
45 Fn(Vec<String>, Vec<Stmt>, Env),
46 Struct {
48 name: String,
49 fields: Vec<(String, Value)>,
50 },
51 Variant {
53 enum_name: String,
54 variant: String,
55 payload: Vec<Value>,
56 },
57}
58
59type Env = HashMap<String, Value>;
60
61impl std::fmt::Display for Value {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 Value::Str(s) => write!(f, "{s}"),
65 Value::Number(n) => {
66 if n.fract() == 0.0 && n.abs() < 1e15 {
67 write!(f, "{}", *n as i64)
68 } else {
69 write!(f, "{n}")
70 }
71 },
72 Value::Bool(b) => write!(f, "{b}"),
73 Value::Unit => write!(f, "()"),
74 Value::List(v) => {
75 write!(f, "[")?;
76 for (i, x) in v.iter().enumerate() {
77 if i > 0 {
78 write!(f, ", ")?;
79 }
80 write!(f, "{x}")?;
81 }
82 write!(f, "]")
83 },
84 Value::Ok(v) => write!(f, "Ok({v})"),
85 Value::Err(v) => write!(f, "Err({v})"),
86 Value::Fn(_, _, _) => write!(f, "<fn>"),
87 Value::Struct { name, fields } => {
88 write!(f, "{name} {{ ")?;
89 for (i, (k, v)) in fields.iter().enumerate() {
90 if i > 0 {
91 write!(f, ", ")?;
92 }
93 write!(f, "{k}: {v}")?;
94 }
95 write!(f, " }}")
96 },
97 Value::Variant { variant, payload, .. } => {
98 write!(f, "{variant}")?;
99 if !payload.is_empty() {
100 write!(f, "(")?;
101 for (i, v) in payload.iter().enumerate() {
102 if i > 0 {
103 write!(f, ", ")?;
104 }
105 write!(f, "{v}")?;
106 }
107 write!(f, ")")?;
108 }
109 Ok(())
110 },
111 }
112 }
113}
114
115#[derive(Debug)]
118pub(crate) enum EvalErr {
119 Runtime(String),
120 Return(Value),
121 #[allow(dead_code)] Break,
123}
124
125impl From<String> for EvalErr {
126 fn from(s: String) -> Self {
127 EvalErr::Runtime(s)
128 }
129}
130
131type EvalResult = Result<Value, EvalErr>;
132
133struct SvgWriter {
138 path: String,
139 width: f64,
140 height: f64,
141 elements: Vec<String>,
142}
143
144impl SvgWriter {
145 fn new(path: String, width: f64, height: f64) -> Self {
146 let bg = format!("<rect width=\"{width}\" height=\"{height}\" fill=\"#0a0a0a\"/>");
147 Self { path, width, height, elements: vec![bg] }
148 }
149
150 fn save(&self) -> std::io::Result<()> {
151 let w = self.width;
152 let h = self.height;
153 let mut out = format!(
154 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
155 <svg xmlns=\"http://www.w3.org/2000/svg\" \
156 width=\"{w}\" height=\"{h}\" viewBox=\"0 0 {w} {h}\">\n"
157 );
158 for elem in &self.elements {
159 out.push_str(" ");
160 out.push_str(elem);
161 out.push('\n');
162 }
163 out.push_str("</svg>\n");
164 if let Some(parent) = std::path::Path::new(&self.path).parent() {
166 if !parent.as_os_str().is_empty() {
167 let _ = std::fs::create_dir_all(parent);
168 }
169 }
170 std::fs::write(&self.path, out.as_bytes())
171 }
172}
173
174fn hsl_to_hex(h: f64, s: f64, l: f64) -> String {
175 let s = s / 100.0;
176 let l = l / 100.0;
177 let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
178 let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
179 let m = l - c / 2.0;
180 let (r1, g1, b1) = if h < 60.0 {
181 (c, x, 0.0)
182 } else if h < 120.0 {
183 (x, c, 0.0)
184 } else if h < 180.0 {
185 (0.0, c, x)
186 } else if h < 240.0 {
187 (0.0, x, c)
188 } else if h < 300.0 {
189 (x, 0.0, c)
190 } else {
191 (c, 0.0, x)
192 };
193 let r = ((r1 + m) * 255.0).round() as u8;
194 let g = ((g1 + m) * 255.0).round() as u8;
195 let b = ((b1 + m) * 255.0).round() as u8;
196 format!("#{r:02x}{g:02x}{b:02x}")
197}
198
199fn tex_hash(x: i32, y: i32, seed: u32) -> f32 {
202 let mut h = (x as u32)
203 .wrapping_add((y as u32).wrapping_mul(2654435769))
204 .wrapping_add(seed.wrapping_mul(1234567891));
205 h ^= h >> 16;
206 h = h.wrapping_mul(0x45d9f3b);
207 h ^= h >> 16;
208 h as f32 / u32::MAX as f32
209}
210
211fn tex_vnoise(x: f32, y: f32, seed: u32) -> f32 {
212 let xi = x.floor() as i32;
213 let yi = y.floor() as i32;
214 let sm = |t: f32| t * t * (3.0 - 2.0 * t);
215 let xf = sm(x - xi as f32);
216 let yf = sm(y - yi as f32);
217 let a = tex_hash(xi, yi, seed);
218 let b = tex_hash(xi + 1, yi, seed);
219 let c = tex_hash(xi, yi + 1, seed);
220 let d = tex_hash(xi + 1, yi + 1, seed);
221 a + (b - a) * xf + (c - a) * yf + (a - b - c + d) * xf * yf
222}
223
224fn tex_fbm(x: f32, y: f32, octaves: u32, seed: u32) -> f32 {
225 let mut v = 0.0f32;
226 let mut amp = 0.5f32;
227 let mut f = 1.0f32;
228 for i in 0..octaves {
229 v += tex_vnoise(x * f, y * f, seed.wrapping_add(i * 7919)) * amp;
230 amp *= 0.5;
231 f *= 2.0;
232 }
233 v
234}
235
236fn tex_palette(name: &str, t: f32) -> [f32; 3] {
237 let (a, b, c, d): ([f32; 3], [f32; 3], [f32; 3], [f32; 3]) = match name {
238 "fire" => (
239 [0.8, 0.4, 0.1],
240 [0.7, 0.3, 0.1],
241 [1.0, 0.5, 0.3],
242 [0.0, 0.5, 0.8],
243 ),
244 "ocean" => (
245 [0.1, 0.4, 0.7],
246 [0.3, 0.3, 0.4],
247 [0.8, 1.0, 0.5],
248 [0.3, 0.0, 0.6],
249 ),
250 "psychedelic" => (
251 [0.5, 0.5, 0.5],
252 [0.8, 0.8, 0.8],
253 [1.0, 1.3, 0.7],
254 [0.0, 0.15, 0.3],
255 ),
256 "neon" => (
257 [0.5, 0.5, 0.5],
258 [0.5, 0.5, 0.5],
259 [2.0, 1.0, 0.0],
260 [0.5, 0.2, 0.25],
261 ),
262 "forest" => (
263 [0.3, 0.5, 0.2],
264 [0.2, 0.3, 0.1],
265 [1.0, 0.5, 0.8],
266 [0.1, 0.3, 0.6],
267 ),
268 _ => (
269 [0.5, 0.5, 0.5],
270 [0.5, 0.5, 0.5],
271 [1.0, 1.0, 1.0],
272 [0.0, 0.333, 0.667],
273 ),
274 };
275 [0, 1, 2]
276 .map(|i| (a[i] + b[i] * (std::f32::consts::TAU * (c[i] * t + d[i])).cos()).clamp(0.0, 1.0))
277}
278
279#[cfg(not(target_arch = "wasm32"))]
281fn key_char(k: minifb::Key) -> Option<char> {
282 use minifb::Key::*;
283 Some(match k {
284 A => 'a',
285 B => 'b',
286 C => 'c',
287 D => 'd',
288 E => 'e',
289 F => 'f',
290 G => 'g',
291 H => 'h',
292 I => 'i',
293 J => 'j',
294 K => 'k',
295 L => 'l',
296 M => 'm',
297 N => 'n',
298 O => 'o',
299 P => 'p',
300 Q => 'q',
301 R => 'r',
302 S => 's',
303 T => 't',
304 U => 'u',
305 V => 'v',
306 W => 'w',
307 X => 'x',
308 Y => 'y',
309 Z => 'z',
310 Key0 => '0',
311 Key1 => '1',
312 Key2 => '2',
313 Key3 => '3',
314 Key4 => '4',
315 Key5 => '5',
316 Key6 => '6',
317 Key7 => '7',
318 Key8 => '8',
319 Key9 => '9',
320 Space => ' ',
321 Minus => '-',
322 Period => '.',
323 _ => return None,
324 })
325}
326
327fn hex_encode(bytes: &[u8]) -> String {
329 let mut s = String::with_capacity(bytes.len() * 2);
330 for b in bytes {
331 s.push_str(&format!("{b:02x}"));
332 }
333 s
334}
335
336#[cfg(not(target_arch = "wasm32"))]
338fn decode_blob(s: &str) -> Result<Vec<u8>, String> {
339 use base64::Engine as _;
340 use std::io::Read as _;
341 let comp = base64::engine::general_purpose::STANDARD
342 .decode(s.trim())
343 .map_err(|e| format!("base64: {e}"))?;
344 let mut out = Vec::new();
345 flate2::read::ZlibDecoder::new(&comp[..])
346 .read_to_end(&mut out)
347 .map_err(|e| format!("inflate: {e}"))?;
348 Ok(out)
349}
350
351fn hex_decode(s: &str) -> Vec<u8> {
353 let s = s.trim();
354 (0..s.len() / 2)
355 .filter_map(|i| u8::from_str_radix(s.get(i * 2..i * 2 + 2)?, 16).ok())
356 .collect()
357}
358
359fn hex_to_32(s: &str) -> [u8; 32] {
361 let v = hex_decode(s);
362 let mut out = [0u8; 32];
363 let n = v.len().min(32);
364 out[..n].copy_from_slice(&v[..n]);
365 out
366}
367
368fn tex_rgb(r: f32, g: f32, b: f32) -> u32 {
369 ((r * 255.0) as u32) << 16 | ((g * 255.0) as u32) << 8 | (b * 255.0) as u32
370}
371
372const PERM: [u8; 512] = [
375 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
376 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
377 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 35, 63, 189, 114, 56, 42, 123,
378 165, 38, 72, 93, 69, 139, 138, 78, 149, 159, 56, 89, 152, 78, 61, 140, 63, 26, 142, 76, 124,
379 132, 72, 11, 90, 44, 82, 59, 96, 41, 148, 126, 157, 13, 49, 27, 176, 33, 47, 14, 97, 78, 71,
380 40, 87, 183, 4, 122, 92, 7, 72, 3, 246, 17, 225, 87, 91, 106, 203, 190, 57, 74, 76, 88, 207,
381 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146,
382 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196,
383 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94,
384 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55,
385 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180,
386 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185,
387 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,
388 140, 161, 137, 13, 191, 230, 66, 104, 153, 199, 167, 147, 99, 179, 92,
389 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
391 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
392 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 35, 63, 189, 114, 56, 42, 123,
393 165, 38, 72, 93, 69, 139, 138, 78, 149, 159, 56, 89, 152, 78, 61, 140, 63, 26, 142, 76, 124,
394 132, 72, 11, 90, 44, 82, 59, 96, 41, 148, 126, 157, 13, 49, 27, 176, 33, 47, 14, 97, 78, 71,
395 40, 87, 183, 4, 122, 92, 7, 72, 3, 246, 17, 225, 87, 91, 106, 203, 190, 57, 74, 76, 88, 207,
396 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146,
397 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196,
398 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94,
399 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55,
400 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174,
401];
402
403fn fade(t: f32) -> f32 {
404 t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
405}
406
407fn grad(hash: u8, x: f32, y: f32, z: f32) -> f32 {
408 let h = hash & 15;
409 let u = if h < 8 { x } else { y };
410 let v = if h < 8 { y } else { z };
411 (if (h & 1) == 0 { u } else { -u }) + (if (h & 2) == 0 { v } else { -v })
412}
413
414fn perlin3(x: f32, y: f32, z: f32) -> f32 {
415 let xi = (x.floor() as i32) & 255;
416 let yi = (y.floor() as i32) & 255;
417 let zi = (z.floor() as i32) & 255;
418
419 let xf = x - x.floor();
420 let yf = y - y.floor();
421 let zf = z - z.floor();
422
423 let u = fade(xf);
424 let v = fade(yf);
425 let w = fade(zf);
426
427 let p0 = PERM[xi as usize] as usize;
428 let p1 = PERM[((xi + 1) & 255) as usize] as usize;
429 let pa = PERM[(p0 + yi as usize) & 255] as usize;
430 let pb = PERM[(p0 + ((yi + 1) & 255) as usize) & 255] as usize;
431 let pc = PERM[(p1 + yi as usize) & 255] as usize;
432 let pd = PERM[(p1 + ((yi + 1) & 255) as usize) & 255] as usize;
433
434 let g000 = grad(PERM[(pa + zi as usize) & 255], xf, yf, zf);
435 let g001 = grad(
436 PERM[(pa + ((zi + 1) & 255) as usize) & 255],
437 xf,
438 yf,
439 zf - 1.0,
440 );
441 let g010 = grad(PERM[(pb + zi as usize) & 255], xf, yf - 1.0, zf);
442 let g011 = grad(
443 PERM[(pb + ((zi + 1) & 255) as usize) & 255],
444 xf,
445 yf - 1.0,
446 zf - 1.0,
447 );
448 let g100 = grad(PERM[(pc + zi as usize) & 255], xf - 1.0, yf, zf);
449 let g101 = grad(
450 PERM[(pc + ((zi + 1) & 255) as usize) & 255],
451 xf - 1.0,
452 yf,
453 zf - 1.0,
454 );
455 let g110 = grad(PERM[(pd + zi as usize) & 255], xf - 1.0, yf - 1.0, zf);
456 let g111 = grad(
457 PERM[(pd + ((zi + 1) & 255) as usize) & 255],
458 xf - 1.0,
459 yf - 1.0,
460 zf - 1.0,
461 );
462
463 let l00 = g000 + u * (g100 - g000);
464 let l01 = g001 + u * (g101 - g001);
465 let l10 = g010 + u * (g110 - g010);
466 let l11 = g011 + u * (g111 - g011);
467
468 let l0 = l00 + v * (l10 - l00);
469 let l1 = l01 + v * (l11 - l01);
470
471 l0 + w * (l1 - l0)
472}
473
474fn fast_rand_f64(state: &mut u64) -> f64 {
475 *state = state
476 .wrapping_mul(6364136223846793005)
477 .wrapping_add(1442695040888963407);
478 ((*state >> 32) as u32) as f64 / 4294967296.0
479}
480
481#[inline]
485fn put_px(buf: &mut [u32], idx: usize, color: u32, blend: u8) {
486 if idx >= buf.len() {
487 return;
488 }
489 if blend == 0 {
490 buf[idx] = color;
491 } else {
492 let old = buf[idx];
493 let r = (((old >> 16) & 255) + ((color >> 16) & 255)).min(255);
494 let g = (((old >> 8) & 255) + ((color >> 8) & 255)).min(255);
495 let b = ((old & 255) + (color & 255)).min(255);
496 buf[idx] = (r << 16) | (g << 8) | b;
497 }
498}
499
500#[inline]
502fn rgb(r: f64, g: f64, b: f64) -> u32 {
503 let r = (r as i64).clamp(0, 255) as u32;
504 let g = (g as i64).clamp(0, 255) as u32;
505 let b = (b as i64).clamp(0, 255) as u32;
506 (r << 16) | (g << 8) | b
507}
508
509fn draw_circle_outline(
510 buf: &mut [u32],
511 w: i32,
512 h: i32,
513 cx: i32,
514 cy: i32,
515 r: i32,
516 color: u32,
517 blend: u8,
518) {
519 let r = r.clamp(0, 20000); if r == 0 {
521 return;
522 }
523 let mut x = 0;
524 let mut y = r;
525 let mut d = 3 - 2 * r;
526 while x <= y {
527 plot_circle_points(buf, w, h, cx, cy, x, y, color, blend);
528 if d < 0 {
529 d += 4 * x + 6;
530 } else {
531 d += 4 * (x - y) + 10;
532 y -= 1;
533 }
534 x += 1;
535 }
536}
537
538fn plot_circle_points(
539 buf: &mut [u32],
540 w: i32,
541 h: i32,
542 cx: i32,
543 cy: i32,
544 x: i32,
545 y: i32,
546 color: u32,
547 blend: u8,
548) {
549 let points = [
550 (cx + x, cy + y),
551 (cx - x, cy + y),
552 (cx + x, cy - y),
553 (cx - x, cy - y),
554 (cx + y, cy + x),
555 (cx - y, cy + x),
556 (cx + y, cy - x),
557 (cx - y, cy - x),
558 ];
559 for &(px, py) in &points {
560 if px >= 0 && px < w && py >= 0 && py < h {
561 put_px(buf, (py * w + px) as usize, color, blend);
562 }
563 }
564}
565
566fn draw_circle_filled(
567 buf: &mut [u32],
568 w: i32,
569 h: i32,
570 cx: i32,
571 cy: i32,
572 r: i32,
573 color: u32,
574 blend: u8,
575) {
576 if r <= 0 {
577 return;
578 }
579 for dy in -r..=r {
580 let dx_max = ((r * r - dy * dy) as f64).sqrt() as i32;
581 let py = cy + dy;
582 if py < 0 || py >= h {
583 continue;
584 }
585 for dx in -dx_max..=dx_max {
586 let px = cx + dx;
587 if px >= 0 && px < w {
588 put_px(buf, (py * w + px) as usize, color, blend);
589 }
590 }
591 }
592}
593
594#[cfg(test)]
595mod draw_tests {
596 use super::*;
597
598 #[test]
599 fn filled_circle_actually_writes_pixels() {
600 let mut buf = vec![0u32; 100 * 100];
601 draw_circle_filled(&mut buf, 100, 100, 50, 50, 10, 0xFF00FF, 0);
602 assert_eq!(buf[50 * 100 + 50], 0xFF00FF, "centre pixel must be filled");
603 assert_eq!(buf[0], 0, "far corner must stay clear");
604 let n = buf.iter().filter(|&&p| p != 0).count();
605 assert!(n > 200 && n < 500, "r=10 disc area ≈ 314, got {n}");
606 }
607
608 #[test]
609 fn circle_outline_writes_a_ring() {
610 let mut buf = vec![0u32; 100 * 100];
611 draw_circle_outline(&mut buf, 100, 100, 50, 50, 20, 0x00FF00, 0);
612 assert_eq!(buf[50 * 100 + 50], 0, "outline must NOT fill the centre");
613 assert!(
614 buf.iter().any(|&p| p == 0x00FF00),
615 "outline must draw a ring"
616 );
617 }
618
619 #[test]
620 fn additive_blend_accumulates_channels() {
621 let mut buf = vec![0x202020u32; 1];
622 put_px(&mut buf, 0, 0x404040, 1);
623 assert_eq!(buf[0], 0x606060);
624 }
625}
626
627#[derive(Clone, Copy)]
633pub struct UiTheme {
634 pub primary: u32,
635 pub accent: u32,
636 pub track: u32,
637 pub warn: u32,
638 pub text: u32,
639 pub bg: u32,
640}
641
642impl Default for UiTheme {
643 fn default() -> Self {
644 Self {
645 primary: 0x00D2FF, accent: 0x28FFB4, track: 0x2C3E64, warn: 0xFF5A5A, text: 0xBEEBFF, bg: 0x0A1018, }
652 }
653}
654
655pub struct Interpreter {
656 globals: HashMap<String, Expr>,
657 global_seed: Env,
660 functions: HashMap<String, FnDef>,
661 pub(crate) structs: HashMap<String, Vec<String>>,
663 enum_variants: HashMap<String, (String, usize)>,
665 _modules: HashMap<String, Vec<FnDef>>,
666 gfx: RefCell<GfxState>,
667 svg: RefCell<Option<SvgWriter>>,
668 pub source_dir: Option<std::path::PathBuf>,
670 loaded_files: std::collections::HashSet<String>,
672 #[cfg(not(target_arch = "wasm32"))]
674 audio: Option<AudioEngine>,
675 #[cfg(not(target_arch = "wasm32"))]
676 fft: RefCell<FftAnalyzer>,
677 fft_bands_cache: RefCell<Vec<f32>>,
678 start_time: std::time::Instant,
680 frame_num: u64,
682 rand_state: u64,
684 #[cfg(not(target_arch = "wasm32"))]
686 mic: Option<ling_mic::MicInput>,
687 #[cfg(not(target_arch = "wasm32"))]
689 crypto_ids: Vec<ling_crypto::KnotIdentity>,
690 text_buffer: String,
692 record_n: u32,
694 #[cfg(not(target_arch = "wasm32"))]
696 mic_buffer: Vec<f32>,
697 #[cfg(not(target_arch = "wasm32"))]
699 fonts: Vec<ling_graphics::VectorFont>,
700 ui_theme: UiTheme,
702 mouse_was_down: bool,
704 #[cfg(not(target_arch = "wasm32"))]
706 music: Option<ling_music::MusicEngine>,
707 #[cfg(not(target_arch = "wasm32"))]
708 music_init: bool,
709 #[cfg(not(target_arch = "wasm32"))]
711 tracks: Vec<ling_music::DecodedAudio>,
712 #[cfg(not(target_arch = "wasm32"))]
714 lyrics: Vec<ling_music::Lyrics>,
715 #[cfg(not(target_arch = "wasm32"))]
717 midis: Vec<ling_music::MidiSong>,
718 soft_bodies: Vec<ling_physics::soft::SoftBody>,
720 rigid_world: ling_physics::rigid::PhysicsWorld,
722 liquids: Vec<ling_physics::liquid::LiquidGrid>,
724 meshes: Vec<crate::gfx::shapes::ColorMesh>,
725 dialog: Option<ling_game::dialog::Dialog>,
727 dialog_colors: [u32; 4],
729 frames: Vec<String>,
731 error_trace: Option<Vec<String>>,
734 #[cfg(not(target_arch = "wasm32"))]
738 input: RefCell<Option<InputState>>,
739}
740
741#[cfg(not(target_arch = "wasm32"))]
743struct InputState {
744 sensorium: ling_input::Sensorium,
745 backend: ling_input::backend::GilrsBackend,
746}
747
748impl Interpreter {
749 pub fn new() -> Self {
750 #[cfg(not(target_arch = "wasm32"))]
751 let audio = AudioEngine::new()
752 .map_err(|e| eprintln!("audio init failed (no sound): {e}"))
753 .ok();
754 Self {
755 globals: HashMap::new(),
756 global_seed: HashMap::new(),
757 functions: HashMap::new(),
758 structs: HashMap::new(),
759 enum_variants: HashMap::new(),
760 _modules: HashMap::new(),
761 gfx: RefCell::new(GfxState::new()),
762 svg: RefCell::new(None),
763 source_dir: None,
764 loaded_files: std::collections::HashSet::new(),
765 #[cfg(not(target_arch = "wasm32"))]
766 audio,
767 #[cfg(not(target_arch = "wasm32"))]
768 fft: RefCell::new(FftAnalyzer::new(2048, 44100)),
769 fft_bands_cache: RefCell::new(vec![]),
770 start_time: std::time::Instant::now(),
771 frame_num: 0,
772 rand_state: 0x123456789ABCDEF,
773 #[cfg(not(target_arch = "wasm32"))]
774 mic: None,
775 #[cfg(not(target_arch = "wasm32"))]
776 crypto_ids: Vec::new(),
777 text_buffer: String::new(),
778 record_n: 0,
779 #[cfg(not(target_arch = "wasm32"))]
780 mic_buffer: Vec::new(),
781 #[cfg(not(target_arch = "wasm32"))]
782 fonts: Vec::new(),
783 ui_theme: UiTheme::default(),
784 mouse_was_down: false,
785 #[cfg(not(target_arch = "wasm32"))]
786 music: None,
787 #[cfg(not(target_arch = "wasm32"))]
788 music_init: false,
789 #[cfg(not(target_arch = "wasm32"))]
790 tracks: Vec::new(),
791 #[cfg(not(target_arch = "wasm32"))]
792 lyrics: Vec::new(),
793 #[cfg(not(target_arch = "wasm32"))]
794 midis: Vec::new(),
795 soft_bodies: Vec::new(),
796 rigid_world: ling_physics::rigid::PhysicsWorld::new(),
797 liquids: Vec::new(),
798 meshes: Vec::new(),
799 dialog: None,
800 dialog_colors: [0xE6F2FF, 0xFFD24A, 0x4AD2FF, 0x6CFF8C], frames: Vec::new(),
802 error_trace: None,
803 #[cfg(not(target_arch = "wasm32"))]
804 input: RefCell::new(None),
805 }
806 }
807
808 #[cfg(not(target_arch = "wasm32"))]
812 fn pad_poll(&self) -> usize {
813 let mut slot = self.input.borrow_mut();
814 if slot.is_none() {
815 match ling_input::backend::GilrsBackend::new() {
816 Ok(backend) => {
817 *slot = Some(InputState { sensorium: ling_input::Sensorium::new(4), backend });
818 },
819 Err(_) => return 0,
820 }
821 }
822 let st = slot.as_mut().unwrap();
823 st.sensorium.begin_frame();
824 st.sensorium.pump(&mut st.backend);
825 st.sensorium.update(1.0 / 60.0);
826 st.sensorium.devices.count()
827 }
828
829 #[cfg(not(target_arch = "wasm32"))]
832 fn with_pad<T>(&self, slot: usize, default: T, f: impl FnOnce(&ling_input::Gamepad) -> T) -> T {
833 let inp = self.input.borrow();
834 match inp.as_ref().and_then(|s| s.sensorium.player(slot)) {
835 Some(p) => f(p),
836 None => default,
837 }
838 }
839
840 pub fn take_error_trace(&mut self) -> Vec<String> {
843 self.error_trace.take().unwrap_or_default()
844 }
845
846 fn framed<T, F>(&mut self, name: &str, body: F) -> Result<T, EvalErr>
849 where
850 F: FnOnce(&mut Self) -> Result<T, EvalErr>,
851 {
852 self.frames.push(name.to_string());
853 let result = body(self);
854 if matches!(result, Err(EvalErr::Runtime(_))) && self.error_trace.is_none() {
855 self.error_trace = Some(self.frames.clone());
856 }
857 self.frames.pop();
858 result
859 }
860
861 #[cfg(not(target_arch = "wasm32"))]
865 fn render_dialog(&mut self, x: f32, y: f32, w: f32, h: f32, font: i64, t: f32) {
866 let (runs, typing) = match &self.dialog {
867 Some(d) if !d.is_closed() => {
868 let runs: Vec<(String, usize, bool)> = d
869 .visible_runs()
870 .into_iter()
871 .map(|r| (r.text, r.role.index(), r.newline_before))
872 .collect();
873 (runs, d.is_typing())
874 },
875 _ => return,
876 };
877 let colors = self.dialog_colors;
878 let b = 12.0;
880 let corners: Vec<[f32; 2]> = vec![
881 [x + b, y],
882 [x + w - b, y],
883 [x + w, y + b],
884 [x + w, y + h - b],
885 [x + w - b, y + h],
886 [x + b, y + h],
887 [x, y + h - b],
888 [x, y + b],
889 [x + b, y],
890 ];
891 {
892 let mut gfx = self.gfx.borrow_mut();
893 let (bw, bh) = (gfx.width, gfx.height);
894 crate::gfx::raster::fill_contours_aa(
895 &mut gfx.buffer,
896 bw,
897 bh,
898 0x0A1018,
899 false,
900 std::slice::from_ref(&corners),
901 );
902 for seg in corners.windows(2) {
903 crate::gfx::raster::draw_line_aa(
904 &mut gfx.buffer,
905 bw,
906 bh,
907 0x00D2FF,
908 false,
909 seg[0][0],
910 seg[0][1],
911 seg[1][0],
912 seg[1][1],
913 );
914 }
915 }
916 let px = 22.0f32;
918 let pad = 20.0f32;
919 let line_h = px * 1.45;
920 let mut cx = x + pad;
921 let mut cy = y + pad;
922 let use_font = font >= 0 && (font as usize) < self.fonts.len();
923 for (text, role, nl) in &runs {
924 if *nl {
925 cx = x + pad;
926 cy += line_h;
927 }
928 for word in text.split_inclusive(' ') {
929 let wpx = if use_font {
930 self.fonts[font as usize].measure(word, px)
931 } else {
932 ling_ui::holo::text_width(word, px * 0.6, px * 0.24)
933 };
934 if cx + wpx > x + w - pad && cx > x + pad + 1.0 {
935 cx = x + pad;
936 cy += line_h;
937 }
938 if cy + line_h > y + h {
939 break;
940 }
941 let col = colors[(*role).min(3)];
942 if use_font {
943 let glyphs = self.font_layout_2d_glyphs(font as usize, cx, cy, px, word);
944 let mut gfx = self.gfx.borrow_mut();
945 let (bw, bh, add) = (gfx.width, gfx.height, gfx.blend == 1);
946 for contours in &glyphs {
947 crate::gfx::raster::fill_contours_aa(
948 &mut gfx.buffer,
949 bw,
950 bh,
951 col,
952 add,
953 contours,
954 );
955 }
956 } else {
957 let segs = ling_ui::holo::text_lines(word, cx, cy, px * 0.6, px, px * 0.24);
958 let mut gfx = self.gfx.borrow_mut();
959 let (bw, bh) = (gfx.width, gfx.height);
960 for s in segs {
961 draw_line(&mut gfx.buffer, bw, bh, col, s[0], s[1], s[2], s[3]);
962 }
963 }
964 cx += wpx;
965 }
966 }
967 if !typing && (t * 3.0).sin() > 0.0 {
969 let ax = x + w - 26.0;
970 let ay = y + h - 22.0;
971 let mut gfx = self.gfx.borrow_mut();
972 let (bw, bh) = (gfx.width, gfx.height);
973 crate::gfx::raster::fill_contours_aa(
974 &mut gfx.buffer,
975 bw,
976 bh,
977 0x00D2FF,
978 false,
979 std::slice::from_ref(&vec![
980 [ax - 7.0, ay],
981 [ax + 7.0, ay],
982 [ax, ay + 9.0],
983 [ax - 7.0, ay],
984 ]),
985 );
986 }
987 }
988
989 #[cfg(not(target_arch = "wasm32"))]
992 fn ensure_music(&mut self) -> bool {
993 if self.music.is_some() {
994 return true;
995 }
996 if self.music_init {
997 return false;
998 }
999 self.music_init = true;
1000 match ling_music::MusicEngine::new() {
1001 Ok(m) => {
1002 self.music = Some(m);
1003 true
1004 },
1005 Err(e) => {
1006 eprintln!("music engine init failed (no music playback): {e}");
1007 false
1008 },
1009 }
1010 }
1011
1012 #[cfg(not(target_arch = "wasm32"))]
1016 fn font_layout_2d(
1017 &mut self,
1018 id: usize,
1019 x: f32,
1020 y: f32,
1021 px: f32,
1022 text: &str,
1023 ) -> Vec<Vec<[f32; 2]>> {
1024 let mut out = Vec::new();
1025 for g in self.font_layout_2d_glyphs(id, x, y, px, text) {
1026 out.extend(g);
1027 }
1028 out
1029 }
1030
1031 #[cfg(not(target_arch = "wasm32"))]
1034 fn font_layout_2d_glyphs(
1035 &mut self,
1036 id: usize,
1037 x: f32,
1038 y: f32,
1039 px: f32,
1040 text: &str,
1041 ) -> Vec<Vec<Vec<[f32; 2]>>> {
1042 let font = &mut self.fonts[id];
1043 let asc = font.ascent();
1044 let tol = 0.3 / px;
1045 let mut pen = 0.0f32;
1046 let mut glyphs = Vec::new();
1047 for ch in text.chars() {
1048 let go = font.glyph_outline(ch, tol);
1049 let mut contours = Vec::with_capacity(go.polylines.len());
1050 for pl in &go.polylines {
1051 let mapped: Vec<[f32; 2]> = pl
1052 .iter()
1053 .map(|p| [x + (pen + p[0]) * px, y + (asc - p[1]) * px])
1054 .collect();
1055 contours.push(mapped);
1056 }
1057 glyphs.push(contours);
1058 pen += go.advance;
1059 }
1060 glyphs
1061 }
1062
1063 pub fn run_program(&mut self, program: &Program) -> Result<(), String> {
1064 for item in &program.items {
1065 self.register_item("", item)?;
1066 }
1067 let entry = self
1068 .find_entry()
1069 .ok_or("no entry point — need `bind start = do {...}` or `ผูก เริ่ม = ทำ {...}`")?;
1070 let mut env = Env::new();
1073 let non_do: Vec<_> = self
1074 .globals
1075 .iter()
1076 .filter(|(_, e)| !matches!(e, Expr::Do(_)))
1077 .map(|(k, e)| (k.clone(), e.clone()))
1078 .collect();
1079 let mut pending: Vec<(String, Expr)> = Vec::new();
1080 for (k, expr) in &non_do {
1081 let mut tmp = Env::new();
1082 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
1083 env.insert(k.clone(), v);
1084 } else {
1085 pending.push((k.clone(), expr.clone()));
1086 }
1087 }
1088 for (k, expr) in &pending {
1089 let mut tmp = env.clone();
1090 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
1091 env.insert(k.clone(), v);
1092 }
1093 }
1094 self.global_seed = env.clone();
1097 self.framed("start", |me| me.eval_expr(&entry, &mut env))
1098 .map(|_| ())
1099 .map_err(|e| match e {
1100 EvalErr::Runtime(s) => s,
1101 EvalErr::Return(_) => "unexpected top-level return".to_string(),
1102 EvalErr::Break => "unexpected break at top level".to_string(),
1103 })
1104 }
1105
1106 fn register_item(&mut self, ns: &str, item: &Item) -> Result<(), String> {
1107 match item {
1108 Item::Bind(name, expr) => {
1109 let key = if ns.is_empty() {
1110 name.clone()
1111 } else {
1112 format!("{ns}::{name}")
1113 };
1114 self.globals.insert(key, expr.clone());
1115 },
1116 Item::Fn(def) => {
1117 let key = if ns.is_empty() {
1118 def.name.clone()
1119 } else {
1120 format!("{ns}::{}", def.name)
1121 };
1122 self.functions.insert(key, def.clone());
1123 },
1124 Item::Mod(name, body) => {
1125 let child_ns = if ns.is_empty() {
1126 name.clone()
1127 } else {
1128 format!("{ns}::{name}")
1129 };
1130 for child in body {
1131 self.register_item(&child_ns, child)?;
1132 }
1133 },
1134 Item::TypeAlias(_, _) => {},
1135 Item::Struct(name, fields) => {
1136 self.structs.insert(name.clone(), fields.clone());
1137 if !ns.is_empty() {
1138 self.structs.insert(format!("{ns}::{name}"), fields.clone());
1139 }
1140 },
1141 Item::Enum(name, variants) => {
1142 for v in variants {
1143 self.enum_variants
1144 .insert(v.name.clone(), (name.clone(), v.arity));
1145 self.enum_variants
1146 .insert(format!("{name}::{}", v.name), (name.clone(), v.arity));
1147 if !ns.is_empty() {
1148 self.enum_variants
1149 .insert(format!("{ns}::{name}::{}", v.name), (name.clone(), v.arity));
1150 }
1151 }
1152 },
1153 Item::Use { path, alias } => {
1154 self.load_module(path, alias.as_deref(), ns)?;
1155 },
1156 }
1157 Ok(())
1158 }
1159
1160 fn load_module(
1165 &mut self,
1166 path: &str,
1167 alias: Option<&str>,
1168 parent_ns: &str,
1169 ) -> Result<(), String> {
1170 let base_dir = self
1172 .source_dir
1173 .clone()
1174 .unwrap_or_else(|| std::path::PathBuf::from("."));
1175 let raw = std::path::Path::new(path);
1176 let candidates: Vec<std::path::PathBuf> = vec![
1177 base_dir.join(format!("{}.ling", path)),
1178 base_dir.join(format!("{}.灵", path)),
1179 base_dir.join(format!("{}.령", path)),
1180 base_dir.join(format!("{}.霊", path)),
1181 base_dir.join(format!("{}.ลิง", path)),
1182 base_dir.join(raw),
1184 std::path::PathBuf::from(format!("{}.ling", path)),
1185 std::path::PathBuf::from(path),
1186 ];
1187
1188 let resolved = candidates
1189 .into_iter()
1190 .find(|p| p.exists())
1191 .ok_or_else(|| format!("use: cannot find module '{path}'"))?;
1192
1193 let canonical = resolved
1194 .canonicalize()
1195 .unwrap_or_else(|_| resolved.clone())
1196 .to_string_lossy()
1197 .to_string();
1198
1199 if self.loaded_files.contains(&canonical) {
1201 return Ok(());
1202 }
1203 self.loaded_files.insert(canonical.clone());
1204
1205 let source = std::fs::read_to_string(&resolved)
1206 .map_err(|e| format!("use: failed to read '{path}': {e}"))?;
1207
1208 let prev_dir = self.source_dir.clone();
1210 self.source_dir = resolved.parent().map(|p| p.to_path_buf());
1211
1212 let program = crate::parser::parse(&source)
1213 .map_err(|e| format!("use: parse error in '{path}': {e}"))?;
1214
1215 let target_ns = match (parent_ns.is_empty(), alias) {
1217 (_, Some(a)) if !parent_ns.is_empty() => format!("{parent_ns}::{a}"),
1218 (_, Some(a)) => a.to_string(),
1219 (false, None) => parent_ns.to_string(),
1220 (true, None) => String::new(),
1221 };
1222
1223 for item in &program.items {
1224 self.register_item(&target_ns, item)?;
1225 }
1226
1227 self.source_dir = prev_dir;
1228 Ok(())
1229 }
1230
1231 fn find_entry(&self) -> Option<Expr> {
1232 for key in crate::entry::ENTRY_NAMES {
1234 if let Some(e) = self.globals.get(*key) {
1235 return Some(e.clone());
1236 }
1237 }
1238 self.globals
1239 .values()
1240 .find(|e| matches!(e, Expr::Do(_)))
1241 .cloned()
1242 }
1243
1244 fn eval_expr(&mut self, expr: &Expr, env: &mut Env) -> EvalResult {
1247 match expr {
1248 Expr::Str(s) => Ok(Value::Str(s.clone())),
1249 Expr::Number(n) => Ok(Value::Number(*n)),
1250 Expr::Bool(b) => Ok(Value::Bool(*b)),
1251 Expr::Unit => Ok(Value::Unit),
1252 Expr::Array(elems) => {
1253 let vs: Vec<_> = elems
1254 .iter()
1255 .map(|e| self.eval_expr(e, env))
1256 .collect::<Result<_, _>>()?;
1257 Ok(Value::List(vs))
1258 },
1259
1260 Expr::Ident(name) => self.lookup(name, env),
1261
1262 Expr::Path(segs) => {
1263 if segs.len() == 1 {
1264 return self.lookup(&segs[0], env);
1265 }
1266 Ok(Value::Str(segs.join("::")))
1267 },
1268
1269 Expr::Ref(inner) => self.eval_expr(inner, env),
1270 Expr::Await(inner) => self.eval_expr(inner, env),
1271
1272 Expr::Do(stmts) => {
1273 let mut local = env.clone();
1274 Ok(self.exec_block(stmts, &mut local)?.unwrap_or(Value::Unit))
1275 },
1276
1277 Expr::BinOp(op, lhs, rhs) => {
1278 let l = self.eval_expr(lhs, env)?;
1279 let r = self.eval_expr(rhs, env)?;
1280 self.apply_binop(op, l, r)
1281 },
1282
1283 Expr::If { cond, then, elseifs, else_body } => {
1284 let cond_val = self.eval_expr(cond, env)?;
1285 if self.is_truthy(&cond_val) {
1286 return Ok(self.exec_block(then, env)?.unwrap_or(Value::Unit));
1287 }
1288 for (ei_cond, ei_body) in elseifs {
1289 let ei_cond_val = self.eval_expr(ei_cond, env)?;
1290 if self.is_truthy(&ei_cond_val) {
1291 return Ok(self.exec_block(ei_body, env)?.unwrap_or(Value::Unit));
1292 }
1293 }
1294 if let Some(eb) = else_body {
1295 return Ok(self.exec_block(eb, env)?.unwrap_or(Value::Unit));
1296 }
1297 Ok(Value::Unit)
1298 },
1299
1300 Expr::While { cond, body } => {
1301 loop {
1305 let cv = self.eval_expr(cond, env)?;
1306 if !self.is_truthy(&cv) {
1307 break;
1308 }
1309 match self.exec_block(body, env) {
1310 Ok(_) => {},
1311 Err(EvalErr::Break) => break,
1312 Err(e) => return Err(e),
1313 }
1314 }
1315 Ok(Value::Unit)
1316 },
1317
1318 Expr::For { var, iter, body } => {
1319 let iter_val = self.eval_expr(iter, env)?;
1320 let items = self.value_to_iter(iter_val)?;
1321 for item in items {
1322 let mut local = env.clone();
1323 local.insert(var.clone(), item);
1324 match self.exec_block(body, &mut local) {
1325 Ok(_) => {},
1326 Err(EvalErr::Break) => break,
1327 Err(e) => return Err(e),
1328 }
1329 }
1330 Ok(Value::Unit)
1331 },
1332
1333 Expr::Match(subject, arms) => {
1334 let subj = self.eval_expr(subject, env)?;
1335 for arm in arms {
1336 if let Some(bindings) = self.match_pattern(&arm.pattern, &subj) {
1337 let mut local = env.clone();
1338 local.extend(bindings);
1339 return self.eval_expr(&arm.body, &mut local);
1340 }
1341 }
1342 Ok(Value::Unit)
1343 },
1344
1345 Expr::Range(lo, hi) => {
1346 let lo_v = self.eval_expr(lo, env)?;
1347 let hi_v = self.eval_expr(hi, env)?;
1348 let lo_n = self.to_number(&lo_v)? as i64;
1349 let hi_n = self.to_number(&hi_v)? as i64;
1350 Ok(Value::List(
1351 (lo_n..hi_n).map(|i| Value::Number(i as f64)).collect(),
1352 ))
1353 },
1354
1355 Expr::Index(base, idx) => {
1356 let b = self.eval_expr(base, env)?;
1357 let i = self.eval_expr(idx, env)?;
1358 let n = self.to_number(&i)? as usize;
1359 match b {
1360 Value::List(v) => v
1361 .get(n)
1362 .cloned()
1363 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
1364 Value::Str(s) => s
1365 .chars()
1366 .nth(n)
1367 .map(|c| Value::Str(c.to_string()))
1368 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
1369 other => Err(EvalErr::from(format!("cannot index {:?}", other))),
1370 }
1371 },
1372
1373 Expr::Call(callee, args) => {
1374 let arg_vals: Vec<Value> = args
1375 .iter()
1376 .map(|a| self.eval_expr(a, env))
1377 .collect::<Result<_, _>>()?;
1378 match callee.as_ref() {
1379 Expr::Ident(name) => self.call_named(name, arg_vals, env),
1380 Expr::Path(segs) => self.call_named(&segs.join("::"), arg_vals, env),
1381 _ => {
1382 let v = self.eval_expr(callee, env)?;
1383 self.call_value(v, arg_vals)
1384 },
1385 }
1386 },
1387
1388 Expr::MethodCall { receiver, method, args } => {
1389 let recv = self.eval_expr(receiver, env)?;
1390 let arg_vals: Vec<Value> = args
1391 .iter()
1392 .map(|a| self.eval_expr(a, env))
1393 .collect::<Result<_, _>>()?;
1394 self.call_method(recv, method, arg_vals)
1395 },
1396
1397 Expr::Closure(params, body) => Ok(Value::Fn(
1398 params.clone(),
1399 vec![Stmt::Expr(*body.clone())],
1400 env.clone(),
1401 )),
1402 }
1403 }
1404
1405 fn exec_block(&mut self, stmts: &[Stmt], env: &mut Env) -> Result<Option<Value>, EvalErr> {
1408 let mut last: Option<Value> = None;
1409 for stmt in stmts {
1410 match stmt {
1411 Stmt::Bind(name, expr) => {
1412 let v = self.eval_expr(expr, env)?;
1413 env.insert(name.clone(), v);
1414 last = None;
1415 },
1416 Stmt::Return(expr) => {
1417 let v = self.eval_expr(expr, env)?;
1418 return Err(EvalErr::Return(v));
1419 },
1420 Stmt::Expr(expr) => {
1421 last = Some(self.eval_expr(expr, env)?);
1422 },
1423 }
1424 }
1425 Ok(last)
1426 }
1427
1428 fn lookup(&self, name: &str, env: &Env) -> EvalResult {
1431 if let Some(v) = env.get(name) {
1432 return Ok(v.clone());
1433 }
1434 if self.functions.contains_key(name) {
1435 let def = &self.functions[name];
1436 return Ok(Value::Fn(def.params.clone(), def.body.clone(), Env::new()));
1437 }
1438 if let Some((enum_name, 0)) = self.enum_variants.get(name).cloned() {
1440 let variant = name.rsplit("::").next().unwrap_or(name).to_string();
1441 return Ok(Value::Variant { enum_name, variant, payload: Vec::new() });
1442 }
1443 match name {
1445 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" => {
1446 return Ok(Value::Number(std::f64::consts::PI))
1447 },
1448 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" => {
1449 return Ok(Value::Number(std::f64::consts::TAU))
1450 },
1451 _ => {},
1452 }
1453 Err(EvalErr::from(format!("undefined: '{name}'")))
1454 }
1455
1456 pub(crate) fn call_named(&mut self, name: &str, args: Vec<Value>, env: &Env) -> EvalResult {
1457 if let Some(def) = self.functions.get(name).cloned() {
1460 let mut call_env = self.global_seed.clone();
1461 let _ = env; for (param, arg) in def.params.iter().zip(args) {
1463 call_env.insert(param.clone(), arg);
1464 }
1465 return match self.framed(name, |me| me.exec_block(&def.body, &mut call_env)) {
1466 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
1467 Err(EvalErr::Return(v)) => Ok(v),
1468 Err(e) => Err(e),
1469 };
1470 }
1471 match name {
1472 "print" | "println" | "印" | "打印" | "印刷" | "พิมพ์" | "출력" | "вывести"
1474 | "imprimir" | "afficher" => {
1475 let s = args
1476 .iter()
1477 .map(|v| v.to_string())
1478 .collect::<Vec<_>>()
1479 .join("");
1480 println!("{s}");
1481 return Ok(Value::Unit);
1482 },
1483 "print_color" | "พิมพ์สี" => {
1486 #[cfg(windows)]
1487 {
1488 use std::sync::Once;
1489 static VT: Once = Once::new();
1490 VT.call_once(|| {
1491 extern "system" {
1492 fn GetStdHandle(n: u32) -> *mut std::ffi::c_void;
1493 fn GetConsoleMode(h: *mut std::ffi::c_void, m: *mut u32) -> i32;
1494 fn SetConsoleMode(h: *mut std::ffi::c_void, m: u32) -> i32;
1495 }
1496 unsafe {
1497 let h = GetStdHandle(0xFFFF_FFF5u32); let mut mode = 0u32;
1499 if GetConsoleMode(h, &mut mode) != 0 {
1500 SetConsoleMode(h, mode | 0x0004); }
1502 }
1503 });
1504 }
1505 let col = self.arg_num(&args, 0, 7.0)? as i64;
1506 let s = args
1507 .iter()
1508 .skip(1)
1509 .map(|v| v.to_string())
1510 .collect::<Vec<_>>()
1511 .join("");
1512 let code = 90 + col.clamp(0, 7);
1513 println!("\x1b[1;{code}m{s}\x1b[0m");
1514 return Ok(Value::Unit);
1515 },
1516 "format"
1518 | "格式"
1519 | "フォーマット"
1520 | "서식"
1521 | "รูปแบบ"
1522 | "форматировать"
1523 | "formatear"
1524 | "formater" => {
1525 return Ok(Value::Str(self.builtin_format(&args)?));
1526 },
1527 "格式::拼接" | "format::join" => match args.first() {
1529 Some(Value::List(items)) => {
1530 return Ok(Value::Str(items.iter().map(|v| v.to_string()).collect()));
1531 },
1532 _ => return Ok(Value::Str(self.builtin_format(&args)?)),
1533 },
1534 "ok" | "好" | "良し" | "좋아" | "โอเค" => {
1536 let val = args.into_iter().next().unwrap_or(Value::Unit);
1537 return Ok(Value::Ok(Box::new(val)));
1538 },
1539 "bad" | "坏" | "err" | "悪い" | "나쁨" | "ผิด" => {
1540 let val = args.into_iter().next().unwrap_or(Value::Unit);
1541 return Ok(Value::Err(Box::new(val)));
1542 },
1543 "向量::从" | "Vec::from" => {
1545 if let Some(Value::List(v)) = args.first() {
1546 return Ok(Value::List(v.clone()));
1547 }
1548 return Ok(Value::List(args));
1549 },
1550 "向量::有容量" | "Vec::with_capacity" => return Ok(Value::List(Vec::new())),
1551 "计时::获取当前小时" | "Timer::hour" => return Ok(Value::Number(14.0)),
1553 "计时::现在" | "Timer::now" => return Ok(Value::Number(1000.0)),
1554 "sleep" | "หยุด" | "นอน" | "sleep_ms" | "睡眠" | "眠る" | "スリープ" | "잠자기"
1556 | "잠" | "流水::睡眠" | "Flow::sleep" => {
1557 if let Some(ms_val) = args.first() {
1558 if let Ok(ms) = self.to_number(ms_val) {
1559 std::thread::sleep(std::time::Duration::from_millis(ms as u64));
1560 }
1561 }
1562 return Ok(Value::Unit);
1563 },
1564 "流水::并行" | "Flow::parallel" => {
1566 if let Some(Value::Fn(params, body, mut cap)) = args.first().cloned() {
1567 let _ = params;
1568 match self.exec_block(&body, &mut cap) {
1569 Ok(Some(v)) => return Ok(v),
1570 Ok(None) => return Ok(Value::Unit),
1571 Err(EvalErr::Return(v)) => return Ok(v),
1572 Err(e) => return Err(e),
1573 }
1574 }
1575 return Ok(Value::Unit);
1576 },
1577
1578 "sin" | "ไซน์" | "正弦" | "サイン" | "사인" => {
1587 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sin()));
1588 },
1589 "cos" | "โคไซน์" | "余弦" | "コサイン" | "코사인" => {
1590 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cos()));
1591 },
1592
1593 "tanh" | "tanhf" | "双曲正切" | "双曲線正接" | "쌍곡탄젠트" => {
1596 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tanh()));
1597 },
1598
1599 "tan" | "แทนเจนต์" | "正切" | "タンジェント" | "탄젠트" => {
1600 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tan()));
1601 },
1602 "asin" | "arcsin" | "反正弦" | "アークサイン" | "아크사인" | "อาร์กไซน์" =>
1603 {
1604 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.asin()));
1605 },
1606 "acos" | "arccos" | "反余弦" | "アークコサイン" | "아크코사인" | "อาร์กโคไซน์" =>
1607 {
1608 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.acos()));
1609 },
1610 "atan" | "arctan" | "反正切" | "アークタンジェント" | "아크탄젠트" | "อาร์กแทนเจนต์" =>
1611 {
1612 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.atan()));
1613 },
1614 "atan2" | "arctan2" | "反正切2" | "アークタンジェント2" | "아크탄젠트2" =>
1615 {
1616 let y = self.arg_num(&args, 0, 0.0)?;
1617 let x = self.arg_num(&args, 1, 1.0)?;
1618 return Ok(Value::Number(y.atan2(x)));
1619 },
1620
1621 "sqrt" | "รากที่สอง" | "平方根" | "根" | "제곱근" => {
1623 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sqrt()));
1624 },
1625 "cbrt" | "立方根" | "세제곱근" | "รากที่สาม" => {
1626 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cbrt()));
1627 },
1628 "pow" | "ยกกำลัง" | "幂" | "べき乗" | "거듭제곱" => {
1629 let base = self.arg_num(&args, 0, 0.0)?;
1630 let exp = self.arg_num(&args, 1, 1.0)?;
1631 return Ok(Value::Number(base.powf(exp)));
1632 },
1633 "exp" | "指数" | "指数関数" | "지수" => {
1634 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.exp()));
1635 },
1636 "hypot" | "斜边" | "斜辺" | "빗변" => {
1637 let x = self.arg_num(&args, 0, 0.0)?;
1638 let y = self.arg_num(&args, 1, 0.0)?;
1639 return Ok(Value::Number(x.hypot(y)));
1640 },
1641
1642 "ln" | "log" | "ลอการิทึม" | "对数" | "対数" | "로그" => {
1644 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.ln()));
1645 },
1646 "log2" | "对数2" | "対数2" | "로그2" => {
1647 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log2()));
1648 },
1649 "log10" | "对数10" | "対数10" | "로그10" => {
1650 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log10()));
1651 },
1652
1653 "abs" | "ค่าสัมบูรณ์" | "绝对值" | "绝对" | "絶対値" | "절댓값" | "절대값" =>
1655 {
1656 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.abs()));
1657 },
1658 "floor" | "ปัดลง" | "向下取整" | "下整" | "床関数" | "내림" => {
1659 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.floor()));
1660 },
1661 "ceil" | "ปัดขึ้น" | "向上取整" | "上整" | "天井関数" | "올림" =>
1662 {
1663 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.ceil()));
1664 },
1665 "round" | "ปัดเศษ" | "四舍五入" | "四舍" | "四捨五入" | "반올림" =>
1666 {
1667 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.round()));
1668 },
1669 "trunc"
1670 | "int"
1671 | "ตัดทศนิยม"
1672 | "取整"
1673 | "整数化"
1674 | "整数"
1675 | "截整"
1676 | "정수화"
1677 | "정수"
1678 | "切り捨て"
1679 | "버림" => {
1680 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.trunc()));
1681 },
1682 "fract" | "小数部分" | "小数部" | "소수부" => {
1683 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.fract()));
1684 },
1685
1686 "min" | "ต่ำสุด" | "最小" | "최솟값" => {
1688 let a = self.arg_num(&args, 0, 0.0)?;
1689 let b = self.arg_num(&args, 1, 0.0)?;
1690 return Ok(Value::Number(a.min(b)));
1691 },
1692 "max" | "สูงสุด" | "最大" | "최댓값" => {
1693 let a = self.arg_num(&args, 0, 0.0)?;
1694 let b = self.arg_num(&args, 1, 0.0)?;
1695 return Ok(Value::Number(a.max(b)));
1696 },
1697 "clamp" | "จำกัด" | "截取" | "範囲制限" | "범위제한" => {
1698 let x = self.arg_num(&args, 0, 0.0)?;
1699 let lo = self.arg_num(&args, 1, 0.0)?;
1700 let hi = self.arg_num(&args, 2, 1.0)?;
1701 return Ok(Value::Number(x.clamp(lo, hi)));
1702 },
1703
1704 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" => {
1706 return Ok(Value::Number(std::f64::consts::PI))
1707 },
1708 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" => {
1709 return Ok(Value::Number(std::f64::consts::TAU))
1710 },
1711
1712 "vnoise" | "noise2" | "นอยส์2ดี" | "柏林噪声2D" | "バリューノイズ2D" | "값노이즈2D" =>
1718 {
1719 let x = self.arg_num(&args, 0, 0.0)? as f32;
1720 let y = self.arg_num(&args, 1, 0.0)? as f32;
1721 let seed = self.arg_num(&args, 2, 0.0)? as u32;
1722 return Ok(Value::Number(tex_vnoise(x, y, seed) as f64));
1723 },
1724
1725 "fbm" | "นอยส์ออร์แกนิก" | "分形噪声" | "フラクタルノイズ" | "프랙탈노이즈" =>
1726 {
1727 let x = self.arg_num(&args, 0, 0.0)? as f32;
1728 let y = self.arg_num(&args, 1, 0.0)? as f32;
1729 let octaves = self.arg_num(&args, 2, 4.0)? as u32;
1730 let seed = self.arg_num(&args, 3, 0.0)? as u32;
1731 return Ok(Value::Number(tex_fbm(x, y, octaves, seed) as f64));
1732 },
1733
1734 "perlin"
1735 | "perlin3"
1736 | "เพอร์ลิน3ดี"
1737 | "柏林噪声3D"
1738 | "パーリンノイズ3D"
1739 | "펄린노이즈3D" => {
1740 let x = self.arg_num(&args, 0, 0.0)? as f32;
1741 let y = self.arg_num(&args, 1, 0.0)? as f32;
1742 let z = self.arg_num(&args, 2, 0.0)? as f32;
1743 return Ok(Value::Number(perlin3(x, y, z) as f64));
1744 },
1745
1746 "lerp" | "ค่าระหว่าง" | "线性插值" | "線形補間" | "선형보간" =>
1748 {
1749 let a = self.arg_num(&args, 0, 0.0)?;
1750 let b = self.arg_num(&args, 1, 1.0)?;
1751 let t = self.arg_num(&args, 2, 0.0)?;
1752 return Ok(Value::Number(a + (b - a) * t));
1753 },
1754
1755 "smoothstep" | "เปลี่ยนแบบนุ่ม" | "平滑步进" | "スムーズステップ" | "스무스스텝" =>
1756 {
1757 let lo = self.arg_num(&args, 0, 0.0)?;
1758 let hi = self.arg_num(&args, 1, 1.0)?;
1759 let x = self.arg_num(&args, 2, 0.5)?;
1760 let t = ((x - lo) / (hi - lo)).clamp(0.0, 1.0);
1761 return Ok(Value::Number(t * t * (3.0 - 2.0 * t)));
1762 },
1763
1764 "rand" | "สุ่ม" | "随机" | "乱数" | "난수" => {
1765 let val = fast_rand_f64(&mut self.rand_state);
1766 return Ok(Value::Number(val));
1767 },
1768
1769 "sign" | "เครื่องหมาย" | "符号" | "符号関数" | "부호" => {
1770 let x = self.arg_num(&args, 0, 0.0)?;
1771 return Ok(Value::Number(x.signum()));
1772 },
1773
1774 "hsv_to_rgb" | "เอชเอสวีเป็นRGB" | "HSV转RGB" | "HSV変換RGB" | "HSV변환RGB" =>
1775 {
1776 let h = self.arg_num(&args, 0, 0.0)?; let s = self.arg_num(&args, 1, 1.0)?; let v = self.arg_num(&args, 2, 1.0)?; let c = v * s;
1780 let x = c * (1.0 - (((h / 60.0) % 2.0) - 1.0).abs());
1781 let m = v - c;
1782 let (r1, g1, b1) = if h < 60.0 {
1783 (c, x, 0.0)
1784 } else if h < 120.0 {
1785 (x, c, 0.0)
1786 } else if h < 180.0 {
1787 (0.0, c, x)
1788 } else if h < 240.0 {
1789 (0.0, x, c)
1790 } else if h < 300.0 {
1791 (x, 0.0, c)
1792 } else {
1793 (c, 0.0, x)
1794 };
1795 let r = ((r1 + m) * 255.0).round();
1796 let g = ((g1 + m) * 255.0).round();
1797 let b = ((b1 + m) * 255.0).round();
1798 return Ok(Value::List(vec![
1799 Value::Number(r),
1800 Value::Number(g),
1801 Value::Number(b),
1802 ]));
1803 },
1804
1805 "lerp_color" | "ไล่สี" | "颜色插值" | "色補間" | "색보간" => {
1806 let r1 = self.arg_num(&args, 0, 0.0)?;
1807 let g1 = self.arg_num(&args, 1, 0.0)?;
1808 let b1 = self.arg_num(&args, 2, 0.0)?;
1809 let r2 = self.arg_num(&args, 3, 255.0)?;
1810 let g2 = self.arg_num(&args, 4, 255.0)?;
1811 let b2 = self.arg_num(&args, 5, 255.0)?;
1812 let t = self.arg_num(&args, 6, 0.0)?;
1813 let r = r1 + (r2 - r1) * t;
1814 let g = g1 + (g2 - g1) * t;
1815 let b = b1 + (b2 - b1) * t;
1816 let c = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
1817 self.gfx.borrow_mut().color = c;
1818 return Ok(Value::Unit);
1819 },
1820
1821 "time_now" | "เวลาปัจจุบัน" | "当前时间" | "経過時間" | "현재시간" =>
1823 {
1824 return Ok(Value::Number(self.start_time.elapsed().as_secs_f64()));
1825 },
1826
1827 "frame_count" | "เฟรม" | "帧数" | "フレーム数" | "프레임수" => {
1828 return Ok(Value::Number(self.frame_num as f64));
1829 },
1830
1831 "mic_open" | "เปิดไมค์" | "开麦克风" | "マイク開く" | "마이크열기" =>
1833 {
1834 #[cfg(not(target_arch = "wasm32"))]
1835 {
1836 match ling_mic::MicInput::open(Default::default()) {
1837 Ok(mic) => {
1838 let _ = mic.start(|_samples: &[f32]| {}); self.mic = Some(mic);
1840 return Ok(Value::Number(1.0)); },
1842 Err(_e) => {
1845 self.mic = None;
1846 return Ok(Value::Number(0.0));
1847 },
1848 }
1849 }
1850 #[cfg(target_arch = "wasm32")]
1851 return Ok(Value::Unit);
1852 },
1853
1854 "mic_rms" | "เสียงRMS" | "麦克风音量" | "マイクRMS" | "마이크RMS" =>
1855 {
1856 #[cfg(not(target_arch = "wasm32"))]
1857 {
1858 let rms = self
1859 .mic
1860 .as_ref()
1861 .map(|m: &ling_mic::MicInput| m.rms())
1862 .unwrap_or(0.0);
1863 return Ok(Value::Number(rms as f64));
1864 }
1865 #[cfg(target_arch = "wasm32")]
1866 return Ok(Value::Number(0.0));
1867 },
1868
1869 "mic_peak" | "เสียงพีค" | "麦克风峰值" | "マイクピーク" | "마이크피크" =>
1870 {
1871 #[cfg(not(target_arch = "wasm32"))]
1872 {
1873 let peak = self
1874 .mic
1875 .as_ref()
1876 .map(|m: &ling_mic::MicInput| m.peak())
1877 .unwrap_or(0.0);
1878 return Ok(Value::Number(peak as f64));
1879 }
1880 #[cfg(target_arch = "wasm32")]
1881 return Ok(Value::Number(0.0));
1882 },
1883
1884 "mic_fft" | "วิเคราะห์เสียงสด" | "实时频谱" | "リアルタイムFFT" | "실시간FFT" =>
1885 {
1886 #[cfg(not(target_arch = "wasm32"))]
1887 {
1888 let n = self.arg_num(&args, 0, 8.0)? as usize;
1889 if let Some(mic) = self.mic.as_ref() {
1890 let samples = mic.latest_samples();
1891 self.fft.borrow_mut().push_samples(&samples);
1892 }
1893 let bands = self.fft.borrow().freq_bands(n);
1894 let result = bands.iter().map(|&v| Value::Number(v as f64)).collect();
1895 return Ok(Value::List(result));
1896 }
1897 #[cfg(target_arch = "wasm32")]
1898 return Ok(Value::List(vec![]));
1899 },
1900
1901 "set_blend" | "โหมดผสม" | "混合模式" | "ブレンドモード" | "블렌드모드" =>
1903 {
1904 let mode = self.arg_num(&args, 0, 0.0)? as u8;
1905 let mut gfx = self.gfx.borrow_mut();
1906 gfx.blend = mode;
1907 let a = gfx.alpha;
1908 gfx.depth_queue.set_state(mode, a); return Ok(Value::Unit);
1910 },
1911
1912 "draw_circle" | "วาดวงกลม" | "画圆" | "円描画" | "원그리기" =>
1914 {
1915 let cx = self.arg_num(&args, 0, 0.0)? as i32;
1916 let cy = self.arg_num(&args, 1, 0.0)? as i32;
1917 let r = self.arg_num(&args, 2, 10.0)? as i32;
1918 let mut gfx = self.gfx.borrow_mut();
1919 let (w, h, color, blend) =
1920 (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
1921 draw_circle_outline(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
1922 return Ok(Value::Unit);
1923 },
1924
1925 "draw_filled_circle"
1926 | "draw_disc"
1927 | "วาดวงกลมทึบ"
1928 | "画实心圆"
1929 | "塗りつぶし円"
1930 | "원채우기" => {
1931 let cx = self.arg_num(&args, 0, 0.0)? as i32;
1932 let cy = self.arg_num(&args, 1, 0.0)? as i32;
1933 let r = self.arg_num(&args, 2, 10.0)? as i32;
1934 let mut gfx = self.gfx.borrow_mut();
1935 let (w, h, color, blend) =
1936 (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
1937 draw_circle_filled(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
1938 return Ok(Value::Unit);
1939 },
1940
1941 "set_alpha" | "ตั้งความโปร่งใส" | "设透明" | "アルファ設定" | "투명도설정" =>
1947 {
1948 let a = self.arg_num(&args, 0, 1.0)? as f32;
1949 let mut gfx = self.gfx.borrow_mut();
1950 gfx.alpha = a.clamp(0.0, 1.0);
1951 let (m, al) = (gfx.blend, gfx.alpha);
1952 gfx.depth_queue.set_state(m, al); return Ok(Value::Unit);
1954 },
1955
1956 "set_color_space" | "ปริภูมิสี" | "色彩空间" | "色空間" | "색공간" =>
1960 {
1961 let m = self.arg_num(&args, 0, 0.0)? as i64;
1962 self.gfx.borrow_mut().linear_blend = m != 0;
1963 return Ok(Value::Unit);
1964 },
1965
1966 "set_gradient_space" | "ปริภูมิไล่สี" | "渐变空间" | "グラデ空間" | "그라데이션공간" =>
1969 {
1970 let m = self.arg_num(&args, 0, 1.0)? as i64;
1971 self.gfx.borrow_mut().grad_oklab = m != 0;
1972 return Ok(Value::Unit);
1973 },
1974
1975 "mix_color" | "ผสมสี" | "混合颜色" | "色混合" | "색혼합" => {
1979 let c0 = rgb(
1980 self.arg_num(&args, 0, 0.0)?,
1981 self.arg_num(&args, 1, 0.0)?,
1982 self.arg_num(&args, 2, 0.0)?,
1983 );
1984 let c1 = rgb(
1985 self.arg_num(&args, 3, 255.0)?,
1986 self.arg_num(&args, 4, 255.0)?,
1987 self.arg_num(&args, 5, 255.0)?,
1988 );
1989 let t = self.arg_num(&args, 6, 0.5)? as f32;
1990 self.gfx.borrow_mut().color = crate::gfx::color::mix_oklab(c0, c1, t);
1991 return Ok(Value::Unit);
1992 },
1993
1994 "set_depth_test" | "ทดสอบความลึก" | "深度测试" | "深度テスト" | "깊이테스트" =>
1998 {
1999 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
2000 self.gfx.borrow_mut().depth_test = on;
2001 return Ok(Value::Unit);
2002 },
2003
2004 "grad_triangle" | "สามเหลี่ยมไล่สี" | "渐变三角" | "グラデ三角" | "그라데삼각" =>
2008 {
2009 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
2010 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
2011 let c0 = rgb(
2012 self.arg_num(&args, 2, 255.0)?,
2013 self.arg_num(&args, 3, 255.0)?,
2014 self.arg_num(&args, 4, 255.0)?,
2015 );
2016 let x1 = self.arg_num(&args, 5, 0.0)? as f32;
2017 let y1 = self.arg_num(&args, 6, 0.0)? as f32;
2018 let c1 = rgb(
2019 self.arg_num(&args, 7, 255.0)?,
2020 self.arg_num(&args, 8, 255.0)?,
2021 self.arg_num(&args, 9, 255.0)?,
2022 );
2023 let x2 = self.arg_num(&args, 10, 0.0)? as f32;
2024 let y2 = self.arg_num(&args, 11, 0.0)? as f32;
2025 let c2 = rgb(
2026 self.arg_num(&args, 12, 255.0)?,
2027 self.arg_num(&args, 13, 255.0)?,
2028 self.arg_num(&args, 14, 255.0)?,
2029 );
2030 let mut gfx = self.gfx.borrow_mut();
2031 let (w, h, alpha, mode, lin, ok) = (
2032 gfx.width,
2033 gfx.height,
2034 gfx.alpha,
2035 gfx.blend,
2036 gfx.linear_blend,
2037 gfx.grad_oklab,
2038 );
2039 crate::gfx::raster::fill_triangle_grad(
2040 &mut gfx.buffer,
2041 w,
2042 h,
2043 alpha,
2044 mode,
2045 lin,
2046 ok,
2047 x0,
2048 y0,
2049 c0,
2050 x1,
2051 y1,
2052 c1,
2053 x2,
2054 y2,
2055 c2,
2056 );
2057 return Ok(Value::Unit);
2058 },
2059
2060 "grad_rect" | "สี่เหลี่ยมไล่สี" | "渐变矩形" | "グラデ矩形" | "그라데사각" =>
2063 {
2064 let x = self.arg_num(&args, 0, 0.0)? as f32;
2065 let y = self.arg_num(&args, 1, 0.0)? as f32;
2066 let rw = self.arg_num(&args, 2, 0.0)? as f32;
2067 let rh = self.arg_num(&args, 3, 0.0)? as f32;
2068 let c0 = rgb(
2069 self.arg_num(&args, 4, 255.0)?,
2070 self.arg_num(&args, 5, 255.0)?,
2071 self.arg_num(&args, 6, 255.0)?,
2072 );
2073 let c1 = rgb(
2074 self.arg_num(&args, 7, 0.0)?,
2075 self.arg_num(&args, 8, 0.0)?,
2076 self.arg_num(&args, 9, 0.0)?,
2077 );
2078 let dir = self.arg_num(&args, 10, 1.0)? as u8;
2079 let mut gfx = self.gfx.borrow_mut();
2080 let (w, h, alpha, mode, lin, ok) = (
2081 gfx.width,
2082 gfx.height,
2083 gfx.alpha,
2084 gfx.blend,
2085 gfx.linear_blend,
2086 gfx.grad_oklab,
2087 );
2088 crate::gfx::raster::fill_rect_grad(
2089 &mut gfx.buffer,
2090 w,
2091 h,
2092 alpha,
2093 mode,
2094 lin,
2095 ok,
2096 x,
2097 y,
2098 rw,
2099 rh,
2100 c0,
2101 c1,
2102 dir,
2103 );
2104 return Ok(Value::Unit);
2105 },
2106
2107 "shadow_blob" | "เงาวงรี" | "阴影斑" | "影ブロブ" | "그림자블롭" =>
2111 {
2112 let cx = self.arg_num(&args, 0, 0.0)? as f32;
2113 let cy = self.arg_num(&args, 1, 0.0)? as f32;
2114 let rx = self.arg_num(&args, 2, 16.0)? as f32;
2115 let ry = self.arg_num(&args, 3, 8.0)? as f32;
2116 let a = self.arg_num(&args, 4, 0.5)? as f32;
2117 let mut gfx = self.gfx.borrow_mut();
2118 let (w, h, color, soft, mode, lin) = (
2119 gfx.width,
2120 gfx.height,
2121 gfx.color,
2122 gfx.shadow.soft,
2123 gfx.blend,
2124 gfx.linear_blend,
2125 );
2126 crate::gfx::raster::fill_disc_soft(
2127 &mut gfx.buffer,
2128 w,
2129 h,
2130 cx,
2131 cy,
2132 rx,
2133 ry,
2134 color,
2135 a,
2136 soft,
2137 mode,
2138 lin,
2139 );
2140 return Ok(Value::Unit);
2141 },
2142
2143 "cast_shadow" | "ทอดเงา" | "投射阴影" | "影を落とす" | "그림자드리우기" =>
2148 {
2149 let cx = self.arg_num(&args, 0, 0.0)? as f32;
2150 let cy = self.arg_num(&args, 1, 0.0)? as f32;
2151 let height = (self.arg_num(&args, 2, 0.0)? as f32).max(0.0);
2152 let mut gfx = self.gfx.borrow_mut();
2153 let sp = gfx.shadow;
2154 let radius = (sp.base + sp.grow * height).max(0.5);
2155 let alpha = (sp.alpha - sp.fade * height).clamp(0.04, 1.0);
2156 let soft = (sp.soft + height * 0.004).clamp(0.0, 0.95);
2157 let (w, h, color, mode, lin) = (
2158 gfx.width,
2159 gfx.height,
2160 gfx.color,
2161 gfx.blend,
2162 gfx.linear_blend,
2163 );
2164 crate::gfx::raster::fill_disc_soft(
2165 &mut gfx.buffer,
2166 w,
2167 h,
2168 cx,
2169 cy,
2170 radius,
2171 radius * 0.62,
2172 color,
2173 alpha,
2174 soft,
2175 mode,
2176 lin,
2177 );
2178 return Ok(Value::Unit);
2179 },
2180
2181 "shadow_params" | "ตั้งค่าเงา" | "阴影参数" | "影設定" | "그림자설정" =>
2184 {
2185 let cur = self.gfx.borrow().shadow;
2186 let base = self.arg_num(&args, 0, cur.base as f64)? as f32;
2187 let grow = self.arg_num(&args, 1, cur.grow as f64)? as f32;
2188 let alpha = self.arg_num(&args, 2, cur.alpha as f64)? as f32;
2189 let fade = self.arg_num(&args, 3, cur.fade as f64)? as f32;
2190 let soft = self.arg_num(&args, 4, cur.soft as f64)? as f32;
2191 self.gfx.borrow_mut().shadow =
2192 crate::gfx::ShadowParams { base, grow, alpha, fade, soft };
2193 return Ok(Value::Unit);
2194 },
2195
2196 "depth_triangle" | "สามเหลี่ยมเรียงลึก" | "深度三角" | "深度三角形" | "깊이삼각" =>
2201 {
2202 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
2203 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
2204 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
2205 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
2206 let x2 = self.arg_num(&args, 4, 0.0)? as f32;
2207 let y2 = self.arg_num(&args, 5, 0.0)? as f32;
2208 let z = self.arg_num(&args, 6, 0.0)? as f32;
2209 let mut gfx = self.gfx.borrow_mut();
2210 let color = gfx.color;
2211 gfx.depth_queue
2212 .push_triangle(z, color, x0, y0, x1, y1, x2, y2);
2213 return Ok(Value::Unit);
2214 },
2215
2216 "depth_line" | "เส้นเรียงลึก" | "深度线" | "深度線" | "깊이선" =>
2219 {
2220 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
2221 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
2222 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
2223 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
2224 let z = self.arg_num(&args, 4, 0.0)? as f32;
2225 let mut gfx = self.gfx.borrow_mut();
2226 let color = gfx.color;
2227 gfx.depth_queue.push_line(z, color, x0, y0, x1, y1);
2228 return Ok(Value::Unit);
2229 },
2230
2231 "เปิดหน้าต่าง" | "open_window" | "gfx_window" | "开窗" | "ウィンドウ開く" | "창열기" =>
2238 {
2239 let w = self.arg_num(&args, 0, 800.0)? as usize;
2240 let h = self.arg_num(&args, 1, 600.0)? as usize;
2241 #[cfg(not(target_arch = "wasm32"))]
2242 {
2243 let title = args
2244 .get(2)
2245 .map(|v| v.to_string())
2246 .unwrap_or_else(|| "Ling".into());
2247 let mut gfx = self.gfx.borrow_mut();
2248 let mut win = minifb::Window::new(
2249 &title,
2250 w,
2251 h,
2252 minifb::WindowOptions {
2253 resize: false,
2254 scale: minifb::Scale::X1,
2255 ..Default::default()
2256 },
2257 )
2258 .map_err(|e| EvalErr::from(format!("cannot open window: {e}")))?;
2259 #[allow(deprecated)]
2260 win.limit_update_rate(Some(std::time::Duration::from_millis(8)));
2261 gfx.buffer = vec![0u32; w * h];
2262 gfx.width = w;
2263 gfx.height = h;
2264 gfx.window = Some(win);
2265 gfx.sync_projection();
2266 hide_console_window();
2267 }
2268 #[cfg(target_arch = "wasm32")]
2269 {
2270 let mut gfx = self.gfx.borrow_mut();
2271 gfx.width = w;
2272 gfx.height = h;
2273 gfx.buffer.resize(w * h, 0); gfx.sync_projection();
2275 crate::gfx::webgl::resize(w as u32, h as u32);
2276 }
2277 return Ok(Value::Unit);
2278 },
2279
2280 "เติม" | "fill" | "gfx_fill" | "clear" | "填" | "塗り潰し" | "채우기" | "清"
2282 | "消去" | "지우기" => {
2283 let r = self.arg_num(&args, 0, 0.0)? as u32;
2284 let g = self.arg_num(&args, 1, 0.0)? as u32;
2285 let b = self.arg_num(&args, 2, 0.0)? as u32;
2286 #[cfg(not(target_arch = "wasm32"))]
2287 {
2288 let c = (r << 16) | (g << 8) | b;
2289 self.gfx.borrow_mut().buffer.fill(c);
2290 }
2291 #[cfg(target_arch = "wasm32")]
2292 {
2293 let mut gfx = self.gfx.borrow_mut();
2294 gfx.fill_r = r as f32 / 255.0;
2295 gfx.fill_g = g as f32 / 255.0;
2296 gfx.fill_b = b as f32 / 255.0;
2297 let c = (r << 16) | (g << 8) | b;
2300 gfx.buffer.fill(c);
2301 }
2302 return Ok(Value::Unit);
2303 },
2304
2305 "set_color_hsl" | "颜色HSL" | "色相" | "HSL色" | "HSL색설정" | "สีHSLวาด" =>
2308 {
2309 let h = self.arg_num(&args, 0, 0.0)?;
2310 let s = self.arg_num(&args, 1, 70.0)?;
2311 let l = self.arg_num(&args, 2, 50.0)?;
2312 let hex = hsl_to_hex(h, s, l);
2313 let r = u32::from_str_radix(&hex[1..3], 16).unwrap_or(255);
2314 let g = u32::from_str_radix(&hex[3..5], 16).unwrap_or(255);
2315 let b = u32::from_str_radix(&hex[5..7], 16).unwrap_or(255);
2316 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
2317 return Ok(Value::Unit);
2318 },
2319
2320 "สีดินสอ" | "set_color" | "gfx_color" | "color" | "设色" | "色設定" | "색설정" =>
2322 {
2323 let r = self.arg_num(&args, 0, 255.0)? as u32;
2324 let g = self.arg_num(&args, 1, 255.0)? as u32;
2325 let b = self.arg_num(&args, 2, 255.0)? as u32;
2326 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
2327 return Ok(Value::Unit);
2328 },
2329
2330 "วาดสามเหลี่ยม"
2332 | "draw_triangle"
2333 | "gfx_triangle"
2334 | "triangle"
2335 | "画三角"
2336 | "三角形描画"
2337 | "삼각형그리기" => {
2338 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
2339 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
2340 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
2341 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
2342 let x2 = self.arg_num(&args, 4, 0.0)? as f32;
2343 let y2 = self.arg_num(&args, 5, 0.0)? as f32;
2344 let mut gfx = self.gfx.borrow_mut();
2345 let color = gfx.color;
2346 #[cfg(not(target_arch = "wasm32"))]
2347 {
2348 let w = gfx.width;
2349 let h = gfx.height;
2350 fill_triangle(&mut gfx.buffer, w, h, color, x0, y0, x1, y1, x2, y2);
2351 }
2352 #[cfg(target_arch = "wasm32")]
2353 gfx.depth_queue
2354 .push_triangle(0.0, color, x0, y0, x1, y1, x2, y2);
2355 return Ok(Value::Unit);
2356 },
2357
2358 "วาดเส้น" | "draw_line" | "gfx_line" | "line" | "画线" | "線描く" | "선그리기" =>
2360 {
2361 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
2362 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
2363 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
2364 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
2365 let mut gfx = self.gfx.borrow_mut();
2366 let color = gfx.color;
2367 #[cfg(not(target_arch = "wasm32"))]
2368 {
2369 let w = gfx.width;
2370 let h = gfx.height;
2371 draw_line(&mut gfx.buffer, w, h, color, x0, y0, x1, y1);
2372 }
2373 #[cfg(target_arch = "wasm32")]
2374 gfx.depth_queue.push_line(0.0, color, x0, y0, x1, y1);
2375 return Ok(Value::Unit);
2376 },
2377
2378 "วาดจุด" | "draw_pixel" | "gfx_pixel" | "pixel" | "画点" | "点描く" | "점그리기" =>
2380 {
2381 let px = self.arg_num(&args, 0, 0.0)? as i32;
2382 let py = self.arg_num(&args, 1, 0.0)? as i32;
2383 #[cfg(not(target_arch = "wasm32"))]
2384 {
2385 let mut gfx = self.gfx.borrow_mut();
2386 let color = gfx.color;
2387 let w = gfx.width;
2388 let h = gfx.height;
2389 if px >= 0 && py >= 0 && (px as usize) < w && (py as usize) < h {
2390 gfx.buffer[py as usize * w + px as usize] = color;
2391 }
2392 }
2393 #[cfg(target_arch = "wasm32")]
2394 {
2395 let mut gfx = self.gfx.borrow_mut();
2397 let color = gfx.color;
2398 let x = px as f32;
2399 let y = py as f32;
2400 gfx.depth_queue
2401 .push_triangle(0.0, color, x, y, x + 1.0, y, x + 1.0, y + 1.0);
2402 gfx.depth_queue
2403 .push_triangle(0.0, color, x, y, x + 1.0, y + 1.0, x, y + 1.0);
2404 }
2405 return Ok(Value::Unit);
2406 },
2407
2408 "แสดงผล" | "present" | "gfx_present" | "show" | "显" | "呈现" | "表示" | "표시" =>
2410 {
2411 #[cfg(not(target_arch = "wasm32"))]
2412 {
2413 {
2415 let mut gfx = self.gfx.borrow_mut();
2416 if !gfx.depth_queue.is_empty() {
2417 let w = gfx.width;
2418 let h = gfx.height;
2419 let dt = gfx.depth_test;
2420 let (bm, ba) = (gfx.blend, gfx.alpha);
2421 let queue = std::mem::take(&mut gfx.depth_queue);
2422 {
2423 let g = &mut *gfx;
2424 let z = if dt { Some(&mut g.depth_buf) } else { None };
2425 queue.flush(&mut g.buffer, z, w, h);
2426 }
2427 gfx.depth_queue.set_state(bm, ba);
2428 }
2429 let buf = gfx.buffer.clone();
2430 let w = gfx.width;
2431 let h = gfx.height;
2432 if let Some(win) = gfx.window.as_mut() {
2433 win.update_with_buffer(&buf, w, h)
2434 .map_err(|e| EvalErr::from(format!("present error: {e}")))?;
2435 }
2436 }
2437 let mouse_pos = {
2439 let gfx = self.gfx.borrow();
2440 gfx.window
2441 .as_ref()
2442 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
2443 };
2444 let mut gfx = self.gfx.borrow_mut();
2445 if gfx.mouse_captured {
2446 let w = gfx.width as f32;
2447 let h = gfx.height as f32;
2448 if let Some((mx, my)) = mouse_pos {
2449 if gfx.last_mx.is_nan() {
2450 gfx.mouse_dx = 0.0;
2451 gfx.mouse_dy = 0.0;
2452 gfx.last_mx = mx;
2453 gfx.last_my = my;
2454 } else {
2455 gfx.mouse_dx = mx - gfx.last_mx;
2456 gfx.mouse_dy = my - gfx.last_my;
2457 let margin = 6.0;
2460 let (mut nx, mut ny, mut warp) = (mx, my, false);
2461 if mx < margin {
2462 nx = w - margin - 2.0;
2463 warp = true;
2464 } else if mx > w - margin {
2465 nx = margin + 2.0;
2466 warp = true;
2467 }
2468 if my < margin {
2469 ny = h - margin - 2.0;
2470 warp = true;
2471 } else if my > h - margin {
2472 ny = margin + 2.0;
2473 warp = true;
2474 }
2475 if warp {
2476 #[cfg(windows)]
2477 unsafe {
2478 #[repr(C)]
2479 struct RECT {
2480 left: i32,
2481 top: i32,
2482 right: i32,
2483 bottom: i32,
2484 }
2485 extern "system" {
2486 fn GetForegroundWindow() -> isize;
2487 fn GetWindowRect(hwnd: isize, lpRect: *mut RECT)
2488 -> i32;
2489 fn SetCursorPos(x: i32, y: i32) -> i32;
2490 }
2491 let hwnd = GetForegroundWindow();
2492 let mut rect =
2493 RECT { left: 0, top: 0, right: 0, bottom: 0 };
2494 if GetWindowRect(hwnd, &mut rect) != 0 {
2495 SetCursorPos(
2496 rect.left + nx as i32,
2497 rect.top + ny as i32,
2498 );
2499 }
2500 }
2501 gfx.last_mx = nx;
2502 gfx.last_my = ny;
2503 } else {
2504 gfx.last_mx = mx;
2505 gfx.last_my = my;
2506 }
2507 }
2508 } else {
2509 gfx.mouse_dx = 0.0;
2510 gfx.mouse_dy = 0.0;
2511 }
2512 } else if let Some((mx, my)) = mouse_pos {
2513 if gfx.last_mx.is_nan() {
2514 gfx.mouse_dx = 0.0;
2515 gfx.mouse_dy = 0.0;
2516 } else {
2517 gfx.mouse_dx = mx - gfx.last_mx;
2518 gfx.mouse_dy = my - gfx.last_my;
2519 }
2520 gfx.last_mx = mx;
2521 gfx.last_my = my;
2522 } else {
2523 gfx.mouse_dx = 0.0;
2524 gfx.mouse_dy = 0.0;
2525 }
2526 }
2527 #[cfg(target_arch = "wasm32")]
2528 {
2529 let mut gfx = self.gfx.borrow_mut();
2533 let w = gfx.width;
2534 let h = gfx.height;
2535 if gfx.buffer.len() != w * h {
2536 gfx.buffer.resize(w * h, 0);
2537 }
2538 if !gfx.depth_queue.is_empty() {
2539 let dt = gfx.depth_test;
2540 let queue = std::mem::take(&mut gfx.depth_queue);
2541 let g = &mut *gfx;
2542 let z = if dt { Some(&mut g.depth_buf) } else { None };
2543 queue.flush(&mut g.buffer, z, w, h);
2544 }
2545 crate::gfx::webgl::blit_rgb(&gfx.buffer, w, h);
2546 }
2547 #[cfg(not(target_arch = "wasm32"))]
2549 {
2550 let (_, _, down) = self.mouse_now();
2551 self.mouse_was_down = down;
2552 }
2553 self.frame_num += 1;
2555 return Ok(Value::Unit);
2556 },
2557
2558 "เปิดหน้าต่างเต็มจอ"
2560 | "open_fullscreen"
2561 | "fullscreen"
2562 | "全屏"
2563 | "全画面"
2564 | "전체화면" => {
2565 #[cfg(target_arch = "wasm32")]
2568 let (default_w, default_h) = {
2569 let (cw, ch) = crate::gfx::webgl::canvas_size();
2570 (cw as f64, ch as f64)
2571 };
2572 #[cfg(all(not(target_arch = "wasm32"), windows))]
2574 let (default_w, default_h) = unsafe {
2575 extern "system" {
2576 fn GetSystemMetrics(nIndex: i32) -> i32;
2577 }
2578 (GetSystemMetrics(0) as f64, GetSystemMetrics(1) as f64)
2579 };
2580 #[cfg(all(not(target_arch = "wasm32"), not(windows)))]
2581 let (default_w, default_h) = native_screen_size();
2582
2583 let w = args
2584 .get(1)
2585 .map(|v| self.to_number(v).unwrap_or(default_w) as usize)
2586 .unwrap_or(default_w as usize);
2587 let h = args
2588 .get(2)
2589 .map(|v| self.to_number(v).unwrap_or(default_h) as usize)
2590 .unwrap_or(default_h as usize);
2591 #[cfg(not(target_arch = "wasm32"))]
2592 {
2593 let title = args
2594 .get(0)
2595 .map(|v| v.to_string())
2596 .unwrap_or_else(|| "Ling".into());
2597 let mut gfx = self.gfx.borrow_mut();
2598 let mut win = minifb::Window::new(
2599 &title,
2600 w,
2601 h,
2602 minifb::WindowOptions {
2603 borderless: true,
2604 title: false,
2605 resize: false,
2606 topmost: true,
2607 scale: minifb::Scale::X1,
2608 ..Default::default()
2609 },
2610 )
2611 .map_err(|e| EvalErr::from(format!("cannot open fullscreen: {e}")))?;
2612 win.set_target_fps(monitor_info().2.max(30) as usize);
2615 #[cfg(windows)]
2617 let hwnd = win.get_window_handle() as isize;
2618 gfx.buffer = vec![0u32; w * h];
2619 gfx.width = w;
2620 gfx.height = h;
2621 gfx.window = Some(win);
2622 gfx.sync_projection();
2623 #[cfg(windows)]
2625 make_borderless_fullscreen(hwnd, w as i32, h as i32);
2626 hide_console_window();
2627 }
2628 #[cfg(target_arch = "wasm32")]
2629 {
2630 let mut gfx = self.gfx.borrow_mut();
2631 gfx.width = w;
2632 gfx.height = h;
2633 gfx.buffer.resize(w * h, 0); gfx.sync_projection();
2635 crate::gfx::webgl::resize(w as u32, h as u32);
2636 }
2637 return Ok(Value::Unit);
2638 },
2639
2640 "get_width" | "ความกว้าง" | "宽" | "幅取得" | "너비" => {
2642 return Ok(Value::Number(self.gfx.borrow().width as f64));
2643 },
2644 "get_height" | "ความสูง" | "高" | "高取得" | "높이" => {
2645 return Ok(Value::Number(self.gfx.borrow().height as f64));
2646 },
2647
2648 "monitor_width" | "screen_width" | "屏宽" | "画面幅" | "화면너비" | "ความกว้างจอ" =>
2651 {
2652 return Ok(Value::Number(monitor_info().0 as f64));
2653 },
2654 "monitor_height" | "screen_height" | "屏高" | "画面高" | "화면높이" | "ความสูงจอ" =>
2656 {
2657 return Ok(Value::Number(monitor_info().1 as f64));
2658 },
2659 "monitor_refresh"
2661 | "monitor_hz"
2662 | "monitor_fps"
2663 | "refresh_rate"
2664 | "刷新率"
2665 | "リフレッシュレート"
2666 | "주사율"
2667 | "อัตรารีเฟรช" => {
2668 return Ok(Value::Number(monitor_info().2 as f64));
2669 },
2670 "monitor_info" | "screen_info" | "屏幕信息" | "画面情報" | "화면정보" | "ข้อมูลจอ" =>
2672 {
2673 let (w, h, hz) = monitor_info();
2674 return Ok(Value::List(vec![
2675 Value::Number(w as f64),
2676 Value::Number(h as f64),
2677 Value::Number(hz as f64),
2678 ]));
2679 },
2680 "set_fps"
2682 | "set_target_fps"
2683 | "target_fps"
2684 | "设帧率"
2685 | "フレームレート設定"
2686 | "프레임설정"
2687 | "ตั้งเฟรมเรต" => {
2688 let fps = self.arg_num(&args, 0, 60.0)?.max(1.0) as usize;
2689 #[cfg(not(target_arch = "wasm32"))]
2690 {
2691 let mut gfx = self.gfx.borrow_mut();
2692 if let Some(win) = gfx.window.as_mut() {
2693 win.set_target_fps(fps);
2694 }
2695 }
2696 return Ok(Value::Unit);
2697 },
2698
2699 "หน้าต่างเปิดอยู่"
2701 | "window_is_open"
2702 | "gfx_is_open"
2703 | "is_open"
2704 | "窗开"
2705 | "開いている"
2706 | "창열림" => {
2707 #[cfg(not(target_arch = "wasm32"))]
2708 {
2709 let gfx = self.gfx.borrow();
2710 let open = gfx
2711 .window
2712 .as_ref()
2713 .map(|w| w.is_open() && !w.is_key_down(minifb::Key::Escape))
2714 .unwrap_or(false);
2715 return Ok(Value::Bool(open));
2716 }
2717 #[cfg(target_arch = "wasm32")]
2718 return Ok(Value::Bool(true));
2719 },
2720
2721 "key_down" | "กดค้าง" | "按键" | "キー押す" | "키누름" => {
2723 #[cfg(not(target_arch = "wasm32"))]
2724 {
2725 let name = self.arg_str(&args, 0, "");
2726 let gfx = self.gfx.borrow();
2727 let down = gfx
2728 .window
2729 .as_ref()
2730 .and_then(|w| str_to_minifb_key(&name).map(|k| w.is_key_down(k)))
2731 .unwrap_or(false);
2732 return Ok(Value::Bool(down));
2733 }
2734 #[cfg(target_arch = "wasm32")]
2735 return Ok(Value::Bool(false));
2736 },
2737
2738 "key_pressed" | "กดปุ่ม" | "键按" | "キー押した" | "키눌림" => {
2740 #[cfg(not(target_arch = "wasm32"))]
2741 {
2742 let name = self.arg_str(&args, 0, "");
2743 let pressed = {
2744 let gfx = self.gfx.borrow();
2745 gfx.window
2746 .as_ref()
2747 .and_then(|w| {
2748 str_to_minifb_key(&name)
2749 .map(|k| w.is_key_pressed(k, minifb::KeyRepeat::No))
2750 })
2751 .unwrap_or(false)
2752 };
2753 let pressed =
2755 pressed || ((name == "enter" || name == "return") && gamepad::start_edge());
2756 return Ok(Value::Bool(pressed));
2757 }
2758 #[cfg(target_arch = "wasm32")]
2759 return Ok(Value::Bool(false));
2760 },
2761
2762 "mouse_dx" | "เมาส์X" | "鼠ΔX" | "マウスΔX" | "마우스ΔX" => {
2764 #[cfg(not(target_arch = "wasm32"))]
2765 return Ok(Value::Number(self.gfx.borrow().mouse_dx as f64));
2766 #[cfg(target_arch = "wasm32")]
2767 return Ok(Value::Number(0.0));
2768 },
2769 #[cfg(not(target_arch = "wasm32"))]
2771 "mouse_scroll" | "ล้อเมาส์" | "滚轮" | "ホイール" | "스크롤" =>
2772 {
2773 let gfx = self.gfx.borrow();
2774 let s = gfx
2775 .window
2776 .as_ref()
2777 .and_then(|w| w.get_scroll_wheel())
2778 .map(|(_, y)| y as f64)
2779 .unwrap_or(0.0);
2780 return Ok(Value::Number(s));
2781 },
2782 "mouse_dy" | "เมาส์Y" | "鼠ΔY" | "マウスΔY" | "마우스ΔY" => {
2783 #[cfg(not(target_arch = "wasm32"))]
2784 return Ok(Value::Number(self.gfx.borrow().mouse_dy as f64));
2785 #[cfg(target_arch = "wasm32")]
2786 return Ok(Value::Number(0.0));
2787 },
2788
2789 "pad_poll" | "手柄轮询" | "パッド更新" | "패드폴링" | "อัปเดตแพด" =>
2792 {
2793 #[cfg(not(target_arch = "wasm32"))]
2794 return Ok(Value::Number(self.pad_poll() as f64));
2795 #[cfg(target_arch = "wasm32")]
2796 return Ok(Value::Number(0.0));
2797 },
2798 "pad_count" | "手柄数" | "パッド数" | "패드수" | "จำนวนแพด" =>
2800 {
2801 #[cfg(not(target_arch = "wasm32"))]
2802 {
2803 let inp = self.input.borrow();
2804 let n = inp.as_ref().map_or(0, |s| s.sensorium.devices.count());
2805 return Ok(Value::Number(n as f64));
2806 }
2807 #[cfg(target_arch = "wasm32")]
2808 return Ok(Value::Number(0.0));
2809 },
2810 "pad_connected" | "手柄连接" | "パッド接続" | "패드연결" | "แพดเชื่อม" =>
2812 {
2813 #[cfg(not(target_arch = "wasm32"))]
2814 {
2815 let i = self.arg_num(&args, 0, 0.0)? as usize;
2816 let inp = self.input.borrow();
2817 let c = inp
2818 .as_ref()
2819 .is_some_and(|s| s.sensorium.devices.for_player(i as u8).is_some());
2820 return Ok(Value::Bool(c));
2821 }
2822 #[cfg(target_arch = "wasm32")]
2823 return Ok(Value::Bool(false));
2824 },
2825 "pad_button" | "手柄按键" | "パッドボタン" | "패드버튼" | "ปุ่มแพด" =>
2827 {
2828 #[cfg(not(target_arch = "wasm32"))]
2829 {
2830 let i = self.arg_num(&args, 0, 0.0)? as usize;
2831 let name = self.arg_str(&args, 1, "");
2832 let down = parse_pad_button(&name)
2833 .is_some_and(|b| self.with_pad(i, false, |p| p.is_down(b)));
2834 return Ok(Value::Bool(down));
2835 }
2836 #[cfg(target_arch = "wasm32")]
2837 return Ok(Value::Bool(false));
2838 },
2839 "pad_pressed" | "手柄按下" | "パッド押下" | "패드눌림" | "แพดกด" =>
2841 {
2842 #[cfg(not(target_arch = "wasm32"))]
2843 {
2844 let i = self.arg_num(&args, 0, 0.0)? as usize;
2845 let name = self.arg_str(&args, 1, "");
2846 let p = parse_pad_button(&name)
2847 .is_some_and(|b| self.with_pad(i, false, |g| g.just_pressed(b)));
2848 return Ok(Value::Bool(p));
2849 }
2850 #[cfg(target_arch = "wasm32")]
2851 return Ok(Value::Bool(false));
2852 },
2853 "pad_lx" | "手柄左X" | "パッド左X" | "패드왼X" | "แพดซ้ายX" => {
2855 #[cfg(not(target_arch = "wasm32"))]
2856 {
2857 let i = self.arg_num(&args, 0, 0.0)? as usize;
2858 return Ok(Value::Number(
2859 self.with_pad(i, 0.0, |p| p.left_stick.x as f64),
2860 ));
2861 }
2862 #[cfg(target_arch = "wasm32")]
2863 return Ok(Value::Number(0.0));
2864 },
2865 "pad_ly" | "手柄左Y" | "パッド左Y" | "패드왼Y" | "แพดซ้ายY" => {
2866 #[cfg(not(target_arch = "wasm32"))]
2867 {
2868 let i = self.arg_num(&args, 0, 0.0)? as usize;
2869 return Ok(Value::Number(
2870 self.with_pad(i, 0.0, |p| p.left_stick.y as f64),
2871 ));
2872 }
2873 #[cfg(target_arch = "wasm32")]
2874 return Ok(Value::Number(0.0));
2875 },
2876 "pad_rx" | "手柄右X" | "パッド右X" | "패드오X" | "แพดขวาX" => {
2877 #[cfg(not(target_arch = "wasm32"))]
2878 {
2879 let i = self.arg_num(&args, 0, 0.0)? as usize;
2880 return Ok(Value::Number(
2881 self.with_pad(i, 0.0, |p| p.right_stick.x as f64),
2882 ));
2883 }
2884 #[cfg(target_arch = "wasm32")]
2885 return Ok(Value::Number(0.0));
2886 },
2887 "pad_ry" | "手柄右Y" | "パッド右Y" | "패드오Y" | "แพดขวาY" => {
2888 #[cfg(not(target_arch = "wasm32"))]
2889 {
2890 let i = self.arg_num(&args, 0, 0.0)? as usize;
2891 return Ok(Value::Number(
2892 self.with_pad(i, 0.0, |p| p.right_stick.y as f64),
2893 ));
2894 }
2895 #[cfg(target_arch = "wasm32")]
2896 return Ok(Value::Number(0.0));
2897 },
2898 "pad_lt" | "手柄左扳机" | "パッド左トリガー" | "패드왼트리거" | "ไกแพดซ้าย" =>
2900 {
2901 #[cfg(not(target_arch = "wasm32"))]
2902 {
2903 let i = self.arg_num(&args, 0, 0.0)? as usize;
2904 return Ok(Value::Number(
2905 self.with_pad(i, 0.0, |p| p.left_trigger as f64),
2906 ));
2907 }
2908 #[cfg(target_arch = "wasm32")]
2909 return Ok(Value::Number(0.0));
2910 },
2911 "pad_rt" | "手柄右扳机" | "パッド右トリガー" | "패드오트리거" | "ไกแพดขวา" =>
2912 {
2913 #[cfg(not(target_arch = "wasm32"))]
2914 {
2915 let i = self.arg_num(&args, 0, 0.0)? as usize;
2916 return Ok(Value::Number(
2917 self.with_pad(i, 0.0, |p| p.right_trigger as f64),
2918 ));
2919 }
2920 #[cfg(target_arch = "wasm32")]
2921 return Ok(Value::Number(0.0));
2922 },
2923 "pad_rumble" | "手柄震动" | "パッド振動" | "패드진동" | "แพดสั่น" =>
2925 {
2926 #[cfg(not(target_arch = "wasm32"))]
2927 {
2928 use ling_input::backend::InputBackend;
2929 let i = self.arg_num(&args, 0, 0.0)? as usize;
2930 let lo = self.arg_num(&args, 1, 0.0)? as f32;
2931 let hi = self.arg_num(&args, 2, lo as f64)? as f32;
2932 let mut inp = self.input.borrow_mut();
2933 if let Some(s) = inp.as_mut() {
2934 if let Some(dev) = s.sensorium.devices.for_player(i as u8).map(|d| d.id) {
2935 s.backend.set_rumble(
2936 dev,
2937 ling_input::Rumble { low: lo, high: hi, ..Default::default() },
2938 );
2939 }
2940 }
2941 return Ok(Value::Unit);
2942 }
2943 #[cfg(target_arch = "wasm32")]
2944 return Ok(Value::Unit);
2945 },
2946
2947 "set_camera_pos" | "ตั้งตำแหน่งกล้อง" | "镜坐标" | "カメラ座標" | "카메라좌표" =>
2949 {
2950 let x = self.arg_num(&args, 0, 0.0)? as f32;
2951 let y = self.arg_num(&args, 1, 0.0)? as f32;
2952 let z = self.arg_num(&args, 2, 0.0)? as f32;
2953 {
2954 let mut gfx = self.gfx.borrow_mut();
2955 gfx.camera.tx = x;
2956 gfx.camera.ty = y;
2957 gfx.camera.tz = z;
2958 }
2959 #[cfg(not(target_arch = "wasm32"))]
2960 if let Some(audio) = &self.audio {
2961 audio.set_listener_pos(x, y, z);
2962 }
2963 return Ok(Value::Unit);
2964 },
2965
2966 "move_camera" => {
2968 let dx = self.arg_num(&args, 0, 0.0)? as f32;
2969 let dy = self.arg_num(&args, 1, 0.0)? as f32;
2970 let dz = self.arg_num(&args, 2, 0.0)? as f32;
2971 let mut gfx = self.gfx.borrow_mut();
2972 gfx.camera.tx += dx;
2973 gfx.camera.ty += dy;
2974 gfx.camera.tz += dz;
2975 return Ok(Value::Unit);
2976 },
2977
2978 "set_zdist" | "ตั้งระยะห่าง" | "镜距" | "Z距離設定" | "Z거리설정" =>
2980 {
2981 let d = self.arg_num(&args, 0, 5.0)? as f32;
2982 self.gfx.borrow_mut().camera.zdist = d;
2983 return Ok(Value::Unit);
2984 },
2985
2986 "capture_mouse" | "จับเมาส์" | "捕鼠" | "マウス捕捉" | "마우스잡기" =>
2988 {
2989 #[cfg(not(target_arch = "wasm32"))]
2990 {
2991 let mut gfx = self.gfx.borrow_mut();
2992 gfx.mouse_captured = true;
2993 gfx.last_mx = f32::NAN;
2994 if let Some(win) = gfx.window.as_mut() {
2995 win.set_cursor_visibility(false);
2996 }
2997 }
2998 return Ok(Value::Unit);
2999 },
3000
3001 "release_mouse" => {
3003 #[cfg(not(target_arch = "wasm32"))]
3004 {
3005 let mut gfx = self.gfx.borrow_mut();
3006 gfx.mouse_captured = false;
3007 gfx.last_mx = f32::NAN;
3008 if let Some(win) = gfx.window.as_mut() {
3009 win.set_cursor_visibility(true);
3010 }
3011 #[cfg(windows)]
3012 unsafe {
3013 extern "system" {
3015 fn ClipCursor(lpRect: *const std::ffi::c_void) -> i32;
3016 }
3017 ClipCursor(std::ptr::null());
3018 }
3019 }
3020 return Ok(Value::Unit);
3021 },
3022
3023 "set_camera" | "ตั้งกล้อง" | "设镜" | "设置摄像机" | "カメラ設定" | "카메라설정" =>
3030 {
3031 let cry = self.arg_num(&args, 0, 1.0)? as f32;
3032 let sry = self.arg_num(&args, 1, 0.0)? as f32;
3033 let crx = self.arg_num(&args, 2, 1.0)? as f32;
3034 let srx = self.arg_num(&args, 3, 0.0)? as f32;
3035 let mut gfx = self.gfx.borrow_mut();
3036 gfx.camera.cry = cry;
3037 gfx.camera.sry = sry;
3038 gfx.camera.crx = crx;
3039 gfx.camera.srx = srx;
3040 return Ok(Value::Unit);
3041 },
3042
3043 "set_projection" | "ตั้งโปรเจกชัน" | "投影" | "投影設定" | "투영설정" =>
3046 {
3047 let cx = self.arg_num(&args, 0, 960.0)? as f32;
3048 let cy = self.arg_num(&args, 1, 540.0)? as f32;
3049 let focal = self.arg_num(&args, 2, 1080.0)? as f32;
3050 let zdist = self.arg_num(&args, 3, 5.0)? as f32;
3051 let mut gfx = self.gfx.borrow_mut();
3052 gfx.camera.cx = cx;
3053 gfx.camera.cy = cy;
3054 gfx.camera.focal = focal;
3055 gfx.camera.zdist = zdist;
3056 return Ok(Value::Unit);
3057 },
3058
3059 "draw_mesh" | "วาดเมช" => {
3066 let pos = match args.first() {
3067 Some(Value::List(v)) => v,
3068 _ => return Ok(Value::Unit),
3069 };
3070 let idx = match args.get(1) {
3071 Some(Value::List(v)) => v,
3072 _ => return Ok(Value::Unit),
3073 };
3074 let ox = self.arg_num(&args, 2, 0.0)? as f32;
3075 let oy = self.arg_num(&args, 3, 0.0)? as f32;
3076 let oz = self.arg_num(&args, 4, 0.0)? as f32;
3077 let scale = self.arg_num(&args, 5, 1.0)? as f32;
3078 let mode = self.arg_num(&args, 6, 0.0)? as i64;
3079 let nv = pos.len() / 3;
3080 if nv == 0 {
3081 return Ok(Value::Unit);
3082 }
3083 let mut world = vec![0.0f32; nv * 3];
3084 for i in 0..nv {
3085 world[i * 3] = ox + self.to_number(&pos[i * 3]).unwrap_or(0.0) as f32 * scale;
3086 world[i * 3 + 1] =
3087 oy + self.to_number(&pos[i * 3 + 1]).unwrap_or(0.0) as f32 * scale;
3088 world[i * 3 + 2] =
3089 oz + self.to_number(&pos[i * 3 + 2]).unwrap_or(0.0) as f32 * scale;
3090 }
3091 let mut gfx = self.gfx.borrow_mut();
3092 let cp = {
3093 let c = &gfx.camera;
3094 ling_gpu::CameraParams {
3095 cry: c.cry,
3096 sry: c.sry,
3097 crx: c.crx,
3098 srx: c.srx,
3099 cx: c.cx,
3100 cy: c.cy,
3101 focal: c.focal,
3102 zdist: c.zdist,
3103 tx: c.tx,
3104 ty: c.ty,
3105 tz: c.tz,
3106 }
3107 };
3108 let near = -gfx.camera.zdist + 0.02;
3109 let base = gfx.color;
3110 let ambient = gfx.ambient;
3111 let mut proj = vec![0.0f32; nv * 3]; ling_gpu::backend().project_points(&world, &cp, &mut proj);
3113 let nt = idx.len() / 3;
3114 for t in 0..nt {
3115 let ia = self.to_number(&idx[t * 3]).unwrap_or(0.0) as usize;
3116 let ib = self.to_number(&idx[t * 3 + 1]).unwrap_or(0.0) as usize;
3117 let ic = self.to_number(&idx[t * 3 + 2]).unwrap_or(0.0) as usize;
3118 if ia >= nv || ib >= nv || ic >= nv {
3119 continue;
3120 }
3121 let (da, db, dc) = (proj[ia * 3 + 2], proj[ib * 3 + 2], proj[ic * 3 + 2]);
3122 if (da + db + dc) / 3.0 <= near {
3123 continue;
3124 } let col = if mode == 1 {
3126 let h = t as f32 * 0.6;
3127 let r = ((h.sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
3128 let g = (((h + 2.094).sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
3129 let b = (((h + 4.189).sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
3130 (r << 16) | (g << 8) | b
3131 } else {
3132 let (ax, ay, az) = (world[ia * 3], world[ia * 3 + 1], world[ia * 3 + 2]);
3133 let (bx, by, bz) = (world[ib * 3], world[ib * 3 + 1], world[ib * 3 + 2]);
3134 let (px, py, pz) = (world[ic * 3], world[ic * 3 + 1], world[ic * 3 + 2]);
3135 let (ux, uy, uz) = (bx - ax, by - ay, bz - az);
3136 let (vx, vy, vz) = (px - ax, py - ay, pz - az);
3137 let normal = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];
3138 let centroid = [
3139 (ax + bx + px) / 3.0,
3140 (ay + by + py) / 3.0,
3141 (az + bz + pz) / 3.0,
3142 ];
3143 crate::gfx::light::compute_lit_color(
3144 base,
3145 normal,
3146 centroid,
3147 &gfx.lights,
3148 ambient,
3149 )
3150 };
3151 let depth = (da + db + dc) / 3.0;
3152 let col = gfx.fog_apply(col, depth);
3153 gfx.depth_queue.push_triangle(
3154 depth,
3155 col,
3156 proj[ia * 3],
3157 proj[ia * 3 + 1],
3158 proj[ib * 3],
3159 proj[ib * 3 + 1],
3160 proj[ic * 3],
3161 proj[ic * 3 + 1],
3162 );
3163 }
3164 return Ok(Value::Unit);
3165 },
3166
3167 "add_light" | "เพิ่มแสง" | "加灯" | "ライト追加" | "조명추가" =>
3171 {
3172 let x = self.arg_num(&args, 0, 0.0)? as f32;
3173 let y = self.arg_num(&args, 1, -3.0)? as f32;
3174 let z = self.arg_num(&args, 2, 3.0)? as f32;
3175 let mut r = self.arg_num(&args, 3, 1.0)? as f32;
3176 let mut g = self.arg_num(&args, 4, 1.0)? as f32;
3177 let mut b = self.arg_num(&args, 5, 1.0)? as f32;
3178 if r > 1.5 || g > 1.5 || b > 1.5 {
3181 r /= 255.0;
3182 g /= 255.0;
3183 b /= 255.0;
3184 }
3185 let intensity = self.arg_num(&args, 6, 1.0)? as f32;
3186 let radius = self.arg_num(&args, 7, 0.0)? as f32;
3187 self.gfx
3188 .borrow_mut()
3189 .lights
3190 .push(Light { x, y, z, r, g, b, intensity, radius });
3191 return Ok(Value::Unit);
3192 },
3193
3194 "clear_lights" | "ล้างแสง" | "清灯" | "ライト消去" | "조명초기화" =>
3196 {
3197 self.gfx.borrow_mut().lights.clear();
3198 return Ok(Value::Unit);
3199 },
3200
3201 "set_ambient" | "ตั้งแสงรอบข้าง" | "环境光" | "環境光設定" | "환경광설정" =>
3203 {
3204 let v = self.arg_num(&args, 0, 0.15)? as f32;
3205 self.gfx.borrow_mut().ambient = v;
3206 return Ok(Value::Unit);
3207 },
3208
3209 "set_fog" | "ตั้งหมอก" | "雾" | "霧設定" | "안개설정" => {
3212 let r = self.arg_num(&args, 0, 0.0)?.clamp(0.0, 255.0) as u32;
3213 let g = self.arg_num(&args, 1, 0.0)?.clamp(0.0, 255.0) as u32;
3214 let b = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 255.0) as u32;
3215 let start = self.arg_num(&args, 3, 0.0)? as f32;
3216 let end = self.arg_num(&args, 4, 0.0)? as f32;
3217 let mut gfx = self.gfx.borrow_mut();
3218 gfx.fog_color = (r << 16) | (g << 8) | b;
3219 gfx.fog_start = start;
3220 gfx.fog_end = end;
3221 return Ok(Value::Unit);
3222 },
3223
3224 "วาดสามเหลี่ยม3มิติ" | "draw_triangle_3d" | "triangle3d" =>
3228 {
3229 let ax = self.arg_num(&args, 0, 0.0)? as f32;
3230 let ay = self.arg_num(&args, 1, 0.0)? as f32;
3231 let az = self.arg_num(&args, 2, 0.0)? as f32;
3232 let bx = self.arg_num(&args, 3, 0.0)? as f32;
3233 let by = self.arg_num(&args, 4, 0.0)? as f32;
3234 let bz = self.arg_num(&args, 5, 0.0)? as f32;
3235 let cx = self.arg_num(&args, 6, 0.0)? as f32;
3236 let cy = self.arg_num(&args, 7, 0.0)? as f32;
3237 let cz = self.arg_num(&args, 8, 0.0)? as f32;
3238
3239 let mut gfx = self.gfx.borrow_mut();
3240
3241 let ux = bx - ax;
3243 let uy = by - ay;
3244 let uz = bz - az;
3245 let vx = cx - ax;
3246 let vy = cy - ay;
3247 let vz = cz - az;
3248 let normal = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];
3249 let centroid = [
3251 (ax + bx + cx) / 3.0,
3252 (ay + by + cy) / 3.0,
3253 (az + bz + cz) / 3.0,
3254 ];
3255
3256 let lit_color = crate::gfx::light::compute_lit_color(
3258 gfx.color,
3259 normal,
3260 centroid,
3261 &gfx.lights,
3262 gfx.ambient,
3263 );
3264
3265 let near = -gfx.camera.zdist + 0.05;
3272 let vw = [
3273 (ax, ay, az, gfx.camera.depth(ax, ay, az)),
3274 (bx, by, bz, gfx.camera.depth(bx, by, bz)),
3275 (cx, cy, cz, gfx.camera.depth(cx, cy, cz)),
3276 ];
3277 let mut poly: Vec<(f32, f32, f32)> = Vec::with_capacity(4);
3278 let mut ei = 0;
3279 while ei < 3 {
3280 let a = vw[ei];
3281 let b = vw[(ei + 1) % 3];
3282 let ain = a.3 > near;
3283 let bin = b.3 > near;
3284 if ain {
3285 poly.push((a.0, a.1, a.2));
3286 }
3287 if ain != bin {
3288 let tt = (near - a.3) / (b.3 - a.3);
3289 poly.push((
3290 a.0 + (b.0 - a.0) * tt,
3291 a.1 + (b.1 - a.1) * tt,
3292 a.2 + (b.2 - a.2) * tt,
3293 ));
3294 }
3295 ei += 1;
3296 }
3297 if poly.len() < 3 {
3298 return Ok(Value::Unit);
3299 }
3300 let proj: Vec<(f32, f32, f32)> = poly
3302 .iter()
3303 .map(|p| gfx.camera.project(p.0, p.1, p.2))
3304 .collect();
3305 let mut dsum = 0.0f32;
3306 for p in &proj {
3307 dsum += p.2;
3308 }
3309 let depth = dsum / proj.len() as f32;
3310 let lit_color = gfx.fog_apply(lit_color, depth);
3311 let mut fk = 1;
3312 while fk + 1 < proj.len() {
3313 gfx.depth_queue.push_triangle_zv(
3314 lit_color,
3315 proj[0].0,
3316 proj[0].1,
3317 proj[0].2,
3318 proj[fk].0,
3319 proj[fk].1,
3320 proj[fk].2,
3321 proj[fk + 1].0,
3322 proj[fk + 1].1,
3323 proj[fk + 1].2,
3324 );
3325 fk += 1;
3326 }
3327 return Ok(Value::Unit);
3328 },
3329
3330 "วาดเส้น3มิติ" | "draw_line_3d" | "line3d" | "画3D线" | "3D線描く" | "3D선그리기" =>
3334 {
3335 let ax = self.arg_num(&args, 0, 0.0)? as f32;
3336 let ay = self.arg_num(&args, 1, 0.0)? as f32;
3337 let az = self.arg_num(&args, 2, 0.0)? as f32;
3338 let bx = self.arg_num(&args, 3, 0.0)? as f32;
3339 let by = self.arg_num(&args, 4, 0.0)? as f32;
3340 let bz = self.arg_num(&args, 5, 0.0)? as f32;
3341
3342 let mut gfx = self.gfx.borrow_mut();
3343 let color = gfx.color;
3344 let near = -gfx.camera.zdist + 0.05;
3346 let mut lax = ax;
3347 let mut lay = ay;
3348 let mut laz = az;
3349 let mut lbx = bx;
3350 let mut lby = by;
3351 let mut lbz = bz;
3352 let da_raw = gfx.camera.depth(lax, lay, laz);
3353 let db_raw = gfx.camera.depth(lbx, lby, lbz);
3354 if da_raw <= near && db_raw <= near {
3355 return Ok(Value::Unit);
3356 }
3357 if da_raw <= near {
3358 let t = (near - da_raw) / (db_raw - da_raw);
3359 lax += t * (lbx - lax);
3360 lay += t * (lby - lay);
3361 laz += t * (lbz - laz);
3362 } else if db_raw <= near {
3363 let t = (near - da_raw) / (db_raw - da_raw);
3364 lbx = lax + t * (lbx - lax);
3365 lby = lay + t * (lby - lay);
3366 lbz = laz + t * (lbz - laz);
3367 }
3368 let (sax, say, da) = gfx.camera.project(lax, lay, laz);
3369 let (sbx, sby, db) = gfx.camera.project(lbx, lby, lbz);
3370 let depth = (da + db) / 2.0;
3371 let color = gfx.fog_apply(color, depth);
3372 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
3373 return Ok(Value::Unit);
3374 },
3375
3376 #[cfg(not(target_arch = "wasm32"))]
3386 "orb_shell" | "球壳" | "オーブ殻" | "오브껍질" | "เปลือกทรงกลม" =>
3387 {
3388 let cx = self.arg_num(&args, 0, 0.)? as f32;
3389 let cy = self.arg_num(&args, 1, 0.)? as f32;
3390 let cz = self.arg_num(&args, 2, 0.)? as f32;
3391 let radius = self.arg_num(&args, 3, 1.0)? as f32;
3392 let ry = self.arg_num(&args, 4, 0.)? as f32;
3393 let rx = self.arg_num(&args, 5, 0.)? as f32;
3394 let density = (self.arg_num(&args, 6, 10.)? as i32).clamp(1, 48);
3395 let tr = (self.arg_num(&args, 7, 230.)? as f32).clamp(0., 255.);
3396 let tg = (self.arg_num(&args, 8, 230.)? as f32).clamp(0., 255.);
3397 let tb = (self.arg_num(&args, 9, 235.)? as f32).clamp(0., 255.);
3398 let (cyr, syr) = (ry.cos(), ry.sin());
3399 let (cxr, sxr) = (rx.cos(), rx.sin());
3400 let tau = std::f32::consts::TAU;
3401 let pi = std::f32::consts::PI;
3402 let turns = 6.0_f32; let nseg = 96; let inv_r = if radius.abs() > 1e-5 {
3405 1.0 / radius
3406 } else {
3407 0.0
3408 };
3409 let pt = |u: f32, theta0: f32, dir: f32| -> ([f32; 3], f32) {
3412 let phi = pi * u; let th = dir * turns * tau * u + theta0;
3414 let (mut x, y, mut z) = (
3415 phi.sin() * th.cos() * radius,
3416 phi.cos() * radius,
3417 phi.sin() * th.sin() * radius,
3418 );
3419 let x1 = x * cyr + z * syr; let z1 = -x * syr + z * cyr;
3421 x = x1;
3422 z = z1;
3423 let y2 = y * cxr - z * sxr; let z2 = y * sxr + z * cxr;
3425 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.0, 1.0);
3427 ([cx + x, cy + y2, cz + z2], facing)
3428 };
3429 let mut gfx = self.gfx.borrow_mut();
3430 let near = -gfx.camera.zdist + 0.05;
3431 let seg = |gfx: &mut crate::gfx::GfxState, a: [f32; 3], b: [f32; 3], lum: f32| {
3433 let (mut lax, mut lay, mut laz) = (a[0], a[1], a[2]);
3434 let (mut lbx, mut lby, mut lbz) = (b[0], b[1], b[2]);
3435 let da = gfx.camera.depth(lax, lay, laz);
3436 let db = gfx.camera.depth(lbx, lby, lbz);
3437 if da <= near && db <= near {
3438 return;
3439 }
3440 if da <= near {
3441 let t = (near - da) / (db - da);
3442 lax += t * (lbx - lax);
3443 lay += t * (lby - lay);
3444 laz += t * (lbz - laz);
3445 } else if db <= near {
3446 let t = (near - da) / (db - da);
3447 lbx = lax + t * (lbx - lax);
3448 lby = lay + t * (lby - lay);
3449 lbz = laz + t * (lbz - laz);
3450 }
3451 let (sax, say, da2) = gfx.camera.project(lax, lay, laz);
3452 let (sbx, sby, db2) = gfx.camera.project(lbx, lby, lbz);
3453 let l = (0.12 + 0.88 * lum).clamp(0.0, 1.0);
3455 let cr = (tr * l) as u32;
3456 let cg = (tg * l) as u32;
3457 let cb = (tb * l) as u32;
3458 let color = (cr << 16) | (cg << 8) | cb;
3459 gfx.depth_queue
3460 .push_line((da2 + db2) * 0.5, color, sax, say, sbx, sby);
3461 };
3462 for &dir in &[1.0_f32, -1.0_f32] {
3464 for s in 0..density {
3465 let theta0 = s as f32 * tau / density as f32;
3466 let mut prev = pt(0.0, theta0, dir);
3467 for k in 1..=nseg {
3468 let cur = pt(k as f32 / nseg as f32, theta0, dir);
3469 seg(&mut gfx, prev.0, cur.0, (prev.1 + cur.1) * 0.5);
3470 prev = cur;
3471 }
3472 }
3473 }
3474 return Ok(Value::Unit);
3475 },
3476
3477 #[cfg(not(target_arch = "wasm32"))]
3485 "orb_particles" | "球内粒子" | "オーブ粒子" | "오브입자" | "อนุภาคทรงกลม" =>
3486 {
3487 let cx = self.arg_num(&args, 0, 0.)? as f32;
3488 let cy = self.arg_num(&args, 1, 0.)? as f32;
3489 let cz = self.arg_num(&args, 2, 0.)? as f32;
3490 let radius = self.arg_num(&args, 3, 1.0)? as f32;
3491 let count = (self.arg_num(&args, 4, 160.)? as i32).clamp(1, 4000);
3492 let t = self.arg_num(&args, 5, 0.)? as f32;
3493 let tr = (self.arg_num(&args, 6, 255.)? as f32).clamp(0., 255.);
3494 let tg = (self.arg_num(&args, 7, 255.)? as f32).clamp(0., 255.);
3495 let tb = (self.arg_num(&args, 8, 255.)? as f32).clamp(0., 255.);
3496 let inv_r = if radius.abs() > 1e-5 {
3497 1.0 / radius
3498 } else {
3499 0.0
3500 };
3501 let h = |mut x: u32| -> f32 {
3503 x = x.wrapping_mul(747796405).wrapping_add(2891336453);
3504 x = ((x >> ((x >> 28).wrapping_add(4))) ^ x).wrapping_mul(277803737);
3505 (((x >> 22) ^ x) & 0xFFFFFF) as f32 / 16_777_216.0
3506 };
3507 let tau = std::f32::consts::TAU;
3508 let (cyr, syr) = ((t * 0.5).cos(), (t * 0.5).sin());
3510 let (cxr, sxr) = ((t * 0.23).cos(), (t * 0.23).sin());
3511 let mut gfx = self.gfx.borrow_mut();
3512 let near = -gfx.camera.zdist + 0.05;
3513 let (sw, sh) = (gfx.width as i32, gfx.height as i32);
3514 for i in 0..count {
3515 let i = i as u32;
3516 let u = h(i.wrapping_mul(3) + 1);
3518 let rr = u.cbrt() * radius * (0.85 + 0.15 * (t * 1.3 + i as f32).sin()); let th = h(i.wrapping_mul(3) + 2) * tau + t * (0.3 + 0.5 * h(i * 7 + 5)); let ph = (h(i.wrapping_mul(3) + 3) * 2.0 - 1.0).acos(); let (mut x, y, mut z) = (
3522 rr * ph.sin() * th.cos(),
3523 rr * ph.cos(),
3524 rr * ph.sin() * th.sin(),
3525 );
3526 let x1 = x * cyr + z * syr;
3528 let z1 = -x * syr + z * cyr;
3529 x = x1;
3530 z = z1;
3531 let y2 = y * cxr - z * sxr;
3532 let z2 = y * sxr + z * cxr;
3533 let (wx, wy, wz) = (cx + x, cy + y2, cz + z2);
3534 if gfx.camera.depth(wx, wy, wz) <= near {
3535 continue;
3536 }
3537 let (sx, sy, dep) = gfx.camera.project(wx, wy, wz);
3538 let sxi = sx as i32;
3539 let syi = sy as i32;
3540 if sxi < 0 || syi < 0 || sxi >= sw || syi >= sh {
3541 continue;
3542 }
3543 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.15, 1.0);
3545 let l = facing;
3546 let cr = (tr * l) as u32;
3547 let cg = (tg * l) as u32;
3548 let cb = (tb * l) as u32;
3549 let color = (cr << 16) | (cg << 8) | cb;
3550 let len = if facing > 0.7 { 1.0 } else { 0.0 };
3552 gfx.depth_queue.push_line(dep, color, sx, sy, sx + len, sy);
3553 }
3554 return Ok(Value::Unit);
3555 },
3556
3557 "project_3d" | "投影3D" | "3D投影" | "3D투영" | "ฉาย3มิติ" => {
3561 let x = self.arg_num(&args, 0, 0.0)? as f32;
3562 let y = self.arg_num(&args, 1, 0.0)? as f32;
3563 let z = self.arg_num(&args, 2, 0.0)? as f32;
3564 let gfx = self.gfx.borrow();
3565 let near = -gfx.camera.zdist + 0.05;
3566 let d = gfx.camera.depth(x, y, z);
3567 if d <= near {
3568 return Ok(Value::List(vec![
3569 Value::Number(-99999.0),
3570 Value::Number(-99999.0),
3571 Value::Number(d as f64),
3572 ]));
3573 }
3574 let (sx, sy, depth) = gfx.camera.project(x, y, z);
3575 return Ok(Value::List(vec![
3576 Value::Number(sx as f64),
3577 Value::Number(sy as f64),
3578 Value::Number(depth as f64),
3579 ]));
3580 },
3581 #[cfg(not(target_arch = "wasm32"))]
3584 "draw_poly" | "填充多边形" | "ポリゴン塗り" | "다각형채우기" | "เติมรูปหลายเหลี่ยม" =>
3585 {
3586 let mut pts: Vec<[f32; 2]> = Vec::new();
3587 if let Some(Value::List(v)) = args.first() {
3588 let mut i = 0;
3589 while i + 1 < v.len() {
3590 let x = self.to_number(&v[i]).unwrap_or(0.0) as f32;
3591 let y = self.to_number(&v[i + 1]).unwrap_or(0.0) as f32;
3592 pts.push([x, y]);
3593 i += 2;
3594 }
3595 }
3596 if pts.len() >= 3 {
3597 if pts[0] != pts[pts.len() - 1] {
3598 let p0 = pts[0];
3599 pts.push(p0);
3600 } let mut gfx = self.gfx.borrow_mut();
3602 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
3603 crate::gfx::raster::fill_contours_aa(
3604 &mut gfx.buffer,
3605 w,
3606 h,
3607 color,
3608 add,
3609 std::slice::from_ref(&pts),
3610 );
3611 }
3612 return Ok(Value::Unit);
3613 },
3614
3615 "vtex_grid" | "ลายตาราง" | "纹格" | "格子模様" | "격자무늬" =>
3624 {
3625 let cx = self.arg_num(&args, 0, 0.)? as f32;
3626 let cy = self.arg_num(&args, 1, 0.)? as f32;
3627 let cz = self.arg_num(&args, 2, 0.)? as f32;
3628 let ux = self.arg_num(&args, 3, 1.)? as f32;
3629 let uy = self.arg_num(&args, 4, 0.)? as f32;
3630 let uz = self.arg_num(&args, 5, 0.)? as f32;
3631 let vx = self.arg_num(&args, 6, 0.)? as f32;
3632 let vy = self.arg_num(&args, 7, 0.)? as f32;
3633 let vz = self.arg_num(&args, 8, 1.)? as f32;
3634 let cols = self.arg_num(&args, 9, 10.)? as usize;
3635 let rows = self.arg_num(&args, 10, 10.)? as usize;
3636 let cw = self.arg_num(&args, 11, 1.)? as f32;
3637 let ch = self.arg_num(&args, 12, 1.)? as f32;
3638 let fr = self.arg_num(&args, 13, 0.)? as f32;
3639 let hue = self.arg_num(&args, 14, 0.)? as f32;
3640 let mut gfx = self.gfx.borrow_mut();
3641 let cam = gfx.camera.clone();
3642 crate::gfx::vtex::draw_grid(
3643 &mut gfx.depth_queue,
3644 &cam,
3645 cx,
3646 cy,
3647 cz,
3648 ux,
3649 uy,
3650 uz,
3651 vx,
3652 vy,
3653 vz,
3654 cols,
3655 rows,
3656 cw,
3657 ch,
3658 fr,
3659 hue,
3660 );
3661 return Ok(Value::Unit);
3662 },
3663
3664 "vtex_rings" | "ลายวงซ้อน" | "纹环" | "同心円" | "동심원" => {
3666 let cx = self.arg_num(&args, 0, 0.)? as f32;
3667 let cy = self.arg_num(&args, 1, 0.)? as f32;
3668 let cz = self.arg_num(&args, 2, 0.)? as f32;
3669 let ux = self.arg_num(&args, 3, 1.)? as f32;
3670 let uy = self.arg_num(&args, 4, 0.)? as f32;
3671 let uz = self.arg_num(&args, 5, 0.)? as f32;
3672 let vx = self.arg_num(&args, 6, 0.)? as f32;
3673 let vy = self.arg_num(&args, 7, 0.)? as f32;
3674 let vz = self.arg_num(&args, 8, 1.)? as f32;
3675 let nr = self.arg_num(&args, 9, 6.)? as usize;
3676 let ns = self.arg_num(&args, 10, 6.)? as usize;
3677 let mr = self.arg_num(&args, 11, 3.)? as f32;
3678 let tw = self.arg_num(&args, 12, 0.)? as f32;
3679 let fr = self.arg_num(&args, 13, 0.)? as f32;
3680 let hue = self.arg_num(&args, 14, 0.)? as f32;
3681 let mut gfx = self.gfx.borrow_mut();
3682 let cam = gfx.camera.clone();
3683 crate::gfx::vtex::draw_rings(
3684 &mut gfx.depth_queue,
3685 &cam,
3686 cx,
3687 cy,
3688 cz,
3689 ux,
3690 uy,
3691 uz,
3692 vx,
3693 vy,
3694 vz,
3695 nr,
3696 ns,
3697 mr,
3698 tw,
3699 fr,
3700 hue,
3701 );
3702 return Ok(Value::Unit);
3703 },
3704
3705 "vtex_star" | "ลายดาว" | "纹星" | "星模様" | "별무늬" => {
3707 let cx = self.arg_num(&args, 0, 0.)? as f32;
3708 let cy = self.arg_num(&args, 1, 0.)? as f32;
3709 let cz = self.arg_num(&args, 2, 0.)? as f32;
3710 let ux = self.arg_num(&args, 3, 1.)? as f32;
3711 let uy = self.arg_num(&args, 4, 0.)? as f32;
3712 let uz = self.arg_num(&args, 5, 0.)? as f32;
3713 let vx = self.arg_num(&args, 6, 0.)? as f32;
3714 let vy = self.arg_num(&args, 7, 0.)? as f32;
3715 let vz = self.arg_num(&args, 8, 1.)? as f32;
3716 let np = self.arg_num(&args, 9, 6.)? as usize;
3717 let ro = self.arg_num(&args, 10, 2.)? as f32;
3718 let ri = self.arg_num(&args, 11, 1.)? as f32;
3719 let rs = self.arg_num(&args, 12, 0.01)? as f32;
3720 let fr = self.arg_num(&args, 13, 0.)? as f32;
3721 let hue = self.arg_num(&args, 14, 0.)? as f32;
3722 let mut gfx = self.gfx.borrow_mut();
3723 let cam = gfx.camera.clone();
3724 crate::gfx::vtex::draw_star(
3725 &mut gfx.depth_queue,
3726 &cam,
3727 cx,
3728 cy,
3729 cz,
3730 ux,
3731 uy,
3732 uz,
3733 vx,
3734 vy,
3735 vz,
3736 np,
3737 ro,
3738 ri,
3739 rs,
3740 fr,
3741 hue,
3742 );
3743 return Ok(Value::Unit);
3744 },
3745
3746 "vtex_spiral" | "ลายเกลียว" | "纹螺" | "螺旋" | "나선" => {
3748 let cx = self.arg_num(&args, 0, 0.)? as f32;
3749 let cy = self.arg_num(&args, 1, 0.)? as f32;
3750 let cz = self.arg_num(&args, 2, 0.)? as f32;
3751 let ux = self.arg_num(&args, 3, 1.)? as f32;
3752 let uy = self.arg_num(&args, 4, 0.)? as f32;
3753 let uz = self.arg_num(&args, 5, 0.)? as f32;
3754 let vx = self.arg_num(&args, 6, 0.)? as f32;
3755 let vy = self.arg_num(&args, 7, 0.)? as f32;
3756 let vz = self.arg_num(&args, 8, 1.)? as f32;
3757 let nt = self.arg_num(&args, 9, 3.)? as f32;
3758 let mr = self.arg_num(&args, 10, 3.)? as f32;
3759 let st = self.arg_num(&args, 11, 120.)? as usize;
3760 let fr = self.arg_num(&args, 12, 0.)? as f32;
3761 let hue = self.arg_num(&args, 13, 0.)? as f32;
3762 let mut gfx = self.gfx.borrow_mut();
3763 let cam = gfx.camera.clone();
3764 crate::gfx::vtex::draw_spiral(
3765 &mut gfx.depth_queue,
3766 &cam,
3767 cx,
3768 cy,
3769 cz,
3770 ux,
3771 uy,
3772 uz,
3773 vx,
3774 vy,
3775 vz,
3776 nt,
3777 mr,
3778 st,
3779 fr,
3780 hue,
3781 );
3782 return Ok(Value::Unit);
3783 },
3784
3785 "vtex_flower" | "ลายดอก" | "纹花" | "花模様" | "꽃무늬" => {
3787 let cx = self.arg_num(&args, 0, 0.)? as f32;
3788 let cy = self.arg_num(&args, 1, 0.)? as f32;
3789 let cz = self.arg_num(&args, 2, 0.)? as f32;
3790 let ux = self.arg_num(&args, 3, 1.)? as f32;
3791 let uy = self.arg_num(&args, 4, 0.)? as f32;
3792 let uz = self.arg_num(&args, 5, 0.)? as f32;
3793 let vx = self.arg_num(&args, 6, 0.)? as f32;
3794 let vy = self.arg_num(&args, 7, 0.)? as f32;
3795 let vz = self.arg_num(&args, 8, 1.)? as f32;
3796 let r = self.arg_num(&args, 9, 1.)? as f32;
3797 let ns = self.arg_num(&args, 10, 24.)? as usize;
3798 let fr = self.arg_num(&args, 11, 0.)? as f32;
3799 let hue = self.arg_num(&args, 12, 0.)? as f32;
3800 let mut gfx = self.gfx.borrow_mut();
3801 let cam = gfx.camera.clone();
3802 crate::gfx::vtex::draw_flower(
3803 &mut gfx.depth_queue,
3804 &cam,
3805 cx,
3806 cy,
3807 cz,
3808 ux,
3809 uy,
3810 uz,
3811 vx,
3812 vy,
3813 vz,
3814 r,
3815 ns,
3816 fr,
3817 hue,
3818 );
3819 return Ok(Value::Unit);
3820 },
3821
3822 "vtex_letter_rain" | "ลายอักษรไหล" | "纹字雨" | "文字雨" | "글자비" =>
3824 {
3825 let cx = self.arg_num(&args, 0, 0.)? as f32;
3826 let cy = self.arg_num(&args, 1, 0.)? as f32;
3827 let cz = self.arg_num(&args, 2, 0.)? as f32;
3828 let ux = self.arg_num(&args, 3, 1.)? as f32;
3829 let uy = self.arg_num(&args, 4, 0.)? as f32;
3830 let uz = self.arg_num(&args, 5, 0.)? as f32;
3831 let vx = self.arg_num(&args, 6, 0.)? as f32;
3832 let vy = self.arg_num(&args, 7, 0.)? as f32;
3833 let vz = self.arg_num(&args, 8, 1.)? as f32;
3834 let nc = self.arg_num(&args, 9, 16.)? as usize;
3835 let nv = self.arg_num(&args, 10, 14.)? as usize;
3836 let cw = self.arg_num(&args, 11, 0.65)? as f32;
3837 let rh = self.arg_num(&args, 12, 0.60)? as f32;
3838 let sp = self.arg_num(&args, 13, 0.025)? as f32;
3839 let fr = self.arg_num(&args, 14, 0.)? as f32;
3840 let hue = self.arg_num(&args, 15, 0.)? as f32;
3841 let mut gfx = self.gfx.borrow_mut();
3842 let cam = gfx.camera.clone();
3843 crate::gfx::vtex::draw_letter_rain(
3844 &mut gfx.depth_queue,
3845 &cam,
3846 cx,
3847 cy,
3848 cz,
3849 ux,
3850 uy,
3851 uz,
3852 vx,
3853 vy,
3854 vz,
3855 nc,
3856 nv,
3857 cw,
3858 rh,
3859 sp,
3860 fr,
3861 hue,
3862 );
3863 return Ok(Value::Unit);
3864 },
3865
3866 "vtex_hyperbolic_uv" | "ลายไฮเพอร์โบลิก" | "纹曲面" | "双曲線" | "쌍곡선" =>
3868 {
3869 let cx = self.arg_num(&args, 0, 0.)? as f32;
3870 let cy = self.arg_num(&args, 1, 0.)? as f32;
3871 let cz = self.arg_num(&args, 2, 0.)? as f32;
3872 let ux = self.arg_num(&args, 3, 1.)? as f32;
3873 let uy = self.arg_num(&args, 4, 0.)? as f32;
3874 let uz = self.arg_num(&args, 5, 0.)? as f32;
3875 let vx = self.arg_num(&args, 6, 0.)? as f32;
3876 let vy = self.arg_num(&args, 7, 0.)? as f32;
3877 let vz = self.arg_num(&args, 8, 1.)? as f32;
3878 let mr = self.arg_num(&args, 9, 5.)? as f32;
3879 let nc = self.arg_num(&args, 10, 12.)? as usize;
3880 let nr = self.arg_num(&args, 11, 18.)? as usize;
3881 let fr = self.arg_num(&args, 12, 0.)? as f32;
3882 let hue = self.arg_num(&args, 13, 0.)? as f32;
3883 let mut gfx = self.gfx.borrow_mut();
3884 let cam = gfx.camera.clone();
3885 crate::gfx::vtex::draw_hyperbolic_uv(
3886 &mut gfx.depth_queue,
3887 &cam,
3888 cx,
3889 cy,
3890 cz,
3891 ux,
3892 uy,
3893 uz,
3894 vx,
3895 vy,
3896 vz,
3897 mr,
3898 nc,
3899 nr,
3900 fr,
3901 hue,
3902 );
3903 return Ok(Value::Unit);
3904 },
3905
3906 "vtex_halftone" | "ลายจุด" | "纹半调" | "網点模様" | "망점" => {
3908 let cx = self.arg_num(&args, 0, 0.)? as f32;
3909 let cy = self.arg_num(&args, 1, 0.)? as f32;
3910 let cz = self.arg_num(&args, 2, 0.)? as f32;
3911 let ux = self.arg_num(&args, 3, 1.)? as f32;
3912 let uy = self.arg_num(&args, 4, 0.)? as f32;
3913 let uz = self.arg_num(&args, 5, 0.)? as f32;
3914 let vx = self.arg_num(&args, 6, 0.)? as f32;
3915 let vy = self.arg_num(&args, 7, 0.)? as f32;
3916 let vz = self.arg_num(&args, 8, 1.)? as f32;
3917 let cols = self.arg_num(&args, 9, 16.)? as usize;
3918 let rows = self.arg_num(&args, 10, 12.)? as usize;
3919 let cw = self.arg_num(&args, 11, 0.5)? as f32;
3920 let ch = self.arg_num(&args, 12, 0.5)? as f32;
3921 let dens = self.arg_num(&args, 13, 0.4)? as f32;
3922 let fr = self.arg_num(&args, 14, 0.)? as f32;
3923 let hue = self.arg_num(&args, 15, 0.)? as f32;
3924 let mut gfx = self.gfx.borrow_mut();
3925 let cam = gfx.camera.clone();
3926 crate::gfx::vtex::draw_halftone(
3927 &mut gfx.depth_queue,
3928 &cam,
3929 cx,
3930 cy,
3931 cz,
3932 ux,
3933 uy,
3934 uz,
3935 vx,
3936 vy,
3937 vz,
3938 cols,
3939 rows,
3940 cw,
3941 ch,
3942 dens,
3943 fr,
3944 hue,
3945 );
3946 return Ok(Value::Unit);
3947 },
3948
3949 "vtex_tessellated" | "ลายตาข่าย" | "纹镶嵌" | "網目模様" | "격자망" =>
3951 {
3952 let cx = self.arg_num(&args, 0, 0.)? as f32;
3953 let cy = self.arg_num(&args, 1, 0.)? as f32;
3954 let cz = self.arg_num(&args, 2, 0.)? as f32;
3955 let ux = self.arg_num(&args, 3, 1.)? as f32;
3956 let uy = self.arg_num(&args, 4, 0.)? as f32;
3957 let uz = self.arg_num(&args, 5, 0.)? as f32;
3958 let vx = self.arg_num(&args, 6, 0.)? as f32;
3959 let vy = self.arg_num(&args, 7, 0.)? as f32;
3960 let vz = self.arg_num(&args, 8, 1.)? as f32;
3961 let cols = self.arg_num(&args, 9, 14.)? as usize;
3962 let rows = self.arg_num(&args, 10, 10.)? as usize;
3963 let cell = self.arg_num(&args, 11, 0.6)? as f32;
3964 let amp = self.arg_num(&args, 12, 0.25)? as f32;
3965 let freq = self.arg_num(&args, 13, 4.)? as f32;
3966 let fr = self.arg_num(&args, 14, 0.)? as f32;
3967 let hue = self.arg_num(&args, 15, 0.)? as f32;
3968 let mut gfx = self.gfx.borrow_mut();
3969 let cam = gfx.camera.clone();
3970 crate::gfx::vtex::draw_tessellated(
3971 &mut gfx.depth_queue,
3972 &cam,
3973 cx,
3974 cy,
3975 cz,
3976 ux,
3977 uy,
3978 uz,
3979 vx,
3980 vy,
3981 vz,
3982 cols,
3983 rows,
3984 cell,
3985 amp,
3986 freq,
3987 fr,
3988 hue,
3989 );
3990 return Ok(Value::Unit);
3991 },
3992
3993 "vtex_lotus" | "ลายดอกบัว" | "纹莲" | "蓮模様" | "연꽃무늬" =>
3995 {
3996 let cx = self.arg_num(&args, 0, 0.)? as f32;
3997 let cy = self.arg_num(&args, 1, 0.)? as f32;
3998 let cz = self.arg_num(&args, 2, 0.)? as f32;
3999 let ux = self.arg_num(&args, 3, 1.)? as f32;
4000 let uy = self.arg_num(&args, 4, 0.)? as f32;
4001 let uz = self.arg_num(&args, 5, 0.)? as f32;
4002 let vx = self.arg_num(&args, 6, 0.)? as f32;
4003 let vy = self.arg_num(&args, 7, 0.)? as f32;
4004 let vz = self.arg_num(&args, 8, 1.)? as f32;
4005 let ri = self.arg_num(&args, 9, 1.)? as f32;
4006 let ro = self.arg_num(&args, 10, 2.)? as f32;
4007 let np = self.arg_num(&args, 11, 12.)? as usize;
4008 let fr = self.arg_num(&args, 12, 0.)? as f32;
4009 let hue = self.arg_num(&args, 13, 0.)? as f32;
4010 let mut gfx = self.gfx.borrow_mut();
4011 let cam = gfx.camera.clone();
4012 crate::gfx::vtex::draw_lotus(
4013 &mut gfx.depth_queue,
4014 &cam,
4015 cx,
4016 cy,
4017 cz,
4018 ux,
4019 uy,
4020 uz,
4021 vx,
4022 vy,
4023 vz,
4024 ri,
4025 ro,
4026 np,
4027 fr,
4028 hue,
4029 );
4030 return Ok(Value::Unit);
4031 },
4032
4033 "vtex_chakra" | "ลายจักร" | "纹轮" | "輪模様" | "바퀴무늬" => {
4035 let cx = self.arg_num(&args, 0, 0.)? as f32;
4036 let cy = self.arg_num(&args, 1, 0.)? as f32;
4037 let cz = self.arg_num(&args, 2, 0.)? as f32;
4038 let ux = self.arg_num(&args, 3, 1.)? as f32;
4039 let uy = self.arg_num(&args, 4, 0.)? as f32;
4040 let uz = self.arg_num(&args, 5, 0.)? as f32;
4041 let vx = self.arg_num(&args, 6, 0.)? as f32;
4042 let vy = self.arg_num(&args, 7, 0.)? as f32;
4043 let vz = self.arg_num(&args, 8, 1.)? as f32;
4044 let r = self.arg_num(&args, 9, 2.)? as f32;
4045 let ns = self.arg_num(&args, 10, 8.)? as usize;
4046 let fr = self.arg_num(&args, 11, 0.)? as f32;
4047 let hue = self.arg_num(&args, 12, 0.)? as f32;
4048 let mut gfx = self.gfx.borrow_mut();
4049 let cam = gfx.camera.clone();
4050 crate::gfx::vtex::draw_chakra(
4051 &mut gfx.depth_queue,
4052 &cam,
4053 cx,
4054 cy,
4055 cz,
4056 ux,
4057 uy,
4058 uz,
4059 vx,
4060 vy,
4061 vz,
4062 r,
4063 ns,
4064 fr,
4065 hue,
4066 );
4067 return Ok(Value::Unit);
4068 },
4069
4070 "vtex_yantra" | "ลายยันต์" | "纹咒" | "護符模様" | "부적무늬" =>
4072 {
4073 let cx = self.arg_num(&args, 0, 0.)? as f32;
4074 let cy = self.arg_num(&args, 1, 0.)? as f32;
4075 let cz = self.arg_num(&args, 2, 0.)? as f32;
4076 let ux = self.arg_num(&args, 3, 1.)? as f32;
4077 let uy = self.arg_num(&args, 4, 0.)? as f32;
4078 let uz = self.arg_num(&args, 5, 0.)? as f32;
4079 let vx = self.arg_num(&args, 6, 0.)? as f32;
4080 let vy = self.arg_num(&args, 7, 0.)? as f32;
4081 let vz = self.arg_num(&args, 8, 1.)? as f32;
4082 let nl = self.arg_num(&args, 9, 4.)? as usize;
4083 let mr = self.arg_num(&args, 10, 3.)? as f32;
4084 let fr = self.arg_num(&args, 11, 0.)? as f32;
4085 let hue = self.arg_num(&args, 12, 0.)? as f32;
4086 let mut gfx = self.gfx.borrow_mut();
4087 let cam = gfx.camera.clone();
4088 crate::gfx::vtex::draw_yantra(
4089 &mut gfx.depth_queue,
4090 &cam,
4091 cx,
4092 cy,
4093 cz,
4094 ux,
4095 uy,
4096 uz,
4097 vx,
4098 vy,
4099 vz,
4100 nl,
4101 mr,
4102 fr,
4103 hue,
4104 );
4105 return Ok(Value::Unit);
4106 },
4107
4108 "vtex_spiked_cog" | "ฟันเฟืองหนาม" | "纹棘轮" | "歯車模様" | "톱니바퀴" =>
4110 {
4111 let cx = self.arg_num(&args, 0, 0.)? as f32;
4112 let cy = self.arg_num(&args, 1, 0.)? as f32;
4113 let cz = self.arg_num(&args, 2, 0.)? as f32;
4114 let ux = self.arg_num(&args, 3, 1.)? as f32;
4115 let uy = self.arg_num(&args, 4, 0.)? as f32;
4116 let uz = self.arg_num(&args, 5, 0.)? as f32;
4117 let vx = self.arg_num(&args, 6, 0.)? as f32;
4118 let vy = self.arg_num(&args, 7, 0.)? as f32;
4119 let vz = self.arg_num(&args, 8, 1.)? as f32;
4120 let nt = self.arg_num(&args, 9, 12.)? as usize;
4121 let rb = self.arg_num(&args, 10, 1.)? as f32;
4122 let rs = self.arg_num(&args, 11, 1.3)? as f32;
4123 let rh = self.arg_num(&args, 12, 0.2)? as f32;
4124 let ns = self.arg_num(&args, 13, 6.)? as usize;
4125 let fr = self.arg_num(&args, 14, 0.)? as f32;
4126 let hue = self.arg_num(&args, 15, 0.)? as f32;
4127 let mut gfx = self.gfx.borrow_mut();
4128 let cam = gfx.camera.clone();
4129 crate::gfx::vtex::draw_spiked_cog(
4130 &mut gfx.depth_queue,
4131 &cam,
4132 cx,
4133 cy,
4134 cz,
4135 ux,
4136 uy,
4137 uz,
4138 vx,
4139 vy,
4140 vz,
4141 nt,
4142 rb,
4143 rs,
4144 rh,
4145 ns,
4146 fr,
4147 hue,
4148 );
4149 return Ok(Value::Unit);
4150 },
4151
4152 "vtex_torii" | "ประตูโทริอิ" | "纹鸟居" | "鳥居" | "도리이" =>
4154 {
4155 let cx = self.arg_num(&args, 0, 0.)? as f32;
4156 let cy = self.arg_num(&args, 1, 0.)? as f32;
4157 let cz = self.arg_num(&args, 2, 0.)? as f32;
4158 let ux = self.arg_num(&args, 3, 1.)? as f32;
4159 let uy = self.arg_num(&args, 4, 0.)? as f32;
4160 let uz = self.arg_num(&args, 5, 0.)? as f32;
4161 let vx = self.arg_num(&args, 6, 0.)? as f32;
4162 let vy = self.arg_num(&args, 7, 0.)? as f32;
4163 let vz = self.arg_num(&args, 8, 1.)? as f32;
4164 let w = self.arg_num(&args, 9, 4.)? as f32;
4165 let h = self.arg_num(&args, 10, 5.)? as f32;
4166 let fr = self.arg_num(&args, 11, 0.)? as f32;
4167 let hue = self.arg_num(&args, 12, 0.)? as f32;
4168 let mut gfx = self.gfx.borrow_mut();
4169 let cam = gfx.camera.clone();
4170 crate::gfx::vtex::draw_torii(
4171 &mut gfx.depth_queue,
4172 &cam,
4173 cx,
4174 cy,
4175 cz,
4176 ux,
4177 uy,
4178 uz,
4179 vx,
4180 vy,
4181 vz,
4182 w,
4183 h,
4184 fr,
4185 hue,
4186 );
4187 return Ok(Value::Unit);
4188 },
4189
4190 "vtex_pagoda" | "เจดีย์" | "纹塔" | "塔" | "탑" => {
4192 let cx = self.arg_num(&args, 0, 0.)? as f32;
4193 let cy = self.arg_num(&args, 1, 0.)? as f32;
4194 let cz = self.arg_num(&args, 2, 0.)? as f32;
4195 let ux = self.arg_num(&args, 3, 1.)? as f32;
4196 let uy = self.arg_num(&args, 4, 0.)? as f32;
4197 let uz = self.arg_num(&args, 5, 0.)? as f32;
4198 let vx = self.arg_num(&args, 6, 0.)? as f32;
4199 let vy = self.arg_num(&args, 7, 0.)? as f32;
4200 let vz = self.arg_num(&args, 8, 1.)? as f32;
4201 let nt = self.arg_num(&args, 9, 5.)? as usize;
4202 let bw = self.arg_num(&args, 10, 2.)? as f32;
4203 let th = self.arg_num(&args, 11, 1.)? as f32;
4204 let tp = self.arg_num(&args, 12, 0.72)? as f32;
4205 let eo = self.arg_num(&args, 13, 0.28)? as f32;
4206 let fr = self.arg_num(&args, 14, 0.)? as f32;
4207 let hue = self.arg_num(&args, 15, 0.)? as f32;
4208 let mut gfx = self.gfx.borrow_mut();
4209 let cam = gfx.camera.clone();
4210 crate::gfx::vtex::draw_pagoda(
4211 &mut gfx.depth_queue,
4212 &cam,
4213 cx,
4214 cy,
4215 cz,
4216 ux,
4217 uy,
4218 uz,
4219 vx,
4220 vy,
4221 vz,
4222 nt,
4223 bw,
4224 th,
4225 tp,
4226 eo,
4227 fr,
4228 hue,
4229 );
4230 return Ok(Value::Unit);
4231 },
4232
4233 #[cfg(not(target_arch = "wasm32"))]
4239 "audio_tone"
4240 | "เสียงโทน"
4241 | "音调"
4242 | "音調"
4243 | "음조"
4244 | "空间音"
4245 | "空間音"
4246 | "공간음" => {
4247 let idx = self.arg_num(&args, 0, 0.0)? as usize;
4248 let x = self.arg_num(&args, 1, 0.0)? as f32;
4249 let y = self.arg_num(&args, 2, 0.0)? as f32;
4250 let z = self.arg_num(&args, 3, 0.0)? as f32;
4251 let w = self.arg_num(&args, 4, 1.0)? as f32;
4252 let freq = self.arg_num(&args, 5, 220.0)? as f32;
4253 let amp = self.arg_num(&args, 6, 0.15)? as f32;
4254 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
4255 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
4256 if let Some(audio) = &self.audio {
4257 audio.set_tone(
4258 idx,
4259 ToneParams { x, y, z, w, freq, amp, lfo_rate, lfo_depth },
4260 );
4261 }
4262 return Ok(Value::Unit);
4263 },
4264
4265 #[cfg(not(target_arch = "wasm32"))]
4266 "audio_listener" | "ผู้ฟัง" | "音频监听" | "音声リスナー" | "오디오리스너" =>
4267 {
4268 let cry = self.arg_num(&args, 0, 1.0)? as f32;
4269 let sry = self.arg_num(&args, 1, 0.0)? as f32;
4270 let crx = self.arg_num(&args, 2, 1.0)? as f32;
4271 let srx = self.arg_num(&args, 3, 0.0)? as f32;
4272 if let Some(audio) = &self.audio {
4273 audio.set_listener(cry, sry, crx, srx);
4274 }
4275 return Ok(Value::Unit);
4276 },
4277
4278 #[cfg(not(target_arch = "wasm32"))]
4279 "audio_bgm" | "เพลงพื้นหลัง" | "เพลงประกอบ" | "背景乐" | "BGM" | "배경음악" =>
4280 {
4281 let path = match args.first() {
4282 Some(Value::Str(s)) => s.clone(),
4283 _ => return Ok(Value::Unit),
4284 };
4285 let vol = self.arg_num(&args, 1, 0.5)? as f32;
4286 if let Some(audio) = &self.audio {
4287 audio.load_bgm(&path, vol);
4288 }
4289 return Ok(Value::Unit);
4290 },
4291
4292 #[cfg(not(target_arch = "wasm32"))]
4293 "audio_bgm_volume"
4294 | "ระดับเสียงพื้นหลัง"
4295 | "ระดับเพลงประกอบ"
4296 | "背景乐音量"
4297 | "BGM音量"
4298 | "배경음악음량" => {
4299 let vol = self.arg_num(&args, 0, 0.5)? as f32;
4300 if let Some(audio) = &self.audio {
4301 audio.set_bgm_volume(vol);
4302 }
4303 return Ok(Value::Unit);
4304 },
4305
4306 #[cfg(not(target_arch = "wasm32"))]
4307 "audio_volume" | "ระดับเสียง" | "音量" | "음량" => {
4308 let vol = self.arg_num(&args, 0, 0.7)? as f32;
4309 if let Some(audio) = &self.audio {
4310 audio.set_master_volume(vol);
4311 }
4312 return Ok(Value::Unit);
4313 },
4314
4315 #[cfg(target_arch = "wasm32")]
4317 "audio_tone"
4318 | "เสียงโทน"
4319 | "音调"
4320 | "音調"
4321 | "음조"
4322 | "空间音"
4323 | "空間音"
4324 | "공간음" => {
4325 let idx = self.arg_num(&args, 0, 0.0)? as usize;
4326 let x = self.arg_num(&args, 1, 0.0)? as f32;
4327 let y = self.arg_num(&args, 2, 0.0)? as f32;
4328 let z = self.arg_num(&args, 3, 0.0)? as f32;
4329 let w = self.arg_num(&args, 4, 1.0)? as f32;
4330 let freq = self.arg_num(&args, 5, 220.0)? as f32;
4331 let amp = self.arg_num(&args, 6, 0.15)? as f32;
4332 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
4333 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
4334 crate::gfx::audio_web::set_tone(idx, x, y, z, w, freq, amp, lfo_rate, lfo_depth);
4335 return Ok(Value::Unit);
4336 },
4337
4338 #[cfg(target_arch = "wasm32")]
4339 "audio_listener" | "ผู้ฟัง" | "音频监听" | "音声リスナー" | "오디오리스너" =>
4340 {
4341 let cry = self.arg_num(&args, 0, 1.0)? as f32;
4342 let sry = self.arg_num(&args, 1, 0.0)? as f32;
4343 let crx = self.arg_num(&args, 2, 1.0)? as f32;
4344 let srx = self.arg_num(&args, 3, 0.0)? as f32;
4345 crate::gfx::audio_web::set_listener(cry, sry, crx, srx);
4346 return Ok(Value::Unit);
4347 },
4348
4349 #[cfg(target_arch = "wasm32")]
4350 "audio_bgm" | "เพลงพื้นหลัง" | "เพลงประกอบ" | "背景乐" | "BGM" | "배경음악" =>
4351 {
4352 let path = self.arg_str(&args, 0, "");
4353 let vol = self.arg_num(&args, 1, 0.5)? as f32;
4354 crate::gfx::audio_web::load_bgm(&path, vol);
4355 return Ok(Value::Unit);
4356 },
4357
4358 #[cfg(target_arch = "wasm32")]
4359 "audio_bgm_volume"
4360 | "ระดับเสียงพื้นหลัง"
4361 | "ระดับเพลงประกอบ"
4362 | "背景乐音量"
4363 | "BGM音量"
4364 | "배경음악음량" => {
4365 let vol = self.arg_num(&args, 0, 0.5)? as f32;
4366 crate::gfx::audio_web::set_bgm_volume(vol);
4367 return Ok(Value::Unit);
4368 },
4369
4370 #[cfg(target_arch = "wasm32")]
4371 "audio_volume" | "ระดับเสียง" | "音量" | "음량" => {
4372 let vol = self.arg_num(&args, 0, 0.7)? as f32;
4373 crate::gfx::audio_web::set_master_volume(vol);
4374 return Ok(Value::Unit);
4375 },
4376
4377 "รอหน้าต่าง" | "wait_window" | "gfx_wait" => {
4379 #[cfg(not(target_arch = "wasm32"))]
4380 loop {
4381 let still_open = {
4382 let gfx = self.gfx.borrow();
4383 gfx.window
4384 .as_ref()
4385 .map(|w| w.is_open() && !w.is_key_down(minifb::Key::Escape))
4386 .unwrap_or(false)
4387 };
4388 if !still_open {
4389 break;
4390 }
4391 let (buf, w, h) = {
4392 let gfx = self.gfx.borrow();
4393 (gfx.buffer.clone(), gfx.width, gfx.height)
4394 };
4395 let mut gfx = self.gfx.borrow_mut();
4396 if let Some(win) = gfx.window.as_mut() {
4397 if win.update_with_buffer(&buf, w, h).is_err() {
4398 break;
4399 }
4400 }
4401 }
4402 return Ok(Value::Unit);
4403 },
4404
4405 "read_file" | "อ่านไฟล์" => {
4407 let path = self.arg_str(&args, 0, "");
4408 return std::fs::read_to_string(&path)
4409 .map(Value::Str)
4410 .map_err(|e| EvalErr::from(format!("read_file '{path}': {e}")));
4411 },
4412 #[cfg(not(target_arch = "wasm32"))]
4414 "net_host" | "เน็ตโฮสต์" => {
4415 let port = self.arg_num(&args, 0, 7777.0)? as u16;
4416 net::host(port);
4417 return Ok(Value::Unit);
4418 },
4419 #[cfg(not(target_arch = "wasm32"))]
4420 "net_join" | "เน็ตจอย" => {
4421 let ip = self.arg_str(&args, 0, "127.0.0.1");
4422 let port = self.arg_num(&args, 1, 7777.0)? as u16;
4423 net::join(&ip, port);
4424 return Ok(Value::Unit);
4425 },
4426 #[cfg(not(target_arch = "wasm32"))]
4427 "net_send" | "เน็ตส่ง" => {
4428 let s = self.arg_str(&args, 0, "");
4429 net::send(&s);
4430 return Ok(Value::Unit);
4431 },
4432 #[cfg(not(target_arch = "wasm32"))]
4433 "net_recv" | "เน็ตรับ" => {
4434 return Ok(Value::Str(net::recv()));
4435 },
4436 #[cfg(not(target_arch = "wasm32"))]
4437 "net_status" | "เน็ตสถานะ" => {
4438 return Ok(Value::Number(net::status() as f64));
4439 },
4440 #[cfg(not(target_arch = "wasm32"))]
4442 "net_announce" | "เน็ตประกาศ" => {
4443 let port = self.arg_num(&args, 0, 7778.0)? as u16;
4444 let info = self.arg_str(&args, 1, "");
4445 net::announce(port, &info);
4446 return Ok(Value::Unit);
4447 },
4448 #[cfg(not(target_arch = "wasm32"))]
4449 "net_announce_stop" | "เน็ตหยุดประกาศ" => {
4450 net::announce_stop();
4451 return Ok(Value::Unit);
4452 },
4453 #[cfg(not(target_arch = "wasm32"))]
4454 "net_discover" | "เน็ตค้นหา" => {
4455 let port = self.arg_num(&args, 0, 7778.0)? as u16;
4456 return Ok(Value::Str(net::discover(port)));
4457 },
4458 #[cfg(not(target_arch = "wasm32"))]
4459 "net_test" | "เน็ตทดสอบ" => {
4460 let port = self.arg_num(&args, 0, 7777.0)? as u16;
4461 return Ok(Value::Str(net::test_bind(port)));
4462 },
4463 #[cfg(not(target_arch = "wasm32"))]
4465 "gamepad_poll" | "จอยโพล" => {
4466 gamepad::poll();
4467 return Ok(Value::Unit);
4468 },
4469 #[cfg(not(target_arch = "wasm32"))]
4470 "gamepad_button" | "จอยปุ่ม" => {
4471 let name = self.arg_str(&args, 0, "");
4472 return Ok(Value::Number(if gamepad::button(&name) {
4473 1.0
4474 } else {
4475 0.0
4476 }));
4477 },
4478 #[cfg(not(target_arch = "wasm32"))]
4479 "gamepad_axis" | "จอยแกน" => {
4480 let name = self.arg_str(&args, 0, "");
4481 return Ok(Value::Number(gamepad::axis(&name) as f64));
4482 },
4483 #[cfg(not(target_arch = "wasm32"))]
4484 "gamepad_rumble" | "จอยสั่น" => {
4485 let low = self.arg_num(&args, 0, 0.0)? as f32;
4486 let high = self.arg_num(&args, 1, 0.0)? as f32;
4487 let ms = self.arg_num(&args, 2, 200.0)? as u32;
4488 gamepad::rumble(low, high, ms);
4489 return Ok(Value::Unit);
4490 },
4491 #[cfg(not(target_arch = "wasm32"))]
4492 "gamepad_list" | "จอยรายการ" => {
4493 return Ok(Value::Str(gamepad::list()));
4494 },
4495 #[cfg(not(target_arch = "wasm32"))]
4496 "gamepad_any" | "จอยใดๆ" => {
4497 return Ok(Value::Number(if gamepad::any_button() { 1.0 } else { 0.0 }));
4498 },
4499
4500 #[cfg(not(target_arch = "wasm32"))]
4503 "nn_new" | "建神经网" | "ニューラル作成" | "신경망생성" | "สร้างโครงข่าย" =>
4504 {
4505 let n_in = self.arg_num(&args, 0, 1.0)?.max(0.0) as usize;
4506 let seed = self.arg_num(&args, 1, 1.0)? as u64;
4507 return Ok(Value::Number(ai::nn_new(n_in, seed) as f64));
4508 },
4509 #[cfg(not(target_arch = "wasm32"))]
4511 "nn_dense" | "密集层" | "密層追加" | "밀집층" | "ชั้นหนาแน่น" =>
4512 {
4513 let id = self.arg_num(&args, 0, -1.0)? as i64;
4514 let units = self.arg_num(&args, 1, 1.0)?.max(1.0) as usize;
4515 let act = self.arg_str(&args, 2, "relu");
4516 ai::nn_dense(id, units, &act);
4517 return Ok(Value::Unit);
4518 },
4519 #[cfg(not(target_arch = "wasm32"))]
4521 "nn_forward" | "神经前向" | "順伝播" | "순전파" | "ส่งต่อโครงข่าย" =>
4522 {
4523 let id = self.arg_num(&args, 0, -1.0)? as i64;
4524 let input = self.arg_list_f32(&args, 1);
4525 let out = ai::nn_forward(id, &input);
4526 return Ok(Value::List(
4527 out.into_iter().map(|v| Value::Number(v as f64)).collect(),
4528 ));
4529 },
4530 #[cfg(not(target_arch = "wasm32"))]
4532 "nn_train" | "训练网" | "ニューラル学習" | "신경망학습" | "ฝึกโครงข่าย" =>
4533 {
4534 let id = self.arg_num(&args, 0, -1.0)? as i64;
4535 let input = self.arg_list_f32(&args, 1);
4536 let target = self.arg_list_f32(&args, 2);
4537 let lr = self.arg_num(&args, 3, 0.01)? as f32;
4538 return Ok(Value::Number(ai::nn_train(id, &input, &target, lr) as f64));
4539 },
4540 #[cfg(not(target_arch = "wasm32"))]
4542 "nn_save" | "保存网" | "網保存" | "신경망저장" | "บันทึกโครงข่าย" =>
4543 {
4544 let id = self.arg_num(&args, 0, -1.0)? as i64;
4545 let path = self.arg_str(&args, 1, "model.lnn");
4546 return Ok(Value::Bool(ai::nn_save(id, &path)));
4547 },
4548 #[cfg(not(target_arch = "wasm32"))]
4550 "nn_load" | "载入网" | "網読込" | "신경망불러오기" | "โหลดโครงข่าย" =>
4551 {
4552 let path = self.arg_str(&args, 0, "model.lnn");
4553 return Ok(Value::Number(ai::nn_load(&path) as f64));
4554 },
4555
4556 #[cfg(not(target_arch = "wasm32"))]
4559 "bt_build" | "建行为树" | "行動木構築" | "행동트리구성" | "สร้างต้นไม้พฤติกรรม" =>
4560 {
4561 let spec = self.arg_str(&args, 0, "");
4562 return Ok(Value::Number(ai::bt_build(&spec) as f64));
4563 },
4564 #[cfg(not(target_arch = "wasm32"))]
4566 "bt_set" | "设事实" | "事実設定" | "사실설정" | "ตั้งข้อเท็จจริง" =>
4567 {
4568 let id = self.arg_num(&args, 0, -1.0)? as i64;
4569 let key = self.arg_str(&args, 1, "");
4570 let val = self.arg_num(&args, 2, 0.0)? as f32;
4571 ai::bt_set(id, &key, val);
4572 return Ok(Value::Unit);
4573 },
4574 #[cfg(not(target_arch = "wasm32"))]
4576 "bt_tick" | "行为树滴答" | "行動木更新" | "행동트리틱" | "เดินต้นไม้พฤติกรรม" =>
4577 {
4578 let id = self.arg_num(&args, 0, -1.0)? as i64;
4579 return Ok(Value::Str(ai::bt_tick(id)));
4580 },
4581 #[cfg(not(target_arch = "wasm32"))]
4583 "bt_status" | "行为树状态" | "行動木状態" | "행동트리상태" | "สถานะต้นไม้พฤติกรรม" =>
4584 {
4585 let id = self.arg_num(&args, 0, -1.0)? as i64;
4586 return Ok(Value::Number(ai::bt_status(id) as f64));
4587 },
4588
4589 #[cfg(not(target_arch = "wasm32"))]
4592 "dialog_new" | "建对话模型" | "対話モデル作成" | "대화모델생성" | "สร้างโมเดลสนทนา" =>
4593 {
4594 let ctx = self.arg_num(&args, 0, 3.0)?.max(1.0) as usize;
4595 let embed = self.arg_num(&args, 1, 32.0)?.max(1.0) as usize;
4596 let hidden = self.arg_num(&args, 2, 64.0)?.max(1.0) as usize;
4597 let seed = self.arg_num(&args, 3, 1.0)? as u64;
4598 return Ok(Value::Number(
4599 ai::dialog_new(ctx, embed, hidden, seed) as f64
4600 ));
4601 },
4602 #[cfg(not(target_arch = "wasm32"))]
4604 "dialog_learn" | "对话学习" | "対話学習" | "대화학습" | "เรียนรู้สนทนา" =>
4605 {
4606 let id = self.arg_num(&args, 0, -1.0)? as i64;
4607 let text = self.arg_str(&args, 1, "");
4608 ai::dialog_learn(id, &text);
4609 return Ok(Value::Unit);
4610 },
4611 #[cfg(not(target_arch = "wasm32"))]
4613 "dialog_load" | "对话载入" | "対話読込" | "대화불러오기" | "โหลดชุดสนทนา" =>
4614 {
4615 let id = self.arg_num(&args, 0, -1.0)? as i64;
4616 let path = self.arg_str(&args, 1, "");
4617 return Ok(Value::Number(ai::dialog_load(id, &path) as f64));
4618 },
4619 #[cfg(not(target_arch = "wasm32"))]
4621 "dialog_train" | "对话训练" | "対話訓練" | "대화훈련" | "ฝึกสนทนา" =>
4622 {
4623 let id = self.arg_num(&args, 0, -1.0)? as i64;
4624 let epochs = self.arg_num(&args, 1, 20.0)?.max(1.0) as usize;
4625 let lr = self.arg_num(&args, 2, 0.1)? as f32;
4626 return Ok(Value::Number(ai::dialog_train(id, epochs, lr) as f64));
4627 },
4628 #[cfg(not(target_arch = "wasm32"))]
4630 "dialog_say" | "对话生成" | "対話生成" | "대화생성" | "พูดสนทนา" =>
4631 {
4632 let id = self.arg_num(&args, 0, -1.0)? as i64;
4633 let prompt = self.arg_str(&args, 1, "");
4634 let max = self.arg_num(&args, 2, 24.0)?.max(1.0) as usize;
4635 let temp = self.arg_num(&args, 3, 0.8)? as f32;
4636 return Ok(Value::Str(ai::dialog_say(id, &prompt, max, temp)));
4637 },
4638 #[cfg(not(target_arch = "wasm32"))]
4640 "dialog_save" | "对话存模" | "対話モデル保存" | "대화모델저장" | "บันทึกโมเดลสนทนา" =>
4641 {
4642 let id = self.arg_num(&args, 0, -1.0)? as i64;
4643 let path = self.arg_str(&args, 1, "model.llm");
4644 return Ok(Value::Bool(ai::dialog_save(id, &path)));
4645 },
4646 #[cfg(not(target_arch = "wasm32"))]
4648 "dialog_load_model"
4649 | "对话载模"
4650 | "対話モデル読込"
4651 | "대화모델불러오기"
4652 | "โหลดโมเดลสนทนา" => {
4653 let path = self.arg_str(&args, 0, "model.llm");
4654 return Ok(Value::Number(ai::dialog_load_model(&path) as f64));
4655 },
4656
4657 "write_file" | "เขียนไฟล์" => {
4658 let path = self.arg_str(&args, 0, "");
4659 let content = self.arg_str(&args, 1, "");
4660 std::fs::write(&path, content.as_bytes())
4661 .map_err(|e| EvalErr::from(format!("write_file '{path}': {e}")))?;
4662 return Ok(Value::Unit);
4663 },
4664 "print_file" | "พิมพ์ไฟล์" => {
4665 let content = self.arg_str(&args, 0, "");
4666 print!("{content}");
4667 return Ok(Value::Unit);
4668 },
4669
4670 "get_args" | "รับอาร์กิวเมนต์" => {
4672 let v: Vec<Value> = std::env::args().map(Value::Str).collect();
4673 return Ok(Value::List(v));
4674 },
4675
4676 "split" | "str_split" | "แยก" => {
4678 let s = self.arg_str(&args, 0, "");
4679 let sep = self.arg_str(&args, 1, "\n");
4680 let sep = if sep.is_empty() { "\n".into() } else { sep };
4681 let parts: Vec<Value> = s
4682 .split(sep.as_str())
4683 .map(|p| Value::Str(p.to_string()))
4684 .collect();
4685 return Ok(Value::List(parts));
4686 },
4687 "trim" | "str_trim" | "ตัดช่องว่าง" => {
4688 let s = self.arg_str(&args, 0, "");
4689 return Ok(Value::Str(s.trim().to_string()));
4690 },
4691 "starts_with" | "str_starts_with" | "เริ่มด้วย" => {
4692 let s = self.arg_str(&args, 0, "");
4693 let prefix = self.arg_str(&args, 1, "");
4694 return Ok(Value::Bool(s.starts_with(prefix.as_str())));
4695 },
4696 "ends_with" | "str_ends_with" | "ลงท้ายด้วย" => {
4697 let s = self.arg_str(&args, 0, "");
4698 let suffix = self.arg_str(&args, 1, "");
4699 return Ok(Value::Bool(s.ends_with(suffix.as_str())));
4700 },
4701 "str_replace" | "แทนสตริง" => {
4702 let s = self.arg_str(&args, 0, "");
4703 let from = self.arg_str(&args, 1, "");
4704 let to = self.arg_str(&args, 2, "");
4705 return Ok(Value::Str(s.replace(from.as_str(), to.as_str())));
4706 },
4707 "str_find" | "หาในสตริง" => {
4708 let s = self.arg_str(&args, 0, "");
4709 let needle = self.arg_str(&args, 1, "");
4710 let pos = s
4712 .find(needle.as_str())
4713 .map(|byte_i| s[..byte_i].chars().count() as f64)
4714 .unwrap_or(-1.0);
4715 return Ok(Value::Number(pos));
4716 },
4717 "substr" | "str_slice" | "ส่วนสตริง" => {
4718 let s = self.arg_str(&args, 0, "");
4719 let start = self.arg_num(&args, 1, 0.0)? as usize;
4720 let len = args
4721 .get(2)
4722 .map(|v| self.to_number(v).unwrap_or(999999.0) as usize)
4723 .unwrap_or_else(|| s.chars().count().saturating_sub(start));
4724 let chars: Vec<char> = s.chars().collect();
4725 let end = (start + len).min(chars.len());
4726 let slice: String = chars.get(start..end).unwrap_or(&[]).iter().collect();
4727 return Ok(Value::Str(slice));
4728 },
4729 "to_str" | "str" | "num_str" | "แปลงสตริง" => {
4730 let v = args.into_iter().next().unwrap_or(Value::Unit);
4731 return Ok(Value::Str(v.to_string()));
4732 },
4733 "str_repeat" | "ทำซ้ำสตริง" => {
4734 let s = self.arg_str(&args, 0, "");
4735 let n = self.arg_num(&args, 1, 1.0)? as usize;
4736 return Ok(Value::Str(s.repeat(n)));
4737 },
4738 "str_upper" => {
4739 let s = self.arg_str(&args, 0, "");
4740 return Ok(Value::Str(s.to_uppercase()));
4741 },
4742 "str_lower" => {
4743 let s = self.arg_str(&args, 0, "");
4744 return Ok(Value::Str(s.to_lowercase()));
4745 },
4746 "str_len" | "len" | "ความยาว" | "长度" | "長さ" | "길이" => {
4747 match args.first() {
4748 Some(Value::Str(s)) => return Ok(Value::Number(s.chars().count() as f64)),
4749 Some(Value::List(v)) => return Ok(Value::Number(v.len() as f64)),
4750 _ => return Ok(Value::Number(0.0)),
4751 }
4752 },
4753
4754 "hash_str" | "แฮช" => {
4756 let s = self.arg_str(&args, 0, "");
4757 let mut h: u64 = 14695981039346656037_u64;
4758 for b in s.bytes() {
4759 h ^= b as u64;
4760 h = h.wrapping_mul(1099511628211);
4761 }
4762 return Ok(Value::Number((h & 0xFFFFFF) as f64 / 16777215.0));
4763 },
4764 "hash_int" | "แฮชจำนวน" => {
4765 let s = self.arg_str(&args, 0, "");
4766 let n = self.arg_num(&args, 1, 100.0)? as u64;
4767 let mut h: u64 = 14695981039346656037_u64;
4768 for b in s.bytes() {
4769 h ^= b as u64;
4770 h = h.wrapping_mul(1099511628211);
4771 }
4772 return Ok(Value::Number((h % n.max(1)) as f64));
4773 },
4774
4775 "list_new" | "รายการใหม่" | "新建列表" | "新規リスト" | "새목록" =>
4777 {
4778 return Ok(Value::List(Vec::new()));
4779 },
4780 "list_push" | "เพิ่มรายการ" | "列表添加" | "リスト追加" | "목록추가" =>
4781 {
4782 let lst = args.first().cloned().unwrap_or(Value::List(vec![]));
4783 let val = args.get(1).cloned().unwrap_or(Value::Unit);
4784 if let Value::List(mut v) = lst {
4785 v.push(val);
4786 return Ok(Value::List(v));
4787 }
4788 return Ok(Value::List(vec![val]));
4789 },
4790 "list_get" | "รับรายการ" | "取元素" | "要素取得" | "요소가져오기" =>
4791 {
4792 let lst = args.first().cloned().unwrap_or(Value::List(vec![]));
4793 let i = self.arg_num(&args, 1, 0.0)? as usize;
4794 if let Value::List(v) = lst {
4795 return Ok(v.get(i).cloned().unwrap_or(Value::Str(String::new())));
4796 }
4797 return Ok(Value::Str(String::new()));
4798 },
4799 "list_join" | "join" | "รวมรายการ" | "连接" | "連結" | "연결" =>
4800 {
4801 let lst = args.first().cloned().unwrap_or(Value::List(vec![]));
4802 let sep = args.get(1).map(|v| v.to_string()).unwrap_or_default();
4803 if let Value::List(v) = lst {
4804 return Ok(Value::Str(
4805 v.iter()
4806 .map(|x| x.to_string())
4807 .collect::<Vec<_>>()
4808 .join(&sep),
4809 ));
4810 }
4811 return Ok(Value::Str(String::new()));
4812 },
4813 #[cfg(not(target_arch = "wasm32"))]
4817 "blob_f32" | "blob_i32" => {
4818 let s = self.arg_str(&args, 0, "");
4819 let is_i32 = name == "blob_i32";
4820 match decode_blob(&s) {
4821 Ok(bytes) => {
4822 let mut out = Vec::with_capacity(bytes.len() / 4);
4823 for ch in bytes.chunks_exact(4) {
4824 let arr = [ch[0], ch[1], ch[2], ch[3]];
4825 let n = if is_i32 {
4826 i32::from_le_bytes(arr) as f64
4827 } else {
4828 f32::from_le_bytes(arr) as f64
4829 };
4830 out.push(Value::Number(n));
4831 }
4832 return Ok(Value::List(out));
4833 },
4834 Err(e) => {
4835 eprintln!("blob decode failed: {e}");
4836 return Ok(Value::List(vec![]));
4837 },
4838 }
4839 },
4840
4841 "svg_begin" | "开始SVG" | "เริ่มSVG" => {
4848 let path = self.arg_str(&args, 0, "output.svg");
4849 let width = self.arg_num(&args, 1, 800.0)?;
4850 let height = self.arg_num(&args, 2, 600.0)?;
4851 *self.svg.borrow_mut() = Some(SvgWriter::new(path, width, height));
4852 return Ok(Value::Unit);
4853 },
4854
4855 "svg_rect" | "SVG矩形" | "SVGสี่เหลี่ยม" => {
4856 let x = self.arg_num(&args, 0, 0.0)?;
4857 let y = self.arg_num(&args, 1, 0.0)?;
4858 let w = self.arg_num(&args, 2, 10.0)?;
4859 let h = self.arg_num(&args, 3, 10.0)?;
4860 let fill = self.arg_str(&args, 4, "#ffffff");
4861 if let Some(svg) = self.svg.borrow_mut().as_mut() {
4862 svg.elements.push(format!(
4863 "<rect x=\"{x:.1}\" y=\"{y:.1}\" width=\"{w:.1}\" \
4864 height=\"{h:.1}\" fill=\"{fill}\"/>"
4865 ));
4866 }
4867 return Ok(Value::Unit);
4868 },
4869
4870 "svg_circle" | "SVG圆形" | "SVGวงกลม" => {
4871 let cx = self.arg_num(&args, 0, 0.0)?;
4872 let cy = self.arg_num(&args, 1, 0.0)?;
4873 let r = self.arg_num(&args, 2, 5.0)?;
4874 let fill = self.arg_str(&args, 3, "#ffffff");
4875 if let Some(svg) = self.svg.borrow_mut().as_mut() {
4876 svg.elements.push(format!(
4877 "<circle cx=\"{cx:.1}\" cy=\"{cy:.1}\" r=\"{r:.1}\" fill=\"{fill}\"/>"
4878 ));
4879 }
4880 return Ok(Value::Unit);
4881 },
4882
4883 "svg_line" | "SVG线段" | "SVGเส้น" => {
4884 let x1 = self.arg_num(&args, 0, 0.0)?;
4885 let y1 = self.arg_num(&args, 1, 0.0)?;
4886 let x2 = self.arg_num(&args, 2, 0.0)?;
4887 let y2 = self.arg_num(&args, 3, 0.0)?;
4888 let stroke = self.arg_str(&args, 4, "#ffffff");
4889 let sw = self.arg_num(&args, 5, 1.0)?;
4890 if let Some(svg) = self.svg.borrow_mut().as_mut() {
4891 svg.elements.push(format!(
4892 "<line x1=\"{x1:.1}\" y1=\"{y1:.1}\" x2=\"{x2:.1}\" y2=\"{y2:.1}\" \
4893 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"
4894 ));
4895 }
4896 return Ok(Value::Unit);
4897 },
4898
4899 "svg_polyline" | "SVG折线" | "SVGเส้นหัก" => {
4900 let pts = self.arg_str(&args, 0, "");
4901 let stroke = self.arg_str(&args, 1, "#ffffff");
4902 let sw = self.arg_num(&args, 2, 1.0)?;
4903 if let Some(svg) = self.svg.borrow_mut().as_mut() {
4904 svg.elements.push(format!(
4905 "<polyline points=\"{pts}\" fill=\"none\" \
4906 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"
4907 ));
4908 }
4909 return Ok(Value::Unit);
4910 },
4911
4912 "svg_text" | "SVG文本" | "SVGข้อความ" => {
4913 let x = self.arg_num(&args, 0, 0.0)?;
4914 let y = self.arg_num(&args, 1, 0.0)?;
4915 let text = self.arg_str(&args, 2, "");
4916 let fill = self.arg_str(&args, 3, "#ffffff");
4917 let size = self.arg_num(&args, 4, 12.0)?;
4918 if let Some(svg) = self.svg.borrow_mut().as_mut() {
4919 let safe = text
4920 .replace('&', "&")
4921 .replace('<', "<")
4922 .replace('>', ">");
4923 svg.elements.push(format!(
4924 "<text x=\"{x:.1}\" y=\"{y:.1}\" fill=\"{fill}\" \
4925 font-family=\"monospace\" font-size=\"{size:.0}\">{safe}</text>"
4926 ));
4927 }
4928 return Ok(Value::Unit);
4929 },
4930
4931 "svg_end" | "结束SVG" | "จบSVG" => {
4932 {
4933 let borrow = self.svg.borrow();
4934 if let Some(svg) = borrow.as_ref() {
4935 svg.save()
4936 .map_err(|e| EvalErr::from(format!("svg_end: {e}")))?;
4937 }
4938 }
4939 *self.svg.borrow_mut() = None;
4940 return Ok(Value::Unit);
4941 },
4942
4943 "hsl_color" | "HSL颜色" | "สีHSL" => {
4944 let h = self.arg_num(&args, 0, 0.0)?;
4945 let s = self.arg_num(&args, 1, 70.0)?;
4946 let l = self.arg_num(&args, 2, 50.0)?;
4947 return Ok(Value::Str(hsl_to_hex(h, s, l)));
4948 },
4949
4950 #[cfg(not(target_arch = "wasm32"))]
4956 "fft_push" | "วิเคราะห์เสียง" | "频谱输入" | "FFT入力" | "FFT입력" =>
4957 {
4958 if let Some(Value::List(v)) = args.first() {
4959 let samples: Vec<f32> = v
4960 .iter()
4961 .filter_map(|x| {
4962 if let Value::Number(n) = x {
4963 Some(*n as f32)
4964 } else {
4965 None
4966 }
4967 })
4968 .collect();
4969 self.fft.borrow_mut().push_samples(&samples);
4970 }
4971 return Ok(Value::Unit);
4972 },
4973
4974 #[cfg(not(target_arch = "wasm32"))]
4976 "fft_bands" | "แถบความถี่" | "频段" | "周波数帯" | "주파수대" =>
4977 {
4978 let n = self.arg_num(&args, 0, 32.0)? as usize;
4979 let bands = self.fft.borrow().freq_bands(n);
4980 *self.fft_bands_cache.borrow_mut() = bands.clone();
4981 return Ok(Value::List(
4982 bands.into_iter().map(|v| Value::Number(v as f64)).collect(),
4983 ));
4984 },
4985
4986 #[cfg(not(target_arch = "wasm32"))]
4988 "fft_beat" | "จังหวะเสียง" | "节拍检测" | "ビート検出" | "비트" =>
4989 {
4990 return Ok(Value::Bool(self.fft.borrow().is_beat()));
4991 },
4992
4993 #[cfg(not(target_arch = "wasm32"))]
4995 "fft_beat_ratio" | "อัตราจังหวะ" | "节拍比" | "ビート比" | "비트비율" =>
4996 {
4997 return Ok(Value::Number(self.fft.borrow().beat_ratio() as f64));
4998 },
4999
5000 #[cfg(not(target_arch = "wasm32"))]
5002 "fft_rms" | "ระดับRMS" | "均方根" | "二乗平均" | "RMS레벨" => {
5003 return Ok(Value::Number(self.fft.borrow().rms() as f64));
5004 },
5005
5006 #[cfg(not(target_arch = "wasm32"))]
5008 "fft_dominant_freq" | "ความถี่หลัก" | "主频" | "主要周波数" | "주파수" =>
5009 {
5010 return Ok(Value::Number(self.fft.borrow().dominant_freq() as f64));
5011 },
5012
5013 #[cfg(target_arch = "wasm32")]
5015 "fft_push" | "วิเคราะห์เสียง" | "频谱输入" | "FFT入力" | "FFT입력" =>
5016 {
5017 return Ok(Value::Unit);
5018 },
5019 #[cfg(target_arch = "wasm32")]
5020 "fft_bands" | "แถบความถี่" | "频段" | "周波数帯" | "주파수대" =>
5021 {
5022 let n = self.arg_num(&args, 0, 32.0)? as usize;
5023 return Ok(Value::List(vec![Value::Number(0.0); n]));
5024 },
5025 #[cfg(target_arch = "wasm32")]
5026 "fft_beat" | "จังหวะเสียง" | "节拍检测" | "ビート検出" | "비트" =>
5027 {
5028 return Ok(Value::Bool(false));
5029 },
5030 #[cfg(target_arch = "wasm32")]
5031 "fft_beat_ratio" | "อัตราจังหวะ" | "节拍比" | "ビート比" | "비트비율" =>
5032 {
5033 return Ok(Value::Number(1.0));
5034 },
5035 #[cfg(target_arch = "wasm32")]
5036 "fft_rms" | "ระดับRMS" | "均方根" | "二乗平均" | "RMS레벨" => {
5037 return Ok(Value::Number(0.0));
5038 },
5039 #[cfg(target_arch = "wasm32")]
5040 "fft_dominant_freq" | "ความถี่หลัก" | "主频" | "主要周波数" | "주파수" =>
5041 {
5042 return Ok(Value::Number(0.0));
5043 },
5044
5045 "tex_checkerboard" | "ลายตารางหมากรุก" => {
5053 let (tx, ty, tw, th) = self.tex_rect(&args)?;
5054 let tiles = self.arg_num(&args, 4, 8.0)? as u32;
5055 let (r1, g1, b1) = (
5056 self.arg_num(&args, 5, 255.)? as u32,
5057 self.arg_num(&args, 6, 255.)? as u32,
5058 self.arg_num(&args, 7, 255.)? as u32,
5059 );
5060 let (r2, g2, b2) = (
5061 self.arg_num(&args, 8, 0.)? as u32,
5062 self.arg_num(&args, 9, 0.)? as u32,
5063 self.arg_num(&args, 10, 0.)? as u32,
5064 );
5065 let c1 = (r1 << 16) | (g1 << 8) | b1;
5066 let c2 = (r2 << 16) | (g2 << 8) | b2;
5067 let mut gfx = self.gfx.borrow_mut();
5068 let (bw, bh) = (gfx.width, gfx.height);
5069 for row in 0..th {
5070 for col in 0..tw {
5071 let cx = col as u32 * tiles / tw as u32;
5072 let cy = row as u32 * tiles / th as u32;
5073 let (dx, dy) = (tx + col, ty + row);
5074 if dx < bw && dy < bh {
5075 gfx.buffer[dy * bw + dx] = if (cx + cy) % 2 == 0 { c1 } else { c2 };
5076 }
5077 }
5078 }
5079 return Ok(Value::Unit);
5080 },
5081
5082 "tex_gradient" | "ลายไล่สี" => {
5084 let (tx, ty, tw, th) = self.tex_rect(&args)?;
5085 let angle = self.arg_num(&args, 4, 0.0)? as f32;
5086 let (r1, g1, b1) = (
5087 self.arg_num(&args, 5, 0.)? as f32 / 255.,
5088 self.arg_num(&args, 6, 0.)? as f32 / 255.,
5089 self.arg_num(&args, 7, 0.)? as f32 / 255.,
5090 );
5091 let (r2, g2, b2) = (
5092 self.arg_num(&args, 8, 255.)? as f32 / 255.,
5093 self.arg_num(&args, 9, 255.)? as f32 / 255.,
5094 self.arg_num(&args, 10, 255.)? as f32 / 255.,
5095 );
5096 let (ca, sa) = (angle.to_radians().cos(), angle.to_radians().sin());
5097 let mut gfx = self.gfx.borrow_mut();
5098 let (bw, bh) = (gfx.width, gfx.height);
5099 for row in 0..th {
5100 for col in 0..tw {
5101 let nx = col as f32 / tw as f32 - 0.5;
5102 let ny = row as f32 / th as f32 - 0.5;
5103 let t = ((nx * ca + ny * sa + 0.707) / 1.414).clamp(0., 1.);
5104 let (dx, dy) = (tx + col, ty + row);
5105 if dx < bw && dy < bh {
5106 gfx.buffer[dy * bw + dx] =
5107 tex_rgb(r1 + (r2 - r1) * t, g1 + (g2 - g1) * t, b1 + (b2 - b1) * t);
5108 }
5109 }
5110 }
5111 return Ok(Value::Unit);
5112 },
5113
5114 "tex_noise" | "ลายนอยส์" => {
5116 let (tx, ty, tw, th) = self.tex_rect(&args)?;
5117 let scale = self.arg_num(&args, 4, 4.0)? as f32;
5118 let octaves = self.arg_num(&args, 5, 4.0)? as u32;
5119 let seed = self.arg_num(&args, 6, 0.0)? as u32;
5120 let palette = self.arg_str(&args, 7, "rainbow");
5121 let mut gfx = self.gfx.borrow_mut();
5122 let (bw, bh) = (gfx.width, gfx.height);
5123 for row in 0..th {
5124 for col in 0..tw {
5125 let v = tex_fbm(
5126 col as f32 * scale / tw as f32,
5127 row as f32 * scale / th as f32,
5128 octaves,
5129 seed,
5130 );
5131 let [r, g, b] = tex_palette(&palette, v);
5132 let (dx, dy) = (tx + col, ty + row);
5133 if dx < bw && dy < bh {
5134 gfx.buffer[dy * bw + dx] = tex_rgb(r, g, b);
5135 }
5136 }
5137 }
5138 return Ok(Value::Unit);
5139 },
5140
5141 "tex_freq_map" | "ลายความถี่" => {
5144 let (tx, ty, tw, th) = self.tex_rect(&args)?;
5145 let time = self.arg_num(&args, 4, 0.0)? as f32;
5146 let speed = self.arg_num(&args, 5, 0.3)? as f32;
5147 let palette = self.arg_str(&args, 6, "rainbow");
5148 let bands: Vec<f32> = {
5149 let c = self.fft_bands_cache.borrow();
5150 if c.is_empty() {
5151 vec![0.0; 32]
5152 } else {
5153 c.clone()
5154 }
5155 };
5156 let n = bands.len().max(1);
5157 let mut gfx = self.gfx.borrow_mut();
5158 let (bw, bh) = (gfx.width, gfx.height);
5159 for row in 0..th {
5160 for col in 0..tw {
5161 let band_idx = (col * n / tw.max(1)).min(n - 1);
5162 let mag = bands[band_idx].clamp(0., 1.);
5163 let fill_y = (mag * th as f32) as usize;
5164 if row >= th.saturating_sub(fill_y) {
5165 let t = (col as f32 / tw as f32 + time * speed) % 1.0;
5166 let [r, g, b] = tex_palette(&palette, t);
5167 let bright = mag * (1.0 - row as f32 / th as f32 * 0.5);
5168 let (dx, dy) = (tx + col, ty + row);
5169 if dx < bw && dy < bh {
5170 gfx.buffer[dy * bw + dx] =
5171 tex_rgb(r * bright, g * bright, b * bright);
5172 }
5173 }
5174 }
5175 }
5176 return Ok(Value::Unit);
5177 },
5178
5179 "tex_spiral" | "ลายเกลียวหมุน" => {
5181 let (tx, ty, tw, th) = self.tex_rect(&args)?;
5182 let freq = self.arg_num(&args, 4, 5.0)? as f32;
5183 let n_bands = self.arg_num(&args, 5, 8.0)? as f32;
5184 let time = self.arg_num(&args, 6, 0.0)? as f32;
5185 let palette = self.arg_str(&args, 7, "rainbow");
5186 let mut gfx = self.gfx.borrow_mut();
5187 let (bw, bh) = (gfx.width, gfx.height);
5188 for row in 0..th {
5189 for col in 0..tw {
5190 let nx = col as f32 / tw as f32 - 0.5;
5191 let ny = row as f32 / th as f32 - 0.5;
5192 let r = (nx * nx + ny * ny).sqrt();
5193 let theta = ny.atan2(nx);
5194 let t = ((r * freq - theta / std::f32::consts::TAU + time * 0.5) * n_bands
5195 % 1.0)
5196 .abs();
5197 let [cr, cg, cb] = tex_palette(&palette, t);
5198 let (dx, dy) = (tx + col, ty + row);
5199 if dx < bw && dy < bh {
5200 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
5201 }
5202 }
5203 }
5204 return Ok(Value::Unit);
5205 },
5206
5207 "tex_ripple" | "ลายระลอก" => {
5209 let (tx, ty, tw, th) = self.tex_rect(&args)?;
5210 let freq = self.arg_num(&args, 4, 10.0)? as f32;
5211 let rcx = self.arg_num(&args, 5, 0.5)? as f32;
5212 let rcy = self.arg_num(&args, 6, 0.5)? as f32;
5213 let time = self.arg_num(&args, 7, 0.0)? as f32;
5214 let palette = self.arg_str(&args, 8, "ocean");
5215 let mut gfx = self.gfx.borrow_mut();
5216 let (bw, bh) = (gfx.width, gfx.height);
5217 for row in 0..th {
5218 for col in 0..tw {
5219 let nx = col as f32 / tw as f32 - rcx;
5220 let ny = row as f32 / th as f32 - rcy;
5221 let r = (nx * nx + ny * ny).sqrt();
5222 let t = ((r * freq - time) % 1.0).abs();
5223 let [cr, cg, cb] = tex_palette(&palette, t);
5224 let (dx, dy) = (tx + col, ty + row);
5225 if dx < bw && dy < bh {
5226 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
5227 }
5228 }
5229 }
5230 return Ok(Value::Unit);
5231 },
5232
5233 "tex_mandelbrot" | "ลายแมนเดลบรอต" => {
5235 let (tx, ty, tw, th) = self.tex_rect(&args)?;
5236 let zoom = self.arg_num(&args, 4, 1.0)?;
5237 let mcx = self.arg_num(&args, 5, -0.5)?;
5238 let mcy = self.arg_num(&args, 6, 0.0)?;
5239 let max_iter = self.arg_num(&args, 7, 64.0)? as u32;
5240 let palette = self.arg_str(&args, 8, "psychedelic");
5241 let mut gfx = self.gfx.borrow_mut();
5242 let (bw, bh) = (gfx.width, gfx.height);
5243 for row in 0..th {
5244 for col in 0..tw {
5245 let zx0 = (col as f64 / tw as f64 - 0.5) / zoom + mcx;
5246 let zy0 = (row as f64 / th as f64 - 0.5) / zoom + mcy;
5247 let mut x = 0.0f64;
5248 let mut y = 0.0f64;
5249 let mut i = 0u32;
5250 while i < max_iter && x * x + y * y < 4.0 {
5251 let t = x * x - y * y + zx0;
5252 y = 2.0 * x * y + zy0;
5253 x = t;
5254 i += 1;
5255 }
5256 let t = if i == max_iter {
5257 0.0f32
5258 } else {
5259 (i as f32
5260 - (x as f32 * x as f32 + y as f32 * y as f32).ln().ln()
5261 / 2.0f32.ln())
5262 / max_iter as f32
5263 };
5264 let [cr, cg, cb] = tex_palette(&palette, t.clamp(0., 1.));
5265 let (dx, dy) = (tx + col, ty + row);
5266 if dx < bw && dy < bh {
5267 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
5268 }
5269 }
5270 }
5271 return Ok(Value::Unit);
5272 },
5273
5274 "tex_julia" | "ลายจูเลีย" => {
5276 let (tx, ty, tw, th) = self.tex_rect(&args)?;
5277 let c_re = self.arg_num(&args, 4, -0.7)?;
5278 let c_im = self.arg_num(&args, 5, 0.27)?;
5279 let max_iter = self.arg_num(&args, 6, 64.0)? as u32;
5280 let palette = self.arg_str(&args, 7, "neon");
5281 let mut gfx = self.gfx.borrow_mut();
5282 let (bw, bh) = (gfx.width, gfx.height);
5283 for row in 0..th {
5284 for col in 0..tw {
5285 let mut zx = (col as f64 / tw as f64 - 0.5) * 3.5;
5286 let mut zy = (row as f64 / th as f64 - 0.5) * 3.5;
5287 let mut i = 0u32;
5288 while i < max_iter && zx * zx + zy * zy < 4.0 {
5289 let t = zx * zx - zy * zy + c_re;
5290 zy = 2.0 * zx * zy + c_im;
5291 zx = t;
5292 i += 1;
5293 }
5294 let t = i as f32 / max_iter as f32;
5295 let [cr, cg, cb] = tex_palette(&palette, t);
5296 let (dx, dy) = (tx + col, ty + row);
5297 if dx < bw && dy < bh {
5298 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
5299 }
5300 }
5301 }
5302 return Ok(Value::Unit);
5303 },
5304
5305 "tex_voronoi" | "ลายโวโรนอย" => {
5307 let (tx, ty, tw, th) = self.tex_rect(&args)?;
5308 let cells = self.arg_num(&args, 4, 16.0)? as u32;
5309 let seed = self.arg_num(&args, 5, 42.0)? as u32;
5310 let palette = self.arg_str(&args, 6, "rainbow");
5311 let pts: Vec<[f32; 2]> = (0..cells)
5312 .map(|i| {
5313 [
5314 tex_hash(i as i32, 0, seed),
5315 tex_hash(i as i32, 1, seed + 999),
5316 ]
5317 })
5318 .collect();
5319 let mut gfx = self.gfx.borrow_mut();
5320 let (bw, bh) = (gfx.width, gfx.height);
5321 for row in 0..th {
5322 for col in 0..tw {
5323 let (fx, fy) = (col as f32 / tw as f32, row as f32 / th as f32);
5324 let (min_d, nearest) = pts.iter().enumerate().fold(
5325 (f32::MAX, 0usize),
5326 |(d, idx), (i, &[cx, cy])| {
5327 let dd = (fx - cx).powi(2) + (fy - cy).powi(2);
5328 if dd < d {
5329 (dd, i)
5330 } else {
5331 (d, idx)
5332 }
5333 },
5334 );
5335 let t = (nearest as f32 / cells as f32 + min_d * 4.0) % 1.0;
5336 let [cr, cg, cb] = tex_palette(&palette, t);
5337 let (dx, dy) = (tx + col, ty + row);
5338 if dx < bw && dy < bh {
5339 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
5340 }
5341 }
5342 }
5343 return Ok(Value::Unit);
5344 },
5345
5346 "tex_halftone" | "ลายฮาล์ฟโทน" => {
5348 let (tx, ty, tw, th) = self.tex_rect(&args)?;
5349 let dot_size = self.arg_num(&args, 4, 0.05)? as f32;
5350 let time = self.arg_num(&args, 5, 0.0)? as f32;
5351 let palette = self.arg_str(&args, 6, "rainbow");
5352 let mut gfx = self.gfx.borrow_mut();
5353 let (bw, bh) = (gfx.width, gfx.height);
5354 for row in 0..th {
5355 for col in 0..tw {
5356 let (fx, fy) = (col as f32 / tw as f32, row as f32 / th as f32);
5357 let gx = (fx / dot_size).floor();
5358 let gy = (fy / dot_size).floor();
5359 let lx = (fx / dot_size - gx - 0.5) * 2.0;
5360 let ly = (fy / dot_size - gy - 0.5) * 2.0;
5361 let r = (lx * lx + ly * ly).sqrt();
5362 let t = (gx / (1.0 / dot_size) + time * 0.1) % 1.0;
5363 let a = if r < 0.7 {
5364 ((0.7 - r) / 0.7).clamp(0., 1.)
5365 } else {
5366 0.0
5367 };
5368 if a > 0.0 {
5369 let [cr, cg, cb] = tex_palette(&palette, t);
5370 let (dx, dy) = (tx + col, ty + row);
5371 if dx < bw && dy < bh {
5372 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
5373 }
5374 }
5375 }
5376 }
5377 return Ok(Value::Unit);
5378 },
5379
5380 "set_shade_mode" | "设置着色" | "シェード設定" | "셰이드모드" | "ตั้งการแรเงา" =>
5385 {
5386 let m = self.arg_num(&args, 0, 2.0)? as u8;
5387 self.gfx.borrow_mut().shade_mode = m;
5388 return Ok(Value::Unit);
5389 },
5390 "set_cel_bands" | "设置色阶" | "セル段数" | "셀밴드" | "ตั้งระดับสี" =>
5392 {
5393 let n = (self.arg_num(&args, 0, 4.0)? as u32).max(2);
5394 self.gfx.borrow_mut().shade.bands = n;
5395 return Ok(Value::Unit);
5396 },
5397 "set_shadow_color" | "设置阴影色" | "影の色" | "그림자색" | "ตั้งสีเงา" =>
5399 {
5400 let r = self.arg_num(&args, 0, 26.)? as f32 / 255.0;
5401 let g = self.arg_num(&args, 1, 33.)? as f32 / 255.0;
5402 let b = self.arg_num(&args, 2, 77.)? as f32 / 255.0;
5403 self.gfx.borrow_mut().shade.shadow = [r, g, b];
5404 return Ok(Value::Unit);
5405 },
5406 #[cfg(not(target_arch = "wasm32"))]
5412 "crypto_hash" | "แฮชเข้ารหัส" | "几何哈希" | "幾何ハッシュ" | "기하해시" =>
5413 {
5414 let s = self.arg_str(&args, 0, "");
5415 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(
5416 s.as_bytes(),
5417 ))));
5418 },
5419 #[cfg(not(target_arch = "wasm32"))]
5421 "knot_points" | "จุดปม" | "结点坐标" | "結び目点" | "매듭점" => {
5422 let s = self.arg_str(&args, 0, "");
5423 let shape = ling_crypto::geo::KnotShape::from_bytes(s.as_bytes());
5424 let mut out = Vec::with_capacity(shape.points.len() * 3);
5425 for p in &shape.points {
5426 out.push(Value::Number(p[0] as f64));
5427 out.push(Value::Number(p[1] as f64));
5428 out.push(Value::Number(p[2] as f64));
5429 }
5430 return Ok(Value::List(out));
5431 },
5432 #[cfg(not(target_arch = "wasm32"))]
5433 "knot_label" | "ป้ายปม" | "结点标签" | "結び目ラベル" | "매듭라벨" =>
5434 {
5435 let s = self.arg_str(&args, 0, "");
5436 return Ok(Value::Str(
5437 ling_crypto::geo::KnotShape::from_bytes(s.as_bytes()).label(),
5438 ));
5439 },
5440 #[cfg(not(target_arch = "wasm32"))]
5442 "knot_keygen" | "hybrid_keygen" | "สร้างกุญแจปม" | "生成密钥" | "鍵生成" | "키생성" =>
5443 {
5444 self.crypto_ids.push(ling_crypto::KnotIdentity::generate());
5445 return Ok(Value::Number((self.crypto_ids.len() - 1) as f64));
5446 },
5447 #[cfg(not(target_arch = "wasm32"))]
5448 "knot_public" | "hybrid_public" | "กุญแจสาธารณะปม" | "公钥" | "公開鍵" | "공개키" =>
5449 {
5450 let h = self.arg_num(&args, 0, 0.0)? as usize;
5451 let pk = self
5452 .crypto_ids
5453 .get(h)
5454 .map(|id| hex_encode(id.public_key()))
5455 .unwrap_or_default();
5456 return Ok(Value::Str(pk));
5457 },
5458 #[cfg(not(target_arch = "wasm32"))]
5460 "knot_encapsulate"
5461 | "hybrid_encapsulate"
5462 | "ห่อกุญแจปม"
5463 | "封装密钥"
5464 | "カプセル化"
5465 | "캡슐화" => {
5466 let pk = hex_decode(&self.arg_str(&args, 0, ""));
5467 match ling_crypto::geo::knot_encapsulate(&pk) {
5468 Ok((ct, ss)) => {
5469 return Ok(Value::List(vec![
5470 Value::Str(hex_encode(&ct)),
5471 Value::Str(hex_encode(&ss)),
5472 ]))
5473 },
5474 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
5475 }
5476 },
5477 #[cfg(not(target_arch = "wasm32"))]
5479 "knot_decapsulate"
5480 | "hybrid_decapsulate"
5481 | "แกะกุญแจปม"
5482 | "解封装密钥"
5483 | "カプセル解除"
5484 | "캡슐해제" => {
5485 let h = self.arg_num(&args, 0, 0.0)? as usize;
5486 let ct = hex_decode(&self.arg_str(&args, 1, ""));
5487 let ss = self
5488 .crypto_ids
5489 .get(h)
5490 .and_then(|id| id.decapsulate(&ct).ok())
5491 .map(|s| hex_encode(&s))
5492 .unwrap_or_default();
5493 return Ok(Value::Str(ss));
5494 },
5495 #[cfg(not(target_arch = "wasm32"))]
5497 "crypto_seal" | "ผนึก" | "封印" | "封印する" | "봉인" => {
5498 let key = hex_to_32(&self.arg_str(&args, 0, ""));
5499 let pt = self.arg_str(&args, 1, "");
5500 match ling_crypto::geo::holo_seal(key, pt.as_bytes()) {
5501 Ok(ct) => return Ok(Value::Str(hex_encode(&ct))),
5502 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
5503 }
5504 },
5505 #[cfg(not(target_arch = "wasm32"))]
5506 "crypto_open" | "เปิดผนึก" | "解封" | "封印解除" | "봉인해제" =>
5507 {
5508 let key = hex_to_32(&self.arg_str(&args, 0, ""));
5509 let ct = hex_decode(&self.arg_str(&args, 1, ""));
5510 match ling_crypto::geo::holo_open(key, &ct) {
5511 Ok(pt) => return Ok(Value::Str(String::from_utf8_lossy(&pt).into_owned())),
5512 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
5513 }
5514 },
5515 #[cfg(not(target_arch = "wasm32"))]
5517 "holo_points" | "จุดโฮโลแกรม" | "全息点" | "ホログラム点" | "홀로그램점" =>
5518 {
5519 let s = self.arg_str(&args, 0, "");
5520 let frags = ling_crypto::geo::scatter(s.as_bytes());
5521 let mut out = Vec::with_capacity(frags.len() * 4);
5522 for f in &frags {
5523 for c in f.coord {
5524 out.push(Value::Number(c as f64));
5525 }
5526 }
5527 return Ok(Value::List(out));
5528 },
5529 #[cfg(not(target_arch = "wasm32"))]
5530 "holo_fragment_count"
5531 | "จำนวนชิ้นโฮโลแกรม"
5532 | "全息碎片数"
5533 | "ホログラム断片数"
5534 | "홀로그램조각수" => {
5535 let s = self.arg_str(&args, 0, "");
5536 return Ok(Value::Number(
5537 ling_crypto::geo::scatter(s.as_bytes()).len() as f64
5538 ));
5539 },
5540
5541 "ease" => {
5545 let name = self.arg_str(&args, 0, "ease");
5546 let t = self.arg_num(&args, 1, 0.0)? as f32;
5547 return Ok(Value::Number(
5548 ling_ui::Easing::from_name(&name).apply(t) as f64
5549 ));
5550 },
5551
5552 "tween" | "补间" | "補間" | "트윈" | "แทรกค่า" => {
5557 let a = self.arg_num(&args, 0, 0.0)?;
5558 let b = self.arg_num(&args, 1, 0.0)?;
5559 let t = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 1.0);
5560 return Ok(Value::Number(a + (b - a) * t));
5561 },
5562 "tween_ease" | "缓动补间" | "緩和補間" | "이징트윈" | "แทรกนุ่ม" =>
5563 {
5564 let a = self.arg_num(&args, 0, 0.0)? as f32;
5565 let b = self.arg_num(&args, 1, 0.0)? as f32;
5566 let t = self.arg_num(&args, 2, 0.0)? as f32;
5567 let kind = self.arg_str(&args, 3, "linear");
5568 let e = ling_animation::EaseFunction::from_name(&kind);
5569 return Ok(Value::Number(
5570 ling_animation::ease::tween_ease(&a, &b, t, e) as f64,
5571 ));
5572 },
5573 "breathe" | "呼吸" | "호흡" | "หายใจ" => {
5575 let t = self.arg_num(&args, 0, 0.0)? as f32;
5576 let rate = self.arg_num(&args, 1, 1.0)? as f32;
5577 let depth = self.arg_num(&args, 2, 0.1)? as f32;
5578 return Ok(Value::Number(
5579 ling_animation::scalar::breathe(t, rate, depth) as f64,
5580 ));
5581 },
5582 "wobble" | "摆动" | "揺れ" | "흔들림" | "โยก" => {
5583 let t = self.arg_num(&args, 0, 0.0)? as f32;
5584 let freq = self.arg_num(&args, 1, 1.0)? as f32;
5585 let amp = self.arg_num(&args, 2, 1.0)? as f32;
5586 let phase = self.arg_num(&args, 3, 0.0)? as f32;
5587 return Ok(Value::Number(
5588 ling_animation::scalar::wobble(t, freq, amp, phase) as f64,
5589 ));
5590 },
5591 "gait_phase" | "步相" | "歩相" | "걸음위상" | "เฟสก้าว" => {
5592 let t = self.arg_num(&args, 0, 0.0)? as f32;
5593 let speed = self.arg_num(&args, 1, 1.0)? as f32;
5594 return Ok(Value::Number(
5595 ling_animation::scalar::gait_phase(t, speed) as f64
5596 ));
5597 },
5598 "gait_swing" | "步摆" | "歩振り" | "걸음흔들" | "ก้าวแกว่ง" =>
5599 {
5600 let t = self.arg_num(&args, 0, 0.0)? as f32;
5601 let speed = self.arg_num(&args, 1, 1.0)? as f32;
5602 let stride = self.arg_num(&args, 2, 1.0)? as f32;
5603 return Ok(Value::Number(
5604 ling_animation::scalar::gait_swing(t, speed, stride) as f64,
5605 ));
5606 },
5607 "gait_lift" | "抬脚" | "足上げ" | "발들기" | "ยกเท้า" => {
5608 let t = self.arg_num(&args, 0, 0.0)? as f32;
5609 let speed = self.arg_num(&args, 1, 1.0)? as f32;
5610 let height = self.arg_num(&args, 2, 1.0)? as f32;
5611 return Ok(Value::Number(
5612 ling_animation::scalar::gait_lift(t, speed, height) as f64,
5613 ));
5614 },
5615 "spring_to" | "弹向" | "バネ寄せ" | "스프링이동" | "สปริงไป" =>
5616 {
5617 let pos = self.arg_num(&args, 0, 0.0)? as f32;
5618 let vel = self.arg_num(&args, 1, 0.0)? as f32;
5619 let target = self.arg_num(&args, 2, 0.0)? as f32;
5620 let stiffness = self.arg_num(&args, 3, 120.0)? as f32;
5621 let damping = self.arg_num(&args, 4, 14.0)? as f32;
5622 let dt = self.arg_num(&args, 5, 1.0 / 60.0)? as f32;
5623 let (np, nv) =
5624 ling_animation::scalar::spring_step(pos, vel, target, stiffness, damping, dt);
5625 return Ok(Value::List(vec![
5626 Value::Number(np as f64),
5627 Value::Number(nv as f64),
5628 ]));
5629 },
5630 "ik2" | "反解" | "逆運動" | "역운동" | "ไอเค2" => {
5631 let l1 = self.arg_num(&args, 0, 1.0)? as f32;
5632 let l2 = self.arg_num(&args, 1, 1.0)? as f32;
5633 let tx = self.arg_num(&args, 2, 0.0)? as f32;
5634 let ty = self.arg_num(&args, 3, 0.0)? as f32;
5635 let (sh, el) = ling_animation::scalar::two_bone_ik(l1, l2, tx, ty);
5636 return Ok(Value::List(vec![
5637 Value::Number(sh as f64),
5638 Value::Number(el as f64),
5639 ]));
5640 },
5641 "gear_couple" | "齿轮联动" | "歯車連動" | "기어연동" | "เฟืองทด" =>
5643 {
5644 let angle = self.arg_num(&args, 0, 0.0)? as f32;
5645 let ti = self.arg_num(&args, 1, 1.0)? as f32;
5646 let to = self.arg_num(&args, 2, 1.0)? as f32;
5647 return Ok(Value::Number(
5648 ling_animation::scalar::gear(angle, ti, to) as f64
5649 ));
5650 },
5651 "gear_train" | "齿轮组" | "歯車列" | "기어열" | "ชุดเฟือง" => {
5652 let angle = self.arg_num(&args, 0, 0.0)? as f32;
5653 let teeth: Vec<f32> = match args.get(1) {
5654 Some(Value::List(items)) => items
5655 .iter()
5656 .filter_map(|v| {
5657 if let Value::Number(n) = v {
5658 Some(*n as f32)
5659 } else {
5660 None
5661 }
5662 })
5663 .collect(),
5664 _ => Vec::new(),
5665 };
5666 let out = ling_animation::mechanism::gear_train(angle, &teeth);
5667 return Ok(Value::List(
5668 out.into_iter().map(|a| Value::Number(a as f64)).collect(),
5669 ));
5670 },
5671 "cam_lift" | "凸轮升程" | "カム揚程" | "캠리프트" | "ยกลูกเบี้ยว" =>
5672 {
5673 let angle = self.arg_num(&args, 0, 0.0)? as f32;
5674 let lift = self.arg_num(&args, 1, 1.0)? as f32;
5675 return Ok(Value::Number(
5676 ling_animation::scalar::cam_lift(angle, lift) as f64
5677 ));
5678 },
5679 "piston" | "活塞" | "ピストン" | "피스톤" | "ลูกสูบ" => {
5680 let angle = self.arg_num(&args, 0, 0.0)? as f32;
5681 let crank = self.arg_num(&args, 1, 1.0)? as f32;
5682 let rod = self.arg_num(&args, 2, 2.0)? as f32;
5683 return Ok(Value::Number(
5684 ling_animation::scalar::piston(angle, crank, rod) as f64,
5685 ));
5686 },
5687 "rack" | "齿条" | "ラック" | "랙" | "แร็ค" => {
5688 let angle = self.arg_num(&args, 0, 0.0)? as f32;
5689 let radius = self.arg_num(&args, 1, 1.0)? as f32;
5690 return Ok(Value::Number(
5691 ling_animation::scalar::rack(angle, radius) as f64
5692 ));
5693 },
5694 #[cfg(not(target_arch = "wasm32"))]
5695 "mouse_x" => {
5696 let gfx = self.gfx.borrow();
5697 let v = gfx
5698 .window
5699 .as_ref()
5700 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
5701 .map(|p| p.0 as f64)
5702 .unwrap_or(0.0);
5703 return Ok(Value::Number(v));
5704 },
5705 #[cfg(not(target_arch = "wasm32"))]
5706 "mouse_y" => {
5707 let gfx = self.gfx.borrow();
5708 let v = gfx
5709 .window
5710 .as_ref()
5711 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
5712 .map(|p| p.1 as f64)
5713 .unwrap_or(0.0);
5714 return Ok(Value::Number(v));
5715 },
5716 #[cfg(not(target_arch = "wasm32"))]
5717 "mouse_down" => {
5718 let gfx = self.gfx.borrow();
5719 let d = gfx
5720 .window
5721 .as_ref()
5722 .map(|w| w.get_mouse_down(minifb::MouseButton::Left))
5723 .unwrap_or(false);
5724 return Ok(Value::Bool(d));
5725 },
5726 #[cfg(not(target_arch = "wasm32"))]
5727 "mouse_down_right" | "เมาส์ขวา" => {
5728 let gfx = self.gfx.borrow();
5729 let d = gfx
5730 .window
5731 .as_ref()
5732 .map(|w| w.get_mouse_down(minifb::MouseButton::Right))
5733 .unwrap_or(false);
5734 return Ok(Value::Bool(d));
5735 },
5736 #[cfg(not(target_arch = "wasm32"))]
5737 "mouse_down_middle" | "เมาส์กลาง" => {
5738 let gfx = self.gfx.borrow();
5739 let d = gfx
5740 .window
5741 .as_ref()
5742 .map(|w| w.get_mouse_down(minifb::MouseButton::Middle))
5743 .unwrap_or(false);
5744 return Ok(Value::Bool(d));
5745 },
5746 #[cfg(not(target_arch = "wasm32"))]
5747 "ui_hot" | "热区" | "ホットエリア" | "핫존" | "พื้นที่สัมผัส" =>
5748 {
5749 let x = self.arg_num(&args, 0, 0.0)? as f32;
5750 let y = self.arg_num(&args, 1, 0.0)? as f32;
5751 let w = self.arg_num(&args, 2, 0.0)? as f32;
5752 let h = self.arg_num(&args, 3, 0.0)? as f32;
5753 let gfx = self.gfx.borrow();
5754 let (mx, my) = gfx
5755 .window
5756 .as_ref()
5757 .and_then(|win| win.get_mouse_pos(minifb::MouseMode::Clamp))
5758 .unwrap_or((0.0, 0.0));
5759 return Ok(Value::Bool(ling_ui::holo::hit_rect(mx, my, x, y, w, h)));
5760 },
5761 "ui_text" | "界面文字" | "UI文字" | "UI텍스트" | "ข้อความหน้าจอ" =>
5763 {
5764 let x = self.arg_num(&args, 0, 0.0)? as f32;
5765 let y = self.arg_num(&args, 1, 0.0)? as f32;
5766 let scale = self.arg_num(&args, 2, 16.0)? as f32;
5767 let s = self.arg_str(&args, 3, "");
5768 let segs = ling_ui::holo::text_lines(&s, x, y, scale * 0.62, scale, scale * 0.24);
5769 let mut gfx = self.gfx.borrow_mut();
5770 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
5771 for sg in segs {
5772 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
5773 }
5774 return Ok(Value::Unit);
5775 },
5776 #[cfg(not(target_arch = "wasm32"))]
5779 "font_load" | "โหลดฟอนต์" | "加载字体" | "フォント読込" | "글꼴로드" =>
5780 {
5781 let path = self.arg_str(&args, 0, "");
5782 let weight = match self.arg_num(&args, 1, 0.0)? {
5784 w if w > 0.0 => Some(w as f32),
5785 _ => None,
5786 };
5787 let mut loaded = ling_graphics::VectorFont::from_path_weight(&path, weight);
5789 if loaded.is_err() {
5790 if let Some(dir) = &self.source_dir {
5791 let joined = dir.join(&path);
5792 loaded = ling_graphics::VectorFont::from_path_weight(
5793 &joined.to_string_lossy(),
5794 weight,
5795 );
5796 }
5797 }
5798 match loaded {
5799 Ok(f) => {
5800 let id = self.fonts.len();
5801 self.fonts.push(f);
5802 return Ok(Value::Number(id as f64));
5803 },
5804 Err(e) => {
5805 eprintln!("font_load failed ({path}): {e}");
5806 return Ok(Value::Number(-1.0));
5807 },
5808 }
5809 },
5810 #[cfg(not(target_arch = "wasm32"))]
5813 "font_text" | "ข้อความฟอนต์" | "字体文本" | "フォント文字" | "글꼴텍스트" =>
5814 {
5815 let id = self.arg_num(&args, 0, 0.0)? as i64;
5816 let x = self.arg_num(&args, 1, 0.0)? as f32;
5817 let y = self.arg_num(&args, 2, 0.0)? as f32;
5818 let px = self.arg_num(&args, 3, 16.0)? as f32;
5819 let s = self.arg_str(&args, 4, "");
5820 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
5821 let strokes = self.font_layout_2d(id as usize, x, y, px, &s);
5822 let mut gfx = self.gfx.borrow_mut();
5823 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
5824 for pl in &strokes {
5825 for seg in pl.windows(2) {
5826 crate::gfx::raster::draw_line_aa(
5827 &mut gfx.buffer,
5828 w,
5829 h,
5830 color,
5831 add,
5832 seg[0][0],
5833 seg[0][1],
5834 seg[1][0],
5835 seg[1][1],
5836 );
5837 }
5838 }
5839 }
5840 return Ok(Value::Unit);
5841 },
5842 #[cfg(not(target_arch = "wasm32"))]
5844 "font_text_fill" | "เติมฟอนต์" | "填充字体" | "フォント塗り" | "글꼴채움" =>
5845 {
5846 let id = self.arg_num(&args, 0, 0.0)? as i64;
5847 let x = self.arg_num(&args, 1, 0.0)? as f32;
5848 let y = self.arg_num(&args, 2, 0.0)? as f32;
5849 let px = self.arg_num(&args, 3, 16.0)? as f32;
5850 let s = self.arg_str(&args, 4, "");
5851 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
5852 let glyphs = self.font_layout_2d_glyphs(id as usize, x, y, px, &s);
5854 let mut gfx = self.gfx.borrow_mut();
5855 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
5856 for contours in &glyphs {
5857 crate::gfx::raster::fill_contours_aa(
5858 &mut gfx.buffer,
5859 w,
5860 h,
5861 color,
5862 add,
5863 contours,
5864 );
5865 }
5866 }
5867 return Ok(Value::Unit);
5868 },
5869 #[cfg(not(target_arch = "wasm32"))]
5873 "font_text_3d" | "ข้อความฟอนต์3มิติ" | "字体3D" | "フォント3D" | "글꼴3D" =>
5874 {
5875 let id = self.arg_num(&args, 0, 0.0)? as i64;
5876 let cx = self.arg_num(&args, 1, 0.0)? as f32;
5877 let cy = self.arg_num(&args, 2, 0.0)? as f32;
5878 let cz = self.arg_num(&args, 3, 0.0)? as f32;
5879 let ux = self.arg_num(&args, 4, 1.0)? as f32;
5880 let uy = self.arg_num(&args, 5, 0.0)? as f32;
5881 let uz = self.arg_num(&args, 6, 0.0)? as f32;
5882 let vx = self.arg_num(&args, 7, 0.0)? as f32;
5883 let vy = self.arg_num(&args, 8, 1.0)? as f32;
5884 let vz = self.arg_num(&args, 9, 0.0)? as f32;
5885 let size = self.arg_num(&args, 10, 1.0)? as f32;
5886 let s = self.arg_str(&args, 11, "");
5887 if id >= 0 && (id as usize) < self.fonts.len() && size > 0.0 {
5888 let font = &mut self.fonts[id as usize];
5890 let asc = font.ascent();
5891 let mut pen = 0.0f32;
5892 let mut lines: Vec<[f32; 6]> = Vec::new();
5893 for ch in s.chars() {
5894 let go = font.glyph_outline(ch, 0.01);
5895 for pl in &go.polylines {
5896 for seg in pl.windows(2) {
5897 let map = |p: [f32; 2]| {
5898 let a = pen + p[0];
5899 let b = p[1] - asc; [
5901 cx + a * size * ux + b * size * vx,
5902 cy + a * size * uy + b * size * vy,
5903 cz + a * size * uz + b * size * vz,
5904 ]
5905 };
5906 let p0 = map(seg[0]);
5907 let p1 = map(seg[1]);
5908 lines.push([p0[0], p0[1], p0[2], p1[0], p1[1], p1[2]]);
5909 }
5910 }
5911 pen += go.advance;
5912 }
5913 let mut gfx = self.gfx.borrow_mut();
5914 let color = gfx.color;
5915 let near = -gfx.camera.zdist + 0.05;
5916 for l in &lines {
5917 let (mut ax, mut ay, mut az) = (l[0], l[1], l[2]);
5918 let (mut bx, mut by, mut bz) = (l[3], l[4], l[5]);
5919 let da = gfx.camera.depth(ax, ay, az);
5920 let db = gfx.camera.depth(bx, by, bz);
5921 if da <= near && db <= near {
5922 continue;
5923 }
5924 if da <= near {
5925 let t = (near - da) / (db - da);
5926 ax += t * (bx - ax);
5927 ay += t * (by - ay);
5928 az += t * (bz - az);
5929 } else if db <= near {
5930 let t = (near - da) / (db - da);
5931 bx = ax + t * (bx - ax);
5932 by = ay + t * (by - ay);
5933 bz = az + t * (bz - az);
5934 }
5935 let (sax, say, da2) = gfx.camera.project(ax, ay, az);
5936 let (sbx, sby, db2) = gfx.camera.project(bx, by, bz);
5937 let depth = (da2 + db2) / 2.0;
5938 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
5939 }
5940 }
5941 return Ok(Value::Unit);
5942 },
5943 #[cfg(not(target_arch = "wasm32"))]
5945 "font_width" | "ความกว้างฟอนต์" | "字体宽度" | "フォント幅" | "글꼴너비" =>
5946 {
5947 let id = self.arg_num(&args, 0, 0.0)? as i64;
5948 let px = self.arg_num(&args, 1, 16.0)? as f32;
5949 let s = self.arg_str(&args, 2, "");
5950 if id >= 0 && (id as usize) < self.fonts.len() {
5951 return Ok(Value::Number(self.fonts[id as usize].measure(&s, px) as f64));
5952 }
5953 return Ok(Value::Number(0.0));
5954 },
5955 "ui_frame" | "边框" | "フレーム枠" | "프레임틀" | "กรอบ" => {
5957 let x = self.arg_num(&args, 0, 0.0)? as f32;
5958 let y = self.arg_num(&args, 1, 0.0)? as f32;
5959 let w0 = self.arg_num(&args, 2, 0.0)? as f32;
5960 let h0 = self.arg_num(&args, 3, 0.0)? as f32;
5961 let l = self.arg_num(&args, 4, 14.0)? as f32;
5962 let segs = ling_ui::holo::corner_brackets(x, y, w0, h0, l);
5963 let mut gfx = self.gfx.borrow_mut();
5964 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
5965 for sg in segs {
5966 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
5967 }
5968 return Ok(Value::Unit);
5969 },
5970 "ui_bevel" | "斜角框" | "ベベル枠" | "베벨틀" | "กรอบเฉียง" =>
5972 {
5973 let x = self.arg_num(&args, 0, 0.0)? as f32;
5974 let y = self.arg_num(&args, 1, 0.0)? as f32;
5975 let w0 = self.arg_num(&args, 2, 0.0)? as f32;
5976 let h0 = self.arg_num(&args, 3, 0.0)? as f32;
5977 let bv = self.arg_num(&args, 4, 10.0)? as f32;
5978 let segs = ling_ui::holo::beveled_rect(x, y, w0, h0, bv);
5979 let mut gfx = self.gfx.borrow_mut();
5980 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
5981 for sg in segs {
5982 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
5983 }
5984 return Ok(Value::Unit);
5985 },
5986
5987 #[cfg(not(target_arch = "wasm32"))]
5993 "ui_theme" | "界面主题" | "UIテーマ" | "인터페이스테마" | "ธีมส่วนติดต่อ" =>
5994 {
5995 let cur = self.ui_theme;
5996 let primary = self.color_at(&args, 0, cur.primary);
5997 let accent = self.color_at(&args, 3, cur.accent);
5998 let track = self.color_at(&args, 6, cur.track);
5999 let warn = self.color_at(&args, 9, cur.warn);
6000 let text = self.color_at(&args, 12, cur.text);
6001 let bg = self.color_at(&args, 15, cur.bg);
6002 self.ui_theme = UiTheme { primary, accent, track, warn, text, bg };
6003 return Ok(Value::Unit);
6004 },
6005
6006 #[cfg(not(target_arch = "wasm32"))]
6008 "ui_radar" | "雷达" | "レーダー" | "레이더" | "เรดาร์" => {
6009 let cx = self.arg_num(&args, 0, 0.)? as f32;
6010 let cy = self.arg_num(&args, 1, 0.)? as f32;
6011 let r = self.arg_num(&args, 2, 60.)? as f32;
6012 let sweep = self.arg_num(&args, 3, 0.)? as f32;
6013 let th = self.ui_theme;
6014 let prim = self.color_at(&args, 4, th.primary);
6015 self.draw_ui(&ling_ui::widgets::radar(
6016 cx, cy, r, sweep, prim, th.accent, th.track,
6017 ));
6018 return Ok(Value::Unit);
6019 },
6020 #[cfg(not(target_arch = "wasm32"))]
6021 "ui_compass" | "罗盘" | "コンパス" | "나침반" | "เข็มทิศ" => {
6022 let x = self.arg_num(&args, 0, 0.)? as f32;
6023 let y = self.arg_num(&args, 1, 0.)? as f32;
6024 let w0 = self.arg_num(&args, 2, 300.)? as f32;
6025 let h0 = self.arg_num(&args, 3, 24.)? as f32;
6026 let head = self.arg_num(&args, 4, 0.)? as f32;
6027 let th = self.ui_theme;
6028 let prim = self.color_at(&args, 5, th.primary);
6029 self.draw_ui(&ling_ui::widgets::compass(
6030 x, y, w0, h0, head, prim, th.track,
6031 ));
6032 return Ok(Value::Unit);
6033 },
6034 #[cfg(not(target_arch = "wasm32"))]
6035 "ui_reticle" | "准星" | "照準" | "조준선" | "เป้าเล็ง" => {
6036 let cx = self.arg_num(&args, 0, 0.)? as f32;
6037 let cy = self.arg_num(&args, 1, 0.)? as f32;
6038 let r = self.arg_num(&args, 2, 30.)? as f32;
6039 let spread = self.arg_num(&args, 3, 0.)? as f32;
6040 let th = self.ui_theme;
6041 let prim = self.color_at(&args, 4, th.primary);
6042 self.draw_ui(&ling_ui::widgets::reticle(cx, cy, r, spread, prim));
6043 return Ok(Value::Unit);
6044 },
6045 #[cfg(not(target_arch = "wasm32"))]
6046 "ui_target" | "锁定框" | "ターゲット" | "표적" | "กรอบเป้า" =>
6047 {
6048 let x = self.arg_num(&args, 0, 0.)? as f32;
6049 let y = self.arg_num(&args, 1, 0.)? as f32;
6050 let w0 = self.arg_num(&args, 2, 80.)? as f32;
6051 let h0 = self.arg_num(&args, 3, 80.)? as f32;
6052 let lock = self.arg_num(&args, 4, 0.)? as f32;
6053 let th = self.ui_theme;
6054 let prim = self.color_at(&args, 5, th.primary);
6055 self.draw_ui(&ling_ui::widgets::target(
6056 x, y, w0, h0, lock, prim, th.accent,
6057 ));
6058 return Ok(Value::Unit);
6059 },
6060 #[cfg(not(target_arch = "wasm32"))]
6061 "ui_panel" | "面板" | "パネル" | "패널" | "แผง" => {
6062 let x = self.arg_num(&args, 0, 0.)? as f32;
6063 let y = self.arg_num(&args, 1, 0.)? as f32;
6064 let w0 = self.arg_num(&args, 2, 200.)? as f32;
6065 let h0 = self.arg_num(&args, 3, 120.)? as f32;
6066 let bv = self.arg_num(&args, 4, 12.)? as f32;
6067 let th = self.ui_theme;
6068 let prim = self.color_at(&args, 5, th.primary);
6069 self.draw_ui(&ling_ui::widgets::panel(x, y, w0, h0, bv, prim, th.bg));
6070 return Ok(Value::Unit);
6071 },
6072 #[cfg(not(target_arch = "wasm32"))]
6073 "ui_scanlines" | "扫描线" | "走査線" | "스캔라인" | "เส้นสแกน" =>
6074 {
6075 let x = self.arg_num(&args, 0, 0.)? as f32;
6076 let y = self.arg_num(&args, 1, 0.)? as f32;
6077 let w0 = self.arg_num(&args, 2, 200.)? as f32;
6078 let h0 = self.arg_num(&args, 3, 120.)? as f32;
6079 let dens = self.arg_num(&args, 4, 24.)? as usize;
6080 let th = self.ui_theme;
6081 let line = self.color_at(&args, 5, th.track);
6082 self.draw_ui(&ling_ui::widgets::scanlines(x, y, w0, h0, dens, line));
6083 return Ok(Value::Unit);
6084 },
6085
6086 #[cfg(not(target_arch = "wasm32"))]
6088 "ui_bar" | "进度条" | "バー" | "막대" | "แถบ" => {
6089 let x = self.arg_num(&args, 0, 0.)? as f32;
6090 let y = self.arg_num(&args, 1, 0.)? as f32;
6091 let w0 = self.arg_num(&args, 2, 160.)? as f32;
6092 let h0 = self.arg_num(&args, 3, 16.)? as f32;
6093 let val = self.arg_num(&args, 4, 0.)? as f32;
6094 let max = self.arg_num(&args, 5, 1.)? as f32;
6095 let th = self.ui_theme;
6096 let fill = self.color_at(&args, 6, th.primary);
6097 self.draw_ui(&ling_ui::widgets::bar(
6098 x,
6099 y,
6100 w0,
6101 h0,
6102 val / max.max(1e-6),
6103 fill,
6104 th.track,
6105 ));
6106 return Ok(Value::Unit);
6107 },
6108 #[cfg(not(target_arch = "wasm32"))]
6109 "ui_segbar" | "分段条" | "分割バー" | "분할막대" | "แถบแบ่ง" =>
6110 {
6111 let x = self.arg_num(&args, 0, 0.)? as f32;
6112 let y = self.arg_num(&args, 1, 0.)? as f32;
6113 let w0 = self.arg_num(&args, 2, 160.)? as f32;
6114 let h0 = self.arg_num(&args, 3, 16.)? as f32;
6115 let val = self.arg_num(&args, 4, 0.)? as f32;
6116 let max = self.arg_num(&args, 5, 1.)? as f32;
6117 let segs = self.arg_num(&args, 6, 10.)? as usize;
6118 let th = self.ui_theme;
6119 let fill = self.color_at(&args, 7, th.primary);
6120 self.draw_ui(&ling_ui::widgets::segbar(
6121 x,
6122 y,
6123 w0,
6124 h0,
6125 val / max.max(1e-6),
6126 segs,
6127 fill,
6128 th.track,
6129 ));
6130 return Ok(Value::Unit);
6131 },
6132 #[cfg(not(target_arch = "wasm32"))]
6133 "ui_gauge" | "仪表" | "ゲージ" | "게이지" | "มาตรวัด" => {
6134 let cx = self.arg_num(&args, 0, 0.)? as f32;
6135 let cy = self.arg_num(&args, 1, 0.)? as f32;
6136 let r = self.arg_num(&args, 2, 50.)? as f32;
6137 let val = self.arg_num(&args, 3, 0.)? as f32;
6138 let max = self.arg_num(&args, 4, 1.)? as f32;
6139 let th = self.ui_theme;
6140 let needle = self.color_at(&args, 5, th.warn);
6141 self.draw_ui(&ling_ui::widgets::gauge(
6142 cx,
6143 cy,
6144 r,
6145 val / max.max(1e-6),
6146 needle,
6147 th.accent,
6148 th.track,
6149 ));
6150 return Ok(Value::Unit);
6151 },
6152 #[cfg(not(target_arch = "wasm32"))]
6153 "ui_ring" | "环表" | "リングメーター" | "링미터" | "วงแหวนวัด" =>
6154 {
6155 let cx = self.arg_num(&args, 0, 0.)? as f32;
6156 let cy = self.arg_num(&args, 1, 0.)? as f32;
6157 let r = self.arg_num(&args, 2, 40.)? as f32;
6158 let val = self.arg_num(&args, 3, 0.)? as f32;
6159 let max = self.arg_num(&args, 4, 1.)? as f32;
6160 let th = self.ui_theme;
6161 let fill = self.color_at(&args, 5, th.primary);
6162 self.draw_ui(&ling_ui::widgets::ring(
6163 cx,
6164 cy,
6165 r,
6166 val / max.max(1e-6),
6167 fill,
6168 th.track,
6169 ));
6170 return Ok(Value::Unit);
6171 },
6172 #[cfg(not(target_arch = "wasm32"))]
6173 "ui_vu" | "音量条" | "VUメーター" | "음량막대" | "มาตรเสียง" =>
6174 {
6175 let x = self.arg_num(&args, 0, 0.)? as f32;
6176 let y = self.arg_num(&args, 1, 0.)? as f32;
6177 let w0 = self.arg_num(&args, 2, 160.)? as f32;
6178 let h0 = self.arg_num(&args, 3, 60.)? as f32;
6179 let levels = self.arg_list_f32(&args, 4);
6180 let th = self.ui_theme;
6181 let fill = self.color_at(&args, 5, th.primary);
6182 self.draw_ui(&ling_ui::widgets::vu(x, y, w0, h0, &levels, fill, th.warn));
6183 return Ok(Value::Unit);
6184 },
6185 #[cfg(not(target_arch = "wasm32"))]
6186 "ui_spark" | "迷你图" | "スパークライン" | "스파크라인" | "กราฟจิ๋ว" =>
6187 {
6188 let x = self.arg_num(&args, 0, 0.)? as f32;
6189 let y = self.arg_num(&args, 1, 0.)? as f32;
6190 let w0 = self.arg_num(&args, 2, 160.)? as f32;
6191 let h0 = self.arg_num(&args, 3, 40.)? as f32;
6192 let vals = self.arg_list_f32(&args, 4);
6193 let th = self.ui_theme;
6194 let line = self.color_at(&args, 5, th.accent);
6195 self.draw_ui(&ling_ui::widgets::spark(x, y, w0, h0, &vals, line));
6196 return Ok(Value::Unit);
6197 },
6198 #[cfg(not(target_arch = "wasm32"))]
6199 "ui_battery" | "电池" | "バッテリー" | "배터리" | "แบตเตอรี่" =>
6200 {
6201 let x = self.arg_num(&args, 0, 0.)? as f32;
6202 let y = self.arg_num(&args, 1, 0.)? as f32;
6203 let w0 = self.arg_num(&args, 2, 50.)? as f32;
6204 let h0 = self.arg_num(&args, 3, 22.)? as f32;
6205 let val = self.arg_num(&args, 4, 1.)? as f32;
6206 let max = self.arg_num(&args, 5, 1.)? as f32;
6207 let th = self.ui_theme;
6208 let fill = self.color_at(&args, 6, th.accent);
6209 self.draw_ui(&ling_ui::widgets::battery(
6210 x,
6211 y,
6212 w0,
6213 h0,
6214 val / max.max(1e-6),
6215 fill,
6216 th.track,
6217 th.warn,
6218 ));
6219 return Ok(Value::Unit);
6220 },
6221
6222 #[cfg(not(target_arch = "wasm32"))]
6224 "ui_button" | "按钮" | "ボタン" | "버튼" | "ปุ่ม" => {
6225 let x = self.arg_num(&args, 0, 0.)? as f32;
6226 let y = self.arg_num(&args, 1, 0.)? as f32;
6227 let w0 = self.arg_num(&args, 2, 120.)? as f32;
6228 let h0 = self.arg_num(&args, 3, 40.)? as f32;
6229 let (mx, my, down) = self.mouse_now();
6230 let hover = ling_ui::holo::hit_rect(mx, my, x, y, w0, h0);
6231 let clicked = hover && down && !self.mouse_was_down;
6232 let th = self.ui_theme;
6233 let prim = self.color_at(&args, 4, th.primary);
6234 self.draw_ui(&ling_ui::widgets::button(
6235 x,
6236 y,
6237 w0,
6238 h0,
6239 hover,
6240 down && hover,
6241 prim,
6242 th.bg,
6243 ));
6244 return Ok(Value::Number(if clicked { 1.0 } else { 0.0 }));
6245 },
6246 #[cfg(not(target_arch = "wasm32"))]
6247 "ui_toggle" | "开关" | "トグル" | "토글" | "สวิตช์" => {
6248 let x = self.arg_num(&args, 0, 0.)? as f32;
6249 let y = self.arg_num(&args, 1, 0.)? as f32;
6250 let w0 = self.arg_num(&args, 2, 52.)? as f32;
6251 let h0 = self.arg_num(&args, 3, 24.)? as f32;
6252 let mut state = self.arg_num(&args, 4, 0.)? > 0.5;
6253 let (mx, my, down) = self.mouse_now();
6254 let hover = ling_ui::holo::hit_rect(mx, my, x, y, w0, h0);
6255 if hover && down && !self.mouse_was_down {
6256 state = !state;
6257 }
6258 let th = self.ui_theme;
6259 let on = self.color_at(&args, 5, th.accent);
6260 self.draw_ui(&ling_ui::widgets::toggle(x, y, w0, h0, state, on, th.track));
6261 return Ok(Value::Number(if state { 1.0 } else { 0.0 }));
6262 },
6263 #[cfg(not(target_arch = "wasm32"))]
6264 "ui_slider" | "滑块" | "スライダー" | "슬라이더" | "แถบเลื่อน" =>
6265 {
6266 let x = self.arg_num(&args, 0, 0.)? as f32;
6267 let y = self.arg_num(&args, 1, 0.)? as f32;
6268 let w0 = self.arg_num(&args, 2, 160.)? as f32;
6269 let mut val = self.arg_num(&args, 3, 0.)? as f32;
6270 let mn = self.arg_num(&args, 4, 0.)? as f32;
6271 let mx_ = self.arg_num(&args, 5, 1.)? as f32;
6272 let (mx, my, down) = self.mouse_now();
6273 let hover = ling_ui::holo::hit_rect(mx, my, x - 8.0, y - 10.0, w0 + 16.0, 20.0);
6274 if hover && down {
6275 let frac = ((mx - x) / w0).max(0.0).min(1.0);
6276 val = mn + (mx_ - mn) * frac;
6277 }
6278 let frac = ((val - mn) / (mx_ - mn).abs().max(1e-6)).max(0.0).min(1.0);
6279 let th = self.ui_theme;
6280 let fill = self.color_at(&args, 6, th.primary);
6281 self.draw_ui(&ling_ui::widgets::slider(
6282 x, y, w0, frac, hover, fill, th.track,
6283 ));
6284 return Ok(Value::Number(val as f64));
6285 },
6286 #[cfg(not(target_arch = "wasm32"))]
6287 "ui_checkbox" | "复选框" | "チェックボックス" | "체크박스" | "ช่องเลือก" =>
6288 {
6289 let x = self.arg_num(&args, 0, 0.)? as f32;
6290 let y = self.arg_num(&args, 1, 0.)? as f32;
6291 let s = self.arg_num(&args, 2, 20.)? as f32;
6292 let mut checked = self.arg_num(&args, 3, 0.)? > 0.5;
6293 let (mx, my, down) = self.mouse_now();
6294 let hover = ling_ui::holo::hit_rect(mx, my, x, y, s, s);
6295 if hover && down && !self.mouse_was_down {
6296 checked = !checked;
6297 }
6298 let th = self.ui_theme;
6299 let prim = self.color_at(&args, 4, th.primary);
6300 self.draw_ui(&ling_ui::widgets::checkbox(
6301 x, y, s, checked, hover, prim, th.track,
6302 ));
6303 return Ok(Value::Number(if checked { 1.0 } else { 0.0 }));
6304 },
6305 #[cfg(not(target_arch = "wasm32"))]
6306 "ui_tabs" | "标签页" | "タブ" | "탭" | "แท็บ" => {
6307 let x = self.arg_num(&args, 0, 0.)? as f32;
6308 let y = self.arg_num(&args, 1, 0.)? as f32;
6309 let w0 = self.arg_num(&args, 2, 240.)? as f32;
6310 let h0 = self.arg_num(&args, 3, 28.)? as f32;
6311 let count = self.arg_num(&args, 4, 3.)? as usize;
6312 let mut active = self.arg_num(&args, 5, 0.)? as i32;
6313 let (mx, my, down) = self.mouse_now();
6314 let mut hover = -1;
6315 if my >= y && my <= y + h0 && mx >= x && mx <= x + w0 && count > 0 {
6316 hover = (((mx - x) / (w0 / count as f32)) as i32)
6317 .max(0)
6318 .min(count as i32 - 1);
6319 if down && !self.mouse_was_down {
6320 active = hover;
6321 }
6322 }
6323 let th = self.ui_theme;
6324 let prim = self.color_at(&args, 6, th.primary);
6325 self.draw_ui(&ling_ui::widgets::tabs(
6326 x,
6327 y,
6328 w0,
6329 h0,
6330 count,
6331 active as usize,
6332 hover,
6333 prim,
6334 th.track,
6335 ));
6336 return Ok(Value::Number(active as f64));
6337 },
6338 #[cfg(not(target_arch = "wasm32"))]
6339 "ui_progress" | "进度" | "プログレス" | "진행바" | "ความคืบหน้า" =>
6340 {
6341 let x = self.arg_num(&args, 0, 0.)? as f32;
6342 let y = self.arg_num(&args, 1, 0.)? as f32;
6343 let w0 = self.arg_num(&args, 2, 200.)? as f32;
6344 let h0 = self.arg_num(&args, 3, 12.)? as f32;
6345 let frac = self.arg_num(&args, 4, 0.)? as f32;
6346 let th = self.ui_theme;
6347 let fill = self.color_at(&args, 5, th.accent);
6348 self.draw_ui(&ling_ui::widgets::progress(
6349 x, y, w0, h0, frac, fill, th.track,
6350 ));
6351 return Ok(Value::Unit);
6352 },
6353 #[cfg(not(target_arch = "wasm32"))]
6354 "ui_tooltip" | "提示框" | "ツールチップ" | "툴팁" | "คำแนะนำ" =>
6355 {
6356 let x = self.arg_num(&args, 0, 0.)? as f32;
6357 let y = self.arg_num(&args, 1, 0.)? as f32;
6358 let w0 = self.arg_num(&args, 2, 120.)? as f32;
6359 let h0 = self.arg_num(&args, 3, 28.)? as f32;
6360 let th = self.ui_theme;
6361 let prim = self.color_at(&args, 4, th.primary);
6362 self.draw_ui(&ling_ui::widgets::tooltip(x, y, w0, h0, prim, th.bg));
6363 return Ok(Value::Unit);
6364 },
6365 #[cfg(not(target_arch = "wasm32"))]
6366 "ui_stepper" | "步进器" | "ステッパー" | "스테퍼" | "ตัวปรับค่า" =>
6367 {
6368 let x = self.arg_num(&args, 0, 0.)? as f32;
6369 let y = self.arg_num(&args, 1, 0.)? as f32;
6370 let w0 = self.arg_num(&args, 2, 120.)? as f32;
6371 let h0 = self.arg_num(&args, 3, 28.)? as f32;
6372 let mut val = self.arg_num(&args, 4, 0.)? as f32;
6373 let step = self.arg_num(&args, 5, 1.)? as f32;
6374 let (mx, my, down) = self.mouse_now();
6375 let hm = ling_ui::holo::hit_rect(mx, my, x, y, h0, h0);
6376 let hp = ling_ui::holo::hit_rect(mx, my, x + w0 - h0, y, h0, h0);
6377 if down && !self.mouse_was_down {
6378 if hm {
6379 val -= step;
6380 }
6381 if hp {
6382 val += step;
6383 }
6384 }
6385 let th = self.ui_theme;
6386 let prim = self.color_at(&args, 6, th.primary);
6387 self.draw_ui(&ling_ui::widgets::stepper(
6388 x, y, w0, h0, hm, hp, prim, th.track,
6389 ));
6390 return Ok(Value::Number(val as f64));
6391 },
6392
6393 #[cfg(not(target_arch = "wasm32"))]
6395 "ui_healthbar" | "血条" | "体力バー" | "체력바" | "แถบพลังชีวิต" =>
6396 {
6397 let x = self.arg_num(&args, 0, 0.)? as f32;
6398 let y = self.arg_num(&args, 1, 0.)? as f32;
6399 let w0 = self.arg_num(&args, 2, 180.)? as f32;
6400 let h0 = self.arg_num(&args, 3, 16.)? as f32;
6401 let val = self.arg_num(&args, 4, 1.)? as f32;
6402 let max = self.arg_num(&args, 5, 1.)? as f32;
6403 let pulse = self.arg_num(&args, 6, 0.)? as f32;
6404 let th = self.ui_theme;
6405 let full = self.color_at(&args, 7, th.accent);
6406 self.draw_ui(&ling_ui::widgets::healthbar(
6407 x,
6408 y,
6409 w0,
6410 h0,
6411 val / max.max(1e-6),
6412 pulse,
6413 full,
6414 th.warn,
6415 th.track,
6416 ));
6417 return Ok(Value::Unit);
6418 },
6419 #[cfg(not(target_arch = "wasm32"))]
6420 "ui_cooldown" | "冷却" | "クールダウン" | "쿨다운" | "คูลดาวน์" =>
6421 {
6422 let cx = self.arg_num(&args, 0, 0.)? as f32;
6423 let cy = self.arg_num(&args, 1, 0.)? as f32;
6424 let r = self.arg_num(&args, 2, 28.)? as f32;
6425 let frac = self.arg_num(&args, 3, 0.)? as f32;
6426 let th = self.ui_theme;
6427 let fill = self.color_at(&args, 4, th.primary);
6428 self.draw_ui(&ling_ui::widgets::cooldown(cx, cy, r, frac, fill, th.track));
6429 return Ok(Value::Unit);
6430 },
6431 #[cfg(not(target_arch = "wasm32"))]
6432 "ui_counter" | "计数器" | "カウンター" | "카운터" | "ตัวนับ" => {
6433 let x = self.arg_num(&args, 0, 0.)? as f32;
6434 let y = self.arg_num(&args, 1, 0.)? as f32;
6435 let dw = self.arg_num(&args, 2, 14.)? as f32;
6436 let dh = self.arg_num(&args, 3, 24.)? as f32;
6437 let val = self.arg_num(&args, 4, 0.)? as i64;
6438 let digits = self.arg_num(&args, 5, 4.)? as usize;
6439 let th = self.ui_theme;
6440 let on = self.color_at(&args, 6, th.primary);
6441 let off = ling_ui::widgets::shade(th.track, 0.5);
6442 self.draw_ui(&ling_ui::widgets::counter(
6443 x, y, dw, dh, val, digits, on, off,
6444 ));
6445 return Ok(Value::Unit);
6446 },
6447 #[cfg(not(target_arch = "wasm32"))]
6448 "ui_minimap" | "小地图" | "ミニマップ" | "미니맵" | "แผนที่ย่อ" =>
6449 {
6450 let x = self.arg_num(&args, 0, 0.)? as f32;
6451 let y = self.arg_num(&args, 1, 0.)? as f32;
6452 let w0 = self.arg_num(&args, 2, 140.)? as f32;
6453 let h0 = self.arg_num(&args, 3, 140.)? as f32;
6454 let th = self.ui_theme;
6455 let prim = self.color_at(&args, 4, th.primary);
6456 self.draw_ui(&ling_ui::widgets::minimap(x, y, w0, h0, prim, th.bg));
6457 return Ok(Value::Unit);
6458 },
6459 #[cfg(not(target_arch = "wasm32"))]
6460 "ui_dpad" | "方向键" | "方向パッド" | "방향패드" | "ปุ่มทิศทาง" =>
6461 {
6462 let cx = self.arg_num(&args, 0, 0.)? as f32;
6463 let cy = self.arg_num(&args, 1, 0.)? as f32;
6464 let r = self.arg_num(&args, 2, 50.)? as f32;
6465 let (mx, my, down) = self.mouse_now();
6466 let mut dir = 0;
6467 if down {
6468 let (dx, dy) = (mx - cx, my - cy);
6469 if dx * dx + dy * dy <= r * r {
6470 if dx.abs() > dy.abs() {
6471 dir = if dx > 0.0 { 2 } else { 4 };
6472 } else {
6473 dir = if dy > 0.0 { 3 } else { 1 };
6474 }
6475 }
6476 }
6477 let th = self.ui_theme;
6478 let prim = self.color_at(&args, 3, th.primary);
6479 self.draw_ui(&ling_ui::widgets::dpad(cx, cy, r, dir, prim, th.track));
6480 return Ok(Value::Number(dir as f64));
6481 },
6482 #[cfg(not(target_arch = "wasm32"))]
6483 "ui_slotgrid" | "物品格" | "スロットグリッド" | "슬롯격자" | "ช่องไอเทม" =>
6484 {
6485 let x = self.arg_num(&args, 0, 0.)? as f32;
6486 let y = self.arg_num(&args, 1, 0.)? as f32;
6487 let cols = self.arg_num(&args, 2, 4.)? as usize;
6488 let rows = self.arg_num(&args, 3, 1.)? as usize;
6489 let cell = self.arg_num(&args, 4, 36.)? as f32;
6490 let sel = self.arg_num(&args, 5, -1.)? as i32;
6491 let th = self.ui_theme;
6492 let prim = self.color_at(&args, 6, th.primary);
6493 self.draw_ui(&ling_ui::widgets::slotgrid(
6494 x, y, cols, rows, cell, sel, prim, th.track,
6495 ));
6496 return Ok(Value::Unit);
6497 },
6498 #[cfg(not(target_arch = "wasm32"))]
6499 "ui_vignette" | "暗角" | "ビネット" | "비네트" | "ขอบมืด" => {
6500 let intensity = self.arg_num(&args, 0, 0.5)? as f32;
6501 let (w, h) = {
6502 let g = self.gfx.borrow();
6503 (g.width as f32, g.height as f32)
6504 };
6505 let th = self.ui_theme;
6506 let col = self.color_at(&args, 1, th.warn);
6507 self.draw_ui(&ling_ui::widgets::vignette(w, h, intensity, col));
6508 return Ok(Value::Unit);
6509 },
6510
6511 #[cfg(not(target_arch = "wasm32"))]
6513 "ui_gauge3d" | "立体仪表" | "立体ゲージ" | "입체게이지" | "มาตรวัด3มิติ" =>
6514 {
6515 let cx = self.arg_num(&args, 0, 0.)? as f32;
6516 let cy = self.arg_num(&args, 1, 0.)? as f32;
6517 let r = self.arg_num(&args, 2, 50.)? as f32;
6518 let val = self.arg_num(&args, 3, 0.)? as f32;
6519 let max = self.arg_num(&args, 4, 1.)? as f32;
6520 let spin = self.arg_num(&args, 5, 0.)? as f32;
6521 let th = self.ui_theme;
6522 let fill = self.color_at(&args, 6, th.primary);
6523 self.draw_ui(&ling_ui::widgets::gauge3d(
6524 cx,
6525 cy,
6526 r,
6527 val / max.max(1e-6),
6528 spin,
6529 fill,
6530 th.track,
6531 ));
6532 return Ok(Value::Unit);
6533 },
6534 #[cfg(not(target_arch = "wasm32"))]
6535 "ui_panel3d" | "立体面板" | "立体パネル" | "입체패널" | "แผง3มิติ" =>
6536 {
6537 let x = self.arg_num(&args, 0, 0.)? as f32;
6538 let y = self.arg_num(&args, 1, 0.)? as f32;
6539 let w0 = self.arg_num(&args, 2, 200.)? as f32;
6540 let h0 = self.arg_num(&args, 3, 120.)? as f32;
6541 let depth = self.arg_num(&args, 4, 14.)? as f32;
6542 let th = self.ui_theme;
6543 let prim = self.color_at(&args, 5, th.primary);
6544 self.draw_ui(&ling_ui::widgets::panel3d(x, y, w0, h0, depth, prim, th.bg));
6545 return Ok(Value::Unit);
6546 },
6547 #[cfg(not(target_arch = "wasm32"))]
6548 "ui_radar3d" | "立体雷达" | "立体レーダー" | "입체레이더" | "เรดาร์3มิติ" =>
6549 {
6550 let cx = self.arg_num(&args, 0, 0.)? as f32;
6551 let cy = self.arg_num(&args, 1, 0.)? as f32;
6552 let r = self.arg_num(&args, 2, 60.)? as f32;
6553 let tilt = self.arg_num(&args, 3, 0.9)? as f32;
6554 let sweep = self.arg_num(&args, 4, 0.)? as f32;
6555 let th = self.ui_theme;
6556 let prim = self.color_at(&args, 5, th.primary);
6557 self.draw_ui(&ling_ui::widgets::radar3d(
6558 cx, cy, r, tilt, sweep, prim, th.track,
6559 ));
6560 return Ok(Value::Unit);
6561 },
6562
6563 #[cfg(not(target_arch = "wasm32"))]
6565 "audio_blip" | "提示音" | "ビープ音" | "효과음" | "เสียงบี๊บ" =>
6566 {
6567 let freq = self.arg_num(&args, 0, 660.)? as f32;
6568 let dur = self.arg_num(&args, 1, 0.08)? as f32;
6569 let wave = Wave::from_name(&self.arg_str(&args, 2, "sine"));
6570 let amp = self.arg_num(&args, 3, 0.25)? as f32;
6571 if let Some(audio) = &self.audio {
6572 audio.blip(freq, amp, dur, wave);
6573 }
6574 return Ok(Value::Unit);
6575 },
6576 #[cfg(not(target_arch = "wasm32"))]
6577 "ui_sound" | "界面音" | "UI音" | "인터페이스음" | "เสียงปุ่ม" =>
6578 {
6579 let name = self.arg_str(&args, 0, "click");
6580 if let Some(audio) = &self.audio {
6581 match name.as_str() {
6582 "hover" => audio.blip(880.0, 0.10, 0.04, Wave::Sine),
6583 "confirm" => {
6584 audio.blip(660.0, 0.22, 0.07, Wave::Square);
6585 audio.blip(990.0, 0.18, 0.10, Wave::Square);
6586 },
6587 "error" => {
6588 audio.blip(180.0, 0.30, 0.16, Wave::Saw);
6589 audio.blip(140.0, 0.30, 0.18, Wave::Saw);
6590 },
6591 "toggle" => audio.blip(520.0, 0.22, 0.05, Wave::Triangle),
6592 "tick" => audio.blip(1500.0, 0.12, 0.02, Wave::Square),
6593 _ => audio.blip(720.0, 0.26, 0.05, Wave::Square), }
6595 }
6596 return Ok(Value::Unit);
6597 },
6598
6599 #[cfg(not(target_arch = "wasm32"))]
6607 "music_load" | "载入音乐" | "音楽読込" | "음악로드" | "โหลดเพลง" =>
6608 {
6609 let path = self.arg_str(&args, 0, "");
6610 let resolved = if std::path::Path::new(&path).exists() {
6611 path.clone()
6612 } else if let Some(d) = &self.source_dir {
6613 d.join(&path).to_string_lossy().into_owned()
6614 } else {
6615 path.clone()
6616 };
6617 match ling_music::load(&resolved) {
6618 Ok(t) => {
6619 let id = self.tracks.len();
6620 self.tracks.push(t);
6621 return Ok(Value::Number(id as f64));
6622 },
6623 Err(e) => {
6624 eprintln!("music_load failed ({path}): {e}");
6625 return Ok(Value::Number(-1.0));
6626 },
6627 }
6628 },
6629 #[cfg(not(target_arch = "wasm32"))]
6630 "music_duration" | "音乐时长" | "音楽長さ" | "음악길이" | "ความยาวเพลง" =>
6631 {
6632 let id = self.arg_num(&args, 0, 0.0)? as i64;
6633 let d = self
6634 .tracks
6635 .get(id as usize)
6636 .map(|t| t.duration)
6637 .unwrap_or(0.0);
6638 return Ok(Value::Number(d as f64));
6639 },
6640 #[cfg(not(target_arch = "wasm32"))]
6641 "music_bpm" | "节拍速度" | "テンポ" | "템포" | "จังหวะต่อนาที" =>
6642 {
6643 let id = self.arg_num(&args, 0, 0.0)? as i64;
6644 let b = self
6645 .tracks
6646 .get(id as usize)
6647 .map(|t| ling_music::analysis::bpm(&t.mono, t.rate))
6648 .unwrap_or(0.0);
6649 return Ok(Value::Number(b as f64));
6650 },
6651 #[cfg(not(target_arch = "wasm32"))]
6652 "music_key" | "调性" | "調性" | "조성" | "คีย์เพลง" => {
6653 let id = self.arg_num(&args, 0, 0.0)? as i64;
6654 let k = self
6655 .tracks
6656 .get(id as usize)
6657 .map(|t| ling_music::analysis::key_name(&t.mono, t.rate))
6658 .unwrap_or_default();
6659 return Ok(Value::Str(k));
6660 },
6661 #[cfg(not(target_arch = "wasm32"))]
6662 "music_onsets" | "音符起点" | "オンセット" | "온셋" | "จุดเริ่มเสียง" =>
6663 {
6664 let id = self.arg_num(&args, 0, 0.0)? as i64;
6665 let v = self
6666 .tracks
6667 .get(id as usize)
6668 .map(|t| ling_music::analysis::onsets(&t.mono, t.rate))
6669 .unwrap_or_default();
6670 return Ok(Value::List(
6671 v.into_iter().map(|x| Value::Number(x as f64)).collect(),
6672 ));
6673 },
6674 #[cfg(not(target_arch = "wasm32"))]
6675 "music_beat_grid" | "节拍网格" | "ビートグリッド" | "비트그리드" | "กริดจังหวะ" =>
6676 {
6677 let id = self.arg_num(&args, 0, 0.0)? as i64;
6678 let beats = self
6679 .tracks
6680 .get(id as usize)
6681 .map(|t| {
6682 let b = ling_music::analysis::bpm(&t.mono, t.rate);
6683 ling_music::analysis::beat_grid(&t.mono, t.rate, b)
6684 })
6685 .unwrap_or_default();
6686 return Ok(Value::List(
6687 beats.into_iter().map(|x| Value::Number(x as f64)).collect(),
6688 ));
6689 },
6690
6691 #[cfg(not(target_arch = "wasm32"))]
6693 "music_play" | "播放音乐" | "音楽再生" | "음악재생" | "เล่นเพลง" =>
6694 {
6695 let id = self.arg_num(&args, 0, 0.0)? as i64;
6696 if self.ensure_music() {
6697 let track = self
6698 .tracks
6699 .get(id as usize)
6700 .map(|t| (t.stereo.clone(), t.rate));
6701 if let (Some((st, rate)), Some(m)) = (track, &self.music) {
6702 m.set_track(st, rate);
6703 m.play();
6704 } else if let Some(m) = &self.music {
6705 m.play();
6706 }
6707 }
6708 return Ok(Value::Unit);
6709 },
6710 #[cfg(not(target_arch = "wasm32"))]
6711 "music_pause" | "暂停音乐" | "音楽一時停止" | "음악일시정지" | "หยุดเพลงชั่วคราว" =>
6712 {
6713 if let Some(m) = &self.music {
6714 m.pause();
6715 }
6716 return Ok(Value::Unit);
6717 },
6718 #[cfg(not(target_arch = "wasm32"))]
6719 "music_stop" | "停止音乐" | "音楽停止" | "음악정지" | "หยุดเพลง" =>
6720 {
6721 if let Some(m) = &self.music {
6722 m.stop();
6723 }
6724 return Ok(Value::Unit);
6725 },
6726 #[cfg(not(target_arch = "wasm32"))]
6727 "music_seek" | "定位音乐" | "音楽シーク" | "음악탐색" | "ค้นหาเพลง" =>
6728 {
6729 let sec = self.arg_num(&args, 0, 0.0)? as f32;
6730 if let Some(m) = &self.music {
6731 m.seek(sec);
6732 }
6733 return Ok(Value::Unit);
6734 },
6735 #[cfg(not(target_arch = "wasm32"))]
6736 "music_pos" | "音乐位置" | "音楽位置" | "음악위치" | "ตำแหน่งเพลง" =>
6737 {
6738 let p = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
6739 return Ok(Value::Number(p as f64));
6740 },
6741 #[cfg(not(target_arch = "wasm32"))]
6742 "music_volume" | "音乐音量" | "音楽音量" | "음악음량" | "ระดับเพลง" =>
6743 {
6744 let v = self.arg_num(&args, 0, 0.8)? as f32;
6745 if self.ensure_music() {
6746 if let Some(m) = &self.music {
6747 m.set_volume(v);
6748 }
6749 }
6750 return Ok(Value::Unit);
6751 },
6752
6753 #[cfg(not(target_arch = "wasm32"))]
6755 "music_patch" | "乐器音色" | "音色読込" | "악기패치" | "แพตช์เครื่องดนตรี" =>
6756 {
6757 let path = self.arg_str(&args, 0, "");
6758 let resolved = if std::path::Path::new(&path).exists() {
6759 path.clone()
6760 } else if let Some(d) = &self.source_dir {
6761 d.join(&path).to_string_lossy().into_owned()
6762 } else {
6763 path.clone()
6764 };
6765 if !self.ensure_music() {
6766 return Ok(Value::Number(-1.0));
6767 }
6768 match ling_music::patch::from_path(&resolved) {
6769 Ok(p) => {
6770 let id = self.music.as_ref().unwrap().add_patch(p);
6771 return Ok(Value::Number(id as f64));
6772 },
6773 Err(e) => {
6774 eprintln!("music_patch failed ({path}): {e}");
6775 return Ok(Value::Number(-1.0));
6776 },
6777 }
6778 },
6779 #[cfg(not(target_arch = "wasm32"))]
6780 "music_note" | "弹音符" | "音符演奏" | "음표연주" | "เล่นโน้ต" =>
6781 {
6782 let inst = self.arg_num(&args, 0, 0.0)? as usize;
6783 let midi = self.pitch_arg(&args, 1, 60);
6784 let dur = self.arg_num(&args, 2, 0.5)? as f32;
6785 let vel = self.arg_num(&args, 3, 0.9)? as f32;
6786 if self.ensure_music() {
6787 if let Some(m) = &self.music {
6788 m.note(inst, midi, vel, dur);
6789 }
6790 }
6791 return Ok(Value::Unit);
6792 },
6793 #[cfg(not(target_arch = "wasm32"))]
6794 "music_note_on" | "音符开始" | "音符オン" | "음표켜기" | "โน้ตเริ่ม" =>
6795 {
6796 let inst = self.arg_num(&args, 0, 0.0)? as usize;
6797 let midi = self.pitch_arg(&args, 1, 60);
6798 let vel = self.arg_num(&args, 2, 0.9)? as f32;
6799 if self.ensure_music() {
6800 if let Some(m) = &self.music {
6801 m.note_on(inst, midi, vel);
6802 }
6803 }
6804 return Ok(Value::Unit);
6805 },
6806 #[cfg(not(target_arch = "wasm32"))]
6807 "music_note_off" | "音符结束" | "音符オフ" | "음표끄기" | "โน้ตจบ" =>
6808 {
6809 let inst = self.arg_num(&args, 0, 0.0)? as usize;
6810 let midi = self.pitch_arg(&args, 1, 60);
6811 if let Some(m) = &self.music {
6812 m.note_off(inst, midi);
6813 }
6814 return Ok(Value::Unit);
6815 },
6816
6817 #[cfg(not(target_arch = "wasm32"))]
6819 "music_judge" | "判定" | "判定する" | "판정" | "ตัดสินจังหวะ" =>
6820 {
6821 let delta_ms = self.arg_num(&args, 0, 9999.0)? as f32;
6822 return Ok(Value::Number(
6823 ling_music::Grade::judge(delta_ms).index() as f64
6824 ));
6825 },
6826 #[cfg(not(target_arch = "wasm32"))]
6827 "music_grade_name" | "判定名" | "判定名称" | "판정이름" | "ชื่อการตัดสิน" =>
6828 {
6829 let idx = self.arg_num(&args, 0, 4.0)? as i32;
6830 return Ok(Value::Str(
6831 ling_music::Grade::from_index(idx).name().to_string(),
6832 ));
6833 },
6834
6835 #[cfg(not(target_arch = "wasm32"))]
6837 "music_lrc" | "载入歌词" | "歌詞読込" | "가사로드" | "โหลดเนื้อเพลง" =>
6838 {
6839 let path = self.arg_str(&args, 0, "");
6840 let resolved = if std::path::Path::new(&path).exists() {
6841 path.clone()
6842 } else if let Some(d) = &self.source_dir {
6843 d.join(&path).to_string_lossy().into_owned()
6844 } else {
6845 path.clone()
6846 };
6847 match std::fs::read_to_string(&resolved) {
6848 Ok(text) => {
6849 let id = self.lyrics.len();
6850 self.lyrics.push(ling_music::Lyrics::parse(&text));
6851 return Ok(Value::Number(id as f64));
6852 },
6853 Err(e) => {
6854 eprintln!("music_lrc failed ({path}): {e}");
6855 return Ok(Value::Number(-1.0));
6856 },
6857 }
6858 },
6859 #[cfg(not(target_arch = "wasm32"))]
6860 "music_lyric" | "当前歌词" | "現在歌詞" | "현재가사" | "เนื้อเพลงปัจจุบัน" =>
6861 {
6862 let id = self.arg_num(&args, 0, 0.0)? as i64;
6863 let t = self.arg_num(&args, 1, 0.0)? as f32;
6864 let line = self
6865 .lyrics
6866 .get(id as usize)
6867 .map(|l| l.line_at(t).to_string())
6868 .unwrap_or_default();
6869 return Ok(Value::Str(line));
6870 },
6871 #[cfg(not(target_arch = "wasm32"))]
6872 "music_mic_pitch" | "麦克风音高" | "マイク音程" | "마이크음정" | "ระดับเสียงไมค์" =>
6873 {
6874 let hz = if let Some(mic) = self.mic.as_ref() {
6875 let s = mic.latest_samples();
6876 let rate = mic.sample_rate();
6877 ling_music::pitch::detect(&s, rate).unwrap_or(0.0)
6878 } else {
6879 0.0
6880 };
6881 return Ok(Value::Number(hz as f64));
6882 },
6883 #[cfg(not(target_arch = "wasm32"))]
6884 "music_note_name" | "音名" | "音名称" | "음이름" | "ชื่อโน้ต" =>
6885 {
6886 let hz = self.arg_num(&args, 0, 0.0)? as f32;
6887 return Ok(Value::Str(ling_music::note::hz_to_name(hz)));
6888 },
6889 #[cfg(not(target_arch = "wasm32"))]
6890 "music_hz" | "音符频率" | "音符周波数" | "음표주파수" | "ความถี่โน้ต" =>
6891 {
6892 let midi = self.pitch_arg(&args, 0, 69);
6893 return Ok(Value::Number(
6894 ling_music::note::midi_to_hz(midi as f32) as f64
6895 ));
6896 },
6897 #[cfg(not(target_arch = "wasm32"))]
6898 "music_pitch_score" | "音准评分" | "音程スコア" | "음정점수" | "คะแนนเสียง" =>
6899 {
6900 let hz = self.arg_num(&args, 0, 0.0)? as f32;
6901 let target = self.arg_num(&args, 1, 0.0)? as f32;
6902 return Ok(Value::Number(
6903 ling_music::karaoke::pitch_score(hz, target) as f64
6904 ));
6905 },
6906
6907 #[cfg(not(target_arch = "wasm32"))]
6909 "music_midi_load" | "载入MIDI" | "MIDI読込" | "미디로드" | "โหลดมิดี" =>
6910 {
6911 let path = self.arg_str(&args, 0, "");
6912 let resolved = if std::path::Path::new(&path).exists() {
6913 path.clone()
6914 } else if let Some(d) = &self.source_dir {
6915 d.join(&path).to_string_lossy().into_owned()
6916 } else {
6917 path.clone()
6918 };
6919 match ling_music::midi::load(&resolved) {
6920 Ok(m) => {
6921 let id = self.midis.len();
6922 self.midis.push(m);
6923 return Ok(Value::Number(id as f64));
6924 },
6925 Err(e) => {
6926 eprintln!("music_midi_load failed ({path}): {e}");
6927 return Ok(Value::Number(-1.0));
6928 },
6929 }
6930 },
6931 #[cfg(not(target_arch = "wasm32"))]
6932 "music_midi_count" | "MIDI数量" | "MIDI数" | "미디수" | "จำนวนมิดี" =>
6933 {
6934 let id = self.arg_num(&args, 0, 0.0)? as i64;
6935 let n = self
6936 .midis
6937 .get(id as usize)
6938 .map(|m| m.notes.len())
6939 .unwrap_or(0);
6940 return Ok(Value::Number(n as f64));
6941 },
6942 #[cfg(not(target_arch = "wasm32"))]
6944 "music_midi_notes" | "MIDI音符" | "MIDIノート" | "미디음표" | "โน้ตมิดี" =>
6945 {
6946 let id = self.arg_num(&args, 0, 0.0)? as i64;
6947 let mut out = Vec::new();
6948 if let Some(m) = self.midis.get(id as usize) {
6949 for n in &m.notes {
6950 out.push(Value::Number(n.time as f64));
6951 out.push(Value::Number(n.midi as f64));
6952 }
6953 }
6954 return Ok(Value::List(out));
6955 },
6956 #[cfg(not(target_arch = "wasm32"))]
6958 "music_midi_bars" | "MIDI音条" | "MIDIバー" | "미디바" | "แท่งมิดี" =>
6959 {
6960 let id = self.arg_num(&args, 0, 0.0)? as i64;
6961 let mut out = Vec::new();
6962 if let Some(m) = self.midis.get(id as usize) {
6963 for n in &m.notes {
6964 out.push(Value::Number(n.time as f64));
6965 out.push(Value::Number(n.midi as f64));
6966 out.push(Value::Number(n.dur as f64));
6967 }
6968 }
6969 return Ok(Value::List(out));
6970 },
6971
6972 #[cfg(not(target_arch = "wasm32"))]
6974 "music_fft" | "音乐频谱" | "音楽スペクトル" | "음악스펙트럼" | "สเปกตรัมเพลง" =>
6975 {
6976 let id = self.arg_num(&args, 0, 0.0)? as i64;
6977 let nbands = self.arg_num(&args, 1, 16.0)? as usize;
6978 let pos = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
6979 if let Some(t) = self.tracks.get(id as usize) {
6980 let idx = (pos * t.rate as f32) as usize;
6981 let end = (idx + 2048).min(t.mono.len());
6982 if end > idx + 64 {
6983 self.fft.borrow_mut().push_samples(&t.mono[idx..end]);
6984 }
6985 }
6986 let bands = self.fft.borrow().freq_bands(nbands);
6987 return Ok(Value::List(
6988 bands.into_iter().map(|x| Value::Number(x as f64)).collect(),
6989 ));
6990 },
6991
6992 #[cfg(not(target_arch = "wasm32"))]
6994 "audio_sfx" | "音效" | "空間効果音" | "공간효과음" | "เสียงเอฟเฟกต์" =>
6995 {
6996 let x = self.arg_num(&args, 0, 0.0)? as f32;
6997 let y = self.arg_num(&args, 1, 0.0)? as f32;
6998 let z = self.arg_num(&args, 2, 0.0)? as f32;
6999 let w = self.arg_num(&args, 3, 1.0)? as f32;
7000 let freq = self.arg_num(&args, 4, 440.0)? as f32;
7001 let amp = self.arg_num(&args, 5, 0.3)? as f32;
7002 let dur = self.arg_num(&args, 6, 0.15)? as f32;
7003 let wave = Wave::from_name(&self.arg_str(&args, 7, "sine"));
7004 if let Some(a) = &self.audio {
7005 a.sfx(x, y, z, w, freq, amp, dur, wave);
7006 }
7007 return Ok(Value::Unit);
7008 },
7009 #[cfg(not(target_arch = "wasm32"))]
7011 "audio_sample_load" | "载入采样" | "サンプル読込" | "샘플로드" | "โหลดตัวอย่างเสียง" =>
7012 {
7013 let path = self.arg_str(&args, 0, "");
7014 let resolved = if std::path::Path::new(&path).exists() {
7015 path.clone()
7016 } else if let Some(d) = &self.source_dir {
7017 d.join(&path).to_string_lossy().into_owned()
7018 } else {
7019 path.clone()
7020 };
7021 match ling_music::load(&resolved) {
7022 Ok(t) => {
7023 if let Some(a) = &self.audio {
7024 return Ok(Value::Number(a.add_sample(t.mono, t.rate) as f64));
7025 }
7026 return Ok(Value::Number(-1.0));
7027 },
7028 Err(e) => {
7029 eprintln!("audio_sample_load failed ({path}): {e}");
7030 return Ok(Value::Number(-1.0));
7031 },
7032 }
7033 },
7034 #[cfg(not(target_arch = "wasm32"))]
7035 "audio_sample_play" | "播放采样" | "サンプル再生" | "샘플재생" | "เล่นตัวอย่างเสียง" =>
7036 {
7037 let id = self.arg_num(&args, 0, 0.0)? as usize;
7038 let x = self.arg_num(&args, 1, 0.0)? as f32;
7039 let y = self.arg_num(&args, 2, 0.0)? as f32;
7040 let z = self.arg_num(&args, 3, 0.0)? as f32;
7041 let w = self.arg_num(&args, 4, 1.0)? as f32;
7042 let vol = self.arg_num(&args, 5, 1.0)? as f32;
7043 let looping = self.arg_num(&args, 6, 0.0)? > 0.5;
7044 let v = self
7045 .audio
7046 .as_ref()
7047 .map(|a| a.play_sample(id, x, y, z, w, vol, looping))
7048 .unwrap_or(0);
7049 return Ok(Value::Number(v as f64));
7050 },
7051 #[cfg(not(target_arch = "wasm32"))]
7052 "audio_sample_stop" | "停止采样" | "サンプル停止" | "샘플정지" | "หยุดตัวอย่างเสียง" =>
7053 {
7054 let v = self.arg_num(&args, 0, 0.0)? as u32;
7055 if let Some(a) = &self.audio {
7056 a.stop_sample(v);
7057 }
7058 return Ok(Value::Unit);
7059 },
7060 #[cfg(not(target_arch = "wasm32"))]
7062 "audio_fx_delay" | "回声" | "ディレイ効果" | "딜레이" | "เสียงสะท้อน" =>
7063 {
7064 let time = self.arg_num(&args, 0, 0.3)? as f32;
7065 let fb = self.arg_num(&args, 1, 0.3)? as f32;
7066 let mix = self.arg_num(&args, 2, 0.3)? as f32;
7067 if let Some(a) = &self.audio {
7068 a.fx_delay(time, fb, mix);
7069 }
7070 return Ok(Value::Unit);
7071 },
7072 #[cfg(not(target_arch = "wasm32"))]
7073 "audio_fx_reverb" | "混响" | "リバーブ" | "리버브" | "เสียงก้อง" =>
7074 {
7075 let mix = self.arg_num(&args, 0, 0.3)? as f32;
7076 if let Some(a) = &self.audio {
7077 a.fx_reverb(mix);
7078 }
7079 return Ok(Value::Unit);
7080 },
7081 #[cfg(not(target_arch = "wasm32"))]
7082 "audio_fx_lowpass" | "低通滤波" | "ローパス" | "저역통과" | "กรองความถี่ต่ำ" =>
7083 {
7084 let cutoff = self.arg_num(&args, 0, 1.0)? as f32;
7085 if let Some(a) = &self.audio {
7086 a.fx_lowpass(cutoff);
7087 }
7088 return Ok(Value::Unit);
7089 },
7090
7091 #[cfg(not(target_arch = "wasm32"))]
7098 "soft_ball" | "软球" | "ソフトボール" | "소프트볼" | "ลูกบอลนุ่ม" =>
7099 {
7100 let x = self.arg_num(&args, 0, 0.)? as f32;
7101 let y = self.arg_num(&args, 1, 0.)? as f32;
7102 let z = self.arg_num(&args, 2, 0.)? as f32;
7103 let r = self.arg_num(&args, 3, 1.0)? as f32;
7104 let b = ling_physics::soft::SoftBody::sphere(
7105 ling_physics::Vec3::new(x, y, z),
7106 r,
7107 8,
7108 12,
7109 1.0,
7110 );
7111 let id = self.soft_bodies.len();
7112 self.soft_bodies.push(b);
7113 return Ok(Value::Number(id as f64));
7114 },
7115 #[cfg(not(target_arch = "wasm32"))]
7116 "soft_step" | "软体步进" | "ソフト更新" | "소프트스텝" | "ก้าวนุ่ม" =>
7117 {
7118 let id = self.arg_num(&args, 0, 0.)? as usize;
7119 let dt = self.arg_num(&args, 1, 0.016)? as f32;
7120 let gy = self.arg_num(&args, 2, 15.0)? as f32;
7121 if let Some(b) = self.soft_bodies.get_mut(id) {
7122 b.integrate(dt, ling_physics::Vec3::new(0.0, gy, 0.0), 4);
7123 }
7124 return Ok(Value::Unit);
7125 },
7126 #[cfg(not(target_arch = "wasm32"))]
7127 "soft_bounce" | "软体落地" | "ソフト着地" | "소프트바운스" | "เด้งนุ่ม" =>
7128 {
7129 let id = self.arg_num(&args, 0, 0.)? as usize;
7130 let fy = self.arg_num(&args, 1, 0.)? as f32;
7131 let rest = self.arg_num(&args, 2, 0.5)? as f32;
7132 if let Some(b) = self.soft_bodies.get_mut(id) {
7133 b.floor_collision(fy, rest);
7134 }
7135 return Ok(Value::Unit);
7136 },
7137 #[cfg(not(target_arch = "wasm32"))]
7138 "soft_contain" | "软体边界" | "ソフト箱" | "소프트경계" | "กล่องนุ่ม" =>
7139 {
7140 let id = self.arg_num(&args, 0, 0.)? as usize;
7141 let nx = self.arg_num(&args, 1, -5.)? as f32;
7142 let ny = self.arg_num(&args, 2, -5.)? as f32;
7143 let nz = self.arg_num(&args, 3, -5.)? as f32;
7144 let mx = self.arg_num(&args, 4, 5.)? as f32;
7145 let my = self.arg_num(&args, 5, 5.)? as f32;
7146 let mz = self.arg_num(&args, 6, 5.)? as f32;
7147 let rest = self.arg_num(&args, 7, 0.6)? as f32;
7148 if let Some(b) = self.soft_bodies.get_mut(id) {
7149 b.contain(
7150 ling_physics::Vec3::new(nx, ny, nz),
7151 ling_physics::Vec3::new(mx, my, mz),
7152 rest,
7153 );
7154 }
7155 return Ok(Value::Unit);
7156 },
7157 #[cfg(not(target_arch = "wasm32"))]
7158 "soft_kick" | "软体踢" | "ソフト衝撃" | "소프트킥" | "เตะนุ่ม" =>
7159 {
7160 let id = self.arg_num(&args, 0, 0.)? as usize;
7161 let dx = self.arg_num(&args, 1, 0.)? as f32;
7162 let dy = self.arg_num(&args, 2, 0.)? as f32;
7163 let dz = self.arg_num(&args, 3, 0.)? as f32;
7164 let s = self.arg_num(&args, 4, 0.1)? as f32;
7165 if let Some(b) = self.soft_bodies.get_mut(id) {
7166 b.kick(ling_physics::Vec3::new(dx, dy, dz), s);
7167 }
7168 return Ok(Value::Unit);
7169 },
7170 #[cfg(not(target_arch = "wasm32"))]
7173 "soft_spin" | "软体自旋" | "ソフト回転" | "소프트회전" | "หมุนนุ่ม" =>
7174 {
7175 let id = self.arg_num(&args, 0, 0.)? as usize;
7176 let ax = self.arg_num(&args, 1, 0.)? as f32;
7177 let ay = self.arg_num(&args, 2, 0.)? as f32;
7178 let az = self.arg_num(&args, 3, 0.)? as f32;
7179 let rate = self.arg_num(&args, 4, 0.1)? as f32;
7180 if let Some(b) = self.soft_bodies.get_mut(id) {
7181 b.spin(ling_physics::Vec3::new(ax, ay, az), rate);
7182 }
7183 return Ok(Value::Unit);
7184 },
7185 #[cfg(not(target_arch = "wasm32"))]
7186 "soft_deform" | "形变量" | "変形量" | "변형량" | "ความบิดเบี้ยว" =>
7187 {
7188 let id = self.arg_num(&args, 0, 0.)? as usize;
7189 let d = self
7190 .soft_bodies
7191 .get(id)
7192 .map(|b| b.deformation())
7193 .unwrap_or(0.0);
7194 return Ok(Value::Number(d as f64));
7195 },
7196 #[cfg(not(target_arch = "wasm32"))]
7199 "soft_angular_speed"
7200 | "软体角速"
7201 | "ソフト角速度"
7202 | "소프트각속도"
7203 | "ความเร็วเชิงมุมนุ่ม" => {
7204 let id = self.arg_num(&args, 0, 0.)? as usize;
7205 let w = self
7206 .soft_bodies
7207 .get(id)
7208 .map(|b| b.angular_speed())
7209 .unwrap_or(0.0);
7210 return Ok(Value::Number(w as f64));
7211 },
7212 #[cfg(not(target_arch = "wasm32"))]
7213 "soft_centroid" | "软体质心" | "ソフト重心" | "소프트중심" | "จุดศูนย์กลางนุ่ม" =>
7214 {
7215 let id = self.arg_num(&args, 0, 0.)? as usize;
7216 let c = self
7217 .soft_bodies
7218 .get(id)
7219 .map(|b| b.centroid())
7220 .unwrap_or(ling_physics::Vec3::ZERO);
7221 return Ok(Value::List(vec![
7222 Value::Number(c.x as f64),
7223 Value::Number(c.y as f64),
7224 Value::Number(c.z as f64),
7225 ]));
7226 },
7227 #[cfg(not(target_arch = "wasm32"))]
7229 "soft_nodes" | "软体节点" | "ソフト節点" | "소프트노드" | "จุดนุ่ม" =>
7230 {
7231 let id = self.arg_num(&args, 0, 0.)? as usize;
7232 let mut out = Vec::new();
7233 if let Some(b) = self.soft_bodies.get(id) {
7234 for n in &b.nodes {
7235 out.push(Value::Number(n.pos.x as f64));
7236 out.push(Value::Number(n.pos.y as f64));
7237 out.push(Value::Number(n.pos.z as f64));
7238 }
7239 }
7240 return Ok(Value::List(out));
7241 },
7242
7243 #[cfg(not(target_arch = "wasm32"))]
7245 "rb_add" | "刚体添加" | "剛体追加" | "강체추가" | "เพิ่มวัตถุแข็ง" =>
7246 {
7247 let x = self.arg_num(&args, 0, 0.)? as f32;
7248 let y = self.arg_num(&args, 1, 0.)? as f32;
7249 let z = self.arg_num(&args, 2, 0.)? as f32;
7250 let mass = self.arg_num(&args, 3, 1.0)? as f32;
7251 let mut b =
7252 ling_physics::rigid::RigidBody::new(ling_physics::Vec3::new(x, y, z), mass);
7253 b.restitution = 0.6;
7254 return Ok(Value::Number(self.rigid_world.add(b) as f64));
7255 },
7256 #[cfg(not(target_arch = "wasm32"))]
7257 "rb_torque" | "扭矩" | "トルク" | "토크" | "แรงบิด" => {
7258 let i = self.arg_num(&args, 0, 0.)? as usize;
7259 let tx = self.arg_num(&args, 1, 0.)? as f32;
7260 let ty = self.arg_num(&args, 2, 0.)? as f32;
7261 let tz = self.arg_num(&args, 3, 0.)? as f32;
7262 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
7263 b.apply_torque(ling_physics::Vec3::new(tx, ty, tz));
7264 }
7265 return Ok(Value::Unit);
7266 },
7267 #[cfg(not(target_arch = "wasm32"))]
7268 "rb_spin" | "自旋" | "スピン" | "스핀" | "หมุน" => {
7269 let i = self.arg_num(&args, 0, 0.)? as usize;
7270 let wx = self.arg_num(&args, 1, 0.)? as f32;
7271 let wy = self.arg_num(&args, 2, 0.)? as f32;
7272 let wz = self.arg_num(&args, 3, 0.)? as f32;
7273 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
7274 b.apply_spin(ling_physics::Vec3::new(wx, wy, wz));
7275 }
7276 return Ok(Value::Unit);
7277 },
7278 #[cfg(not(target_arch = "wasm32"))]
7279 "rb_impulse" | "刚体冲量" | "剛体インパルス" | "강체충격" | "แรงดลแข็ง" =>
7280 {
7281 let i = self.arg_num(&args, 0, 0.)? as usize;
7282 let ix = self.arg_num(&args, 1, 0.)? as f32;
7283 let iy = self.arg_num(&args, 2, 0.)? as f32;
7284 let iz = self.arg_num(&args, 3, 0.)? as f32;
7285 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
7286 b.apply_impulse(ling_physics::Vec3::new(ix, iy, iz));
7287 }
7288 return Ok(Value::Unit);
7289 },
7290 #[cfg(not(target_arch = "wasm32"))]
7291 "rb_floor" | "刚体落地" | "剛体着地" | "강체바닥" | "พื้นแข็ง" =>
7292 {
7293 let i = self.arg_num(&args, 0, 0.)? as usize;
7294 let fy = self.arg_num(&args, 1, 0.)? as f32;
7295 let rest = self.arg_num(&args, 2, 0.6)? as f32;
7296 let fric = self.arg_num(&args, 3, 0.6)? as f32;
7297 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
7298 b.bounce_floor(fy, rest, fric);
7299 }
7300 return Ok(Value::Unit);
7301 },
7302 #[cfg(not(target_arch = "wasm32"))]
7303 "rb_gravity" | "刚体重力" | "剛体重力" | "강체중력" | "แรงโน้มถ่วงแข็ง" =>
7304 {
7305 let gx = self.arg_num(&args, 0, 0.)? as f32;
7306 let gy = self.arg_num(&args, 1, 9.81)? as f32;
7307 let gz = self.arg_num(&args, 2, 0.)? as f32;
7308 self.rigid_world.gravity = ling_physics::Vec3::new(gx, gy, gz);
7309 return Ok(Value::Unit);
7310 },
7311 #[cfg(not(target_arch = "wasm32"))]
7312 "rb_step" | "刚体步进" | "剛体更新" | "강체스텝" | "ก้าวแข็ง" =>
7313 {
7314 let dt = self.arg_num(&args, 0, 0.016)? as f32;
7315 self.rigid_world.step(dt);
7316 return Ok(Value::Unit);
7317 },
7318 #[cfg(not(target_arch = "wasm32"))]
7319 "rb_pos" | "刚体位置" | "剛体位置" | "강체위치" | "ตำแหน่งแข็ง" =>
7320 {
7321 let i = self.arg_num(&args, 0, 0.)? as usize;
7322 let p = self
7323 .rigid_world
7324 .bodies
7325 .get(i)
7326 .map(|b| b.pos)
7327 .unwrap_or(ling_physics::Vec3::ZERO);
7328 return Ok(Value::List(vec![
7329 Value::Number(p.x as f64),
7330 Value::Number(p.y as f64),
7331 Value::Number(p.z as f64),
7332 ]));
7333 },
7334 #[cfg(not(target_arch = "wasm32"))]
7335 "rb_rot" | "刚体旋转" | "剛体回転" | "강체회전" | "การหมุนแข็ง" =>
7336 {
7337 let i = self.arg_num(&args, 0, 0.)? as usize;
7338 let q = self
7339 .rigid_world
7340 .bodies
7341 .get(i)
7342 .map(|b| b.orientation)
7343 .unwrap_or(ling_physics::Quat::IDENTITY);
7344 return Ok(Value::List(vec![
7345 Value::Number(q.x as f64),
7346 Value::Number(q.y as f64),
7347 Value::Number(q.z as f64),
7348 Value::Number(q.w as f64),
7349 ]));
7350 },
7351
7352 #[cfg(not(target_arch = "wasm32"))]
7354 "mesh_load" | "โหลดเมช" | "载入网格" | "メッシュ読込" | "메시로드" =>
7355 {
7356 let path = self.arg_str(&args, 0, "");
7357 let resolved = if std::path::Path::new(&path).exists() {
7358 path.clone()
7359 } else if let Some(d) = &self.source_dir {
7360 d.join(&path).to_string_lossy().into_owned()
7361 } else {
7362 path.clone()
7363 };
7364 let bytes = match std::fs::read(&resolved) {
7365 Ok(b) => b,
7366 Err(e) => {
7367 eprintln!("mesh_load failed ({path}): {e}");
7368 return Ok(Value::Number(-1.0));
7369 },
7370 };
7371 if bytes.len() < 16 || &bytes[0..4] != b"LMSH" {
7372 eprintln!("mesh_load: bad header ({path})");
7373 return Ok(Value::Number(-1.0));
7374 }
7375 let rd4 =
7376 |o: usize| -> [u8; 4] { [bytes[o], bytes[o + 1], bytes[o + 2], bytes[o + 3]] };
7377 let height = f32::from_le_bytes(rd4(8));
7378 let ntri = u32::from_le_bytes(rd4(12)) as usize;
7379 let need = 16usize.saturating_add(ntri.saturating_mul(9 * 4 + 3));
7380 if bytes.len() < need {
7381 eprintln!("mesh_load: truncated ({path})");
7382 return Ok(Value::Number(-1.0));
7383 }
7384 let mut pos = Vec::with_capacity(ntri * 3);
7385 let mut col = Vec::with_capacity(ntri);
7386 let mut off = 16usize;
7387 for _ in 0..ntri {
7388 for _k in 0..3 {
7389 let x = f32::from_le_bytes(rd4(off));
7390 let y = f32::from_le_bytes(rd4(off + 4));
7391 let z = f32::from_le_bytes(rd4(off + 8));
7392 off += 12;
7393 pos.push([x, y, z]);
7394 }
7395 col.push([bytes[off], bytes[off + 1], bytes[off + 2]]);
7396 off += 3;
7397 }
7398 eprintln!("mesh_load: {} ({} tris, h={:.2})", path, ntri, height);
7399 let id = self.meshes.len();
7400 self.meshes
7401 .push(crate::gfx::shapes::ColorMesh { pos, col, height });
7402 return Ok(Value::Number(id as f64));
7403 },
7404 #[cfg(not(target_arch = "wasm32"))]
7405 "mesh_draw" | "วาดเมชสี" | "绘制网格" | "メッシュ描画" | "메시그리기" =>
7406 {
7407 let id = self.arg_num(&args, 0, 0.)? as usize;
7409 let cx = self.arg_num(&args, 1, 0.)? as f32;
7410 let cy = self.arg_num(&args, 2, 0.)? as f32;
7411 let cz = self.arg_num(&args, 3, 0.)? as f32;
7412 let sc = self.arg_num(&args, 4, 1.)? as f32;
7413 let yaw = self.arg_num(&args, 5, 0.)? as f32;
7414 let sway = self.arg_num(&args, 6, 0.)? as f32;
7415 let arm = self.arg_num(&args, 7, 0.)? as f32;
7416 let lean = self.arg_num(&args, 8, 0.)? as f32;
7417 let leg = self.arg_num(&args, 9, 0.)? as f32;
7418 let tuck = self.arg_num(&args, 10, 0.)? as f32;
7419 if id < self.meshes.len() {
7420 let m = &self.meshes[id];
7421 let mut gfx = self.gfx.borrow_mut();
7422 gfx.draw_color_mesh(m, cx, cy, cz, sc, yaw, sway, arm, lean, leg, tuck);
7423 }
7424 return Ok(Value::Unit);
7425 },
7426
7427 #[cfg(not(target_arch = "wasm32"))]
7429 "liquid_new" | "新建液体" | "液体新規" | "액체생성" | "สร้างของเหลว" =>
7430 {
7431 let w = self.arg_num(&args, 0, 64.)? as usize;
7432 let h = self.arg_num(&args, 1, 64.)? as usize;
7433 let id = self.liquids.len();
7434 self.liquids
7435 .push(ling_physics::liquid::LiquidGrid::new(w, h));
7436 return Ok(Value::Number(id as f64));
7437 },
7438 #[cfg(not(target_arch = "wasm32"))]
7439 "liquid_set_colors" | "液体颜色" | "液体配色" | "액체색상" | "สีของเหลว" =>
7440 {
7441 let id = self.arg_num(&args, 0, 0.)? as usize;
7442 let wr = self.arg_num(&args, 1, 40.)? as f32;
7443 let wg = self.arg_num(&args, 2, 110.)? as f32;
7444 let wb = self.arg_num(&args, 3, 235.)? as f32;
7445 let or_ = self.arg_num(&args, 4, 240.)? as f32;
7446 let og = self.arg_num(&args, 5, 175.)? as f32;
7447 let ob = self.arg_num(&args, 6, 45.)? as f32;
7448 if let Some(g) = self.liquids.get_mut(id) {
7449 g.set_colors(wr, wg, wb, or_, og, ob);
7450 }
7451 return Ok(Value::Unit);
7452 },
7453 #[cfg(not(target_arch = "wasm32"))]
7454 "liquid_splat" | "液体注入" | "液体追加" | "액체분사" | "หยดของเหลว" =>
7455 {
7456 let id = self.arg_num(&args, 0, 0.)? as usize;
7457 let x = self.arg_num(&args, 1, 0.)? as f32;
7458 let y = self.arg_num(&args, 2, 0.)? as f32;
7459 let kind = self.arg_num(&args, 3, 0.)? as i32;
7460 let amt = self.arg_num(&args, 4, 1.0)? as f32;
7461 let rad = self.arg_num(&args, 5, 4.0)? as f32;
7462 if let Some(g) = self.liquids.get_mut(id) {
7463 g.splat(x, y, kind, amt, rad);
7464 }
7465 return Ok(Value::Unit);
7466 },
7467 #[cfg(not(target_arch = "wasm32"))]
7468 "liquid_gravity" | "液体重力" | "液体重力ベクトル" | "액체중력" | "แรงโน้มถ่วงเหลว" =>
7469 {
7470 let id = self.arg_num(&args, 0, 0.)? as usize;
7471 let gx = self.arg_num(&args, 1, 0.)? as f32;
7472 let gy = self.arg_num(&args, 2, 60.)? as f32;
7473 if let Some(g) = self.liquids.get_mut(id) {
7474 g.set_gravity(gx, gy);
7475 }
7476 return Ok(Value::Unit);
7477 },
7478 #[cfg(not(target_arch = "wasm32"))]
7479 "liquid_step" | "液体步进" | "液体更新" | "액체스텝" | "ก้าวของเหลว" =>
7480 {
7481 let id = self.arg_num(&args, 0, 0.)? as usize;
7482 let dt = self.arg_num(&args, 1, 0.016)? as f32;
7483 if let Some(g) = self.liquids.get_mut(id) {
7484 g.step(dt);
7485 }
7486 return Ok(Value::Unit);
7487 },
7488 #[cfg(not(target_arch = "wasm32"))]
7490 "liquid_rainbow" | "液体彩虹" | "液体虹" | "액체무지개" | "ของเหลวสายรุ้ง" =>
7491 {
7492 let id = self.arg_num(&args, 0, 0.)? as usize;
7493 let on = self.arg_num(&args, 1, 1.0)? > 0.5;
7494 if let Some(g) = self.liquids.get_mut(id) {
7495 g.rainbow = on;
7496 }
7497 return Ok(Value::Unit);
7498 },
7499 #[cfg(not(target_arch = "wasm32"))]
7501 "liquid_mix" | "液体混合" | "液体混合度" | "액체혼합" | "การผสมของเหลว" =>
7502 {
7503 let id = self.arg_num(&args, 0, 0.)? as usize;
7504 let m = self.liquids.get(id).map(|g| g.mix_amount()).unwrap_or(0.0);
7505 return Ok(Value::Number(m as f64));
7506 },
7507 #[cfg(not(target_arch = "wasm32"))]
7509 "liquid_draw" | "绘制液体" | "液体描画" | "액체그리기" | "วาดของเหลว" =>
7510 {
7511 let id = self.arg_num(&args, 0, 0.)? as usize;
7512 let sx = self.arg_num(&args, 1, 0.)? as i32;
7513 let sy = self.arg_num(&args, 2, 0.)? as i32;
7514 let scale = (self.arg_num(&args, 3, 4.)? as i32).max(1);
7515 if id < self.liquids.len() {
7516 let (gw, gh) = {
7517 let g = &self.liquids[id];
7518 (g.w, g.h)
7519 };
7520 let mut gfx = self.gfx.borrow_mut();
7521 let (w, h) = (gfx.width as i32, gfx.height as i32);
7522 let g = &self.liquids[id];
7523 for cy in 0..gh {
7524 for cx in 0..gw {
7525 let col = g.sample_rgb(cx, cy);
7526 let bx = sx + cx as i32 * scale;
7527 let by = sy + cy as i32 * scale;
7528 for dy in 0..scale {
7529 for dx in 0..scale {
7530 let px = bx + dx;
7531 let py = by + dy;
7532 if px >= 0 && py >= 0 && px < w && py < h {
7533 gfx.buffer[(py * w + px) as usize] = col;
7534 }
7535 }
7536 }
7537 }
7538 }
7539 }
7540 return Ok(Value::Unit);
7541 },
7542 #[cfg(not(target_arch = "wasm32"))]
7545 "liquid_draw_surface" | "液体贴面" | "液体曲面" | "액체곡면" | "ของเหลวบนพื้นผิว" =>
7546 {
7547 let id = self.arg_num(&args, 0, 0.)? as usize;
7548 let kind = self.arg_num(&args, 1, 1.)? as i32;
7549 let cx = self.arg_num(&args, 2, 0.)? as f32;
7550 let cy = self.arg_num(&args, 3, 0.)? as f32;
7551 let cz = self.arg_num(&args, 4, 0.)? as f32;
7552 let radius = self.arg_num(&args, 5, 2.0)? as f32;
7553 let height = self.arg_num(&args, 6, 3.0)? as f32;
7554 if id < self.liquids.len() {
7555 let (gw, gh) = {
7556 let g = &self.liquids[id];
7557 (g.w, g.h)
7558 };
7559 let mut gfx = self.gfx.borrow_mut();
7560 let (w, h, add) = (gfx.width, gfx.height, gfx.blend == 1);
7561 let cam = gfx.camera.clone();
7562 let near = -cam.zdist + 0.05;
7563 let g = &self.liquids[id];
7564 let tau = std::f32::consts::TAU;
7565 let pi = std::f32::consts::PI;
7566 let sp = |u: f32, v: f32| -> [f32; 3] {
7568 if kind == 0 {
7569 [
7570 cx + (u - 0.5) * 2.0 * radius,
7571 cy,
7572 cz + (v - 0.5) * 2.0 * radius,
7573 ]
7574 } else if kind == 2 {
7575 let th = u * tau;
7576 [
7577 cx + th.cos() * radius,
7578 cy + (v - 0.5) * height,
7579 cz + th.sin() * radius,
7580 ]
7581 } else if kind == 3 {
7582 let th = u * tau;
7583 let rr = radius * (1.0 - v);
7584 [
7585 cx + th.cos() * rr,
7586 cy + (v - 0.5) * height,
7587 cz + th.sin() * rr,
7588 ]
7589 } else if kind == 4 {
7590 let th = u * tau;
7591 let ph = v * pi * 0.5;
7592 [
7593 cx + ph.sin() * th.cos() * radius,
7594 cy - ph.cos() * radius,
7595 cz + ph.sin() * th.sin() * radius,
7596 ]
7597 } else {
7598 let th = u * tau;
7599 let ph = v * pi;
7600 [
7601 cx + ph.sin() * th.cos() * radius,
7602 cy + ph.cos() * radius,
7603 cz + ph.sin() * th.sin() * radius,
7604 ]
7605 }
7606 };
7607 let nrm = |u: f32, v: f32| -> [f32; 3] {
7608 if kind == 0 {
7609 [0.0, -1.0, 0.0]
7610 } else if kind == 2 {
7611 let th = u * tau;
7612 [th.cos(), 0.0, th.sin()]
7613 } else if kind == 3 {
7614 let th = u * tau;
7615 let s = (radius / height.max(0.01)).atan();
7616 [th.cos() * s.cos(), s.sin(), th.sin() * s.cos()]
7617 } else if kind == 4 {
7618 let th = u * tau;
7619 let ph = v * pi * 0.5;
7620 [ph.sin() * th.cos(), -ph.cos(), ph.sin() * th.sin()]
7621 } else {
7622 let th = u * tau;
7623 let ph = v * pi;
7624 [ph.sin() * th.cos(), ph.cos(), ph.sin() * th.sin()]
7625 }
7626 };
7627 let gwf = gw as f32;
7628 let ghf = gh as f32;
7629 let mut cyc = 0usize;
7630 while cyc < gh {
7631 let mut cxc = 0usize;
7632 while cxc < gw {
7633 let uc = (cxc as f32 + 0.5) / gwf;
7635 let vc = (cyc as f32 + 0.5) / ghf;
7636 let c = sp(uc, vc);
7637 let n = nrm(uc, vc);
7638 let dc = cam.depth(c[0], c[1], c[2]);
7639 if dc > near {
7640 let cull = kind != 0
7641 && cam.depth(
7642 c[0] + n[0] * 0.06,
7643 c[1] + n[1] * 0.06,
7644 c[2] + n[2] * 0.06,
7645 ) > dc;
7646 if !cull {
7647 let u0 = cxc as f32 / gwf;
7649 let u1 = (cxc + 1) as f32 / gwf;
7650 let v0 = cyc as f32 / ghf;
7651 let v1 = (cyc + 1) as f32 / ghf;
7652 let q = [sp(u0, v0), sp(u1, v0), sp(u1, v1), sp(u0, v1)];
7653 let mut poly: Vec<[f32; 2]> = Vec::with_capacity(5);
7654 let mut ok = true;
7655 for p in &q {
7656 if cam.depth(p[0], p[1], p[2]) <= near {
7657 ok = false;
7658 break;
7659 }
7660 let (sx, sy, _) = cam.project(p[0], p[1], p[2]);
7661 poly.push([sx, sy]);
7662 }
7663 if ok {
7664 let p0 = poly[0];
7665 poly.push(p0);
7666 let col = g.sample_rgb(cxc, cyc);
7667 crate::gfx::raster::fill_contours_aa(
7668 &mut gfx.buffer,
7669 w,
7670 h,
7671 col,
7672 add,
7673 std::slice::from_ref(&poly),
7674 );
7675 }
7676 }
7677 }
7678 cxc += 1;
7679 }
7680 cyc += 1;
7681 }
7682 }
7683 return Ok(Value::Unit);
7684 },
7685 #[cfg(not(target_arch = "wasm32"))]
7688 "sparkle" | "闪光" | "きらめき" | "반짝임" | "ประกาย" => {
7689 let x = self.arg_num(&args, 0, 0.)? as f32;
7690 let y = self.arg_num(&args, 1, 0.)? as f32;
7691 let ww = self.arg_num(&args, 2, 200.)? as f32;
7692 let hh = self.arg_num(&args, 3, 200.)? as f32;
7693 let count = self.arg_num(&args, 4, 40.)? as i32;
7694 let t = self.arg_num(&args, 5, 0.)? as f32;
7695 let mut gfx = self.gfx.borrow_mut();
7696 let (w, h, add, color) = (gfx.width, gfx.height, gfx.blend == 1, gfx.color);
7697 let (cr, cg, cb) = (
7698 (color >> 16 & 0xFF) as f32,
7699 (color >> 8 & 0xFF) as f32,
7700 (color & 0xFF) as f32,
7701 );
7702 let mut n = 0i32;
7703 while n < count {
7704 let hsh = (n as u32).wrapping_mul(2654435761).wrapping_add(0x9E3779B9);
7705 let u = ((hsh >> 8) & 1023) as f32 / 1023.0;
7706 let v = ((hsh >> 18) & 1023) as f32 / 1023.0;
7707 let phase = (hsh & 255) as f32 / 255.0;
7708 let tw = (t * 3.0 + phase * 6.2831 + n as f32).sin() * 0.5 + 0.5;
7709 let sz = 1.5 + tw * 5.0;
7710 let px = x + u * ww;
7711 let py = y + v * hh;
7712 let b = tw * tw; let col =
7714 (((cr * b) as u32) << 16) | (((cg * b) as u32) << 8) | ((cb * b) as u32);
7715 crate::gfx::raster::draw_line_aa(
7716 &mut gfx.buffer,
7717 w,
7718 h,
7719 col,
7720 add,
7721 px - sz,
7722 py,
7723 px + sz,
7724 py,
7725 );
7726 crate::gfx::raster::draw_line_aa(
7727 &mut gfx.buffer,
7728 w,
7729 h,
7730 col,
7731 add,
7732 px,
7733 py - sz,
7734 px,
7735 py + sz,
7736 );
7737 let d = sz * 0.55;
7738 crate::gfx::raster::draw_line_aa(
7739 &mut gfx.buffer,
7740 w,
7741 h,
7742 col,
7743 add,
7744 px - d,
7745 py - d,
7746 px + d,
7747 py + d,
7748 );
7749 crate::gfx::raster::draw_line_aa(
7750 &mut gfx.buffer,
7751 w,
7752 h,
7753 col,
7754 add,
7755 px - d,
7756 py + d,
7757 px + d,
7758 py - d,
7759 );
7760 n += 1;
7761 }
7762 return Ok(Value::Unit);
7763 },
7764
7765 #[cfg(not(target_arch = "wasm32"))]
7771 "dialog_show" | "对话显示" | "会話表示" | "대화표시" | "แสดงบทสนทนา" =>
7772 {
7773 let text = self.arg_str(&args, 0, "");
7774 let cps = self.arg_num(&args, 1, 32.0)? as f32;
7775 self.dialog = Some(ling_game::dialog::Dialog::new(&text, cps));
7776 return Ok(Value::Unit);
7777 },
7778 #[cfg(not(target_arch = "wasm32"))]
7779 "dialog_step" | "对话步进" | "会話更新" | "대화스텝" | "ก้าวบทสนทนา" =>
7780 {
7781 let dt = self.arg_num(&args, 0, 0.016)? as f32;
7782 if let Some(d) = self.dialog.as_mut() {
7783 d.update(dt);
7784 }
7785 return Ok(Value::Unit);
7786 },
7787 #[cfg(not(target_arch = "wasm32"))]
7788 "dialog_advance" | "对话推进" | "会話送り" | "대화진행" | "เลื่อนบทสนทนา" =>
7789 {
7790 if let Some(d) = self.dialog.as_mut() {
7791 d.advance();
7792 }
7793 return Ok(Value::Unit);
7794 },
7795 #[cfg(not(target_arch = "wasm32"))]
7796 "dialog_active" | "对话激活" | "会話中" | "대화중" | "บทสนทนาทำงาน" =>
7797 {
7798 let a = self
7799 .dialog
7800 .as_ref()
7801 .map(|d| !d.is_closed())
7802 .unwrap_or(false);
7803 return Ok(Value::Bool(a));
7804 },
7805 #[cfg(not(target_arch = "wasm32"))]
7806 "dialog_typing" | "对话打字" | "会話タイプ中" | "대화타이핑" | "กำลังพิมพ์บทสนทนา" =>
7807 {
7808 use ling_game::dialog::Dialog;
7809
7810 let a = self
7811 .dialog
7812 .as_ref()
7813 .map(|d: &Dialog| !d.is_closed() && d.is_typing())
7814 .unwrap_or(false);
7815 return Ok(Value::Bool(a));
7816 },
7817 #[cfg(not(target_arch = "wasm32"))]
7818 "dialog_close" | "对话关闭" | "会話閉じる" | "대화닫기" | "ปิดบทสนทนา" =>
7819 {
7820 self.dialog = None;
7821 return Ok(Value::Unit);
7822 },
7823 #[cfg(not(target_arch = "wasm32"))]
7825 "dialog_color" | "对话颜色" | "会話色" | "대화색" | "สีบทสนทนา" =>
7826 {
7827 let role = (self.arg_num(&args, 0, 0.0)? as usize).min(3);
7828 let r = self.arg_num(&args, 1, 255.0)? as u32 & 0xFF;
7829 let g = self.arg_num(&args, 2, 255.0)? as u32 & 0xFF;
7830 let b = self.arg_num(&args, 3, 255.0)? as u32 & 0xFF;
7831 self.dialog_colors[role] = (r << 16) | (g << 8) | b;
7832 return Ok(Value::Unit);
7833 },
7834 #[cfg(not(target_arch = "wasm32"))]
7836 "dialog_draw" | "对话绘制" | "会話描画" | "대화그리기" | "วาดบทสนทนา" =>
7837 {
7838 let x = self.arg_num(&args, 0, 40.0)? as f32;
7839 let y = self.arg_num(&args, 1, 0.0)? as f32;
7840 let ww = self.arg_num(&args, 2, 720.0)? as f32;
7841 let hh = self.arg_num(&args, 3, 150.0)? as f32;
7842 let font = self.arg_num(&args, 4, -1.0)? as i64;
7843 let t = self.start_time.elapsed().as_secs_f32();
7844 self.render_dialog(x, y, ww, hh, font, t);
7845 return Ok(Value::Unit);
7846 },
7847
7848 #[cfg(not(target_arch = "wasm32"))]
7850 "text_poll" => {
7851 let keys = {
7852 let gfx = self.gfx.borrow();
7853 gfx.window
7854 .as_ref()
7855 .map(|w| w.get_keys_pressed(minifb::KeyRepeat::No))
7856 .unwrap_or_default()
7857 };
7858 for k in keys {
7859 if k == minifb::Key::Backspace {
7860 self.text_buffer.pop();
7861 } else if let Some(c) = key_char(k) {
7862 self.text_buffer.push(c);
7863 }
7864 }
7865 return Ok(Value::Str(self.text_buffer.clone()));
7866 },
7867 "text_get" => return Ok(Value::Str(self.text_buffer.clone())),
7868 "text_set" => {
7869 self.text_buffer = self.arg_str(&args, 0, "");
7870 return Ok(Value::Unit);
7871 },
7872 "text_clear" => {
7873 self.text_buffer.clear();
7874 return Ok(Value::Unit);
7875 },
7876 #[cfg(not(target_arch = "wasm32"))]
7878 "record_frame" => {
7879 let n = self.record_n;
7880 let (buf, w, h) = {
7881 let gfx = self.gfx.borrow();
7882 (gfx.buffer.clone(), gfx.width, gfx.height)
7883 };
7884 let _ = std::fs::create_dir_all("recordings");
7885 let mut out = Vec::with_capacity(w * h * 3 + 32);
7886 out.extend_from_slice(format!("P6\n{w} {h}\n255\n").as_bytes());
7887 for px in &buf {
7888 let p = *px;
7889 out.push((p >> 16) as u8);
7890 out.push((p >> 8) as u8);
7891 out.push(p as u8);
7892 }
7893 let _ = std::fs::write(format!("recordings/frame_{n:05}.ppm"), out);
7894 self.record_n += 1;
7895 return Ok(Value::Number(n as f64));
7896 },
7897 "record_count" => return Ok(Value::Number(self.record_n as f64)),
7898 #[cfg(not(target_arch = "wasm32"))]
7900 "screenshot" | "บันทึกภาพ" => {
7901 let mode = self.arg_str(&args, 0, "game");
7902 let (buf, w, h) = {
7903 let gfx = self.gfx.borrow();
7904 (gfx.buffer.clone(), gfx.width, gfx.height)
7905 };
7906 let _ = std::fs::create_dir_all("screenshots");
7907 let ts = std::time::SystemTime::now()
7908 .duration_since(std::time::UNIX_EPOCH)
7909 .map(|d| d.as_secs())
7910 .unwrap_or(0);
7911 let safe: String = mode
7912 .chars()
7913 .map(|c| if c.is_alphanumeric() { c } else { '_' })
7914 .collect();
7915 let path = format!("screenshots/ss_{ts}_{safe}_{w}x{h}.png");
7916 let mut rgb = Vec::with_capacity(w * h * 3);
7917 for px in &buf {
7918 let p = *px;
7919 rgb.push((p >> 16) as u8);
7920 rgb.push((p >> 8) as u8);
7921 rgb.push(p as u8);
7922 }
7923 if let Some(img) = image::RgbImage::from_raw(w as u32, h as u32, rgb) {
7924 let _ = img.save(&path);
7925 }
7926 return Ok(Value::Str(path));
7927 },
7928 #[cfg(not(target_arch = "wasm32"))]
7932 "mic_capture" => {
7933 if let Some(mic) = self.mic.as_ref() {
7934 let s = mic.latest_samples();
7935 self.mic_buffer.extend_from_slice(&s);
7936 let cap = 96_000usize; if self.mic_buffer.len() > cap {
7938 let drop = self.mic_buffer.len() - cap;
7939 self.mic_buffer.drain(0..drop);
7940 }
7941 }
7942 return Ok(Value::Number(self.mic_buffer.len() as f64));
7943 },
7944 #[cfg(not(target_arch = "wasm32"))]
7946 "mic_seed" => {
7947 let mut bytes = Vec::with_capacity(self.mic_buffer.len() * 4);
7948 for f in &self.mic_buffer {
7949 bytes.extend_from_slice(&f.to_le_bytes());
7950 }
7951 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(&bytes))));
7952 },
7953 #[cfg(not(target_arch = "wasm32"))]
7954 "mic_clear" => {
7955 self.mic_buffer.clear();
7956 return Ok(Value::Number(0.0));
7957 },
7958 #[cfg(not(target_arch = "wasm32"))]
7961 "flush_3d" | "render_3d" => {
7962 let mut gfx = self.gfx.borrow_mut();
7963 if !gfx.depth_queue.is_empty() {
7964 let w = gfx.width;
7965 let h = gfx.height;
7966 let dt = gfx.depth_test;
7967 let (bm, ba) = (gfx.blend, gfx.alpha);
7968 let queue = std::mem::take(&mut gfx.depth_queue);
7969 {
7970 let g = &mut *gfx;
7971 let z = if dt { Some(&mut g.depth_buf) } else { None };
7972 queue.flush(&mut g.buffer, z, w, h);
7973 }
7974 gfx.depth_queue.set_state(bm, ba); }
7976 return Ok(Value::Unit);
7977 },
7978
7979 #[cfg(not(target_arch = "wasm32"))]
7982 "screen_distort" | "บิดจอ" | "屏幕扭曲" | "画面歪み" | "화면왜곡" =>
7983 {
7984 let amount = self.arg_num(&args, 0, 8.0)? as f32;
7985 let t = self.arg_num(&args, 1, 0.0)? as f32;
7986 self.gfx.borrow_mut().distort(amount, t);
7987 return Ok(Value::Unit);
7988 },
7989
7990 "set_rim" | "设置边缘光" | "リム設定" | "림라이트" | "ตั้งขอบเรือง" =>
7991 {
7992 let s = self.arg_num(&args, 0, 0.6)? as f32;
7993 let r = self.arg_num(&args, 1, 115.)? as f32 / 255.0;
7994 let g = self.arg_num(&args, 2, 217.)? as f32 / 255.0;
7995 let b = self.arg_num(&args, 3, 255.)? as f32 / 255.0;
7996 let mut gfx = self.gfx.borrow_mut();
7997 gfx.shade.rim = s;
7998 gfx.shade.rim_color = [r, g, b];
7999 return Ok(Value::Unit);
8000 },
8001
8002 n if crate::gfx::shapes::canon(n).is_some() => {
8011 let kind = crate::gfx::shapes::canon(n).unwrap();
8012 let cx = self.arg_num(&args, 0, 0.)? as f32;
8013 let cy = self.arg_num(&args, 1, 0.)? as f32;
8014 let cz = self.arg_num(&args, 2, 0.)? as f32;
8015 let sx = self.arg_num(&args, 3, 1.)? as f32;
8016 let sy = self.arg_num(&args, 4, 1.)? as f32;
8017 let sz = self.arg_num(&args, 5, 1.)? as f32;
8018 let rx = self.arg_num(&args, 6, 0.)? as f32;
8019 let ry = self.arg_num(&args, 7, 0.)? as f32;
8020 let rz = self.arg_num(&args, 8, 0.)? as f32;
8021 let mode = self.arg_num(&args, 9, 0.)? as i32;
8022 let e0 = self.arg_num(&args, 10, 0.)? as f32;
8023 let e1 = self.arg_num(&args, 11, 0.)? as f32;
8024 let e2 = self.arg_num(&args, 12, 0.)? as f32;
8025 if let Some(mesh) = crate::gfx::shapes::build(
8026 kind,
8027 [cx, cy, cz, sx, sy, sz, rx, ry, rz],
8028 e0,
8029 e1,
8030 e2,
8031 ) {
8032 let mut gfx = self.gfx.borrow_mut();
8033 gfx.emit_mesh(&mesh, mode);
8034 }
8035 return Ok(Value::Unit);
8036 },
8037
8038 _ => {},
8039 }
8040
8041 if let Some(field_names) = self.structs.get(name).cloned() {
8043 if args.len() != field_names.len() {
8044 return Err(EvalErr::from(format!(
8045 "{name} expects {} field(s), got {}",
8046 field_names.len(),
8047 args.len()
8048 )));
8049 }
8050 let fields = field_names.into_iter().zip(args).collect();
8051 return Ok(Value::Struct { name: name.to_string(), fields });
8052 }
8053
8054 if let Some((enum_name, arity)) = self.enum_variants.get(name).cloned() {
8056 if args.len() != arity {
8057 return Err(EvalErr::from(format!(
8058 "{name} expects {arity} value(s), got {}",
8059 args.len()
8060 )));
8061 }
8062 let variant = name.rsplit("::").next().unwrap_or(name).to_string();
8063 return Ok(Value::Variant { enum_name, variant, payload: args });
8064 }
8065
8066 Err(EvalErr::from(format!("unknown function '{name}'")))
8067 }
8068
8069 fn call_value(&mut self, v: Value, args: Vec<Value>) -> EvalResult {
8070 match v {
8071 Value::Fn(params, body, mut captured) => {
8072 for (p, a) in params.iter().zip(args) {
8073 captured.insert(p.clone(), a);
8074 }
8075 match self.framed("<closure>", |me| me.exec_block(&body, &mut captured)) {
8076 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
8077 Err(EvalErr::Return(v)) => Ok(v),
8078 Err(e) => Err(e),
8079 }
8080 },
8081 other => Err(EvalErr::from(format!("cannot call {:?}", other))),
8082 }
8083 }
8084
8085 fn call_method(&self, recv: Value, method: &str, args: Vec<Value>) -> EvalResult {
8086 match (&recv, method) {
8087 (Value::Str(s), "is_empty" | "是空") => Ok(Value::Bool(s.is_empty())),
8088 (Value::Str(s), "len" | "长") => Ok(Value::Number(s.len() as f64)),
8089 (Value::Str(s), "to_string" | "转文") => Ok(Value::Str(s.clone())),
8090 (Value::Str(s), "contains" | "包含") => {
8091 if let Some(Value::Str(sub)) = args.first() {
8092 Ok(Value::Bool(s.contains(sub.as_str())))
8093 } else {
8094 Ok(Value::Bool(false))
8095 }
8096 },
8097 (Value::Str(s), "push_str" | "推_文") => {
8098 let mut s2 = s.clone();
8099 if let Some(Value::Str(a)) = args.first() {
8100 s2.push_str(a);
8101 }
8102 Ok(Value::Str(s2))
8103 },
8104 (Value::List(v), "len" | "长") => Ok(Value::Number(v.len() as f64)),
8105 (Value::List(v), "push" | "推") => {
8106 let mut v2 = v.clone();
8107 if let Some(a) = args.first() {
8108 v2.push(a.clone());
8109 }
8110 Ok(Value::List(v2))
8111 },
8112 (Value::Struct { fields, .. }, _) if args.is_empty() => fields
8114 .iter()
8115 .find(|(k, _)| k == method)
8116 .map(|(_, v)| v.clone())
8117 .ok_or_else(|| EvalErr::from(format!("no field '{method}' on {recv}"))),
8118 (Value::Variant { variant, .. }, "tag" | "标签" | "タグ" | "태그" | "ป้าย")
8120 if args.is_empty() =>
8121 {
8122 Ok(Value::Str(variant.clone()))
8123 },
8124 (Value::Ok(inner), _) | (Value::Err(inner), _) => Ok(*inner.clone()),
8125 _ => Err(EvalErr::from(format!("no method '{method}' on {recv}"))),
8126 }
8127 }
8128
8129 fn match_pattern(&self, pat: &Pattern, val: &Value) -> Option<Env> {
8132 match (pat, val) {
8133 (Pattern::Wildcard, _) => Some(Env::new()),
8134 (Pattern::Str(s), Value::Str(v)) if s == v => Some(Env::new()),
8135 (Pattern::Number(n), Value::Number(v)) if (n - v).abs() < 1e-12 => Some(Env::new()),
8136 (Pattern::Bool(b), Value::Bool(v)) if b == v => Some(Env::new()),
8137 (Pattern::Ident(name), _) => {
8138 let mut e = Env::new();
8139 e.insert(name.clone(), val.clone());
8140 Some(e)
8141 },
8142 (Pattern::Constructor(ctor, inner_pat), _) => {
8143 let (matches, inner_val) = match (ctor.as_str(), val) {
8144 ("ok" | "好", Value::Ok(v)) => (true, Some(v.as_ref().clone())),
8145 ("bad" | "坏", Value::Err(v)) => (true, Some(v.as_ref().clone())),
8146 ("ok" | "好", v) if !matches!(v, Value::Err(_)) => (true, Some(v.clone())),
8147 _ => (false, None),
8148 };
8149 if !matches {
8150 return None;
8151 }
8152 match (inner_pat, inner_val) {
8153 (Some(p), Some(v)) => self.match_pattern(p, &v),
8154 (None, _) => Some(Env::new()),
8155 (Some(p), None) => self.match_pattern(p, &Value::Unit),
8156 }
8157 },
8158 (Pattern::Variant(vname, sub_pats), Value::Variant { variant, payload, .. }) => {
8160 if vname != variant || sub_pats.len() != payload.len() {
8161 return None;
8162 }
8163 let mut bindings = Env::new();
8164 for (p, v) in sub_pats.iter().zip(payload.iter()) {
8165 bindings.extend(self.match_pattern(p, v)?);
8166 }
8167 Some(bindings)
8168 },
8169 (Pattern::Variant(vname, sub), Value::Ok(v)) if (vname == "ok" || vname == "好") => {
8172 match sub.as_slice() {
8173 [] => Some(Env::new()),
8174 [p] => self.match_pattern(p, v),
8175 _ => None,
8176 }
8177 },
8178 (Pattern::Variant(vname, sub), Value::Err(v))
8179 if (vname == "bad" || vname == "坏" || vname == "err") =>
8180 {
8181 match sub.as_slice() {
8182 [] => Some(Env::new()),
8183 [p] => self.match_pattern(p, v),
8184 _ => None,
8185 }
8186 },
8187 _ => None,
8188 }
8189 }
8190
8191 fn value_to_iter(&self, val: Value) -> Result<Vec<Value>, EvalErr> {
8194 match val {
8195 Value::List(v) => Ok(v),
8196 Value::Str(s) => Ok(s.chars().map(|c| Value::Str(c.to_string())).collect()),
8197 Value::Number(n) => Ok((0..n as i64).map(|i| Value::Number(i as f64)).collect()),
8198 other => Err(EvalErr::from(format!("cannot iterate over {:?}", other))),
8199 }
8200 }
8201
8202 pub(crate) fn is_truthy(&self, val: &Value) -> bool {
8203 match val {
8204 Value::Bool(b) => *b,
8205 Value::Unit => false,
8206 Value::Number(n) => *n != 0.0,
8207 Value::Str(s) => !s.is_empty(),
8208 Value::List(v) => !v.is_empty(),
8209 Value::Ok(_) => true,
8210 Value::Err(_) => false,
8211 Value::Fn(_, _, _) => true,
8212 Value::Struct { .. } => true,
8213 Value::Variant { .. } => true,
8214 }
8215 }
8216
8217 fn to_number(&self, val: &Value) -> Result<f64, EvalErr> {
8218 match val {
8219 Value::Number(n) => Ok(*n),
8220 Value::Str(s) => s
8221 .parse()
8222 .map_err(|_| EvalErr::from(format!("cannot convert '{s}' to number"))),
8223 other => Err(EvalErr::from(format!("expected number, got {:?}", other))),
8224 }
8225 }
8226
8227 fn arg_num(&self, args: &[Value], n: usize, default: f64) -> Result<f64, EvalErr> {
8229 match args.get(n) {
8230 Some(v) => self.to_number(v),
8231 None => Ok(default),
8232 }
8233 }
8234
8235 fn arg_str(&self, args: &[Value], n: usize, default: &str) -> String {
8236 args.get(n)
8237 .map(|v| v.to_string())
8238 .unwrap_or_else(|| default.to_string())
8239 }
8240
8241 #[allow(dead_code)]
8243 fn arg_list_f32(&self, args: &[Value], n: usize) -> Vec<f32> {
8244 match args.get(n) {
8245 Some(Value::List(v)) => v
8246 .iter()
8247 .filter_map(|x| match x {
8248 Value::Number(n) => Some(*n as f32),
8249 _ => None,
8250 })
8251 .collect(),
8252 _ => Vec::new(),
8253 }
8254 }
8255
8256 #[cfg(not(target_arch = "wasm32"))]
8259 fn color_at(&self, args: &[Value], i: usize, default: u32) -> u32 {
8260 match (args.get(i), args.get(i + 1), args.get(i + 2)) {
8261 (Some(a), Some(b), Some(c)) => {
8262 match (self.to_number(a), self.to_number(b), self.to_number(c)) {
8263 (Ok(r), Ok(g), Ok(bl)) => {
8264 ((r as u32 & 0xFF) << 16) | ((g as u32 & 0xFF) << 8) | (bl as u32 & 0xFF)
8265 },
8266 _ => default,
8267 }
8268 },
8269 _ => default,
8270 }
8271 }
8272
8273 #[cfg(not(target_arch = "wasm32"))]
8275 fn pitch_arg(&self, args: &[Value], i: usize, default: i32) -> i32 {
8276 match args.get(i) {
8277 Some(Value::Str(s)) => ling_music::note::parse_pitch(s).unwrap_or(default),
8278 Some(Value::Number(n)) => *n as i32,
8279 _ => default,
8280 }
8281 }
8282
8283 #[cfg(not(target_arch = "wasm32"))]
8285 fn mouse_now(&self) -> (f32, f32, bool) {
8286 let gfx = self.gfx.borrow();
8287 let (mx, my) = gfx
8288 .window
8289 .as_ref()
8290 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
8291 .unwrap_or((0.0, 0.0));
8292 let down = gfx
8293 .window
8294 .as_ref()
8295 .map(|w| w.get_mouse_down(minifb::MouseButton::Left))
8296 .unwrap_or(false);
8297 (mx, my, down)
8298 }
8299
8300 #[cfg(not(target_arch = "wasm32"))]
8304 fn draw_ui(&self, d: &ling_ui::widgets::Draw) {
8305 let mut gfx = self.gfx.borrow_mut();
8306 let (w, h, add) = (gfx.width, gfx.height, gfx.blend == 1);
8307 for (c, poly) in &d.fills {
8308 crate::gfx::raster::fill_contours_aa(
8309 &mut gfx.buffer,
8310 w,
8311 h,
8312 *c,
8313 add,
8314 std::slice::from_ref(poly),
8315 );
8316 }
8317 for (c, pl) in &d.strokes {
8318 for s in pl.windows(2) {
8319 crate::gfx::raster::draw_line_aa(
8320 &mut gfx.buffer,
8321 w,
8322 h,
8323 *c,
8324 add,
8325 s[0][0],
8326 s[0][1],
8327 s[1][0],
8328 s[1][1],
8329 );
8330 }
8331 }
8332 }
8333
8334 fn tex_rect(&self, args: &[Value]) -> Result<(usize, usize, usize, usize), EvalErr> {
8336 let tx = self.arg_num(args, 0, 0.0)? as usize;
8337 let ty = self.arg_num(args, 1, 0.0)? as usize;
8338 let tw = self.arg_num(args, 2, 256.0)? as usize;
8339 let th = self.arg_num(args, 3, 256.0)? as usize;
8340 Ok((tx, ty, tw.max(1), th.max(1)))
8341 }
8342
8343 pub(crate) fn apply_binop(&self, op: &BinOp, l: Value, r: Value) -> EvalResult {
8344 match op {
8345 BinOp::Add => match (l, r) {
8346 (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
8347 (Value::Str(a), Value::Str(b)) => Ok(Value::Str(a + &b)),
8348 (Value::Str(a), b) => Ok(Value::Str(a + &b.to_string())),
8349 (a, Value::Str(b)) => Ok(Value::Str(a.to_string() + &b)),
8350 (a, b) => Err(EvalErr::from(format!("cannot add {:?} and {:?}", a, b))),
8351 },
8352 BinOp::Sub => Ok(Value::Number(self.to_number(&l)? - self.to_number(&r)?)),
8353 BinOp::Mul => Ok(Value::Number(self.to_number(&l)? * self.to_number(&r)?)),
8354 BinOp::Div => Ok(Value::Number(self.to_number(&l)? / self.to_number(&r)?)),
8355 BinOp::Rem => Ok(Value::Number(self.to_number(&l)? % self.to_number(&r)?)),
8356 BinOp::Eq => Ok(Value::Bool(values_equal(&l, &r))),
8357 BinOp::Ne => Ok(Value::Bool(!values_equal(&l, &r))),
8358 BinOp::Lt => Ok(Value::Bool(self.to_number(&l)? < self.to_number(&r)?)),
8359 BinOp::Gt => Ok(Value::Bool(self.to_number(&l)? > self.to_number(&r)?)),
8360 BinOp::Le => Ok(Value::Bool(self.to_number(&l)? <= self.to_number(&r)?)),
8361 BinOp::Ge => Ok(Value::Bool(self.to_number(&l)? >= self.to_number(&r)?)),
8362 BinOp::And => Ok(Value::Bool(self.is_truthy(&l) && self.is_truthy(&r))),
8363 BinOp::Or => Ok(Value::Bool(self.is_truthy(&l) || self.is_truthy(&r))),
8364 }
8365 }
8366
8367 fn builtin_format(&self, args: &[Value]) -> Result<String, EvalErr> {
8368 if args.is_empty() {
8369 return Ok(String::new());
8370 }
8371 let fmt = match &args[0] {
8372 Value::Str(s) => s.clone(),
8373 other => return Ok(other.to_string()),
8374 };
8375
8376 let mut result = String::new();
8377 let mut arg_idx = 1usize;
8378 let mut chars = fmt.chars().peekable();
8379 while let Some(c) = chars.next() {
8380 if c == '{' {
8381 if chars.peek() == Some(&'}') {
8382 chars.next();
8383 if arg_idx < args.len() {
8384 result.push_str(&args[arg_idx].to_string());
8385 arg_idx += 1;
8386 }
8387 } else {
8388 let mut spec = String::new();
8389 for ch in chars.by_ref() {
8390 if ch == '}' {
8391 break;
8392 }
8393 spec.push(ch);
8394 }
8395 if arg_idx < args.len() {
8396 if spec.starts_with(":.") {
8397 if let Value::Number(n) = &args[arg_idx] {
8398 let prec: usize =
8399 spec[2..].trim_end_matches('f').parse().unwrap_or(2);
8400 result.push_str(&format!("{:.prec$}", n));
8401 arg_idx += 1;
8402 continue;
8403 }
8404 }
8405 result.push_str(&args[arg_idx].to_string());
8406 arg_idx += 1;
8407 }
8408 }
8409 } else {
8410 result.push(c);
8411 }
8412 }
8413 Ok(result)
8414 }
8415}
8416
8417#[cfg(not(target_arch = "wasm32"))]
8418#[cfg(not(target_arch = "wasm32"))]
8420fn parse_pad_button(name: &str) -> Option<ling_input::GamepadButton> {
8421 use ling_input::GamepadButton as B;
8422 Some(match name.to_ascii_lowercase().as_str() {
8423 "a" | "south" | "cross" => B::South,
8424 "b" | "east" | "circle" => B::East,
8425 "x" | "west" | "square" => B::West,
8426 "y" | "north" | "triangle" => B::North,
8427 "lb" | "l1" | "left_shoulder" => B::LeftShoulder,
8428 "rb" | "r1" | "right_shoulder" => B::RightShoulder,
8429 "lt" | "l2" | "left_trigger" => B::LeftTrigger,
8430 "rt" | "r2" | "right_trigger" => B::RightTrigger,
8431 "start" | "menu" | "options" => B::Start,
8432 "select" | "back" | "share" | "view" => B::Select,
8433 "guide" | "home" => B::Guide,
8434 "l3" | "left_stick" => B::LeftStick,
8435 "r3" | "right_stick" => B::RightStick,
8436 "up" | "dpad_up" => B::DpadUp,
8437 "down" | "dpad_down" => B::DpadDown,
8438 "left" | "dpad_left" => B::DpadLeft,
8439 "right" | "dpad_right" => B::DpadRight,
8440 _ => return None,
8441 })
8442}
8443
8444#[cfg(not(target_arch = "wasm32"))]
8445fn str_to_minifb_key(name: &str) -> Option<minifb::Key> {
8446 use minifb::Key;
8447 Some(match name {
8448 "numpad0" | "kp0" => Key::NumPad0,
8449 "numpad1" | "kp1" => Key::NumPad1,
8450 "numpad2" | "kp2" => Key::NumPad2,
8451 "numpad3" | "kp3" => Key::NumPad3,
8452 "numpad4" | "kp4" => Key::NumPad4,
8453 "numpad5" | "kp5" => Key::NumPad5,
8454 "numpad6" | "kp6" => Key::NumPad6,
8455 "numpad7" | "kp7" => Key::NumPad7,
8456 "numpad8" | "kp8" => Key::NumPad8,
8457 "numpad9" | "kp9" => Key::NumPad9,
8458 "numpad+" | "kp+" => Key::NumPadPlus,
8459 "numpad-" | "kp-" => Key::NumPadMinus,
8460 "numpad*" | "kp*" => Key::NumPadAsterisk,
8461 "numpad/" | "kp/" => Key::NumPadSlash,
8462 "left" => Key::Left,
8463 "right" => Key::Right,
8464 "up" => Key::Up,
8465 "down" => Key::Down,
8466 "space" => Key::Space,
8467 "enter" => Key::Enter,
8468 "escape" => Key::Escape,
8469 "pageup" => Key::PageUp,
8470 "pagedown" => Key::PageDown,
8471 "lshift" | "leftshift" => Key::LeftShift,
8472 "rshift" | "rightshift" => Key::RightShift,
8473 "lctrl" | "leftctrl" => Key::LeftCtrl,
8474 "rctrl" | "rightctrl" => Key::RightCtrl,
8475 "lalt" | "leftalt" => Key::LeftAlt,
8476 "ralt" | "rightalt" => Key::RightAlt,
8477 "tab" => Key::Tab,
8478 "backspace" => Key::Backspace,
8479 "delete" => Key::Delete,
8480 "insert" => Key::Insert,
8481 "home" => Key::Home,
8482 "end" => Key::End,
8483 "a" => Key::A,
8484 "b" => Key::B,
8485 "c" => Key::C,
8486 "d" => Key::D,
8487 "e" => Key::E,
8488 "f" => Key::F,
8489 "g" => Key::G,
8490 "h" => Key::H,
8491 "i" => Key::I,
8492 "j" => Key::J,
8493 "k" => Key::K,
8494 "l" => Key::L,
8495 "m" => Key::M,
8496 "n" => Key::N,
8497 "o" => Key::O,
8498 "p" => Key::P,
8499 "q" => Key::Q,
8500 "r" => Key::R,
8501 "s" => Key::S,
8502 "t" => Key::T,
8503 "u" => Key::U,
8504 "v" => Key::V,
8505 "w" => Key::W,
8506 "x" => Key::X,
8507 "y" => Key::Y,
8508 "z" => Key::Z,
8509 "0" => Key::Key0,
8510 "1" => Key::Key1,
8511 "2" => Key::Key2,
8512 "3" => Key::Key3,
8513 "4" => Key::Key4,
8514 "5" => Key::Key5,
8515 "6" => Key::Key6,
8516 "7" => Key::Key7,
8517 "8" => Key::Key8,
8518 "9" => Key::Key9,
8519 _ => return None,
8520 })
8521}
8522
8523pub(crate) fn values_equal(a: &Value, b: &Value) -> bool {
8524 match (a, b) {
8525 (Value::Number(x), Value::Number(y)) => (x - y).abs() < 1e-12,
8526 (Value::Str(x), Value::Str(y)) => x == y,
8527 (Value::Bool(x), Value::Bool(y)) => x == y,
8528 (Value::Unit, Value::Unit) => true,
8529 _ => false,
8530 }
8531}
8532
8533#[cfg(not(target_arch = "wasm32"))]
8540fn hide_console_window() {
8541 #[cfg(windows)]
8542 unsafe {
8543 extern "system" {
8544 fn GetConsoleWindow() -> isize;
8545 fn ShowWindow(hwnd: isize, nCmdShow: i32) -> i32;
8546 }
8547 let hwnd = GetConsoleWindow();
8548 if hwnd != 0 {
8549 ShowWindow(hwnd, 0); }
8551 }
8552}
8553
8554#[cfg(all(not(target_arch = "wasm32"), windows))]
8559fn make_borderless_fullscreen(hwnd: isize, screen_w: i32, screen_h: i32) {
8560 if hwnd == 0 {
8561 return;
8562 }
8563 unsafe {
8564 extern "system" {
8565 fn SetWindowLongPtrW(hwnd: isize, index: i32, new: isize) -> isize;
8566 fn SetWindowPos(
8567 hwnd: isize,
8568 insert_after: isize,
8569 x: i32,
8570 y: i32,
8571 cx: i32,
8572 cy: i32,
8573 flags: u32,
8574 ) -> i32;
8575 fn ShowWindow(hwnd: isize, cmd: i32) -> i32;
8576 }
8577 const GWL_STYLE: i32 = -16;
8578 const GWL_EXSTYLE: i32 = -20;
8579 SetWindowLongPtrW(hwnd, GWL_STYLE, 0x9000_0000isize);
8582 SetWindowLongPtrW(hwnd, GWL_EXSTYLE, 0);
8584 SetWindowPos(hwnd, -1isize, 0, 0, screen_w, screen_h, 0x0020 | 0x0040);
8586 ShowWindow(hwnd, 3); }
8588}
8589
8590#[cfg(all(not(target_arch = "wasm32"), windows))]
8593fn monitor_info() -> (i32, i32, i32) {
8594 unsafe {
8595 extern "system" {
8596 fn GetSystemMetrics(index: i32) -> i32;
8597 fn GetDC(hwnd: isize) -> isize;
8598 fn ReleaseDC(hwnd: isize, hdc: isize) -> i32;
8599 fn GetDeviceCaps(hdc: isize, index: i32) -> i32;
8600 }
8601 let w = GetSystemMetrics(0).max(1); let h = GetSystemMetrics(1).max(1); let hdc = GetDC(0);
8604 let mut hz = if hdc != 0 { GetDeviceCaps(hdc, 116) } else { 0 }; if hdc != 0 {
8606 ReleaseDC(0, hdc);
8607 }
8608 if hz <= 1 {
8609 hz = 60; }
8611 (w, h, hz)
8612 }
8613}
8614
8615#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
8617fn monitor_info() -> (i32, i32, i32) {
8618 let (w, h) = native_screen_size();
8619 (w as i32, h as i32, 60)
8620}
8621
8622#[cfg(target_arch = "wasm32")]
8624fn monitor_info() -> (i32, i32, i32) {
8625 let (w, h) = crate::gfx::webgl::canvas_size();
8626 (w as i32, h as i32, 60)
8627}
8628
8629#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
8632fn native_screen_size() -> (f64, f64) {
8633 (1920.0, 1080.0)
8636}