1#[cfg(not(target_arch = "wasm32"))]
3mod net;
4#[cfg(not(target_arch = "wasm32"))]
5mod gamepad;
6#[cfg(not(target_arch = "wasm32"))]
7mod ai;
8use std::cell::RefCell;
9use std::collections::HashMap;
10use crate::parser::ast::*;
11use crate::gfx::{GfxState, Light};
12use crate::gfx::raster::draw_line;
15#[cfg(not(target_arch = "wasm32"))]
16use crate::gfx::raster::fill_triangle;
17#[cfg(not(target_arch = "wasm32"))]
18use ling_audio::{AudioEngine, ToneParams, Wave};
19
20#[cfg(not(target_arch = "wasm32"))]
21use ling_audio::FftAnalyzer;
22
23#[cfg(not(target_arch = "wasm32"))]
24use ling_mic;
25
26#[derive(Debug, Clone)]
29pub enum Value {
30 Str(String),
31 Number(f64),
32 Bool(bool),
33 Unit,
34 List(Vec<Value>),
35 Ok(Box<Value>),
36 Err(Box<Value>),
37 Fn(Vec<String>, Vec<Stmt>, Env),
38 Struct { name: String, fields: Vec<(String, Value)> },
40 Variant { enum_name: String, variant: String, payload: Vec<Value> },
42}
43
44type Env = HashMap<String, Value>;
45
46impl std::fmt::Display for Value {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 Value::Str(s) => write!(f, "{s}"),
50 Value::Number(n) => {
51 if n.fract() == 0.0 && n.abs() < 1e15 { write!(f, "{}", *n as i64) }
52 else { write!(f, "{n}") }
53 }
54 Value::Bool(b) => write!(f, "{b}"),
55 Value::Unit => write!(f, "()"),
56 Value::List(v) => {
57 write!(f, "[")?;
58 for (i, x) in v.iter().enumerate() {
59 if i > 0 { write!(f, ", ")?; }
60 write!(f, "{x}")?;
61 }
62 write!(f, "]")
63 }
64 Value::Ok(v) => write!(f, "Ok({v})"),
65 Value::Err(v) => write!(f, "Err({v})"),
66 Value::Fn(_, _, _) => write!(f, "<fn>"),
67 Value::Struct { name, fields } => {
68 write!(f, "{name} {{ ")?;
69 for (i, (k, v)) in fields.iter().enumerate() {
70 if i > 0 { write!(f, ", ")?; }
71 write!(f, "{k}: {v}")?;
72 }
73 write!(f, " }}")
74 }
75 Value::Variant { variant, payload, .. } => {
76 write!(f, "{variant}")?;
77 if !payload.is_empty() {
78 write!(f, "(")?;
79 for (i, v) in payload.iter().enumerate() {
80 if i > 0 { write!(f, ", ")?; }
81 write!(f, "{v}")?;
82 }
83 write!(f, ")")?;
84 }
85 Ok(())
86 }
87 }
88 }
89}
90
91#[derive(Debug)]
94enum EvalErr {
95 Runtime(String),
96 Return(Value),
97 #[allow(dead_code)] Break,
99}
100
101impl From<String> for EvalErr {
102 fn from(s: String) -> Self { EvalErr::Runtime(s) }
103}
104
105type EvalResult = Result<Value, EvalErr>;
106
107struct SvgWriter {
112 path: String,
113 width: f64,
114 height: f64,
115 elements: Vec<String>,
116}
117
118impl SvgWriter {
119 fn new(path: String, width: f64, height: f64) -> Self {
120 let bg = format!(
121 "<rect width=\"{width}\" height=\"{height}\" fill=\"#0a0a0a\"/>"
122 );
123 Self { path, width, height, elements: vec![bg] }
124 }
125
126 fn save(&self) -> std::io::Result<()> {
127 let w = self.width;
128 let h = self.height;
129 let mut out = format!(
130 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
131 <svg xmlns=\"http://www.w3.org/2000/svg\" \
132 width=\"{w}\" height=\"{h}\" viewBox=\"0 0 {w} {h}\">\n"
133 );
134 for elem in &self.elements {
135 out.push_str(" ");
136 out.push_str(elem);
137 out.push('\n');
138 }
139 out.push_str("</svg>\n");
140 if let Some(parent) = std::path::Path::new(&self.path).parent() {
142 if !parent.as_os_str().is_empty() {
143 let _ = std::fs::create_dir_all(parent);
144 }
145 }
146 std::fs::write(&self.path, out.as_bytes())
147 }
148}
149
150fn hsl_to_hex(h: f64, s: f64, l: f64) -> String {
151 let s = s / 100.0;
152 let l = l / 100.0;
153 let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
154 let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
155 let m = l - c / 2.0;
156 let (r1, g1, b1) = if h < 60.0 { (c, x, 0.0) }
157 else if h < 120.0 { (x, c, 0.0) }
158 else if h < 180.0 { (0.0, c, x) }
159 else if h < 240.0 { (0.0, x, c) }
160 else if h < 300.0 { (x, 0.0, c) }
161 else { (c, 0.0, x) };
162 let r = ((r1 + m) * 255.0).round() as u8;
163 let g = ((g1 + m) * 255.0).round() as u8;
164 let b = ((b1 + m) * 255.0).round() as u8;
165 format!("#{r:02x}{g:02x}{b:02x}")
166}
167
168fn tex_hash(x: i32, y: i32, seed: u32) -> f32 {
171 let mut h = (x as u32).wrapping_add((y as u32).wrapping_mul(2654435769)).wrapping_add(seed.wrapping_mul(1234567891));
172 h ^= h >> 16; h = h.wrapping_mul(0x45d9f3b); h ^= h >> 16;
173 h as f32 / u32::MAX as f32
174}
175
176fn tex_vnoise(x: f32, y: f32, seed: u32) -> f32 {
177 let xi = x.floor() as i32; let yi = y.floor() as i32;
178 let sm = |t: f32| t * t * (3.0 - 2.0 * t);
179 let xf = sm(x - xi as f32); let yf = sm(y - yi as f32);
180 let a = tex_hash(xi, yi, seed); let b = tex_hash(xi+1, yi, seed);
181 let c = tex_hash(xi, yi+1, seed); let d = tex_hash(xi+1, yi+1, seed);
182 a + (b-a)*xf + (c-a)*yf + (a-b-c+d)*xf*yf
183}
184
185fn tex_fbm(x: f32, y: f32, octaves: u32, seed: u32) -> f32 {
186 let mut v = 0.0f32; let mut amp = 0.5f32; let mut f = 1.0f32;
187 for i in 0..octaves {
188 v += tex_vnoise(x * f, y * f, seed.wrapping_add(i * 7919)) * amp;
189 amp *= 0.5; f *= 2.0;
190 }
191 v
192}
193
194fn tex_palette(name: &str, t: f32) -> [f32; 3] {
195 let (a, b, c, d): ([f32;3],[f32;3],[f32;3],[f32;3]) = match name {
196 "fire" => ([0.8,0.4,0.1],[0.7,0.3,0.1],[1.0,0.5,0.3],[0.0,0.5,0.8]),
197 "ocean" => ([0.1,0.4,0.7],[0.3,0.3,0.4],[0.8,1.0,0.5],[0.3,0.0,0.6]),
198 "psychedelic" => ([0.5,0.5,0.5],[0.8,0.8,0.8],[1.0,1.3,0.7],[0.0,0.15,0.3]),
199 "neon" => ([0.5,0.5,0.5],[0.5,0.5,0.5],[2.0,1.0,0.0],[0.5,0.2,0.25]),
200 "forest" => ([0.3,0.5,0.2],[0.2,0.3,0.1],[1.0,0.5,0.8],[0.1,0.3,0.6]),
201 _ => ([0.5,0.5,0.5],[0.5,0.5,0.5],[1.0,1.0,1.0],[0.0,0.333,0.667]),
202 };
203 [0,1,2].map(|i| (a[i] + b[i] * (std::f32::consts::TAU * (c[i] * t + d[i])).cos()).clamp(0.0, 1.0))
204}
205
206#[cfg(not(target_arch = "wasm32"))]
208fn key_char(k: minifb::Key) -> Option<char> {
209 use minifb::Key::*;
210 Some(match k {
211 A=>'a',B=>'b',C=>'c',D=>'d',E=>'e',F=>'f',G=>'g',H=>'h',I=>'i',J=>'j',K=>'k',L=>'l',M=>'m',
212 N=>'n',O=>'o',P=>'p',Q=>'q',R=>'r',S=>'s',T=>'t',U=>'u',V=>'v',W=>'w',X=>'x',Y=>'y',Z=>'z',
213 Key0=>'0',Key1=>'1',Key2=>'2',Key3=>'3',Key4=>'4',Key5=>'5',Key6=>'6',Key7=>'7',Key8=>'8',Key9=>'9',
214 Space=>' ', Minus=>'-', Period=>'.',
215 _ => return None,
216 })
217}
218
219fn hex_encode(bytes: &[u8]) -> String {
221 let mut s = String::with_capacity(bytes.len() * 2);
222 for b in bytes { s.push_str(&format!("{b:02x}")); }
223 s
224}
225
226#[cfg(not(target_arch = "wasm32"))]
228fn decode_blob(s: &str) -> Result<Vec<u8>, String> {
229 use base64::Engine as _;
230 use std::io::Read as _;
231 let comp = base64::engine::general_purpose::STANDARD
232 .decode(s.trim())
233 .map_err(|e| format!("base64: {e}"))?;
234 let mut out = Vec::new();
235 flate2::read::ZlibDecoder::new(&comp[..])
236 .read_to_end(&mut out)
237 .map_err(|e| format!("inflate: {e}"))?;
238 Ok(out)
239}
240
241fn hex_decode(s: &str) -> Vec<u8> {
243 let s = s.trim();
244 (0..s.len() / 2)
245 .filter_map(|i| u8::from_str_radix(s.get(i * 2..i * 2 + 2)?, 16).ok())
246 .collect()
247}
248
249fn hex_to_32(s: &str) -> [u8; 32] {
251 let v = hex_decode(s);
252 let mut out = [0u8; 32];
253 let n = v.len().min(32);
254 out[..n].copy_from_slice(&v[..n]);
255 out
256}
257
258fn tex_rgb(r: f32, g: f32, b: f32) -> u32 {
259 ((r * 255.0) as u32) << 16 | ((g * 255.0) as u32) << 8 | (b * 255.0) as u32
260}
261
262const PERM: [u8; 512] = [
265 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,
266 140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,
267 247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,
268 57,177,33,88,237,149,56,87,174,35,63,189,114,56,42,123,
269 165,38,72,93,69,139,138,78,149,159,56,89,152,78,61,140,
270 63,26,142,76,124,132,72,11,90,44,82,59,96,41,148,126,
271 157,13,49,27,176,33,47,14,97,78,71,40,87,183,4,122,
272 92,7,72,3,246,17,225,87,91,106,203,190,57,74,76,88,
273 207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,
274 168,81,163,64,143,146,157,56,245,188,182,218,33,16,255,243,
275 210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,25,
276 115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,
277 219,224,50,58,10,73,6,36,92,194,211,172,98,145,149,228,
278 121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,
279 8,186,120,37,46,28,166,180,198,232,221,116,31,75,189,139,
280 138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,
281 158,225,248,152,17,105,217,142,148,155,30,135,233,206,85,40,
282 223,140,161,137,13,191,230,66,104,153,199,167,147,99,179,92,
283 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,
285 140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,
286 247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,
287 57,177,33,88,237,149,56,87,174,35,63,189,114,56,42,123,
288 165,38,72,93,69,139,138,78,149,159,56,89,152,78,61,140,
289 63,26,142,76,124,132,72,11,90,44,82,59,96,41,148,126,
290 157,13,49,27,176,33,47,14,97,78,71,40,87,183,4,122,
291 92,7,72,3,246,17,225,87,91,106,203,190,57,74,76,88,
292 207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,
293 168,81,163,64,143,146,157,56,245,188,182,218,33,16,255,243,
294 210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,25,
295 115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,
296 219,224,50,58,10,73,6,36,92,194,211,172,98,145,149,228,
297 121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,
298];
299
300fn fade(t: f32) -> f32 {
301 t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
302}
303
304fn grad(hash: u8, x: f32, y: f32, z: f32) -> f32 {
305 let h = hash & 15;
306 let u = if h < 8 { x } else { y };
307 let v = if h < 8 { y } else { z };
308 (if (h & 1) == 0 { u } else { -u }) + (if (h & 2) == 0 { v } else { -v })
309}
310
311fn perlin3(x: f32, y: f32, z: f32) -> f32 {
312 let xi = (x.floor() as i32) & 255;
313 let yi = (y.floor() as i32) & 255;
314 let zi = (z.floor() as i32) & 255;
315
316 let xf = x - x.floor();
317 let yf = y - y.floor();
318 let zf = z - z.floor();
319
320 let u = fade(xf);
321 let v = fade(yf);
322 let w = fade(zf);
323
324 let p0 = PERM[xi as usize] as usize;
325 let p1 = PERM[((xi + 1) & 255) as usize] as usize;
326 let pa = PERM[(p0 + yi as usize) & 255] as usize;
327 let pb = PERM[(p0 + ((yi + 1) & 255) as usize) & 255] as usize;
328 let pc = PERM[(p1 + yi as usize) & 255] as usize;
329 let pd = PERM[(p1 + ((yi + 1) & 255) as usize) & 255] as usize;
330
331 let g000 = grad(PERM[(pa + zi as usize) & 255], xf, yf, zf);
332 let g001 = grad(PERM[(pa + ((zi + 1) & 255) as usize) & 255], xf, yf, zf - 1.0);
333 let g010 = grad(PERM[(pb + zi as usize) & 255], xf, yf - 1.0, zf);
334 let g011 = grad(PERM[(pb + ((zi + 1) & 255) as usize) & 255], xf, yf - 1.0, zf - 1.0);
335 let g100 = grad(PERM[(pc + zi as usize) & 255], xf - 1.0, yf, zf);
336 let g101 = grad(PERM[(pc + ((zi + 1) & 255) as usize) & 255], xf - 1.0, yf, zf - 1.0);
337 let g110 = grad(PERM[(pd + zi as usize) & 255], xf - 1.0, yf - 1.0, zf);
338 let g111 = grad(PERM[(pd + ((zi + 1) & 255) as usize) & 255], xf - 1.0, yf - 1.0, zf - 1.0);
339
340 let l00 = g000 + u * (g100 - g000);
341 let l01 = g001 + u * (g101 - g001);
342 let l10 = g010 + u * (g110 - g010);
343 let l11 = g011 + u * (g111 - g011);
344
345 let l0 = l00 + v * (l10 - l00);
346 let l1 = l01 + v * (l11 - l01);
347
348 l0 + w * (l1 - l0)
349}
350
351fn fast_rand_f64(state: &mut u64) -> f64 {
352 *state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
353 ((*state >> 32) as u32) as f64 / 4294967296.0
354}
355
356#[inline]
360fn put_px(buf: &mut [u32], idx: usize, color: u32, blend: u8) {
361 if idx >= buf.len() { return; }
362 if blend == 0 {
363 buf[idx] = color;
364 } else {
365 let old = buf[idx];
366 let r = (((old >> 16) & 255) + ((color >> 16) & 255)).min(255);
367 let g = (((old >> 8) & 255) + ((color >> 8) & 255)).min(255);
368 let b = ((old & 255) + (color & 255)).min(255);
369 buf[idx] = (r << 16) | (g << 8) | b;
370 }
371}
372
373fn draw_circle_outline(buf: &mut [u32], w: i32, h: i32, cx: i32, cy: i32, r: i32, color: u32, blend: u8) {
374 let r = r.clamp(0, 20000); if r == 0 { return; }
376 let mut x = 0;
377 let mut y = r;
378 let mut d = 3 - 2 * r;
379 while x <= y {
380 plot_circle_points(buf, w, h, cx, cy, x, y, color, blend);
381 if d < 0 {
382 d += 4 * x + 6;
383 } else {
384 d += 4 * (x - y) + 10;
385 y -= 1;
386 }
387 x += 1;
388 }
389}
390
391fn plot_circle_points(buf: &mut [u32], w: i32, h: i32, cx: i32, cy: i32, x: i32, y: i32, color: u32, blend: u8) {
392 let points = [(cx+x, cy+y), (cx-x, cy+y), (cx+x, cy-y), (cx-x, cy-y),
393 (cx+y, cy+x), (cx-y, cy+x), (cx+y, cy-x), (cx-y, cy-x)];
394 for &(px, py) in &points {
395 if px >= 0 && px < w && py >= 0 && py < h {
396 put_px(buf, (py * w + px) as usize, color, blend);
397 }
398 }
399}
400
401fn draw_circle_filled(buf: &mut [u32], w: i32, h: i32, cx: i32, cy: i32, r: i32, color: u32, blend: u8) {
402 if r <= 0 { return; }
403 for dy in -r..=r {
404 let dx_max = ((r*r - dy*dy) as f64).sqrt() as i32;
405 let py = cy + dy;
406 if py < 0 || py >= h { continue; }
407 for dx in -dx_max..=dx_max {
408 let px = cx + dx;
409 if px >= 0 && px < w {
410 put_px(buf, (py * w + px) as usize, color, blend);
411 }
412 }
413 }
414}
415
416#[cfg(test)]
417mod draw_tests {
418 use super::*;
419
420 #[test]
421 fn filled_circle_actually_writes_pixels() {
422 let mut buf = vec![0u32; 100 * 100];
423 draw_circle_filled(&mut buf, 100, 100, 50, 50, 10, 0xFF00FF, 0);
424 assert_eq!(buf[50 * 100 + 50], 0xFF00FF, "centre pixel must be filled");
425 assert_eq!(buf[0], 0, "far corner must stay clear");
426 let n = buf.iter().filter(|&&p| p != 0).count();
427 assert!(n > 200 && n < 500, "r=10 disc area ≈ 314, got {n}");
428 }
429
430 #[test]
431 fn circle_outline_writes_a_ring() {
432 let mut buf = vec![0u32; 100 * 100];
433 draw_circle_outline(&mut buf, 100, 100, 50, 50, 20, 0x00FF00, 0);
434 assert_eq!(buf[50 * 100 + 50], 0, "outline must NOT fill the centre");
435 assert!(buf.iter().any(|&p| p == 0x00FF00), "outline must draw a ring");
436 }
437
438 #[test]
439 fn additive_blend_accumulates_channels() {
440 let mut buf = vec![0x202020u32; 1];
441 put_px(&mut buf, 0, 0x404040, 1);
442 assert_eq!(buf[0], 0x606060);
443 }
444}
445
446#[derive(Clone, Copy)]
452pub struct UiTheme {
453 pub primary: u32,
454 pub accent: u32,
455 pub track: u32,
456 pub warn: u32,
457 pub text: u32,
458 pub bg: u32,
459}
460
461impl Default for UiTheme {
462 fn default() -> Self {
463 Self {
464 primary: 0x00D2FF, accent: 0x28FFB4, track: 0x2C3E64, warn: 0xFF5A5A, text: 0xBEEBFF, bg: 0x0A1018, }
471 }
472}
473
474pub struct Interpreter {
475 globals: HashMap<String, Expr>,
476 global_seed: Env,
479 functions: HashMap<String, FnDef>,
480 structs: HashMap<String, Vec<String>>,
482 enum_variants: HashMap<String, (String, usize)>,
484 _modules: HashMap<String, Vec<FnDef>>,
485 gfx: RefCell<GfxState>,
486 svg: RefCell<Option<SvgWriter>>,
487 pub source_dir: Option<std::path::PathBuf>,
489 loaded_files: std::collections::HashSet<String>,
491 #[cfg(not(target_arch = "wasm32"))]
493 audio: Option<AudioEngine>,
494 #[cfg(not(target_arch = "wasm32"))]
495 fft: RefCell<FftAnalyzer>,
496 fft_bands_cache: RefCell<Vec<f32>>,
497 start_time: std::time::Instant,
499 frame_num: u64,
501 rand_state: u64,
503 #[cfg(not(target_arch = "wasm32"))]
505 mic: Option<ling_mic::MicInput>,
506 #[cfg(not(target_arch = "wasm32"))]
508 crypto_ids: Vec<ling_crypto::KnotIdentity>,
509 text_buffer: String,
511 record_n: u32,
513 #[cfg(not(target_arch = "wasm32"))]
515 mic_buffer: Vec<f32>,
516 #[cfg(not(target_arch = "wasm32"))]
518 fonts: Vec<ling_graphics::VectorFont>,
519 ui_theme: UiTheme,
521 mouse_was_down: bool,
523 #[cfg(not(target_arch = "wasm32"))]
525 music: Option<ling_music::MusicEngine>,
526 #[cfg(not(target_arch = "wasm32"))]
527 music_init: bool,
528 #[cfg(not(target_arch = "wasm32"))]
530 tracks: Vec<ling_music::DecodedAudio>,
531 #[cfg(not(target_arch = "wasm32"))]
533 lyrics: Vec<ling_music::Lyrics>,
534 #[cfg(not(target_arch = "wasm32"))]
536 midis: Vec<ling_music::MidiSong>,
537 soft_bodies: Vec<ling_physics::soft::SoftBody>,
539 rigid_world: ling_physics::rigid::PhysicsWorld,
541 liquids: Vec<ling_physics::liquid::LiquidGrid>,
543 meshes: Vec<crate::gfx::shapes::ColorMesh>,
544 dialog: Option<ling_game::dialog::Dialog>,
546 dialog_colors: [u32; 4],
548 frames: Vec<String>,
550 error_trace: Option<Vec<String>>,
553 #[cfg(not(target_arch = "wasm32"))]
557 input: RefCell<Option<InputState>>,
558}
559
560#[cfg(not(target_arch = "wasm32"))]
562struct InputState {
563 sensorium: ling_input::Sensorium,
564 backend: ling_input::backend::GilrsBackend,
565}
566
567impl Interpreter {
568 pub fn new() -> Self {
569 #[cfg(not(target_arch = "wasm32"))]
570 let audio = AudioEngine::new()
571 .map_err(|e| eprintln!("audio init failed (no sound): {e}"))
572 .ok();
573 Self {
574 globals: HashMap::new(),
575 global_seed: HashMap::new(),
576 functions: HashMap::new(),
577 structs: HashMap::new(),
578 enum_variants: HashMap::new(),
579 _modules: HashMap::new(),
580 gfx: RefCell::new(GfxState::new()),
581 svg: RefCell::new(None),
582 source_dir: None,
583 loaded_files: std::collections::HashSet::new(),
584 #[cfg(not(target_arch = "wasm32"))]
585 audio,
586 #[cfg(not(target_arch = "wasm32"))]
587 fft: RefCell::new(FftAnalyzer::new(2048, 44100)),
588 fft_bands_cache: RefCell::new(vec![]),
589 start_time: std::time::Instant::now(),
590 frame_num: 0,
591 rand_state: 0x123456789ABCDEF,
592 #[cfg(not(target_arch = "wasm32"))]
593 mic: None,
594 #[cfg(not(target_arch = "wasm32"))]
595 crypto_ids: Vec::new(),
596 text_buffer: String::new(),
597 record_n: 0,
598 #[cfg(not(target_arch = "wasm32"))]
599 mic_buffer: Vec::new(),
600 #[cfg(not(target_arch = "wasm32"))]
601 fonts: Vec::new(),
602 ui_theme: UiTheme::default(),
603 mouse_was_down: false,
604 #[cfg(not(target_arch = "wasm32"))]
605 music: None,
606 #[cfg(not(target_arch = "wasm32"))]
607 music_init: false,
608 #[cfg(not(target_arch = "wasm32"))]
609 tracks: Vec::new(),
610 #[cfg(not(target_arch = "wasm32"))]
611 lyrics: Vec::new(),
612 #[cfg(not(target_arch = "wasm32"))]
613 midis: Vec::new(),
614 soft_bodies: Vec::new(),
615 rigid_world: ling_physics::rigid::PhysicsWorld::new(),
616 liquids: Vec::new(),
617 meshes: Vec::new(),
618 dialog: None,
619 dialog_colors: [0xE6F2FF, 0xFFD24A, 0x4AD2FF, 0x6CFF8C], frames: Vec::new(),
621 error_trace: None,
622 #[cfg(not(target_arch = "wasm32"))]
623 input: RefCell::new(None),
624 }
625 }
626
627 #[cfg(not(target_arch = "wasm32"))]
631 fn pad_poll(&self) -> usize {
632 let mut slot = self.input.borrow_mut();
633 if slot.is_none() {
634 match ling_input::backend::GilrsBackend::new() {
635 Ok(backend) => {
636 *slot = Some(InputState { sensorium: ling_input::Sensorium::new(4), backend });
637 },
638 Err(_) => return 0,
639 }
640 }
641 let st = slot.as_mut().unwrap();
642 st.sensorium.begin_frame();
643 st.sensorium.pump(&mut st.backend);
644 st.sensorium.update(1.0 / 60.0);
645 st.sensorium.devices.count()
646 }
647
648 #[cfg(not(target_arch = "wasm32"))]
651 fn with_pad<T>(&self, slot: usize, default: T, f: impl FnOnce(&ling_input::Gamepad) -> T) -> T {
652 let inp = self.input.borrow();
653 match inp.as_ref().and_then(|s| s.sensorium.player(slot)) {
654 Some(p) => f(p),
655 None => default,
656 }
657 }
658
659 pub fn take_error_trace(&mut self) -> Vec<String> {
662 self.error_trace.take().unwrap_or_default()
663 }
664
665 fn framed<T, F>(&mut self, name: &str, body: F) -> Result<T, EvalErr>
668 where
669 F: FnOnce(&mut Self) -> Result<T, EvalErr>,
670 {
671 self.frames.push(name.to_string());
672 let result = body(self);
673 if matches!(result, Err(EvalErr::Runtime(_))) && self.error_trace.is_none() {
674 self.error_trace = Some(self.frames.clone());
675 }
676 self.frames.pop();
677 result
678 }
679
680 #[cfg(not(target_arch = "wasm32"))]
684 fn render_dialog(&mut self, x: f32, y: f32, w: f32, h: f32, font: i64, t: f32) {
685 let (runs, typing) = match &self.dialog {
686 Some(d) if !d.is_closed() => {
687 let runs: Vec<(String, usize, bool)> = d.visible_runs().into_iter()
688 .map(|r| (r.text, r.role.index(), r.newline_before)).collect();
689 (runs, d.is_typing())
690 }
691 _ => return,
692 };
693 let colors = self.dialog_colors;
694 let b = 12.0;
696 let corners: Vec<[f32; 2]> = vec![
697 [x+b,y],[x+w-b,y],[x+w,y+b],[x+w,y+h-b],[x+w-b,y+h],[x+b,y+h],[x,y+h-b],[x,y+b],[x+b,y],
698 ];
699 {
700 let mut gfx = self.gfx.borrow_mut();
701 let (bw, bh) = (gfx.width, gfx.height);
702 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, bw, bh, 0x0A1018, false, std::slice::from_ref(&corners));
703 for seg in corners.windows(2) {
704 crate::gfx::raster::draw_line_aa(&mut gfx.buffer, bw, bh, 0x00D2FF, false, seg[0][0], seg[0][1], seg[1][0], seg[1][1]);
705 }
706 }
707 let px = 22.0f32;
709 let pad = 20.0f32;
710 let line_h = px * 1.45;
711 let mut cx = x + pad;
712 let mut cy = y + pad;
713 let use_font = font >= 0 && (font as usize) < self.fonts.len();
714 for (text, role, nl) in &runs {
715 if *nl { cx = x + pad; cy += line_h; }
716 for word in text.split_inclusive(' ') {
717 let wpx = if use_font { self.fonts[font as usize].measure(word, px) }
718 else { ling_ui::holo::text_width(word, px * 0.6, px * 0.24) };
719 if cx + wpx > x + w - pad && cx > x + pad + 1.0 { cx = x + pad; cy += line_h; }
720 if cy + line_h > y + h { break; }
721 let col = colors[(*role).min(3)];
722 if use_font {
723 let glyphs = self.font_layout_2d_glyphs(font as usize, cx, cy, px, word);
724 let mut gfx = self.gfx.borrow_mut();
725 let (bw, bh, add) = (gfx.width, gfx.height, gfx.blend == 1);
726 for contours in &glyphs {
727 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, bw, bh, col, add, contours);
728 }
729 } else {
730 let segs = ling_ui::holo::text_lines(word, cx, cy, px * 0.6, px, px * 0.24);
731 let mut gfx = self.gfx.borrow_mut();
732 let (bw, bh) = (gfx.width, gfx.height);
733 for s in segs { draw_line(&mut gfx.buffer, bw, bh, col, s[0], s[1], s[2], s[3]); }
734 }
735 cx += wpx;
736 }
737 }
738 if !typing && (t * 3.0).sin() > 0.0 {
740 let ax = x + w - 26.0; let ay = y + h - 22.0;
741 let mut gfx = self.gfx.borrow_mut();
742 let (bw, bh) = (gfx.width, gfx.height);
743 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, bw, bh, 0x00D2FF, false,
744 std::slice::from_ref(&vec![[ax-7.0,ay],[ax+7.0,ay],[ax,ay+9.0],[ax-7.0,ay]]));
745 }
746 }
747
748 #[cfg(not(target_arch = "wasm32"))]
751 fn ensure_music(&mut self) -> bool {
752 if self.music.is_some() { return true; }
753 if self.music_init { return false; }
754 self.music_init = true;
755 match ling_music::MusicEngine::new() {
756 Ok(m) => { self.music = Some(m); true }
757 Err(e) => { eprintln!("music engine init failed (no music playback): {e}"); false }
758 }
759 }
760
761 #[cfg(not(target_arch = "wasm32"))]
765 fn font_layout_2d(&mut self, id: usize, x: f32, y: f32, px: f32, text: &str) -> Vec<Vec<[f32; 2]>> {
766 let mut out = Vec::new();
767 for g in self.font_layout_2d_glyphs(id, x, y, px, text) { out.extend(g); }
768 out
769 }
770
771 #[cfg(not(target_arch = "wasm32"))]
774 fn font_layout_2d_glyphs(&mut self, id: usize, x: f32, y: f32, px: f32, text: &str) -> Vec<Vec<Vec<[f32; 2]>>> {
775 let font = &mut self.fonts[id];
776 let asc = font.ascent();
777 let tol = 0.3 / px;
778 let mut pen = 0.0f32;
779 let mut glyphs = Vec::new();
780 for ch in text.chars() {
781 let go = font.glyph_outline(ch, tol);
782 let mut contours = Vec::with_capacity(go.polylines.len());
783 for pl in &go.polylines {
784 let mapped: Vec<[f32; 2]> = pl.iter()
785 .map(|p| [x + (pen + p[0]) * px, y + (asc - p[1]) * px])
786 .collect();
787 contours.push(mapped);
788 }
789 glyphs.push(contours);
790 pen += go.advance;
791 }
792 glyphs
793 }
794
795 pub fn run_program(&mut self, program: &Program) -> Result<(), String> {
796 for item in &program.items {
797 self.register_item("", item)?;
798 }
799 let entry = self.find_entry()
800 .ok_or("no entry point — need `bind start = do {...}` or `ผูก เริ่ม = ทำ {...}`")?;
801 let mut env = Env::new();
804 let non_do: Vec<_> = self.globals.iter()
805 .filter(|(_, e)| !matches!(e, Expr::Do(_)))
806 .map(|(k, e)| (k.clone(), e.clone()))
807 .collect();
808 let mut pending: Vec<(String, Expr)> = Vec::new();
809 for (k, expr) in &non_do {
810 let mut tmp = Env::new();
811 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
812 env.insert(k.clone(), v);
813 } else {
814 pending.push((k.clone(), expr.clone()));
815 }
816 }
817 for (k, expr) in &pending {
818 let mut tmp = env.clone();
819 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
820 env.insert(k.clone(), v);
821 }
822 }
823 self.global_seed = env.clone();
826 self.framed("start", |me| me.eval_expr(&entry, &mut env)).map(|_| ()).map_err(|e| match e {
827 EvalErr::Runtime(s) => s,
828 EvalErr::Return(_) => "unexpected top-level return".to_string(),
829 EvalErr::Break => "unexpected break at top level".to_string(),
830 })
831 }
832
833 fn register_item(&mut self, ns: &str, item: &Item) -> Result<(), String> {
834 match item {
835 Item::Bind(name, expr) => {
836 let key = if ns.is_empty() { name.clone() } else { format!("{ns}::{name}") };
837 self.globals.insert(key, expr.clone());
838 }
839 Item::Fn(def) => {
840 let key = if ns.is_empty() { def.name.clone() } else { format!("{ns}::{}", def.name) };
841 self.functions.insert(key, def.clone());
842 }
843 Item::Mod(name, body) => {
844 let child_ns = if ns.is_empty() { name.clone() } else { format!("{ns}::{name}") };
845 for child in body {
846 self.register_item(&child_ns, child)?;
847 }
848 }
849 Item::TypeAlias(_, _) => {}
850 Item::Struct(name, fields) => {
851 self.structs.insert(name.clone(), fields.clone());
852 if !ns.is_empty() { self.structs.insert(format!("{ns}::{name}"), fields.clone()); }
853 }
854 Item::Enum(name, variants) => {
855 for v in variants {
856 self.enum_variants.insert(v.name.clone(), (name.clone(), v.arity));
857 self.enum_variants.insert(format!("{name}::{}", v.name), (name.clone(), v.arity));
858 if !ns.is_empty() {
859 self.enum_variants.insert(format!("{ns}::{name}::{}", v.name), (name.clone(), v.arity));
860 }
861 }
862 }
863 Item::Use { path, alias } => {
864 self.load_module(path, alias.as_deref(), ns)?;
865 }
866 }
867 Ok(())
868 }
869
870 fn load_module(&mut self, path: &str, alias: Option<&str>, parent_ns: &str) -> Result<(), String> {
875 let base_dir = self.source_dir.clone().unwrap_or_else(|| std::path::PathBuf::from("."));
877 let raw = std::path::Path::new(path);
878 let candidates: Vec<std::path::PathBuf> = vec![
879 base_dir.join(format!("{}.ling", path)),
880 base_dir.join(format!("{}.灵", path)),
881 base_dir.join(format!("{}.령", path)),
882 base_dir.join(format!("{}.霊", path)),
883 base_dir.join(format!("{}.ลิง", path)),
884 base_dir.join(raw),
886 std::path::PathBuf::from(format!("{}.ling", path)),
887 std::path::PathBuf::from(path),
888 ];
889
890 let resolved = candidates.into_iter().find(|p| p.exists())
891 .ok_or_else(|| format!("use: cannot find module '{path}'"))?;
892
893 let canonical = resolved.canonicalize()
894 .unwrap_or_else(|_| resolved.clone())
895 .to_string_lossy()
896 .to_string();
897
898 if self.loaded_files.contains(&canonical) {
900 return Ok(());
901 }
902 self.loaded_files.insert(canonical.clone());
903
904 let source = std::fs::read_to_string(&resolved)
905 .map_err(|e| format!("use: failed to read '{path}': {e}"))?;
906
907 let prev_dir = self.source_dir.clone();
909 self.source_dir = resolved.parent().map(|p| p.to_path_buf());
910
911 let program = crate::parser::parse(&source)
912 .map_err(|e| format!("use: parse error in '{path}': {e}"))?;
913
914 let target_ns = match (parent_ns.is_empty(), alias) {
916 (_, Some(a)) if !parent_ns.is_empty() => format!("{parent_ns}::{a}"),
917 (_, Some(a)) => a.to_string(),
918 (false, None) => parent_ns.to_string(),
919 (true, None) => String::new(),
920 };
921
922 for item in &program.items {
923 self.register_item(&target_ns, item)?;
924 }
925
926 self.source_dir = prev_dir;
927 Ok(())
928 }
929
930 fn find_entry(&self) -> Option<Expr> {
931 for key in &[
933 "start", "main",
934 "启",
935 "เริ่ม", "시작",
937 "начать", "начало",
938 "inicio", "comenzar",
939 "début", "commencer",
940 "anfang", "starten",
941 "início",
942 "शुरू",
943 "ابدأ",
944 ] {
945 if let Some(e) = self.globals.get(*key) { return Some(e.clone()); }
946 }
947 self.globals.values().find(|e| matches!(e, Expr::Do(_))).cloned()
948 }
949
950 fn eval_expr(&mut self, expr: &Expr, env: &mut Env) -> EvalResult {
953 match expr {
954 Expr::Str(s) => Ok(Value::Str(s.clone())),
955 Expr::Number(n) => Ok(Value::Number(*n)),
956 Expr::Bool(b) => Ok(Value::Bool(*b)),
957 Expr::Unit => Ok(Value::Unit),
958 Expr::Array(elems) => {
959 let vs: Vec<_> = elems.iter()
960 .map(|e| self.eval_expr(e, env))
961 .collect::<Result<_,_>>()?;
962 Ok(Value::List(vs))
963 }
964
965 Expr::Ident(name) => self.lookup(name, env),
966
967 Expr::Path(segs) => {
968 if segs.len() == 1 { return self.lookup(&segs[0], env); }
969 Ok(Value::Str(segs.join("::")))
970 }
971
972 Expr::Ref(inner) => self.eval_expr(inner, env),
973 Expr::Await(inner) => self.eval_expr(inner, env),
974
975 Expr::Do(stmts) => {
976 let mut local = env.clone();
977 Ok(self.exec_block(stmts, &mut local)?.unwrap_or(Value::Unit))
978 }
979
980 Expr::BinOp(op, lhs, rhs) => {
981 let l = self.eval_expr(lhs, env)?;
982 let r = self.eval_expr(rhs, env)?;
983 self.apply_binop(op, l, r)
984 }
985
986 Expr::If { cond, then, elseifs, else_body } => {
987 let cond_val = self.eval_expr(cond, env)?;
988 if self.is_truthy(&cond_val) {
989 return Ok(self.exec_block(then, env)?.unwrap_or(Value::Unit));
990 }
991 for (ei_cond, ei_body) in elseifs {
992 let ei_cond_val = self.eval_expr(ei_cond, env)?;
993 if self.is_truthy(&ei_cond_val) {
994 return Ok(self.exec_block(ei_body, env)?.unwrap_or(Value::Unit));
995 }
996 }
997 if let Some(eb) = else_body {
998 return Ok(self.exec_block(eb, env)?.unwrap_or(Value::Unit));
999 }
1000 Ok(Value::Unit)
1001 }
1002
1003 Expr::While { cond, body } => {
1004 loop {
1008 let cv = self.eval_expr(cond, env)?;
1009 if !self.is_truthy(&cv) { break; }
1010 match self.exec_block(body, env) {
1011 Ok(_) => {}
1012 Err(EvalErr::Break) => break,
1013 Err(e) => return Err(e),
1014 }
1015 }
1016 Ok(Value::Unit)
1017 }
1018
1019 Expr::For { var, iter, body } => {
1020 let iter_val = self.eval_expr(iter, env)?;
1021 let items = self.value_to_iter(iter_val)?;
1022 for item in items {
1023 let mut local = env.clone();
1024 local.insert(var.clone(), item);
1025 match self.exec_block(body, &mut local) {
1026 Ok(_) => {}
1027 Err(EvalErr::Break) => break,
1028 Err(e) => return Err(e),
1029 }
1030 }
1031 Ok(Value::Unit)
1032 }
1033
1034 Expr::Match(subject, arms) => {
1035 let subj = self.eval_expr(subject, env)?;
1036 for arm in arms {
1037 if let Some(bindings) = self.match_pattern(&arm.pattern, &subj) {
1038 let mut local = env.clone();
1039 local.extend(bindings);
1040 return self.eval_expr(&arm.body, &mut local);
1041 }
1042 }
1043 Ok(Value::Unit)
1044 }
1045
1046 Expr::Range(lo, hi) => {
1047 let lo_v = self.eval_expr(lo, env)?;
1048 let hi_v = self.eval_expr(hi, env)?;
1049 let lo_n = self.to_number(&lo_v)? as i64;
1050 let hi_n = self.to_number(&hi_v)? as i64;
1051 Ok(Value::List((lo_n..hi_n).map(|i| Value::Number(i as f64)).collect()))
1052 }
1053
1054 Expr::Index(base, idx) => {
1055 let b = self.eval_expr(base, env)?;
1056 let i = self.eval_expr(idx, env)?;
1057 let n = self.to_number(&i)? as usize;
1058 match b {
1059 Value::List(v) => v.get(n).cloned()
1060 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
1061 Value::Str(s) => s.chars().nth(n)
1062 .map(|c| Value::Str(c.to_string()))
1063 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
1064 other => Err(EvalErr::from(format!("cannot index {:?}", other))),
1065 }
1066 }
1067
1068 Expr::Call(callee, args) => {
1069 let arg_vals: Vec<Value> = args.iter()
1070 .map(|a| self.eval_expr(a, env))
1071 .collect::<Result<_,_>>()?;
1072 match callee.as_ref() {
1073 Expr::Ident(name) => self.call_named(name, arg_vals, env),
1074 Expr::Path(segs) => self.call_named(&segs.join("::"), arg_vals, env),
1075 _ => {
1076 let v = self.eval_expr(callee, env)?;
1077 self.call_value(v, arg_vals)
1078 }
1079 }
1080 }
1081
1082 Expr::MethodCall { receiver, method, args } => {
1083 let recv = self.eval_expr(receiver, env)?;
1084 let arg_vals: Vec<Value> = args.iter()
1085 .map(|a| self.eval_expr(a, env))
1086 .collect::<Result<_,_>>()?;
1087 self.call_method(recv, method, arg_vals)
1088 }
1089
1090 Expr::Closure(params, body) => {
1091 Ok(Value::Fn(params.clone(), vec![Stmt::Expr(*body.clone())], env.clone()))
1092 }
1093 }
1094 }
1095
1096 fn exec_block(&mut self, stmts: &[Stmt], env: &mut Env) -> Result<Option<Value>, EvalErr> {
1099 let mut last: Option<Value> = None;
1100 for stmt in stmts {
1101 match stmt {
1102 Stmt::Bind(name, expr) => {
1103 let v = self.eval_expr(expr, env)?;
1104 env.insert(name.clone(), v);
1105 last = None;
1106 }
1107 Stmt::Return(expr) => {
1108 let v = self.eval_expr(expr, env)?;
1109 return Err(EvalErr::Return(v));
1110 }
1111 Stmt::Expr(expr) => {
1112 last = Some(self.eval_expr(expr, env)?);
1113 }
1114 }
1115 }
1116 Ok(last)
1117 }
1118
1119 fn lookup(&self, name: &str, env: &Env) -> EvalResult {
1122 if let Some(v) = env.get(name) { return Ok(v.clone()); }
1123 if self.functions.contains_key(name) {
1124 let def = &self.functions[name];
1125 return Ok(Value::Fn(def.params.clone(), def.body.clone(), Env::new()));
1126 }
1127 if let Some((enum_name, 0)) = self.enum_variants.get(name).cloned() {
1129 let variant = name.rsplit("::").next().unwrap_or(name).to_string();
1130 return Ok(Value::Variant { enum_name, variant, payload: Vec::new() });
1131 }
1132 match name {
1134 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" => return Ok(Value::Number(std::f64::consts::PI)),
1135 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" => return Ok(Value::Number(std::f64::consts::TAU)),
1136 _ => {}
1137 }
1138 Err(EvalErr::from(format!("undefined: '{name}'")))
1139 }
1140
1141 fn call_named(&mut self, name: &str, args: Vec<Value>, env: &Env) -> EvalResult {
1142 match name {
1143 "print" | "println" | "印" | "打印" | "印刷" | "พิมพ์" | "출력" | "вывести" | "imprimir" | "afficher" => {
1145 let s = args.iter().map(|v| v.to_string()).collect::<Vec<_>>().join("");
1146 println!("{s}");
1147 return Ok(Value::Unit);
1148 }
1149 "print_color" | "พิมพ์สี" => {
1152 #[cfg(windows)]
1153 {
1154 use std::sync::Once;
1155 static VT: Once = Once::new();
1156 VT.call_once(|| {
1157 extern "system" {
1158 fn GetStdHandle(n: u32) -> *mut std::ffi::c_void;
1159 fn GetConsoleMode(h: *mut std::ffi::c_void, m: *mut u32) -> i32;
1160 fn SetConsoleMode(h: *mut std::ffi::c_void, m: u32) -> i32;
1161 }
1162 unsafe {
1163 let h = GetStdHandle(0xFFFF_FFF5u32); let mut mode = 0u32;
1165 if GetConsoleMode(h, &mut mode) != 0 {
1166 SetConsoleMode(h, mode | 0x0004); }
1168 }
1169 });
1170 }
1171 let col = self.arg_num(&args, 0, 7.0)? as i64;
1172 let s = args.iter().skip(1).map(|v| v.to_string()).collect::<Vec<_>>().join("");
1173 let code = 90 + col.clamp(0, 7);
1174 println!("\x1b[1;{code}m{s}\x1b[0m");
1175 return Ok(Value::Unit);
1176 }
1177 "format" | "格式" | "フォーマット" | "서식" | "รูปแบบ" | "форматировать" | "formatear" | "formater" => {
1179 return Ok(Value::Str(self.builtin_format(&args)?));
1180 }
1181 "格式::拼接" | "format::join" => {
1183 match args.first() {
1184 Some(Value::List(items)) => {
1185 return Ok(Value::Str(items.iter().map(|v| v.to_string()).collect()));
1186 }
1187 _ => return Ok(Value::Str(self.builtin_format(&args)?)),
1188 }
1189 }
1190 "ok" | "好" | "良し" | "좋아" | "โอเค" => {
1192 let val = args.into_iter().next().unwrap_or(Value::Unit);
1193 return Ok(Value::Ok(Box::new(val)));
1194 }
1195 "bad" | "坏" | "err" | "悪い" | "나쁨" | "ผิด" => {
1196 let val = args.into_iter().next().unwrap_or(Value::Unit);
1197 return Ok(Value::Err(Box::new(val)));
1198 }
1199 "向量::从" | "Vec::from" => {
1201 if let Some(Value::List(v)) = args.first() {
1202 return Ok(Value::List(v.clone()));
1203 }
1204 return Ok(Value::List(args));
1205 }
1206 "向量::有容量" | "Vec::with_capacity" => return Ok(Value::List(Vec::new())),
1207 "计时::获取当前小时" | "Timer::hour" => return Ok(Value::Number(14.0)),
1209 "计时::现在" | "Timer::now" => return Ok(Value::Number(1000.0)),
1210 "sleep" | "หยุด" | "นอน" | "sleep_ms" | "睡眠" | "眠る" | "スリープ" | "잠자기" | "잠" | "流水::睡眠" | "Flow::sleep" => {
1212 if let Some(ms_val) = args.first() {
1213 if let Ok(ms) = self.to_number(ms_val) {
1214 std::thread::sleep(std::time::Duration::from_millis(ms as u64));
1215 }
1216 }
1217 return Ok(Value::Unit);
1218 }
1219 "流水::并行" | "Flow::parallel" => {
1221 if let Some(Value::Fn(params, body, mut cap)) = args.first().cloned() {
1222 let _ = params;
1223 match self.exec_block(&body, &mut cap) {
1224 Ok(Some(v)) => return Ok(v),
1225 Ok(None) => return Ok(Value::Unit),
1226 Err(EvalErr::Return(v)) => return Ok(v),
1227 Err(e) => return Err(e),
1228 }
1229 }
1230 return Ok(Value::Unit);
1231 }
1232
1233 "sin" | "ไซน์" | "正弦" | "サイン" | "사인" => {
1242 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sin()));
1243 }
1244 "cos" | "โคไซน์" | "余弦" | "コサイン" | "코사인" => {
1245 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cos()));
1246 }
1247
1248 "tanh" | "tanhf" | "双曲正切" | "双曲線正接" | "쌍곡탄젠트" => {
1251 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tanh()));
1252 }
1253
1254
1255 "tan" | "แทนเจนต์" | "正切" | "タンジェント" | "탄젠트" => {
1256 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tan()));
1257 }
1258 "asin" | "arcsin" | "反正弦" | "アークサイン" | "아크사인" | "อาร์กไซน์" => {
1259 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.asin()));
1260 }
1261 "acos" | "arccos" | "反余弦" | "アークコサイン" | "아크코사인" | "อาร์กโคไซน์" => {
1262 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.acos()));
1263 }
1264 "atan" | "arctan" | "反正切" | "アークタンジェント" | "아크탄젠트" | "อาร์กแทนเจนต์" => {
1265 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.atan()));
1266 }
1267 "atan2" | "arctan2" | "反正切2" | "アークタンジェント2" | "아크탄젠트2" => {
1268 let y = self.arg_num(&args, 0, 0.0)?;
1269 let x = self.arg_num(&args, 1, 1.0)?;
1270 return Ok(Value::Number(y.atan2(x)));
1271 }
1272
1273 "sqrt" | "รากที่สอง" | "平方根" | "根" | "제곱근" => {
1275 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sqrt()));
1276 }
1277 "cbrt" | "立方根" | "세제곱근" | "รากที่สาม" => {
1278 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cbrt()));
1279 }
1280 "pow" | "ยกกำลัง" | "幂" | "べき乗" | "거듭제곱" => {
1281 let base = self.arg_num(&args, 0, 0.0)?;
1282 let exp = self.arg_num(&args, 1, 1.0)?;
1283 return Ok(Value::Number(base.powf(exp)));
1284 }
1285 "exp" | "指数" | "指数関数" | "지수" => {
1286 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.exp()));
1287 }
1288 "hypot" | "斜边" | "斜辺" | "빗변" => {
1289 let x = self.arg_num(&args, 0, 0.0)?;
1290 let y = self.arg_num(&args, 1, 0.0)?;
1291 return Ok(Value::Number(x.hypot(y)));
1292 }
1293
1294 "ln" | "log" | "ลอการิทึม" | "对数" | "対数" | "로그" => {
1296 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.ln()));
1297 }
1298 "log2" | "对数2" | "対数2" | "로그2" => {
1299 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log2()));
1300 }
1301 "log10" | "对数10" | "対数10" | "로그10" => {
1302 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log10()));
1303 }
1304
1305 "abs" | "ค่าสัมบูรณ์" | "绝对值" | "绝对" | "絶対値" | "절댓값" | "절대값" => {
1307 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.abs()));
1308 }
1309 "floor" | "ปัดลง" | "向下取整" | "下整" | "床関数" | "내림" => {
1310 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.floor()));
1311 }
1312 "ceil" | "ปัดขึ้น" | "向上取整" | "上整" | "天井関数" | "올림" => {
1313 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.ceil()));
1314 }
1315 "round" | "ปัดเศษ" | "四舍五入" | "四舍" | "四捨五入" | "반올림" => {
1316 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.round()));
1317 }
1318 "trunc" | "int" | "ตัดทศนิยม" | "取整" | "整数化" | "整数" | "截整"
1319 | "정수화" | "정수" | "切り捨て" | "버림" => {
1320 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.trunc()));
1321 }
1322 "fract" | "小数部分" | "小数部" | "소수부" => {
1323 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.fract()));
1324 }
1325
1326 "min" | "ต่ำสุด" | "最小" | "최솟값" => {
1328 let a = self.arg_num(&args, 0, 0.0)?;
1329 let b = self.arg_num(&args, 1, 0.0)?;
1330 return Ok(Value::Number(a.min(b)));
1331 }
1332 "max" | "สูงสุด" | "最大" | "최댓값" => {
1333 let a = self.arg_num(&args, 0, 0.0)?;
1334 let b = self.arg_num(&args, 1, 0.0)?;
1335 return Ok(Value::Number(a.max(b)));
1336 }
1337 "clamp" | "จำกัด" | "截取" | "範囲制限" | "범위제한" => {
1338 let x = self.arg_num(&args, 0, 0.0)?;
1339 let lo = self.arg_num(&args, 1, 0.0)?;
1340 let hi = self.arg_num(&args, 2, 1.0)?;
1341 return Ok(Value::Number(x.clamp(lo, hi)));
1342 }
1343
1344 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" => return Ok(Value::Number(std::f64::consts::PI)),
1346 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" => return Ok(Value::Number(std::f64::consts::TAU)),
1347
1348 "vnoise" | "noise2" | "นอยส์2ดี" | "柏林噪声2D" | "バリューノイズ2D" | "값노이즈2D" => {
1354 let x = self.arg_num(&args, 0, 0.0)? as f32;
1355 let y = self.arg_num(&args, 1, 0.0)? as f32;
1356 let seed = self.arg_num(&args, 2, 0.0)? as u32;
1357 return Ok(Value::Number(tex_vnoise(x, y, seed) as f64));
1358 }
1359
1360 "fbm" | "นอยส์ออร์แกนิก" | "分形噪声" | "フラクタルノイズ" | "프랙탈노이즈" => {
1361 let x = self.arg_num(&args, 0, 0.0)? as f32;
1362 let y = self.arg_num(&args, 1, 0.0)? as f32;
1363 let octaves = self.arg_num(&args, 2, 4.0)? as u32;
1364 let seed = self.arg_num(&args, 3, 0.0)? as u32;
1365 return Ok(Value::Number(tex_fbm(x, y, octaves, seed) as f64));
1366 }
1367
1368 "perlin" | "perlin3" | "เพอร์ลิน3ดี" | "柏林噪声3D" | "パーリンノイズ3D" | "펄린노이즈3D" => {
1369 let x = self.arg_num(&args, 0, 0.0)? as f32;
1370 let y = self.arg_num(&args, 1, 0.0)? as f32;
1371 let z = self.arg_num(&args, 2, 0.0)? as f32;
1372 return Ok(Value::Number(perlin3(x, y, z) as f64));
1373 }
1374
1375 "lerp" | "ค่าระหว่าง" | "线性插值" | "線形補間" | "선형보간" => {
1377 let a = self.arg_num(&args, 0, 0.0)?;
1378 let b = self.arg_num(&args, 1, 1.0)?;
1379 let t = self.arg_num(&args, 2, 0.0)?;
1380 return Ok(Value::Number(a + (b - a) * t));
1381 }
1382
1383 "smoothstep" | "เปลี่ยนแบบนุ่ม" | "平滑步进" | "スムーズステップ" | "스무스스텝" => {
1384 let lo = self.arg_num(&args, 0, 0.0)?;
1385 let hi = self.arg_num(&args, 1, 1.0)?;
1386 let x = self.arg_num(&args, 2, 0.5)?;
1387 let t = ((x - lo) / (hi - lo)).clamp(0.0, 1.0);
1388 return Ok(Value::Number(t * t * (3.0 - 2.0 * t)));
1389 }
1390
1391 "rand" | "สุ่ม" | "随机" | "乱数" | "난수" => {
1392 let val = fast_rand_f64(&mut self.rand_state);
1393 return Ok(Value::Number(val));
1394 }
1395
1396 "sign" | "เครื่องหมาย" | "符号" | "符号関数" | "부호" => {
1397 let x = self.arg_num(&args, 0, 0.0)?;
1398 return Ok(Value::Number(x.signum()));
1399 }
1400
1401 "hsv_to_rgb" | "เอชเอสวีเป็นRGB" | "HSV转RGB" | "HSV変換RGB" | "HSV변환RGB" => {
1402 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;
1406 let x = c * (1.0 - (((h / 60.0) % 2.0) - 1.0).abs());
1407 let m = v - c;
1408 let (r1, g1, b1) = if h < 60.0 { (c, x, 0.0) }
1409 else if h < 120.0 { (x, c, 0.0) }
1410 else if h < 180.0 { (0.0, c, x) }
1411 else if h < 240.0 { (0.0, x, c) }
1412 else if h < 300.0 { (x, 0.0, c) }
1413 else { (c, 0.0, x) };
1414 let r = ((r1 + m) * 255.0).round();
1415 let g = ((g1 + m) * 255.0).round();
1416 let b = ((b1 + m) * 255.0).round();
1417 return Ok(Value::List(vec![Value::Number(r), Value::Number(g), Value::Number(b)]));
1418 }
1419
1420 "lerp_color" | "ไล่สี" | "颜色插值" | "色補間" | "색보간" => {
1421 let r1 = self.arg_num(&args, 0, 0.0)?;
1422 let g1 = self.arg_num(&args, 1, 0.0)?;
1423 let b1 = self.arg_num(&args, 2, 0.0)?;
1424 let r2 = self.arg_num(&args, 3, 255.0)?;
1425 let g2 = self.arg_num(&args, 4, 255.0)?;
1426 let b2 = self.arg_num(&args, 5, 255.0)?;
1427 let t = self.arg_num(&args, 6, 0.0)?;
1428 let r = r1 + (r2 - r1) * t;
1429 let g = g1 + (g2 - g1) * t;
1430 let b = b1 + (b2 - b1) * t;
1431 let c = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
1432 self.gfx.borrow_mut().color = c;
1433 return Ok(Value::Unit);
1434 }
1435
1436 "time_now" | "เวลาปัจจุบัน" | "当前时间" | "経過時間" | "현재시간" => {
1438 return Ok(Value::Number(self.start_time.elapsed().as_secs_f64()));
1439 }
1440
1441 "frame_count" | "เฟรม" | "帧数" | "フレーム数" | "프레임수" => {
1442 return Ok(Value::Number(self.frame_num as f64));
1443 }
1444
1445 "mic_open" | "เปิดไมค์" | "开麦克风" | "マイク開く" | "마이크열기" => {
1447 #[cfg(not(target_arch = "wasm32"))]
1448 {
1449 match ling_mic::MicInput::open(Default::default()) {
1450 Ok(mic) => {
1451 let _ = mic.start(|_samples: &[f32]| {}); self.mic = Some(mic);
1453 return Ok(Value::Number(1.0)); }
1455 Err(_e) => { self.mic = None; return Ok(Value::Number(0.0)); }
1458 }
1459 }
1460 #[cfg(target_arch = "wasm32")]
1461 return Ok(Value::Unit);
1462 }
1463
1464 "mic_rms" | "เสียงRMS" | "麦克风音量" | "マイクRMS" | "마이크RMS" => {
1465 #[cfg(not(target_arch = "wasm32"))]
1466 {
1467 let rms = self.mic.as_ref().map(|m: &ling_mic::MicInput| m.rms()).unwrap_or(0.0);
1468 return Ok(Value::Number(rms as f64));
1469 }
1470 #[cfg(target_arch = "wasm32")]
1471 return Ok(Value::Number(0.0));
1472 }
1473
1474 "mic_peak" | "เสียงพีค" | "麦克风峰值" | "マイクピーク" | "마이크피크" => {
1475 #[cfg(not(target_arch = "wasm32"))]
1476 {
1477 let peak = self.mic.as_ref().map(|m: &ling_mic::MicInput| m.peak()).unwrap_or(0.0);
1478 return Ok(Value::Number(peak as f64));
1479 }
1480 #[cfg(target_arch = "wasm32")]
1481 return Ok(Value::Number(0.0));
1482 }
1483
1484 "mic_fft" | "วิเคราะห์เสียงสด" | "实时频谱" | "リアルタイムFFT" | "실시간FFT" => {
1485 #[cfg(not(target_arch = "wasm32"))]
1486 {
1487 let n = self.arg_num(&args, 0, 8.0)? as usize;
1488 if let Some(mic) = self.mic.as_ref() {
1489 let samples = mic.latest_samples();
1490 self.fft.borrow_mut().push_samples(&samples);
1491 }
1492 let bands = self.fft.borrow().freq_bands(n);
1493 let result = bands.iter().map(|&v| Value::Number(v as f64)).collect();
1494 return Ok(Value::List(result));
1495 }
1496 #[cfg(target_arch = "wasm32")]
1497 return Ok(Value::List(vec![]));
1498 }
1499
1500 "set_blend" | "โหมดผสม" | "混合模式" | "ブレンドモード" | "블렌드모드" => {
1502 let mode = self.arg_num(&args, 0, 0.0)? as u8;
1503 self.gfx.borrow_mut().blend = mode;
1504 return Ok(Value::Unit);
1505 }
1506
1507 "draw_circle" | "วาดวงกลม" | "画圆" | "円描画" | "원그리기" => {
1509 let cx = self.arg_num(&args, 0, 0.0)? as i32;
1510 let cy = self.arg_num(&args, 1, 0.0)? as i32;
1511 let r = self.arg_num(&args, 2, 10.0)? as i32;
1512 let mut gfx = self.gfx.borrow_mut();
1513 let (w, h, color, blend) = (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
1514 draw_circle_outline(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
1515 return Ok(Value::Unit);
1516 }
1517
1518 "draw_filled_circle" | "draw_disc" | "วาดวงกลมทึบ" | "画实心圆" | "塗りつぶし円" | "원채우기" => {
1519 let cx = self.arg_num(&args, 0, 0.0)? as i32;
1520 let cy = self.arg_num(&args, 1, 0.0)? as i32;
1521 let r = self.arg_num(&args, 2, 10.0)? as i32;
1522 let mut gfx = self.gfx.borrow_mut();
1523 let (w, h, color, blend) = (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
1524 draw_circle_filled(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
1525 return Ok(Value::Unit);
1526 }
1527
1528 "เปิดหน้าต่าง" | "open_window" | "gfx_window" | "开窗" | "ウィンドウ開く" | "창열기" => {
1535 let w = self.arg_num(&args, 0, 800.0)? as usize;
1536 let h = self.arg_num(&args, 1, 600.0)? as usize;
1537 #[cfg(not(target_arch = "wasm32"))]
1538 {
1539 let title = args.get(2).map(|v| v.to_string()).unwrap_or_else(|| "Ling".into());
1540 let mut gfx = self.gfx.borrow_mut();
1541 let mut win = minifb::Window::new(
1542 &title, w, h,
1543 minifb::WindowOptions {
1544 resize: false,
1545 scale: minifb::Scale::X1,
1546 ..Default::default()
1547 },
1548 ).map_err(|e| EvalErr::from(format!("cannot open window: {e}")))?;
1549 #[allow(deprecated)]
1550 win.limit_update_rate(Some(std::time::Duration::from_millis(8)));
1551 gfx.buffer = vec![0u32; w * h];
1552 gfx.width = w;
1553 gfx.height = h;
1554 gfx.window = Some(win);
1555 gfx.sync_projection();
1556 hide_console_window();
1557 }
1558 #[cfg(target_arch = "wasm32")]
1559 {
1560 let mut gfx = self.gfx.borrow_mut();
1561 gfx.width = w;
1562 gfx.height = h;
1563 gfx.buffer.resize(w * h, 0); gfx.sync_projection();
1565 crate::gfx::webgl::resize(w as u32, h as u32);
1566 }
1567 return Ok(Value::Unit);
1568 }
1569
1570 "เติม" | "fill" | "gfx_fill" | "clear" | "填" | "塗り潰し" | "채우기" | "清" | "消去" | "지우기" => {
1572 let r = self.arg_num(&args, 0, 0.0)? as u32;
1573 let g = self.arg_num(&args, 1, 0.0)? as u32;
1574 let b = self.arg_num(&args, 2, 0.0)? as u32;
1575 #[cfg(not(target_arch = "wasm32"))]
1576 {
1577 let c = (r << 16) | (g << 8) | b;
1578 self.gfx.borrow_mut().buffer.fill(c);
1579 }
1580 #[cfg(target_arch = "wasm32")]
1581 {
1582 let mut gfx = self.gfx.borrow_mut();
1583 gfx.fill_r = r as f32 / 255.0;
1584 gfx.fill_g = g as f32 / 255.0;
1585 gfx.fill_b = b as f32 / 255.0;
1586 let c = (r << 16) | (g << 8) | b;
1589 gfx.buffer.fill(c);
1590 }
1591 return Ok(Value::Unit);
1592 }
1593
1594 "set_color_hsl" | "颜色HSL" | "色相" | "HSL色" | "HSL색설정" | "สีHSLวาด" => {
1597 let h = self.arg_num(&args, 0, 0.0)?;
1598 let s = self.arg_num(&args, 1, 70.0)?;
1599 let l = self.arg_num(&args, 2, 50.0)?;
1600 let hex = hsl_to_hex(h, s, l);
1601 let r = u32::from_str_radix(&hex[1..3], 16).unwrap_or(255);
1602 let g = u32::from_str_radix(&hex[3..5], 16).unwrap_or(255);
1603 let b = u32::from_str_radix(&hex[5..7], 16).unwrap_or(255);
1604 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
1605 return Ok(Value::Unit);
1606 }
1607
1608 "สีดินสอ" | "set_color" | "gfx_color" | "color" | "设色" | "色設定" | "색설정" => {
1610 let r = self.arg_num(&args, 0, 255.0)? as u32;
1611 let g = self.arg_num(&args, 1, 255.0)? as u32;
1612 let b = self.arg_num(&args, 2, 255.0)? as u32;
1613 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
1614 return Ok(Value::Unit);
1615 }
1616
1617 "วาดสามเหลี่ยม" | "draw_triangle" | "gfx_triangle" | "triangle" | "画三角" | "三角形描画" | "삼각형그리기" => {
1619 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
1620 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
1621 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
1622 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
1623 let x2 = self.arg_num(&args, 4, 0.0)? as f32;
1624 let y2 = self.arg_num(&args, 5, 0.0)? as f32;
1625 let mut gfx = self.gfx.borrow_mut();
1626 let color = gfx.color;
1627 #[cfg(not(target_arch = "wasm32"))]
1628 {
1629 let w = gfx.width;
1630 let h = gfx.height;
1631 fill_triangle(&mut gfx.buffer, w, h, color, x0, y0, x1, y1, x2, y2);
1632 }
1633 #[cfg(target_arch = "wasm32")]
1634 gfx.depth_queue.push_triangle(0.0, color, x0, y0, x1, y1, x2, y2);
1635 return Ok(Value::Unit);
1636 }
1637
1638 "วาดเส้น" | "draw_line" | "gfx_line" | "line" | "画线" | "線描く" | "선그리기" => {
1640 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
1641 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
1642 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
1643 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
1644 let mut gfx = self.gfx.borrow_mut();
1645 let color = gfx.color;
1646 #[cfg(not(target_arch = "wasm32"))]
1647 {
1648 let w = gfx.width;
1649 let h = gfx.height;
1650 draw_line(&mut gfx.buffer, w, h, color, x0, y0, x1, y1);
1651 }
1652 #[cfg(target_arch = "wasm32")]
1653 gfx.depth_queue.push_line(0.0, color, x0, y0, x1, y1);
1654 return Ok(Value::Unit);
1655 }
1656
1657 "วาดจุด" | "draw_pixel" | "gfx_pixel" | "pixel" | "画点" | "点描く" | "점그리기" => {
1659 let px = self.arg_num(&args, 0, 0.0)? as i32;
1660 let py = self.arg_num(&args, 1, 0.0)? as i32;
1661 #[cfg(not(target_arch = "wasm32"))]
1662 {
1663 let mut gfx = self.gfx.borrow_mut();
1664 let color = gfx.color;
1665 let w = gfx.width;
1666 let h = gfx.height;
1667 if px >= 0 && py >= 0 && (px as usize) < w && (py as usize) < h {
1668 gfx.buffer[py as usize * w + px as usize] = color;
1669 }
1670 }
1671 #[cfg(target_arch = "wasm32")]
1672 {
1673 let mut gfx = self.gfx.borrow_mut();
1675 let color = gfx.color;
1676 let x = px as f32; let y = py as f32;
1677 gfx.depth_queue.push_triangle(0.0, color, x, y, x+1.0, y, x+1.0, y+1.0);
1678 gfx.depth_queue.push_triangle(0.0, color, x, y, x+1.0, y+1.0, x, y+1.0);
1679 }
1680 return Ok(Value::Unit);
1681 }
1682
1683 "แสดงผล" | "present" | "gfx_present" | "show" | "显" | "呈现" | "表示" | "표시" => {
1685 #[cfg(not(target_arch = "wasm32"))]
1686 {
1687 {
1689 let mut gfx = self.gfx.borrow_mut();
1690 if !gfx.depth_queue.is_empty() {
1691 let w = gfx.width;
1692 let h = gfx.height;
1693 let queue = std::mem::take(&mut gfx.depth_queue);
1694 queue.flush(&mut gfx.buffer, w, h);
1695 }
1696 let buf = gfx.buffer.clone();
1697 let w = gfx.width;
1698 let h = gfx.height;
1699 if let Some(win) = gfx.window.as_mut() {
1700 win.update_with_buffer(&buf, w, h)
1701 .map_err(|e| EvalErr::from(format!("present error: {e}")))?;
1702 }
1703 }
1704 let mouse_pos = {
1706 let gfx = self.gfx.borrow();
1707 gfx.window.as_ref()
1708 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
1709 };
1710 let mut gfx = self.gfx.borrow_mut();
1711 if gfx.mouse_captured {
1712 let w = gfx.width as f32;
1713 let h = gfx.height as f32;
1714 if let Some((mx, my)) = mouse_pos {
1715 if gfx.last_mx.is_nan() {
1716 gfx.mouse_dx = 0.0; gfx.mouse_dy = 0.0;
1717 gfx.last_mx = mx; gfx.last_my = my;
1718 } else {
1719 gfx.mouse_dx = mx - gfx.last_mx;
1720 gfx.mouse_dy = my - gfx.last_my;
1721 let margin = 6.0;
1724 let (mut nx, mut ny, mut warp) = (mx, my, false);
1725 if mx < margin { nx = w - margin - 2.0; warp = true; }
1726 else if mx > w - margin { nx = margin + 2.0; warp = true; }
1727 if my < margin { ny = h - margin - 2.0; warp = true; }
1728 else if my > h - margin { ny = margin + 2.0; warp = true; }
1729 if warp {
1730 #[cfg(windows)]
1731 unsafe {
1732 #[repr(C)]
1733 struct RECT { left: i32, top: i32, right: i32, bottom: i32 }
1734 extern "system" {
1735 fn GetForegroundWindow() -> isize;
1736 fn GetWindowRect(hwnd: isize, lpRect: *mut RECT) -> i32;
1737 fn SetCursorPos(x: i32, y: i32) -> i32;
1738 }
1739 let hwnd = GetForegroundWindow();
1740 let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0 };
1741 if GetWindowRect(hwnd, &mut rect) != 0 {
1742 SetCursorPos(rect.left + nx as i32, rect.top + ny as i32);
1743 }
1744 }
1745 gfx.last_mx = nx; gfx.last_my = ny;
1746 } else {
1747 gfx.last_mx = mx; gfx.last_my = my;
1748 }
1749 }
1750 } else {
1751 gfx.mouse_dx = 0.0; gfx.mouse_dy = 0.0;
1752 }
1753 } else if let Some((mx, my)) = mouse_pos {
1754 if gfx.last_mx.is_nan() {
1755 gfx.mouse_dx = 0.0; gfx.mouse_dy = 0.0;
1756 } else {
1757 gfx.mouse_dx = mx - gfx.last_mx;
1758 gfx.mouse_dy = my - gfx.last_my;
1759 }
1760 gfx.last_mx = mx; gfx.last_my = my;
1761 } else {
1762 gfx.mouse_dx = 0.0; gfx.mouse_dy = 0.0;
1763 }
1764 }
1765 #[cfg(target_arch = "wasm32")]
1766 {
1767 let mut gfx = self.gfx.borrow_mut();
1771 let w = gfx.width;
1772 let h = gfx.height;
1773 if gfx.buffer.len() != w * h {
1774 gfx.buffer.resize(w * h, 0);
1775 }
1776 if !gfx.depth_queue.is_empty() {
1777 let queue = std::mem::take(&mut gfx.depth_queue);
1778 queue.flush(&mut gfx.buffer, w, h);
1779 }
1780 crate::gfx::webgl::blit_rgb(&gfx.buffer, w, h);
1781 }
1782 #[cfg(not(target_arch = "wasm32"))]
1784 {
1785 let (_, _, down) = self.mouse_now();
1786 self.mouse_was_down = down;
1787 }
1788 self.frame_num += 1;
1790 return Ok(Value::Unit);
1791 }
1792
1793 "เปิดหน้าต่างเต็มจอ" | "open_fullscreen" | "fullscreen" | "全屏" | "全画面" | "전체화면" => {
1795 #[cfg(target_arch = "wasm32")]
1798 let (default_w, default_h) = {
1799 let (cw, ch) = crate::gfx::webgl::canvas_size();
1800 (cw as f64, ch as f64)
1801 };
1802 #[cfg(all(not(target_arch = "wasm32"), windows))]
1804 let (default_w, default_h) = unsafe {
1805 extern "system" { fn GetSystemMetrics(nIndex: i32) -> i32; }
1806 (GetSystemMetrics(0) as f64, GetSystemMetrics(1) as f64)
1807 };
1808 #[cfg(all(not(target_arch = "wasm32"), not(windows)))]
1809 let (default_w, default_h) = native_screen_size();
1810
1811 let w = args.get(1).map(|v| self.to_number(v).unwrap_or(default_w) as usize).unwrap_or(default_w as usize);
1812 let h = args.get(2).map(|v| self.to_number(v).unwrap_or(default_h) as usize).unwrap_or(default_h as usize);
1813 #[cfg(not(target_arch = "wasm32"))]
1814 {
1815 let title = args.get(0).map(|v| v.to_string()).unwrap_or_else(|| "Ling".into());
1816 let mut gfx = self.gfx.borrow_mut();
1817 let mut win = minifb::Window::new(
1818 &title, w, h,
1819 minifb::WindowOptions {
1820 borderless: true,
1821 title: false,
1822 resize: false,
1823 topmost: true,
1824 scale: minifb::Scale::X1,
1825 ..Default::default()
1826 },
1827 ).map_err(|e| EvalErr::from(format!("cannot open fullscreen: {e}")))?;
1828 win.set_target_fps(monitor_info().2.max(30) as usize);
1831 #[cfg(windows)]
1833 let hwnd = win.get_window_handle() as isize;
1834 gfx.buffer = vec![0u32; w * h];
1835 gfx.width = w;
1836 gfx.height = h;
1837 gfx.window = Some(win);
1838 gfx.sync_projection();
1839 #[cfg(windows)]
1841 make_borderless_fullscreen(hwnd, w as i32, h as i32);
1842 hide_console_window();
1843 }
1844 #[cfg(target_arch = "wasm32")]
1845 {
1846 let mut gfx = self.gfx.borrow_mut();
1847 gfx.width = w;
1848 gfx.height = h;
1849 gfx.buffer.resize(w * h, 0); gfx.sync_projection();
1851 crate::gfx::webgl::resize(w as u32, h as u32);
1852 }
1853 return Ok(Value::Unit);
1854 }
1855
1856 "get_width" | "ความกว้าง" | "宽" | "幅取得" | "너비" => {
1858 return Ok(Value::Number(self.gfx.borrow().width as f64));
1859 }
1860 "get_height" | "ความสูง" | "高" | "高取得" | "높이" => {
1861 return Ok(Value::Number(self.gfx.borrow().height as f64));
1862 }
1863
1864 "monitor_width" | "screen_width" | "屏宽" | "画面幅" | "화면너비" | "ความกว้างจอ" => {
1867 return Ok(Value::Number(monitor_info().0 as f64));
1868 }
1869 "monitor_height" | "screen_height" | "屏高" | "画面高" | "화면높이" | "ความสูงจอ" => {
1871 return Ok(Value::Number(monitor_info().1 as f64));
1872 }
1873 "monitor_refresh" | "monitor_hz" | "monitor_fps" | "refresh_rate"
1875 | "刷新率" | "リフレッシュレート" | "주사율" | "อัตรารีเฟรช" => {
1876 return Ok(Value::Number(monitor_info().2 as f64));
1877 }
1878 "monitor_info" | "screen_info" | "屏幕信息" | "画面情報" | "화면정보" | "ข้อมูลจอ" => {
1880 let (w, h, hz) = monitor_info();
1881 return Ok(Value::List(vec![
1882 Value::Number(w as f64),
1883 Value::Number(h as f64),
1884 Value::Number(hz as f64),
1885 ]));
1886 }
1887 "set_fps" | "set_target_fps" | "target_fps" | "设帧率" | "フレームレート設定" | "프레임설정" | "ตั้งเฟรมเรต" => {
1889 let fps = self.arg_num(&args, 0, 60.0)?.max(1.0) as usize;
1890 #[cfg(not(target_arch = "wasm32"))]
1891 {
1892 let mut gfx = self.gfx.borrow_mut();
1893 if let Some(win) = gfx.window.as_mut() {
1894 win.set_target_fps(fps);
1895 }
1896 }
1897 return Ok(Value::Unit);
1898 }
1899
1900 "หน้าต่างเปิดอยู่" | "window_is_open" | "gfx_is_open" | "is_open" | "窗开" | "開いている" | "창열림" => {
1902 #[cfg(not(target_arch = "wasm32"))]
1903 {
1904 let gfx = self.gfx.borrow();
1905 let open = gfx.window.as_ref()
1906 .map(|w| w.is_open() && !w.is_key_down(minifb::Key::Escape))
1907 .unwrap_or(false);
1908 return Ok(Value::Bool(open));
1909 }
1910 #[cfg(target_arch = "wasm32")]
1911 return Ok(Value::Bool(true));
1912 }
1913
1914 "key_down" | "กดค้าง" | "按键" | "キー押す" | "키누름" => {
1916 #[cfg(not(target_arch = "wasm32"))]
1917 {
1918 let name = self.arg_str(&args, 0, "");
1919 let gfx = self.gfx.borrow();
1920 let down = gfx.window.as_ref()
1921 .and_then(|w| str_to_minifb_key(&name).map(|k| w.is_key_down(k)))
1922 .unwrap_or(false);
1923 return Ok(Value::Bool(down));
1924 }
1925 #[cfg(target_arch = "wasm32")]
1926 return Ok(Value::Bool(false));
1927 }
1928
1929 "key_pressed" | "กดปุ่ม" | "键按" | "キー押した" | "키눌림" => {
1931 #[cfg(not(target_arch = "wasm32"))]
1932 {
1933 let name = self.arg_str(&args, 0, "");
1934 let pressed = {
1935 let gfx = self.gfx.borrow();
1936 gfx.window.as_ref()
1937 .and_then(|w| str_to_minifb_key(&name)
1938 .map(|k| w.is_key_pressed(k, minifb::KeyRepeat::No)))
1939 .unwrap_or(false)
1940 };
1941 let pressed = pressed || ((name == "enter" || name == "return") && gamepad::start_edge());
1943 return Ok(Value::Bool(pressed));
1944 }
1945 #[cfg(target_arch = "wasm32")]
1946 return Ok(Value::Bool(false));
1947 }
1948
1949 "mouse_dx" | "เมาส์X" | "鼠ΔX" | "マウスΔX" | "마우스ΔX" => {
1951 #[cfg(not(target_arch = "wasm32"))]
1952 return Ok(Value::Number(self.gfx.borrow().mouse_dx as f64));
1953 #[cfg(target_arch = "wasm32")]
1954 return Ok(Value::Number(0.0));
1955 }
1956 #[cfg(not(target_arch = "wasm32"))]
1958 "mouse_scroll" | "ล้อเมาส์" | "滚轮" | "ホイール" | "스크롤" => {
1959 let gfx = self.gfx.borrow();
1960 let s = gfx.window.as_ref()
1961 .and_then(|w| w.get_scroll_wheel())
1962 .map(|(_, y)| y as f64).unwrap_or(0.0);
1963 return Ok(Value::Number(s));
1964 }
1965 "mouse_dy" | "เมาส์Y" | "鼠ΔY" | "マウスΔY" | "마우스ΔY" => {
1966 #[cfg(not(target_arch = "wasm32"))]
1967 return Ok(Value::Number(self.gfx.borrow().mouse_dy as f64));
1968 #[cfg(target_arch = "wasm32")]
1969 return Ok(Value::Number(0.0));
1970 }
1971
1972 "pad_poll" | "手柄轮询" | "パッド更新" | "패드폴링" | "อัปเดตแพด" => {
1975 #[cfg(not(target_arch = "wasm32"))]
1976 return Ok(Value::Number(self.pad_poll() as f64));
1977 #[cfg(target_arch = "wasm32")]
1978 return Ok(Value::Number(0.0));
1979 }
1980 "pad_count" | "手柄数" | "パッド数" | "패드수" | "จำนวนแพด" => {
1982 #[cfg(not(target_arch = "wasm32"))]
1983 {
1984 let inp = self.input.borrow();
1985 let n = inp.as_ref().map_or(0, |s| s.sensorium.devices.count());
1986 return Ok(Value::Number(n as f64));
1987 }
1988 #[cfg(target_arch = "wasm32")]
1989 return Ok(Value::Number(0.0));
1990 }
1991 "pad_connected" | "手柄连接" | "パッド接続" | "패드연결" | "แพดเชื่อม" => {
1993 #[cfg(not(target_arch = "wasm32"))]
1994 {
1995 let i = self.arg_num(&args, 0, 0.0)? as usize;
1996 let inp = self.input.borrow();
1997 let c = inp.as_ref().is_some_and(|s| {
1998 s.sensorium.devices.for_player(i as u8).is_some()
1999 });
2000 return Ok(Value::Bool(c));
2001 }
2002 #[cfg(target_arch = "wasm32")]
2003 return Ok(Value::Bool(false));
2004 }
2005 "pad_button" | "手柄按键" | "パッドボタン" | "패드버튼" | "ปุ่มแพด" => {
2007 #[cfg(not(target_arch = "wasm32"))]
2008 {
2009 let i = self.arg_num(&args, 0, 0.0)? as usize;
2010 let name = self.arg_str(&args, 1, "");
2011 let down = parse_pad_button(&name)
2012 .is_some_and(|b| self.with_pad(i, false, |p| p.is_down(b)));
2013 return Ok(Value::Bool(down));
2014 }
2015 #[cfg(target_arch = "wasm32")]
2016 return Ok(Value::Bool(false));
2017 }
2018 "pad_pressed" | "手柄按下" | "パッド押下" | "패드눌림" | "แพดกด" => {
2020 #[cfg(not(target_arch = "wasm32"))]
2021 {
2022 let i = self.arg_num(&args, 0, 0.0)? as usize;
2023 let name = self.arg_str(&args, 1, "");
2024 let p = parse_pad_button(&name)
2025 .is_some_and(|b| self.with_pad(i, false, |g| g.just_pressed(b)));
2026 return Ok(Value::Bool(p));
2027 }
2028 #[cfg(target_arch = "wasm32")]
2029 return Ok(Value::Bool(false));
2030 }
2031 "pad_lx" | "手柄左X" | "パッド左X" | "패드왼X" | "แพดซ้ายX" => {
2033 #[cfg(not(target_arch = "wasm32"))]
2034 {
2035 let i = self.arg_num(&args, 0, 0.0)? as usize;
2036 return Ok(Value::Number(self.with_pad(i, 0.0, |p| p.left_stick.x as f64)));
2037 }
2038 #[cfg(target_arch = "wasm32")]
2039 return Ok(Value::Number(0.0));
2040 }
2041 "pad_ly" | "手柄左Y" | "パッド左Y" | "패드왼Y" | "แพดซ้ายY" => {
2042 #[cfg(not(target_arch = "wasm32"))]
2043 {
2044 let i = self.arg_num(&args, 0, 0.0)? as usize;
2045 return Ok(Value::Number(self.with_pad(i, 0.0, |p| p.left_stick.y as f64)));
2046 }
2047 #[cfg(target_arch = "wasm32")]
2048 return Ok(Value::Number(0.0));
2049 }
2050 "pad_rx" | "手柄右X" | "パッド右X" | "패드오X" | "แพดขวาX" => {
2051 #[cfg(not(target_arch = "wasm32"))]
2052 {
2053 let i = self.arg_num(&args, 0, 0.0)? as usize;
2054 return Ok(Value::Number(self.with_pad(i, 0.0, |p| p.right_stick.x as f64)));
2055 }
2056 #[cfg(target_arch = "wasm32")]
2057 return Ok(Value::Number(0.0));
2058 }
2059 "pad_ry" | "手柄右Y" | "パッド右Y" | "패드오Y" | "แพดขวาY" => {
2060 #[cfg(not(target_arch = "wasm32"))]
2061 {
2062 let i = self.arg_num(&args, 0, 0.0)? as usize;
2063 return Ok(Value::Number(self.with_pad(i, 0.0, |p| p.right_stick.y as f64)));
2064 }
2065 #[cfg(target_arch = "wasm32")]
2066 return Ok(Value::Number(0.0));
2067 }
2068 "pad_lt" | "手柄左扳机" | "パッド左トリガー" | "패드왼트리거" | "ไกแพดซ้าย" => {
2070 #[cfg(not(target_arch = "wasm32"))]
2071 {
2072 let i = self.arg_num(&args, 0, 0.0)? as usize;
2073 return Ok(Value::Number(self.with_pad(i, 0.0, |p| p.left_trigger as f64)));
2074 }
2075 #[cfg(target_arch = "wasm32")]
2076 return Ok(Value::Number(0.0));
2077 }
2078 "pad_rt" | "手柄右扳机" | "パッド右トリガー" | "패드오트리거" | "ไกแพดขวา" => {
2079 #[cfg(not(target_arch = "wasm32"))]
2080 {
2081 let i = self.arg_num(&args, 0, 0.0)? as usize;
2082 return Ok(Value::Number(self.with_pad(i, 0.0, |p| p.right_trigger as f64)));
2083 }
2084 #[cfg(target_arch = "wasm32")]
2085 return Ok(Value::Number(0.0));
2086 }
2087 "pad_rumble" | "手柄震动" | "パッド振動" | "패드진동" | "แพดสั่น" => {
2089 #[cfg(not(target_arch = "wasm32"))]
2090 {
2091 use ling_input::backend::InputBackend;
2092 let i = self.arg_num(&args, 0, 0.0)? as usize;
2093 let lo = self.arg_num(&args, 1, 0.0)? as f32;
2094 let hi = self.arg_num(&args, 2, lo as f64)? as f32;
2095 let mut inp = self.input.borrow_mut();
2096 if let Some(s) = inp.as_mut() {
2097 if let Some(dev) = s.sensorium.devices.for_player(i as u8).map(|d| d.id) {
2098 s.backend.set_rumble(
2099 dev,
2100 ling_input::Rumble { low: lo, high: hi, ..Default::default() },
2101 );
2102 }
2103 }
2104 return Ok(Value::Unit);
2105 }
2106 #[cfg(target_arch = "wasm32")]
2107 return Ok(Value::Unit);
2108 }
2109
2110 "set_camera_pos" | "ตั้งตำแหน่งกล้อง" | "镜坐标" | "カメラ座標" | "카메라좌표" => {
2112 let x = self.arg_num(&args, 0, 0.0)? as f32;
2113 let y = self.arg_num(&args, 1, 0.0)? as f32;
2114 let z = self.arg_num(&args, 2, 0.0)? as f32;
2115 {
2116 let mut gfx = self.gfx.borrow_mut();
2117 gfx.camera.tx = x; gfx.camera.ty = y; gfx.camera.tz = z;
2118 }
2119 #[cfg(not(target_arch = "wasm32"))]
2120 if let Some(audio) = &self.audio { audio.set_listener_pos(x, y, z); }
2121 return Ok(Value::Unit);
2122 }
2123
2124 "move_camera" => {
2126 let dx = self.arg_num(&args, 0, 0.0)? as f32;
2127 let dy = self.arg_num(&args, 1, 0.0)? as f32;
2128 let dz = self.arg_num(&args, 2, 0.0)? as f32;
2129 let mut gfx = self.gfx.borrow_mut();
2130 gfx.camera.tx += dx; gfx.camera.ty += dy; gfx.camera.tz += dz;
2131 return Ok(Value::Unit);
2132 }
2133
2134 "set_zdist" | "ตั้งระยะห่าง" | "镜距" | "Z距離設定" | "Z거리설정" => {
2136 let d = self.arg_num(&args, 0, 5.0)? as f32;
2137 self.gfx.borrow_mut().camera.zdist = d;
2138 return Ok(Value::Unit);
2139 }
2140
2141 "capture_mouse" | "จับเมาส์" | "捕鼠" | "マウス捕捉" | "마우스잡기" => {
2143 #[cfg(not(target_arch = "wasm32"))]
2144 {
2145 let mut gfx = self.gfx.borrow_mut();
2146 gfx.mouse_captured = true;
2147 gfx.last_mx = f32::NAN;
2148 if let Some(win) = gfx.window.as_mut() {
2149 win.set_cursor_visibility(false);
2150 }
2151 }
2152 return Ok(Value::Unit);
2153 }
2154
2155 "release_mouse" => {
2157 #[cfg(not(target_arch = "wasm32"))]
2158 {
2159 let mut gfx = self.gfx.borrow_mut();
2160 gfx.mouse_captured = false;
2161 gfx.last_mx = f32::NAN;
2162 if let Some(win) = gfx.window.as_mut() {
2163 win.set_cursor_visibility(true);
2164 }
2165 #[cfg(windows)]
2166 unsafe {
2167 extern "system" { fn ClipCursor(lpRect: *const std::ffi::c_void) -> i32; }
2169 ClipCursor(std::ptr::null());
2170 }
2171 }
2172 return Ok(Value::Unit);
2173 }
2174
2175 "set_camera" | "ตั้งกล้อง" | "设镜" | "设置摄像机" | "カメラ設定" | "카메라설정" => {
2182 let cry = self.arg_num(&args, 0, 1.0)? as f32;
2183 let sry = self.arg_num(&args, 1, 0.0)? as f32;
2184 let crx = self.arg_num(&args, 2, 1.0)? as f32;
2185 let srx = self.arg_num(&args, 3, 0.0)? as f32;
2186 let mut gfx = self.gfx.borrow_mut();
2187 gfx.camera.cry = cry; gfx.camera.sry = sry;
2188 gfx.camera.crx = crx; gfx.camera.srx = srx;
2189 return Ok(Value::Unit);
2190 }
2191
2192 "set_projection" | "ตั้งโปรเจกชัน" | "投影" | "投影設定" | "투영설정" => {
2195 let cx = self.arg_num(&args, 0, 960.0)? as f32;
2196 let cy = self.arg_num(&args, 1, 540.0)? as f32;
2197 let focal = self.arg_num(&args, 2, 1080.0)? as f32;
2198 let zdist = self.arg_num(&args, 3, 5.0)? as f32;
2199 let mut gfx = self.gfx.borrow_mut();
2200 gfx.camera.cx = cx;
2201 gfx.camera.cy = cy;
2202 gfx.camera.focal = focal;
2203 gfx.camera.zdist = zdist;
2204 return Ok(Value::Unit);
2205 }
2206
2207 "draw_mesh" | "วาดเมช" => {
2214 let pos = match args.first() { Some(Value::List(v)) => v, _ => return Ok(Value::Unit) };
2215 let idx = match args.get(1) { Some(Value::List(v)) => v, _ => return Ok(Value::Unit) };
2216 let ox = self.arg_num(&args,2,0.0)? as f32;
2217 let oy = self.arg_num(&args,3,0.0)? as f32;
2218 let oz = self.arg_num(&args,4,0.0)? as f32;
2219 let scale = self.arg_num(&args,5,1.0)? as f32;
2220 let mode = self.arg_num(&args,6,0.0)? as i64;
2221 let nv = pos.len() / 3;
2222 if nv == 0 { return Ok(Value::Unit); }
2223 let mut world = vec![0.0f32; nv*3];
2224 for i in 0..nv {
2225 world[i*3] = ox + self.to_number(&pos[i*3]).unwrap_or(0.0) as f32 * scale;
2226 world[i*3+1] = oy + self.to_number(&pos[i*3+1]).unwrap_or(0.0) as f32 * scale;
2227 world[i*3+2] = oz + self.to_number(&pos[i*3+2]).unwrap_or(0.0) as f32 * scale;
2228 }
2229 let mut gfx = self.gfx.borrow_mut();
2230 let cp = {
2231 let c = &gfx.camera;
2232 ling_gpu::CameraParams { cry:c.cry, sry:c.sry, crx:c.crx, srx:c.srx, cx:c.cx, cy:c.cy, focal:c.focal, zdist:c.zdist, tx:c.tx, ty:c.ty, tz:c.tz }
2233 };
2234 let near = -gfx.camera.zdist + 0.02;
2235 let base = gfx.color;
2236 let ambient = gfx.ambient;
2237 let mut proj = vec![0.0f32; nv*3]; ling_gpu::backend().project_points(&world, &cp, &mut proj);
2239 let nt = idx.len() / 3;
2240 for t in 0..nt {
2241 let ia = self.to_number(&idx[t*3]).unwrap_or(0.0) as usize;
2242 let ib = self.to_number(&idx[t*3+1]).unwrap_or(0.0) as usize;
2243 let ic = self.to_number(&idx[t*3+2]).unwrap_or(0.0) as usize;
2244 if ia>=nv || ib>=nv || ic>=nv { continue; }
2245 let (da, db, dc) = (proj[ia*3+2], proj[ib*3+2], proj[ic*3+2]);
2246 if (da+db+dc)/3.0 <= near { continue; } let col = if mode == 1 {
2248 let h = t as f32 * 0.6;
2249 let r = ((h.sin()*0.5+0.5)*150.0+55.0) as u32;
2250 let g = (((h+2.094).sin()*0.5+0.5)*150.0+55.0) as u32;
2251 let b = (((h+4.189).sin()*0.5+0.5)*150.0+55.0) as u32;
2252 (r<<16)|(g<<8)|b
2253 } else {
2254 let (ax,ay,az)=(world[ia*3],world[ia*3+1],world[ia*3+2]);
2255 let (bx,by,bz)=(world[ib*3],world[ib*3+1],world[ib*3+2]);
2256 let (px,py,pz)=(world[ic*3],world[ic*3+1],world[ic*3+2]);
2257 let (ux,uy,uz)=(bx-ax,by-ay,bz-az);
2258 let (vx,vy,vz)=(px-ax,py-ay,pz-az);
2259 let normal=[uy*vz-uz*vy, uz*vx-ux*vz, ux*vy-uy*vx];
2260 let centroid=[(ax+bx+px)/3.0,(ay+by+py)/3.0,(az+bz+pz)/3.0];
2261 crate::gfx::light::compute_lit_color(base, normal, centroid, &gfx.lights, ambient)
2262 };
2263 let depth = (da+db+dc)/3.0;
2264 let col = gfx.fog_apply(col, depth);
2265 gfx.depth_queue.push_triangle(depth, col, proj[ia*3], proj[ia*3+1], proj[ib*3], proj[ib*3+1], proj[ic*3], proj[ic*3+1]);
2266 }
2267 return Ok(Value::Unit);
2268 }
2269
2270 "add_light" | "เพิ่มแสง" | "加灯" | "ライト追加" | "조명추가" => {
2274 let x = self.arg_num(&args, 0, 0.0)? as f32;
2275 let y = self.arg_num(&args, 1, -3.0)? as f32;
2276 let z = self.arg_num(&args, 2, 3.0)? as f32;
2277 let mut r = self.arg_num(&args, 3, 1.0)? as f32;
2278 let mut g = self.arg_num(&args, 4, 1.0)? as f32;
2279 let mut b = self.arg_num(&args, 5, 1.0)? as f32;
2280 if r > 1.5 || g > 1.5 || b > 1.5 { r/=255.0; g/=255.0; b/=255.0; }
2283 let intensity = self.arg_num(&args, 6, 1.0)? as f32;
2284 let radius = self.arg_num(&args, 7, 0.0)? as f32;
2285 self.gfx.borrow_mut().lights.push(Light { x, y, z, r, g, b, intensity, radius });
2286 return Ok(Value::Unit);
2287 }
2288
2289 "clear_lights" | "ล้างแสง" | "清灯" | "ライト消去" | "조명초기화" => {
2291 self.gfx.borrow_mut().lights.clear();
2292 return Ok(Value::Unit);
2293 }
2294
2295 "set_ambient" | "ตั้งแสงรอบข้าง" | "环境光" | "環境光設定" | "환경광설정" => {
2297 let v = self.arg_num(&args, 0, 0.15)? as f32;
2298 self.gfx.borrow_mut().ambient = v;
2299 return Ok(Value::Unit);
2300 }
2301
2302 "set_fog" | "ตั้งหมอก" | "雾" | "霧設定" | "안개설정" => {
2305 let r = self.arg_num(&args, 0, 0.0)?.clamp(0.0, 255.0) as u32;
2306 let g = self.arg_num(&args, 1, 0.0)?.clamp(0.0, 255.0) as u32;
2307 let b = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 255.0) as u32;
2308 let start = self.arg_num(&args, 3, 0.0)? as f32;
2309 let end = self.arg_num(&args, 4, 0.0)? as f32;
2310 let mut gfx = self.gfx.borrow_mut();
2311 gfx.fog_color = (r << 16) | (g << 8) | b;
2312 gfx.fog_start = start;
2313 gfx.fog_end = end;
2314 return Ok(Value::Unit);
2315 }
2316
2317 "วาดสามเหลี่ยม3มิติ" | "draw_triangle_3d" | "triangle3d" => {
2321 let ax = self.arg_num(&args, 0, 0.0)? as f32;
2322 let ay = self.arg_num(&args, 1, 0.0)? as f32;
2323 let az = self.arg_num(&args, 2, 0.0)? as f32;
2324 let bx = self.arg_num(&args, 3, 0.0)? as f32;
2325 let by = self.arg_num(&args, 4, 0.0)? as f32;
2326 let bz = self.arg_num(&args, 5, 0.0)? as f32;
2327 let cx = self.arg_num(&args, 6, 0.0)? as f32;
2328 let cy = self.arg_num(&args, 7, 0.0)? as f32;
2329 let cz = self.arg_num(&args, 8, 0.0)? as f32;
2330
2331 let mut gfx = self.gfx.borrow_mut();
2332
2333 let ux = bx-ax; let uy = by-ay; let uz = bz-az;
2335 let vx = cx-ax; let vy = cy-ay; let vz = cz-az;
2336 let normal = [
2337 uy*vz - uz*vy,
2338 uz*vx - ux*vz,
2339 ux*vy - uy*vx,
2340 ];
2341 let centroid = [
2343 (ax+bx+cx)/3.0,
2344 (ay+by+cy)/3.0,
2345 (az+bz+cz)/3.0,
2346 ];
2347
2348 let lit_color = crate::gfx::light::compute_lit_color(
2350 gfx.color, normal, centroid, &gfx.lights, gfx.ambient,
2351 );
2352
2353 let near = -gfx.camera.zdist + 0.05;
2360 let vw = [
2361 (ax, ay, az, gfx.camera.depth(ax, ay, az)),
2362 (bx, by, bz, gfx.camera.depth(bx, by, bz)),
2363 (cx, cy, cz, gfx.camera.depth(cx, cy, cz)),
2364 ];
2365 let mut poly: Vec<(f32, f32, f32)> = Vec::with_capacity(4);
2366 let mut ei = 0;
2367 while ei < 3 {
2368 let a = vw[ei];
2369 let b = vw[(ei + 1) % 3];
2370 let ain = a.3 > near;
2371 let bin = b.3 > near;
2372 if ain { poly.push((a.0, a.1, a.2)); }
2373 if ain != bin {
2374 let tt = (near - a.3) / (b.3 - a.3);
2375 poly.push((a.0 + (b.0 - a.0) * tt, a.1 + (b.1 - a.1) * tt, a.2 + (b.2 - a.2) * tt));
2376 }
2377 ei += 1;
2378 }
2379 if poly.len() < 3 { return Ok(Value::Unit); }
2380 let proj: Vec<(f32, f32, f32)> =
2382 poly.iter().map(|p| gfx.camera.project(p.0, p.1, p.2)).collect();
2383 let mut dsum = 0.0f32;
2384 for p in &proj { dsum += p.2; }
2385 let depth = dsum / proj.len() as f32;
2386 let lit_color = gfx.fog_apply(lit_color, depth);
2387 let mut fk = 1;
2388 while fk + 1 < proj.len() {
2389 gfx.depth_queue.push_triangle(
2390 depth, lit_color,
2391 proj[0].0, proj[0].1, proj[fk].0, proj[fk].1, proj[fk + 1].0, proj[fk + 1].1,
2392 );
2393 fk += 1;
2394 }
2395 return Ok(Value::Unit);
2396 }
2397
2398 "วาดเส้น3มิติ" | "draw_line_3d" | "line3d" | "画3D线" | "3D線描く" | "3D선그리기" => {
2402 let ax = self.arg_num(&args, 0, 0.0)? as f32;
2403 let ay = self.arg_num(&args, 1, 0.0)? as f32;
2404 let az = self.arg_num(&args, 2, 0.0)? as f32;
2405 let bx = self.arg_num(&args, 3, 0.0)? as f32;
2406 let by = self.arg_num(&args, 4, 0.0)? as f32;
2407 let bz = self.arg_num(&args, 5, 0.0)? as f32;
2408
2409 let mut gfx = self.gfx.borrow_mut();
2410 let color = gfx.color;
2411 let near = -gfx.camera.zdist + 0.05;
2413 let mut lax = ax; let mut lay = ay; let mut laz = az;
2414 let mut lbx = bx; let mut lby = by; let mut lbz = bz;
2415 let da_raw = gfx.camera.depth(lax, lay, laz);
2416 let db_raw = gfx.camera.depth(lbx, lby, lbz);
2417 if da_raw <= near && db_raw <= near {
2418 return Ok(Value::Unit);
2419 }
2420 if da_raw <= near {
2421 let t = (near - da_raw) / (db_raw - da_raw);
2422 lax += t * (lbx - lax);
2423 lay += t * (lby - lay);
2424 laz += t * (lbz - laz);
2425 } else if db_raw <= near {
2426 let t = (near - da_raw) / (db_raw - da_raw);
2427 lbx = lax + t * (lbx - lax);
2428 lby = lay + t * (lby - lay);
2429 lbz = laz + t * (lbz - laz);
2430 }
2431 let (sax, say, da) = gfx.camera.project(lax, lay, laz);
2432 let (sbx, sby, db) = gfx.camera.project(lbx, lby, lbz);
2433 let depth = (da + db) / 2.0;
2434 let color = gfx.fog_apply(color, depth);
2435 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
2436 return Ok(Value::Unit);
2437 }
2438
2439 #[cfg(not(target_arch = "wasm32"))]
2449 "orb_shell" | "球壳" | "オーブ殻" | "오브껍질" | "เปลือกทรงกลม" => {
2450 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32; let cz=self.arg_num(&args,2,0.)? as f32;
2451 let radius=self.arg_num(&args,3,1.0)? as f32;
2452 let ry=self.arg_num(&args,4,0.)? as f32; let rx=self.arg_num(&args,5,0.)? as f32;
2453 let density=(self.arg_num(&args,6,10.)? as i32).clamp(1, 48);
2454 let tr=(self.arg_num(&args,7,230.)? as f32).clamp(0.,255.);
2455 let tg=(self.arg_num(&args,8,230.)? as f32).clamp(0.,255.);
2456 let tb=(self.arg_num(&args,9,235.)? as f32).clamp(0.,255.);
2457 let (cyr, syr) = (ry.cos(), ry.sin());
2458 let (cxr, sxr) = (rx.cos(), rx.sin());
2459 let tau = std::f32::consts::TAU;
2460 let pi = std::f32::consts::PI;
2461 let turns = 6.0_f32; let nseg = 96; let inv_r = if radius.abs() > 1e-5 { 1.0 / radius } else { 0.0 };
2464 let pt = |u: f32, theta0: f32, dir: f32| -> ([f32;3], f32) {
2467 let phi = pi * u; let th = dir * turns * tau * u + theta0;
2469 let (mut x, y, mut z) = (phi.sin()*th.cos()*radius, phi.cos()*radius, phi.sin()*th.sin()*radius);
2470 let x1 = x*cyr + z*syr; let z1 = -x*syr + z*cyr;
2472 x = x1; z = z1;
2473 let y2 = y*cxr - z*sxr; let z2 = y*sxr + z*cxr;
2475 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.0, 1.0);
2477 ([cx + x, cy + y2, cz + z2], facing)
2478 };
2479 let mut gfx = self.gfx.borrow_mut();
2480 let near = -gfx.camera.zdist + 0.05;
2481 let seg = |gfx: &mut crate::gfx::GfxState, a: [f32;3], b: [f32;3], lum: f32| {
2483 let (mut lax,mut lay,mut laz)=(a[0],a[1],a[2]);
2484 let (mut lbx,mut lby,mut lbz)=(b[0],b[1],b[2]);
2485 let da=gfx.camera.depth(lax,lay,laz); let db=gfx.camera.depth(lbx,lby,lbz);
2486 if da<=near && db<=near { return; }
2487 if da<=near { let t=(near-da)/(db-da); lax+=t*(lbx-lax); lay+=t*(lby-lay); laz+=t*(lbz-laz); }
2488 else if db<=near { let t=(near-da)/(db-da); lbx=lax+t*(lbx-lax); lby=lay+t*(lby-lay); lbz=laz+t*(lbz-laz); }
2489 let (sax,say,da2)=gfx.camera.project(lax,lay,laz);
2490 let (sbx,sby,db2)=gfx.camera.project(lbx,lby,lbz);
2491 let l = (0.12 + 0.88 * lum).clamp(0.0, 1.0);
2493 let cr=(tr*l) as u32; let cg=(tg*l) as u32; let cb=(tb*l) as u32;
2494 let color=(cr<<16)|(cg<<8)|cb;
2495 gfx.depth_queue.push_line((da2+db2)*0.5, color, sax,say, sbx,sby);
2496 };
2497 for &dir in &[1.0_f32, -1.0_f32] {
2499 for s in 0..density {
2500 let theta0 = s as f32 * tau / density as f32;
2501 let mut prev = pt(0.0, theta0, dir);
2502 for k in 1..=nseg {
2503 let cur = pt(k as f32 / nseg as f32, theta0, dir);
2504 seg(&mut gfx, prev.0, cur.0, (prev.1 + cur.1) * 0.5);
2505 prev = cur;
2506 }
2507 }
2508 }
2509 return Ok(Value::Unit);
2510 }
2511
2512 #[cfg(not(target_arch = "wasm32"))]
2520 "orb_particles" | "球内粒子" | "オーブ粒子" | "오브입자" | "อนุภาคทรงกลม" => {
2521 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32; let cz=self.arg_num(&args,2,0.)? as f32;
2522 let radius=self.arg_num(&args,3,1.0)? as f32;
2523 let count=(self.arg_num(&args,4,160.)? as i32).clamp(1, 4000);
2524 let t=self.arg_num(&args,5,0.)? as f32;
2525 let tr=(self.arg_num(&args,6,255.)? as f32).clamp(0.,255.);
2526 let tg=(self.arg_num(&args,7,255.)? as f32).clamp(0.,255.);
2527 let tb=(self.arg_num(&args,8,255.)? as f32).clamp(0.,255.);
2528 let inv_r = if radius.abs() > 1e-5 { 1.0/radius } else { 0.0 };
2529 let h = |mut x: u32| -> f32 {
2531 x = x.wrapping_mul(747796405).wrapping_add(2891336453);
2532 x = ((x >> ((x >> 28).wrapping_add(4))) ^ x).wrapping_mul(277803737);
2533 (((x >> 22) ^ x) & 0xFFFFFF) as f32 / 16_777_216.0
2534 };
2535 let tau = std::f32::consts::TAU;
2536 let (cyr, syr) = ((t*0.5).cos(), (t*0.5).sin());
2538 let (cxr, sxr) = ((t*0.23).cos(), (t*0.23).sin());
2539 let mut gfx = self.gfx.borrow_mut();
2540 let near = -gfx.camera.zdist + 0.05;
2541 let (sw, sh) = (gfx.width as i32, gfx.height as i32);
2542 for i in 0..count {
2543 let i = i as u32;
2544 let u = h(i.wrapping_mul(3) + 1);
2546 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) = (rr*ph.sin()*th.cos(), rr*ph.cos(), rr*ph.sin()*th.sin());
2550 let x1 = x*cyr + z*syr; let z1 = -x*syr + z*cyr; x = x1; z = z1;
2552 let y2 = y*cxr - z*sxr; let z2 = y*sxr + z*cxr;
2553 let (wx, wy, wz) = (cx + x, cy + y2, cz + z2);
2554 if gfx.camera.depth(wx, wy, wz) <= near { continue; }
2555 let (sx, sy, dep) = gfx.camera.project(wx, wy, wz);
2556 let sxi = sx as i32; let syi = sy as i32;
2557 if sxi < 0 || syi < 0 || sxi >= sw || syi >= sh { continue; }
2558 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.15, 1.0);
2560 let l = facing;
2561 let cr=(tr*l) as u32; let cg=(tg*l) as u32; let cb=(tb*l) as u32;
2562 let color=(cr<<16)|(cg<<8)|cb;
2563 let len = if facing > 0.7 { 1.0 } else { 0.0 };
2565 gfx.depth_queue.push_line(dep, color, sx, sy, sx + len, sy);
2566 }
2567 return Ok(Value::Unit);
2568 }
2569
2570 "project_3d" | "投影3D" | "3D投影" | "3D투영" | "ฉาย3มิติ" => {
2574 let x = self.arg_num(&args,0,0.0)? as f32;
2575 let y = self.arg_num(&args,1,0.0)? as f32;
2576 let z = self.arg_num(&args,2,0.0)? as f32;
2577 let gfx = self.gfx.borrow();
2578 let near = -gfx.camera.zdist + 0.05;
2579 let d = gfx.camera.depth(x, y, z);
2580 if d <= near {
2581 return Ok(Value::List(vec![Value::Number(-99999.0), Value::Number(-99999.0), Value::Number(d as f64)]));
2582 }
2583 let (sx, sy, depth) = gfx.camera.project(x, y, z);
2584 return Ok(Value::List(vec![Value::Number(sx as f64), Value::Number(sy as f64), Value::Number(depth as f64)]));
2585 }
2586 #[cfg(not(target_arch = "wasm32"))]
2589 "draw_poly" | "填充多边形" | "ポリゴン塗り" | "다각형채우기" | "เติมรูปหลายเหลี่ยม" => {
2590 let mut pts: Vec<[f32; 2]> = Vec::new();
2591 if let Some(Value::List(v)) = args.first() {
2592 let mut i = 0;
2593 while i + 1 < v.len() {
2594 let x = self.to_number(&v[i]).unwrap_or(0.0) as f32;
2595 let y = self.to_number(&v[i + 1]).unwrap_or(0.0) as f32;
2596 pts.push([x, y]);
2597 i += 2;
2598 }
2599 }
2600 if pts.len() >= 3 {
2601 if pts[0] != pts[pts.len() - 1] { let p0 = pts[0]; pts.push(p0); } let mut gfx = self.gfx.borrow_mut();
2603 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
2604 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, w, h, color, add, std::slice::from_ref(&pts));
2605 }
2606 return Ok(Value::Unit);
2607 }
2608
2609 "vtex_grid" | "ลายตาราง" | "纹格" | "格子模様" | "격자무늬" => {
2618 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2619 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2620 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2621 let cols=self.arg_num(&args,9,10.)?as usize; let rows=self.arg_num(&args,10,10.)?as usize;
2622 let cw=self.arg_num(&args,11,1.)?as f32; let ch=self.arg_num(&args,12,1.)?as f32;
2623 let fr=self.arg_num(&args,13,0.)?as f32; let hue=self.arg_num(&args,14,0.)?as f32;
2624 let mut gfx = self.gfx.borrow_mut();
2625 let cam = gfx.camera.clone();
2626 crate::gfx::vtex::draw_grid(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, cols,rows,cw,ch, fr,hue);
2627 return Ok(Value::Unit);
2628 }
2629
2630 "vtex_rings" | "ลายวงซ้อน" | "纹环" | "同心円" | "동심원" => {
2632 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2633 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2634 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2635 let nr=self.arg_num(&args,9,6.)?as usize; let ns=self.arg_num(&args,10,6.)?as usize;
2636 let mr=self.arg_num(&args,11,3.)?as f32; let tw=self.arg_num(&args,12,0.)?as f32;
2637 let fr=self.arg_num(&args,13,0.)?as f32; let hue=self.arg_num(&args,14,0.)?as f32;
2638 let mut gfx = self.gfx.borrow_mut();
2639 let cam = gfx.camera.clone();
2640 crate::gfx::vtex::draw_rings(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, nr,ns,mr,tw, fr,hue);
2641 return Ok(Value::Unit);
2642 }
2643
2644 "vtex_star" | "ลายดาว" | "纹星" | "星模様" | "별무늬" => {
2646 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2647 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2648 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2649 let np=self.arg_num(&args,9,6.)?as usize;
2650 let ro=self.arg_num(&args,10,2.)?as f32; let ri=self.arg_num(&args,11,1.)?as f32;
2651 let rs=self.arg_num(&args,12,0.01)?as f32;
2652 let fr=self.arg_num(&args,13,0.)?as f32; let hue=self.arg_num(&args,14,0.)?as f32;
2653 let mut gfx = self.gfx.borrow_mut();
2654 let cam = gfx.camera.clone();
2655 crate::gfx::vtex::draw_star(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, np,ro,ri,rs, fr,hue);
2656 return Ok(Value::Unit);
2657 }
2658
2659 "vtex_spiral" | "ลายเกลียว" | "纹螺" | "螺旋" | "나선" => {
2661 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2662 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2663 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2664 let nt=self.arg_num(&args,9,3.)?as f32; let mr=self.arg_num(&args,10,3.)?as f32;
2665 let st=self.arg_num(&args,11,120.)?as usize;
2666 let fr=self.arg_num(&args,12,0.)?as f32; let hue=self.arg_num(&args,13,0.)?as f32;
2667 let mut gfx = self.gfx.borrow_mut();
2668 let cam = gfx.camera.clone();
2669 crate::gfx::vtex::draw_spiral(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, nt,mr,st, fr,hue);
2670 return Ok(Value::Unit);
2671 }
2672
2673 "vtex_flower" | "ลายดอก" | "纹花" | "花模様" | "꽃무늬" => {
2675 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2676 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2677 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2678 let r=self.arg_num(&args,9,1.)?as f32; let ns=self.arg_num(&args,10,24.)?as usize;
2679 let fr=self.arg_num(&args,11,0.)?as f32; let hue=self.arg_num(&args,12,0.)?as f32;
2680 let mut gfx = self.gfx.borrow_mut();
2681 let cam = gfx.camera.clone();
2682 crate::gfx::vtex::draw_flower(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, r,ns, fr,hue);
2683 return Ok(Value::Unit);
2684 }
2685
2686 "vtex_letter_rain" | "ลายอักษรไหล" | "纹字雨" | "文字雨" | "글자비" => {
2688 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2689 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2690 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2691 let nc=self.arg_num(&args,9,16.)?as usize; let nv=self.arg_num(&args,10,14.)?as usize;
2692 let cw=self.arg_num(&args,11,0.65)?as f32; let rh=self.arg_num(&args,12,0.60)?as f32;
2693 let sp=self.arg_num(&args,13,0.025)?as f32;
2694 let fr=self.arg_num(&args,14,0.)?as f32; let hue=self.arg_num(&args,15,0.)?as f32;
2695 let mut gfx = self.gfx.borrow_mut();
2696 let cam = gfx.camera.clone();
2697 crate::gfx::vtex::draw_letter_rain(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, nc,nv,cw,rh,sp, fr,hue);
2698 return Ok(Value::Unit);
2699 }
2700
2701 "vtex_hyperbolic_uv" | "ลายไฮเพอร์โบลิก" | "纹曲面" | "双曲線" | "쌍곡선" => {
2703 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2704 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2705 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2706 let mr=self.arg_num(&args,9,5.)?as f32;
2707 let nc=self.arg_num(&args,10,12.)?as usize; let nr=self.arg_num(&args,11,18.)?as usize;
2708 let fr=self.arg_num(&args,12,0.)?as f32; let hue=self.arg_num(&args,13,0.)?as f32;
2709 let mut gfx = self.gfx.borrow_mut();
2710 let cam = gfx.camera.clone();
2711 crate::gfx::vtex::draw_hyperbolic_uv(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, mr,nc,nr, fr,hue);
2712 return Ok(Value::Unit);
2713 }
2714
2715 "vtex_halftone" | "ลายจุด" | "纹半调" | "網点模様" | "망점" => {
2717 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2718 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2719 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2720 let cols=self.arg_num(&args,9,16.)?as usize; let rows=self.arg_num(&args,10,12.)?as usize;
2721 let cw=self.arg_num(&args,11,0.5)?as f32; let ch=self.arg_num(&args,12,0.5)?as f32;
2722 let dens=self.arg_num(&args,13,0.4)?as f32;
2723 let fr=self.arg_num(&args,14,0.)?as f32; let hue=self.arg_num(&args,15,0.)?as f32;
2724 let mut gfx = self.gfx.borrow_mut();
2725 let cam = gfx.camera.clone();
2726 crate::gfx::vtex::draw_halftone(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, cols,rows,cw,ch,dens, fr,hue);
2727 return Ok(Value::Unit);
2728 }
2729
2730 "vtex_tessellated" | "ลายตาข่าย" | "纹镶嵌" | "網目模様" | "격자망" => {
2732 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2733 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2734 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2735 let cols=self.arg_num(&args,9,14.)?as usize; let rows=self.arg_num(&args,10,10.)?as usize;
2736 let cell=self.arg_num(&args,11,0.6)?as f32;
2737 let amp=self.arg_num(&args,12,0.25)?as f32; let freq=self.arg_num(&args,13,4.)?as f32;
2738 let fr=self.arg_num(&args,14,0.)?as f32; let hue=self.arg_num(&args,15,0.)?as f32;
2739 let mut gfx = self.gfx.borrow_mut();
2740 let cam = gfx.camera.clone();
2741 crate::gfx::vtex::draw_tessellated(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, cols,rows,cell,amp,freq, fr,hue);
2742 return Ok(Value::Unit);
2743 }
2744
2745 "vtex_lotus" | "ลายดอกบัว" | "纹莲" | "蓮模様" | "연꽃무늬" => {
2747 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2748 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2749 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2750 let ri=self.arg_num(&args,9,1.)?as f32; let ro=self.arg_num(&args,10,2.)?as f32;
2751 let np=self.arg_num(&args,11,12.)?as usize;
2752 let fr=self.arg_num(&args,12,0.)?as f32; let hue=self.arg_num(&args,13,0.)?as f32;
2753 let mut gfx = self.gfx.borrow_mut();
2754 let cam = gfx.camera.clone();
2755 crate::gfx::vtex::draw_lotus(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, ri,ro,np, fr,hue);
2756 return Ok(Value::Unit);
2757 }
2758
2759 "vtex_chakra" | "ลายจักร" | "纹轮" | "輪模様" | "바퀴무늬" => {
2761 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2762 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2763 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2764 let r=self.arg_num(&args,9,2.)?as f32; let ns=self.arg_num(&args,10,8.)?as usize;
2765 let fr=self.arg_num(&args,11,0.)?as f32; let hue=self.arg_num(&args,12,0.)?as f32;
2766 let mut gfx = self.gfx.borrow_mut();
2767 let cam = gfx.camera.clone();
2768 crate::gfx::vtex::draw_chakra(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, r,ns, fr,hue);
2769 return Ok(Value::Unit);
2770 }
2771
2772 "vtex_yantra" | "ลายยันต์" | "纹咒" | "護符模様" | "부적무늬" => {
2774 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2775 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2776 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2777 let nl=self.arg_num(&args,9,4.)?as usize; let mr=self.arg_num(&args,10,3.)?as f32;
2778 let fr=self.arg_num(&args,11,0.)?as f32; let hue=self.arg_num(&args,12,0.)?as f32;
2779 let mut gfx = self.gfx.borrow_mut();
2780 let cam = gfx.camera.clone();
2781 crate::gfx::vtex::draw_yantra(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, nl,mr, fr,hue);
2782 return Ok(Value::Unit);
2783 }
2784
2785 "vtex_spiked_cog" | "ฟันเฟืองหนาม" | "纹棘轮" | "歯車模様" | "톱니바퀴" => {
2787 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2788 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2789 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2790 let nt=self.arg_num(&args,9,12.)?as usize; let rb=self.arg_num(&args,10,1.)?as f32;
2791 let rs=self.arg_num(&args,11,1.3)?as f32; let rh=self.arg_num(&args,12,0.2)?as f32;
2792 let ns=self.arg_num(&args,13,6.)?as usize;
2793 let fr=self.arg_num(&args,14,0.)?as f32; let hue=self.arg_num(&args,15,0.)?as f32;
2794 let mut gfx = self.gfx.borrow_mut();
2795 let cam = gfx.camera.clone();
2796 crate::gfx::vtex::draw_spiked_cog(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, nt,rb,rs,rh,ns, fr,hue);
2797 return Ok(Value::Unit);
2798 }
2799
2800 "vtex_torii" | "ประตูโทริอิ" | "纹鸟居" | "鳥居" | "도리이" => {
2802 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2803 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2804 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2805 let w=self.arg_num(&args,9,4.)?as f32; let h=self.arg_num(&args,10,5.)?as f32;
2806 let fr=self.arg_num(&args,11,0.)?as f32; let hue=self.arg_num(&args,12,0.)?as f32;
2807 let mut gfx = self.gfx.borrow_mut();
2808 let cam = gfx.camera.clone();
2809 crate::gfx::vtex::draw_torii(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, w,h, fr,hue);
2810 return Ok(Value::Unit);
2811 }
2812
2813 "vtex_pagoda" | "เจดีย์" | "纹塔" | "塔" | "탑" => {
2815 let cx=self.arg_num(&args,0,0.)?as f32; let cy=self.arg_num(&args,1,0.)?as f32; let cz=self.arg_num(&args,2,0.)?as f32;
2816 let ux=self.arg_num(&args,3,1.)?as f32; let uy=self.arg_num(&args,4,0.)?as f32; let uz=self.arg_num(&args,5,0.)?as f32;
2817 let vx=self.arg_num(&args,6,0.)?as f32; let vy=self.arg_num(&args,7,0.)?as f32; let vz=self.arg_num(&args,8,1.)?as f32;
2818 let nt=self.arg_num(&args,9,5.)?as usize; let bw=self.arg_num(&args,10,2.)?as f32;
2819 let th=self.arg_num(&args,11,1.)?as f32; let tp=self.arg_num(&args,12,0.72)?as f32;
2820 let eo=self.arg_num(&args,13,0.28)?as f32;
2821 let fr=self.arg_num(&args,14,0.)?as f32; let hue=self.arg_num(&args,15,0.)?as f32;
2822 let mut gfx = self.gfx.borrow_mut();
2823 let cam = gfx.camera.clone();
2824 crate::gfx::vtex::draw_pagoda(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, nt,bw,th,tp,eo, fr,hue);
2825 return Ok(Value::Unit);
2826 }
2827
2828 #[cfg(not(target_arch = "wasm32"))]
2834 "audio_tone" | "เสียงโทน" | "音调" | "音調" | "음조" | "空间音" | "空間音" | "공간음" => {
2835 let idx = self.arg_num(&args, 0, 0.0)? as usize;
2836 let x = self.arg_num(&args, 1, 0.0)? as f32;
2837 let y = self.arg_num(&args, 2, 0.0)? as f32;
2838 let z = self.arg_num(&args, 3, 0.0)? as f32;
2839 let w = self.arg_num(&args, 4, 1.0)? as f32;
2840 let freq = self.arg_num(&args, 5, 220.0)? as f32;
2841 let amp = self.arg_num(&args, 6, 0.15)? as f32;
2842 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
2843 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
2844 if let Some(audio) = &self.audio {
2845 audio.set_tone(idx, ToneParams { x, y, z, w, freq, amp, lfo_rate, lfo_depth });
2846 }
2847 return Ok(Value::Unit);
2848 }
2849
2850 #[cfg(not(target_arch = "wasm32"))]
2851 "audio_listener" | "ผู้ฟัง" | "音频监听" | "音声リスナー" | "오디오리스너" => {
2852 let cry = self.arg_num(&args, 0, 1.0)? as f32;
2853 let sry = self.arg_num(&args, 1, 0.0)? as f32;
2854 let crx = self.arg_num(&args, 2, 1.0)? as f32;
2855 let srx = self.arg_num(&args, 3, 0.0)? as f32;
2856 if let Some(audio) = &self.audio {
2857 audio.set_listener(cry, sry, crx, srx);
2858 }
2859 return Ok(Value::Unit);
2860 }
2861
2862 #[cfg(not(target_arch = "wasm32"))]
2863 "audio_bgm" | "เพลงพื้นหลัง" | "เพลงประกอบ" | "背景乐" | "BGM" | "배경음악" => {
2864 let path = match args.first() {
2865 Some(Value::Str(s)) => s.clone(),
2866 _ => return Ok(Value::Unit),
2867 };
2868 let vol = self.arg_num(&args, 1, 0.5)? as f32;
2869 if let Some(audio) = &self.audio {
2870 audio.load_bgm(&path, vol);
2871 }
2872 return Ok(Value::Unit);
2873 }
2874
2875 #[cfg(not(target_arch = "wasm32"))]
2876 "audio_bgm_volume" | "ระดับเสียงพื้นหลัง" | "ระดับเพลงประกอบ" | "背景乐音量" | "BGM音量" | "배경음악음량" => {
2877 let vol = self.arg_num(&args, 0, 0.5)? as f32;
2878 if let Some(audio) = &self.audio {
2879 audio.set_bgm_volume(vol);
2880 }
2881 return Ok(Value::Unit);
2882 }
2883
2884 #[cfg(not(target_arch = "wasm32"))]
2885 "audio_volume" | "ระดับเสียง" | "音量" | "음량" => {
2886 let vol = self.arg_num(&args, 0, 0.7)? as f32;
2887 if let Some(audio) = &self.audio {
2888 audio.set_master_volume(vol);
2889 }
2890 return Ok(Value::Unit);
2891 }
2892
2893 #[cfg(target_arch = "wasm32")]
2895 "audio_tone" | "เสียงโทน" | "音调" | "音調" | "음조" | "空间音" | "空間音" | "공간음" => {
2896 let idx = self.arg_num(&args, 0, 0.0)? as usize;
2897 let x = self.arg_num(&args, 1, 0.0)? as f32;
2898 let y = self.arg_num(&args, 2, 0.0)? as f32;
2899 let z = self.arg_num(&args, 3, 0.0)? as f32;
2900 let w = self.arg_num(&args, 4, 1.0)? as f32;
2901 let freq = self.arg_num(&args, 5, 220.0)? as f32;
2902 let amp = self.arg_num(&args, 6, 0.15)? as f32;
2903 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
2904 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
2905 crate::gfx::audio_web::set_tone(idx, x, y, z, w, freq, amp, lfo_rate, lfo_depth);
2906 return Ok(Value::Unit);
2907 }
2908
2909 #[cfg(target_arch = "wasm32")]
2910 "audio_listener" | "ผู้ฟัง" | "音频监听" | "音声リスナー" | "오디오리스너" => {
2911 let cry = self.arg_num(&args, 0, 1.0)? as f32;
2912 let sry = self.arg_num(&args, 1, 0.0)? as f32;
2913 let crx = self.arg_num(&args, 2, 1.0)? as f32;
2914 let srx = self.arg_num(&args, 3, 0.0)? as f32;
2915 crate::gfx::audio_web::set_listener(cry, sry, crx, srx);
2916 return Ok(Value::Unit);
2917 }
2918
2919 #[cfg(target_arch = "wasm32")]
2920 "audio_bgm" | "เพลงพื้นหลัง" | "เพลงประกอบ" | "背景乐" | "BGM" | "배경음악" => {
2921 let path = self.arg_str(&args, 0, "");
2922 let vol = self.arg_num(&args, 1, 0.5)? as f32;
2923 crate::gfx::audio_web::load_bgm(&path, vol);
2924 return Ok(Value::Unit);
2925 }
2926
2927 #[cfg(target_arch = "wasm32")]
2928 "audio_bgm_volume" | "ระดับเสียงพื้นหลัง" | "ระดับเพลงประกอบ" | "背景乐音量" | "BGM音量" | "배경음악음량" => {
2929 let vol = self.arg_num(&args, 0, 0.5)? as f32;
2930 crate::gfx::audio_web::set_bgm_volume(vol);
2931 return Ok(Value::Unit);
2932 }
2933
2934 #[cfg(target_arch = "wasm32")]
2935 "audio_volume" | "ระดับเสียง" | "音量" | "음량" => {
2936 let vol = self.arg_num(&args, 0, 0.7)? as f32;
2937 crate::gfx::audio_web::set_master_volume(vol);
2938 return Ok(Value::Unit);
2939 }
2940
2941 "รอหน้าต่าง" | "wait_window" | "gfx_wait" => {
2943 #[cfg(not(target_arch = "wasm32"))]
2944 loop {
2945 let still_open = {
2946 let gfx = self.gfx.borrow();
2947 gfx.window.as_ref()
2948 .map(|w| w.is_open() && !w.is_key_down(minifb::Key::Escape))
2949 .unwrap_or(false)
2950 };
2951 if !still_open { break; }
2952 let (buf, w, h) = {
2953 let gfx = self.gfx.borrow();
2954 (gfx.buffer.clone(), gfx.width, gfx.height)
2955 };
2956 let mut gfx = self.gfx.borrow_mut();
2957 if let Some(win) = gfx.window.as_mut() {
2958 if win.update_with_buffer(&buf, w, h).is_err() { break; }
2959 }
2960 }
2961 return Ok(Value::Unit);
2962 }
2963
2964 "read_file" | "อ่านไฟล์" => {
2966 let path = self.arg_str(&args, 0, "");
2967 return std::fs::read_to_string(&path)
2968 .map(Value::Str)
2969 .map_err(|e| EvalErr::from(format!("read_file '{path}': {e}")));
2970 }
2971 #[cfg(not(target_arch = "wasm32"))]
2973 "net_host" | "เน็ตโฮสต์" => {
2974 let port = self.arg_num(&args, 0, 7777.0)? as u16;
2975 net::host(port);
2976 return Ok(Value::Unit);
2977 }
2978 #[cfg(not(target_arch = "wasm32"))]
2979 "net_join" | "เน็ตจอย" => {
2980 let ip = self.arg_str(&args, 0, "127.0.0.1");
2981 let port = self.arg_num(&args, 1, 7777.0)? as u16;
2982 net::join(&ip, port);
2983 return Ok(Value::Unit);
2984 }
2985 #[cfg(not(target_arch = "wasm32"))]
2986 "net_send" | "เน็ตส่ง" => {
2987 let s = self.arg_str(&args, 0, "");
2988 net::send(&s);
2989 return Ok(Value::Unit);
2990 }
2991 #[cfg(not(target_arch = "wasm32"))]
2992 "net_recv" | "เน็ตรับ" => {
2993 return Ok(Value::Str(net::recv()));
2994 }
2995 #[cfg(not(target_arch = "wasm32"))]
2996 "net_status" | "เน็ตสถานะ" => {
2997 return Ok(Value::Number(net::status() as f64));
2998 }
2999 #[cfg(not(target_arch = "wasm32"))]
3001 "net_announce" | "เน็ตประกาศ" => {
3002 let port = self.arg_num(&args, 0, 7778.0)? as u16;
3003 let info = self.arg_str(&args, 1, "");
3004 net::announce(port, &info);
3005 return Ok(Value::Unit);
3006 }
3007 #[cfg(not(target_arch = "wasm32"))]
3008 "net_announce_stop" | "เน็ตหยุดประกาศ" => {
3009 net::announce_stop();
3010 return Ok(Value::Unit);
3011 }
3012 #[cfg(not(target_arch = "wasm32"))]
3013 "net_discover" | "เน็ตค้นหา" => {
3014 let port = self.arg_num(&args, 0, 7778.0)? as u16;
3015 return Ok(Value::Str(net::discover(port)));
3016 }
3017 #[cfg(not(target_arch = "wasm32"))]
3018 "net_test" | "เน็ตทดสอบ" => {
3019 let port = self.arg_num(&args, 0, 7777.0)? as u16;
3020 return Ok(Value::Str(net::test_bind(port)));
3021 }
3022 #[cfg(not(target_arch = "wasm32"))]
3024 "gamepad_poll" | "จอยโพล" => { gamepad::poll(); return Ok(Value::Unit); }
3025 #[cfg(not(target_arch = "wasm32"))]
3026 "gamepad_button" | "จอยปุ่ม" => {
3027 let name = self.arg_str(&args, 0, "");
3028 return Ok(Value::Number(if gamepad::button(&name) { 1.0 } else { 0.0 }));
3029 }
3030 #[cfg(not(target_arch = "wasm32"))]
3031 "gamepad_axis" | "จอยแกน" => {
3032 let name = self.arg_str(&args, 0, "");
3033 return Ok(Value::Number(gamepad::axis(&name) as f64));
3034 }
3035 #[cfg(not(target_arch = "wasm32"))]
3036 "gamepad_rumble" | "จอยสั่น" => {
3037 let low = self.arg_num(&args, 0, 0.0)? as f32;
3038 let high = self.arg_num(&args, 1, 0.0)? as f32;
3039 let ms = self.arg_num(&args, 2, 200.0)? as u32;
3040 gamepad::rumble(low, high, ms);
3041 return Ok(Value::Unit);
3042 }
3043 #[cfg(not(target_arch = "wasm32"))]
3044 "gamepad_list" | "จอยรายการ" => { return Ok(Value::Str(gamepad::list())); }
3045 #[cfg(not(target_arch = "wasm32"))]
3046 "gamepad_any" | "จอยใดๆ" => { return Ok(Value::Number(if gamepad::any_button() { 1.0 } else { 0.0 })); }
3047
3048 #[cfg(not(target_arch = "wasm32"))]
3051 "nn_new" | "建神经网" | "ニューラル作成" | "신경망생성" | "สร้างโครงข่าย" => {
3052 let n_in = self.arg_num(&args, 0, 1.0)?.max(0.0) as usize;
3053 let seed = self.arg_num(&args, 1, 1.0)? as u64;
3054 return Ok(Value::Number(ai::nn_new(n_in, seed) as f64));
3055 }
3056 #[cfg(not(target_arch = "wasm32"))]
3058 "nn_dense" | "密集层" | "密層追加" | "밀집층" | "ชั้นหนาแน่น" => {
3059 let id = self.arg_num(&args, 0, -1.0)? as i64;
3060 let units = self.arg_num(&args, 1, 1.0)?.max(1.0) as usize;
3061 let act = self.arg_str(&args, 2, "relu");
3062 ai::nn_dense(id, units, &act);
3063 return Ok(Value::Unit);
3064 }
3065 #[cfg(not(target_arch = "wasm32"))]
3067 "nn_forward" | "神经前向" | "順伝播" | "순전파" | "ส่งต่อโครงข่าย" => {
3068 let id = self.arg_num(&args, 0, -1.0)? as i64;
3069 let input = self.arg_list_f32(&args, 1);
3070 let out = ai::nn_forward(id, &input);
3071 return Ok(Value::List(out.into_iter().map(|v| Value::Number(v as f64)).collect()));
3072 }
3073 #[cfg(not(target_arch = "wasm32"))]
3075 "nn_train" | "训练网" | "ニューラル学習" | "신경망학습" | "ฝึกโครงข่าย" => {
3076 let id = self.arg_num(&args, 0, -1.0)? as i64;
3077 let input = self.arg_list_f32(&args, 1);
3078 let target = self.arg_list_f32(&args, 2);
3079 let lr = self.arg_num(&args, 3, 0.01)? as f32;
3080 return Ok(Value::Number(ai::nn_train(id, &input, &target, lr) as f64));
3081 }
3082 #[cfg(not(target_arch = "wasm32"))]
3084 "nn_save" | "保存网" | "網保存" | "신경망저장" | "บันทึกโครงข่าย" => {
3085 let id = self.arg_num(&args, 0, -1.0)? as i64;
3086 let path = self.arg_str(&args, 1, "model.lnn");
3087 return Ok(Value::Bool(ai::nn_save(id, &path)));
3088 }
3089 #[cfg(not(target_arch = "wasm32"))]
3091 "nn_load" | "载入网" | "網読込" | "신경망불러오기" | "โหลดโครงข่าย" => {
3092 let path = self.arg_str(&args, 0, "model.lnn");
3093 return Ok(Value::Number(ai::nn_load(&path) as f64));
3094 }
3095
3096 #[cfg(not(target_arch = "wasm32"))]
3099 "bt_build" | "建行为树" | "行動木構築" | "행동트리구성" | "สร้างต้นไม้พฤติกรรม" => {
3100 let spec = self.arg_str(&args, 0, "");
3101 return Ok(Value::Number(ai::bt_build(&spec) as f64));
3102 }
3103 #[cfg(not(target_arch = "wasm32"))]
3105 "bt_set" | "设事实" | "事実設定" | "사실설정" | "ตั้งข้อเท็จจริง" => {
3106 let id = self.arg_num(&args, 0, -1.0)? as i64;
3107 let key = self.arg_str(&args, 1, "");
3108 let val = self.arg_num(&args, 2, 0.0)? as f32;
3109 ai::bt_set(id, &key, val);
3110 return Ok(Value::Unit);
3111 }
3112 #[cfg(not(target_arch = "wasm32"))]
3114 "bt_tick" | "行为树滴答" | "行動木更新" | "행동트리틱" | "เดินต้นไม้พฤติกรรม" => {
3115 let id = self.arg_num(&args, 0, -1.0)? as i64;
3116 return Ok(Value::Str(ai::bt_tick(id)));
3117 }
3118 #[cfg(not(target_arch = "wasm32"))]
3120 "bt_status" | "行为树状态" | "行動木状態" | "행동트리상태" | "สถานะต้นไม้พฤติกรรม" => {
3121 let id = self.arg_num(&args, 0, -1.0)? as i64;
3122 return Ok(Value::Number(ai::bt_status(id) as f64));
3123 }
3124
3125 #[cfg(not(target_arch = "wasm32"))]
3128 "dialog_new" | "建对话模型" | "対話モデル作成" | "대화모델생성" | "สร้างโมเดลสนทนา" => {
3129 let ctx = self.arg_num(&args, 0, 3.0)?.max(1.0) as usize;
3130 let embed = self.arg_num(&args, 1, 32.0)?.max(1.0) as usize;
3131 let hidden = self.arg_num(&args, 2, 64.0)?.max(1.0) as usize;
3132 let seed = self.arg_num(&args, 3, 1.0)? as u64;
3133 return Ok(Value::Number(ai::dialog_new(ctx, embed, hidden, seed) as f64));
3134 }
3135 #[cfg(not(target_arch = "wasm32"))]
3137 "dialog_learn" | "对话学习" | "対話学習" | "대화학습" | "เรียนรู้สนทนา" => {
3138 let id = self.arg_num(&args, 0, -1.0)? as i64;
3139 let text = self.arg_str(&args, 1, "");
3140 ai::dialog_learn(id, &text);
3141 return Ok(Value::Unit);
3142 }
3143 #[cfg(not(target_arch = "wasm32"))]
3145 "dialog_load" | "对话载入" | "対話読込" | "대화불러오기" | "โหลดชุดสนทนา" => {
3146 let id = self.arg_num(&args, 0, -1.0)? as i64;
3147 let path = self.arg_str(&args, 1, "");
3148 return Ok(Value::Number(ai::dialog_load(id, &path) as f64));
3149 }
3150 #[cfg(not(target_arch = "wasm32"))]
3152 "dialog_train" | "对话训练" | "対話訓練" | "대화훈련" | "ฝึกสนทนา" => {
3153 let id = self.arg_num(&args, 0, -1.0)? as i64;
3154 let epochs = self.arg_num(&args, 1, 20.0)?.max(1.0) as usize;
3155 let lr = self.arg_num(&args, 2, 0.1)? as f32;
3156 return Ok(Value::Number(ai::dialog_train(id, epochs, lr) as f64));
3157 }
3158 #[cfg(not(target_arch = "wasm32"))]
3160 "dialog_say" | "对话生成" | "対話生成" | "대화생성" | "พูดสนทนา" => {
3161 let id = self.arg_num(&args, 0, -1.0)? as i64;
3162 let prompt = self.arg_str(&args, 1, "");
3163 let max = self.arg_num(&args, 2, 24.0)?.max(1.0) as usize;
3164 let temp = self.arg_num(&args, 3, 0.8)? as f32;
3165 return Ok(Value::Str(ai::dialog_say(id, &prompt, max, temp)));
3166 }
3167 #[cfg(not(target_arch = "wasm32"))]
3169 "dialog_save" | "对话存模" | "対話モデル保存" | "대화모델저장" | "บันทึกโมเดลสนทนา" => {
3170 let id = self.arg_num(&args, 0, -1.0)? as i64;
3171 let path = self.arg_str(&args, 1, "model.llm");
3172 return Ok(Value::Bool(ai::dialog_save(id, &path)));
3173 }
3174 #[cfg(not(target_arch = "wasm32"))]
3176 "dialog_load_model" | "对话载模" | "対話モデル読込" | "대화모델불러오기" | "โหลดโมเดลสนทนา" => {
3177 let path = self.arg_str(&args, 0, "model.llm");
3178 return Ok(Value::Number(ai::dialog_load_model(&path) as f64));
3179 }
3180
3181 "write_file" | "เขียนไฟล์" => {
3182 let path = self.arg_str(&args, 0, "");
3183 let content = self.arg_str(&args, 1, "");
3184 std::fs::write(&path, content.as_bytes())
3185 .map_err(|e| EvalErr::from(format!("write_file '{path}': {e}")))?;
3186 return Ok(Value::Unit);
3187 }
3188 "print_file" | "พิมพ์ไฟล์" => {
3189 let content = self.arg_str(&args, 0, "");
3190 print!("{content}");
3191 return Ok(Value::Unit);
3192 }
3193
3194 "get_args" | "รับอาร์กิวเมนต์" => {
3196 let v: Vec<Value> = std::env::args().map(Value::Str).collect();
3197 return Ok(Value::List(v));
3198 }
3199
3200 "split" | "str_split" | "แยก" => {
3202 let s = self.arg_str(&args, 0, "");
3203 let sep = self.arg_str(&args, 1, "\n");
3204 let sep = if sep.is_empty() { "\n".into() } else { sep };
3205 let parts: Vec<Value> = s.split(sep.as_str())
3206 .map(|p| Value::Str(p.to_string())).collect();
3207 return Ok(Value::List(parts));
3208 }
3209 "trim" | "str_trim" | "ตัดช่องว่าง" => {
3210 let s = self.arg_str(&args, 0, "");
3211 return Ok(Value::Str(s.trim().to_string()));
3212 }
3213 "starts_with" | "str_starts_with" | "เริ่มด้วย" => {
3214 let s = self.arg_str(&args, 0, "");
3215 let prefix = self.arg_str(&args, 1, "");
3216 return Ok(Value::Bool(s.starts_with(prefix.as_str())));
3217 }
3218 "ends_with" | "str_ends_with" | "ลงท้ายด้วย" => {
3219 let s = self.arg_str(&args, 0, "");
3220 let suffix = self.arg_str(&args, 1, "");
3221 return Ok(Value::Bool(s.ends_with(suffix.as_str())));
3222 }
3223 "str_replace" | "แทนสตริง" => {
3224 let s = self.arg_str(&args, 0, "");
3225 let from = self.arg_str(&args, 1, "");
3226 let to = self.arg_str(&args, 2, "");
3227 return Ok(Value::Str(s.replace(from.as_str(), to.as_str())));
3228 }
3229 "str_find" | "หาในสตริง" => {
3230 let s = self.arg_str(&args, 0, "");
3231 let needle = self.arg_str(&args, 1, "");
3232 let pos = s.find(needle.as_str())
3234 .map(|byte_i| s[..byte_i].chars().count() as f64)
3235 .unwrap_or(-1.0);
3236 return Ok(Value::Number(pos));
3237 }
3238 "substr" | "str_slice" | "ส่วนสตริง" => {
3239 let s = self.arg_str(&args, 0, "");
3240 let start = self.arg_num(&args, 1, 0.0)? as usize;
3241 let len = args.get(2)
3242 .map(|v| self.to_number(v).unwrap_or(999999.0) as usize)
3243 .unwrap_or_else(|| s.chars().count().saturating_sub(start));
3244 let chars: Vec<char> = s.chars().collect();
3245 let end = (start + len).min(chars.len());
3246 let slice: String = chars.get(start..end).unwrap_or(&[]).iter().collect();
3247 return Ok(Value::Str(slice));
3248 }
3249 "to_str" | "str" | "num_str" | "แปลงสตริง" => {
3250 let v = args.into_iter().next().unwrap_or(Value::Unit);
3251 return Ok(Value::Str(v.to_string()));
3252 }
3253 "str_repeat" | "ทำซ้ำสตริง" => {
3254 let s = self.arg_str(&args, 0, "");
3255 let n = self.arg_num(&args, 1, 1.0)? as usize;
3256 return Ok(Value::Str(s.repeat(n)));
3257 }
3258 "str_upper" => {
3259 let s = self.arg_str(&args, 0, "");
3260 return Ok(Value::Str(s.to_uppercase()));
3261 }
3262 "str_lower" => {
3263 let s = self.arg_str(&args, 0, "");
3264 return Ok(Value::Str(s.to_lowercase()));
3265 }
3266 "str_len" | "len" | "ความยาว" | "长度" | "長さ" | "길이" => {
3267 match args.first() {
3268 Some(Value::Str(s)) => return Ok(Value::Number(s.chars().count() as f64)),
3269 Some(Value::List(v)) => return Ok(Value::Number(v.len() as f64)),
3270 _ => return Ok(Value::Number(0.0)),
3271 }
3272 }
3273
3274 "hash_str" | "แฮช" => {
3276 let s = self.arg_str(&args, 0, "");
3277 let mut h: u64 = 14695981039346656037_u64;
3278 for b in s.bytes() { h ^= b as u64; h = h.wrapping_mul(1099511628211); }
3279 return Ok(Value::Number((h & 0xFFFFFF) as f64 / 16777215.0));
3280 }
3281 "hash_int" | "แฮชจำนวน" => {
3282 let s = self.arg_str(&args, 0, "");
3283 let n = self.arg_num(&args, 1, 100.0)? as u64;
3284 let mut h: u64 = 14695981039346656037_u64;
3285 for b in s.bytes() { h ^= b as u64; h = h.wrapping_mul(1099511628211); }
3286 return Ok(Value::Number((h % n.max(1)) as f64));
3287 }
3288
3289 "list_new" | "รายการใหม่" | "新建列表" | "新規リスト" | "새목록" => {
3291 return Ok(Value::List(Vec::new()));
3292 }
3293 "list_push" | "เพิ่มรายการ" | "列表添加" | "リスト追加" | "목록추가" => {
3294 let lst = args.first().cloned().unwrap_or(Value::List(vec![]));
3295 let val = args.get(1).cloned().unwrap_or(Value::Unit);
3296 if let Value::List(mut v) = lst { v.push(val); return Ok(Value::List(v)); }
3297 return Ok(Value::List(vec![val]));
3298 }
3299 "list_get" | "รับรายการ" | "取元素" | "要素取得" | "요소가져오기" => {
3300 let lst = args.first().cloned().unwrap_or(Value::List(vec![]));
3301 let i = self.arg_num(&args, 1, 0.0)? as usize;
3302 if let Value::List(v) = lst {
3303 return Ok(v.get(i).cloned().unwrap_or(Value::Str(String::new())));
3304 }
3305 return Ok(Value::Str(String::new()));
3306 }
3307 "list_join" | "join" | "รวมรายการ" | "连接" | "連結" | "연결" => {
3308 let lst = args.first().cloned().unwrap_or(Value::List(vec![]));
3309 let sep = args.get(1).map(|v| v.to_string()).unwrap_or_default();
3310 if let Value::List(v) = lst {
3311 return Ok(Value::Str(v.iter().map(|x| x.to_string())
3312 .collect::<Vec<_>>().join(&sep)));
3313 }
3314 return Ok(Value::Str(String::new()));
3315 }
3316 #[cfg(not(target_arch = "wasm32"))]
3320 "blob_f32" | "blob_i32" => {
3321 let s = self.arg_str(&args, 0, "");
3322 let is_i32 = name == "blob_i32";
3323 match decode_blob(&s) {
3324 Ok(bytes) => {
3325 let mut out = Vec::with_capacity(bytes.len() / 4);
3326 for ch in bytes.chunks_exact(4) {
3327 let arr = [ch[0], ch[1], ch[2], ch[3]];
3328 let n = if is_i32 {
3329 i32::from_le_bytes(arr) as f64
3330 } else {
3331 f32::from_le_bytes(arr) as f64
3332 };
3333 out.push(Value::Number(n));
3334 }
3335 return Ok(Value::List(out));
3336 }
3337 Err(e) => {
3338 eprintln!("blob decode failed: {e}");
3339 return Ok(Value::List(vec![]));
3340 }
3341 }
3342 }
3343
3344 "svg_begin" | "开始SVG" | "เริ่มSVG" => {
3352 let path = self.arg_str(&args, 0, "output.svg");
3353 let width = self.arg_num(&args, 1, 800.0)?;
3354 let height = self.arg_num(&args, 2, 600.0)?;
3355 *self.svg.borrow_mut() = Some(SvgWriter::new(path, width, height));
3356 return Ok(Value::Unit);
3357 }
3358
3359 "svg_rect" | "SVG矩形" | "SVGสี่เหลี่ยม" => {
3360 let x = self.arg_num(&args, 0, 0.0)?;
3361 let y = self.arg_num(&args, 1, 0.0)?;
3362 let w = self.arg_num(&args, 2, 10.0)?;
3363 let h = self.arg_num(&args, 3, 10.0)?;
3364 let fill = self.arg_str(&args, 4, "#ffffff");
3365 if let Some(svg) = self.svg.borrow_mut().as_mut() {
3366 svg.elements.push(format!(
3367 "<rect x=\"{x:.1}\" y=\"{y:.1}\" width=\"{w:.1}\" \
3368 height=\"{h:.1}\" fill=\"{fill}\"/>"));
3369 }
3370 return Ok(Value::Unit);
3371 }
3372
3373 "svg_circle" | "SVG圆形" | "SVGวงกลม" => {
3374 let cx = self.arg_num(&args, 0, 0.0)?;
3375 let cy = self.arg_num(&args, 1, 0.0)?;
3376 let r = self.arg_num(&args, 2, 5.0)?;
3377 let fill = self.arg_str(&args, 3, "#ffffff");
3378 if let Some(svg) = self.svg.borrow_mut().as_mut() {
3379 svg.elements.push(format!(
3380 "<circle cx=\"{cx:.1}\" cy=\"{cy:.1}\" r=\"{r:.1}\" fill=\"{fill}\"/>"));
3381 }
3382 return Ok(Value::Unit);
3383 }
3384
3385 "svg_line" | "SVG线段" | "SVGเส้น" => {
3386 let x1 = self.arg_num(&args, 0, 0.0)?;
3387 let y1 = self.arg_num(&args, 1, 0.0)?;
3388 let x2 = self.arg_num(&args, 2, 0.0)?;
3389 let y2 = self.arg_num(&args, 3, 0.0)?;
3390 let stroke = self.arg_str(&args, 4, "#ffffff");
3391 let sw = self.arg_num(&args, 5, 1.0)?;
3392 if let Some(svg) = self.svg.borrow_mut().as_mut() {
3393 svg.elements.push(format!(
3394 "<line x1=\"{x1:.1}\" y1=\"{y1:.1}\" x2=\"{x2:.1}\" y2=\"{y2:.1}\" \
3395 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"));
3396 }
3397 return Ok(Value::Unit);
3398 }
3399
3400 "svg_polyline" | "SVG折线" | "SVGเส้นหัก" => {
3401 let pts = self.arg_str(&args, 0, "");
3402 let stroke = self.arg_str(&args, 1, "#ffffff");
3403 let sw = self.arg_num(&args, 2, 1.0)?;
3404 if let Some(svg) = self.svg.borrow_mut().as_mut() {
3405 svg.elements.push(format!(
3406 "<polyline points=\"{pts}\" fill=\"none\" \
3407 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"));
3408 }
3409 return Ok(Value::Unit);
3410 }
3411
3412 "svg_text" | "SVG文本" | "SVGข้อความ" => {
3413 let x = self.arg_num(&args, 0, 0.0)?;
3414 let y = self.arg_num(&args, 1, 0.0)?;
3415 let text = self.arg_str(&args, 2, "");
3416 let fill = self.arg_str(&args, 3, "#ffffff");
3417 let size = self.arg_num(&args, 4, 12.0)?;
3418 if let Some(svg) = self.svg.borrow_mut().as_mut() {
3419 let safe = text.replace('&', "&").replace('<', "<").replace('>', ">");
3420 svg.elements.push(format!(
3421 "<text x=\"{x:.1}\" y=\"{y:.1}\" fill=\"{fill}\" \
3422 font-family=\"monospace\" font-size=\"{size:.0}\">{safe}</text>"));
3423 }
3424 return Ok(Value::Unit);
3425 }
3426
3427 "svg_end" | "结束SVG" | "จบSVG" => {
3428 {
3429 let borrow = self.svg.borrow();
3430 if let Some(svg) = borrow.as_ref() {
3431 svg.save().map_err(|e| EvalErr::from(format!("svg_end: {e}")))?;
3432 }
3433 }
3434 *self.svg.borrow_mut() = None;
3435 return Ok(Value::Unit);
3436 }
3437
3438 "hsl_color" | "HSL颜色" | "สีHSL" => {
3439 let h = self.arg_num(&args, 0, 0.0)?;
3440 let s = self.arg_num(&args, 1, 70.0)?;
3441 let l = self.arg_num(&args, 2, 50.0)?;
3442 return Ok(Value::Str(hsl_to_hex(h, s, l)));
3443 }
3444
3445 #[cfg(not(target_arch = "wasm32"))]
3451 "fft_push" | "วิเคราะห์เสียง" | "频谱输入" | "FFT入力" | "FFT입력" => {
3452 if let Some(Value::List(v)) = args.first() {
3453 let samples: Vec<f32> = v.iter()
3454 .filter_map(|x| if let Value::Number(n) = x { Some(*n as f32) } else { None })
3455 .collect();
3456 self.fft.borrow_mut().push_samples(&samples);
3457 }
3458 return Ok(Value::Unit);
3459 }
3460
3461 #[cfg(not(target_arch = "wasm32"))]
3463 "fft_bands" | "แถบความถี่" | "频段" | "周波数帯" | "주파수대" => {
3464 let n = self.arg_num(&args, 0, 32.0)? as usize;
3465 let bands = self.fft.borrow().freq_bands(n);
3466 *self.fft_bands_cache.borrow_mut() = bands.clone();
3467 return Ok(Value::List(bands.into_iter().map(|v| Value::Number(v as f64)).collect()));
3468 }
3469
3470 #[cfg(not(target_arch = "wasm32"))]
3472 "fft_beat" | "จังหวะเสียง" | "节拍检测" | "ビート検出" | "비트" => {
3473 return Ok(Value::Bool(self.fft.borrow().is_beat()));
3474 }
3475
3476 #[cfg(not(target_arch = "wasm32"))]
3478 "fft_beat_ratio" | "อัตราจังหวะ" | "节拍比" | "ビート比" | "비트비율" => {
3479 return Ok(Value::Number(self.fft.borrow().beat_ratio() as f64));
3480 }
3481
3482 #[cfg(not(target_arch = "wasm32"))]
3484 "fft_rms" | "ระดับRMS" | "均方根" | "二乗平均" | "RMS레벨" => {
3485 return Ok(Value::Number(self.fft.borrow().rms() as f64));
3486 }
3487
3488 #[cfg(not(target_arch = "wasm32"))]
3490 "fft_dominant_freq" | "ความถี่หลัก" | "主频" | "主要周波数" | "주파수" => {
3491 return Ok(Value::Number(self.fft.borrow().dominant_freq() as f64));
3492 }
3493
3494 #[cfg(target_arch = "wasm32")]
3496 "fft_push" | "วิเคราะห์เสียง" | "频谱输入" | "FFT入力" | "FFT입력" => { return Ok(Value::Unit); }
3497 #[cfg(target_arch = "wasm32")]
3498 "fft_bands" | "แถบความถี่" | "频段" | "周波数帯" | "주파수대" => {
3499 let n = self.arg_num(&args, 0, 32.0)? as usize;
3500 return Ok(Value::List(vec![Value::Number(0.0); n]));
3501 }
3502 #[cfg(target_arch = "wasm32")]
3503 "fft_beat" | "จังหวะเสียง" | "节拍检测" | "ビート検出" | "비트" => { return Ok(Value::Bool(false)); }
3504 #[cfg(target_arch = "wasm32")]
3505 "fft_beat_ratio" | "อัตราจังหวะ" | "节拍比" | "ビート比" | "비트비율" => { return Ok(Value::Number(1.0)); }
3506 #[cfg(target_arch = "wasm32")]
3507 "fft_rms" | "ระดับRMS" | "均方根" | "二乗平均" | "RMS레벨" => { return Ok(Value::Number(0.0)); }
3508 #[cfg(target_arch = "wasm32")]
3509 "fft_dominant_freq" | "ความถี่หลัก" | "主频" | "主要周波数" | "주파수" => { return Ok(Value::Number(0.0)); }
3510
3511 "tex_checkerboard" | "ลายตารางหมากรุก" => {
3519 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3520 let tiles = self.arg_num(&args, 4, 8.0)? as u32;
3521 let (r1,g1,b1) = (self.arg_num(&args,5,255.)? as u32, self.arg_num(&args,6,255.)? as u32, self.arg_num(&args,7,255.)? as u32);
3522 let (r2,g2,b2) = (self.arg_num(&args,8,0.)? as u32, self.arg_num(&args,9,0.)? as u32, self.arg_num(&args,10,0.)? as u32);
3523 let c1 = (r1<<16)|(g1<<8)|b1; let c2 = (r2<<16)|(g2<<8)|b2;
3524 let mut gfx = self.gfx.borrow_mut();
3525 let (bw, bh) = (gfx.width, gfx.height);
3526 for row in 0..th { for col in 0..tw {
3527 let cx = col as u32 * tiles / tw as u32;
3528 let cy = row as u32 * tiles / th as u32;
3529 let (dx, dy) = (tx+col, ty+row);
3530 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = if (cx+cy)%2==0 { c1 } else { c2 }; }
3531 }}
3532 return Ok(Value::Unit);
3533 }
3534
3535 "tex_gradient" | "ลายไล่สี" => {
3537 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3538 let angle = self.arg_num(&args, 4, 0.0)? as f32;
3539 let (r1,g1,b1) = (self.arg_num(&args,5,0.)? as f32/255., self.arg_num(&args,6,0.)? as f32/255., self.arg_num(&args,7,0.)? as f32/255.);
3540 let (r2,g2,b2) = (self.arg_num(&args,8,255.)? as f32/255., self.arg_num(&args,9,255.)? as f32/255., self.arg_num(&args,10,255.)? as f32/255.);
3541 let (ca, sa) = (angle.to_radians().cos(), angle.to_radians().sin());
3542 let mut gfx = self.gfx.borrow_mut();
3543 let (bw, bh) = (gfx.width, gfx.height);
3544 for row in 0..th { for col in 0..tw {
3545 let nx = col as f32/tw as f32 - 0.5; let ny = row as f32/th as f32 - 0.5;
3546 let t = ((nx*ca + ny*sa + 0.707)/1.414).clamp(0.,1.);
3547 let (dx, dy) = (tx+col, ty+row);
3548 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(r1+(r2-r1)*t, g1+(g2-g1)*t, b1+(b2-b1)*t); }
3549 }}
3550 return Ok(Value::Unit);
3551 }
3552
3553 "tex_noise" | "ลายนอยส์" => {
3555 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3556 let scale = self.arg_num(&args, 4, 4.0)? as f32;
3557 let octaves = self.arg_num(&args, 5, 4.0)? as u32;
3558 let seed = self.arg_num(&args, 6, 0.0)? as u32;
3559 let palette = self.arg_str(&args, 7, "rainbow");
3560 let mut gfx = self.gfx.borrow_mut();
3561 let (bw, bh) = (gfx.width, gfx.height);
3562 for row in 0..th { for col in 0..tw {
3563 let v = tex_fbm(col as f32*scale/tw as f32, row as f32*scale/th as f32, octaves, seed);
3564 let [r,g,b] = tex_palette(&palette, v);
3565 let (dx, dy) = (tx+col, ty+row);
3566 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(r, g, b); }
3567 }}
3568 return Ok(Value::Unit);
3569 }
3570
3571 "tex_freq_map" | "ลายความถี่" => {
3574 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3575 let time = self.arg_num(&args, 4, 0.0)? as f32;
3576 let speed = self.arg_num(&args, 5, 0.3)? as f32;
3577 let palette = self.arg_str(&args, 6, "rainbow");
3578 let bands: Vec<f32> = {
3579 let c = self.fft_bands_cache.borrow();
3580 if c.is_empty() { vec![0.0; 32] } else { c.clone() }
3581 };
3582 let n = bands.len().max(1);
3583 let mut gfx = self.gfx.borrow_mut();
3584 let (bw, bh) = (gfx.width, gfx.height);
3585 for row in 0..th { for col in 0..tw {
3586 let band_idx = (col * n / tw.max(1)).min(n-1);
3587 let mag = bands[band_idx].clamp(0.,1.);
3588 let fill_y = (mag * th as f32) as usize;
3589 if row >= th.saturating_sub(fill_y) {
3590 let t = (col as f32/tw as f32 + time*speed) % 1.0;
3591 let [r,g,b] = tex_palette(&palette, t);
3592 let bright = mag * (1.0 - row as f32/th as f32 * 0.5);
3593 let (dx, dy) = (tx+col, ty+row);
3594 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(r*bright, g*bright, b*bright); }
3595 }
3596 }}
3597 return Ok(Value::Unit);
3598 }
3599
3600 "tex_spiral" | "ลายเกลียวหมุน" => {
3602 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3603 let freq = self.arg_num(&args, 4, 5.0)? as f32;
3604 let n_bands = self.arg_num(&args, 5, 8.0)? as f32;
3605 let time = self.arg_num(&args, 6, 0.0)? as f32;
3606 let palette = self.arg_str(&args, 7, "rainbow");
3607 let mut gfx = self.gfx.borrow_mut();
3608 let (bw, bh) = (gfx.width, gfx.height);
3609 for row in 0..th { for col in 0..tw {
3610 let nx = col as f32/tw as f32 - 0.5; let ny = row as f32/th as f32 - 0.5;
3611 let r = (nx*nx + ny*ny).sqrt();
3612 let theta = ny.atan2(nx);
3613 let t = ((r*freq - theta/std::f32::consts::TAU + time*0.5) * n_bands % 1.0).abs();
3614 let [cr,cg,cb] = tex_palette(&palette, t);
3615 let (dx, dy) = (tx+col, ty+row);
3616 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3617 }}
3618 return Ok(Value::Unit);
3619 }
3620
3621 "tex_ripple" | "ลายระลอก" => {
3623 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3624 let freq = self.arg_num(&args, 4, 10.0)? as f32;
3625 let rcx = self.arg_num(&args, 5, 0.5)? as f32;
3626 let rcy = self.arg_num(&args, 6, 0.5)? as f32;
3627 let time = self.arg_num(&args, 7, 0.0)? as f32;
3628 let palette = self.arg_str(&args, 8, "ocean");
3629 let mut gfx = self.gfx.borrow_mut();
3630 let (bw, bh) = (gfx.width, gfx.height);
3631 for row in 0..th { for col in 0..tw {
3632 let nx = col as f32/tw as f32 - rcx; let ny = row as f32/th as f32 - rcy;
3633 let r = (nx*nx + ny*ny).sqrt();
3634 let t = ((r*freq - time) % 1.0).abs();
3635 let [cr,cg,cb] = tex_palette(&palette, t);
3636 let (dx, dy) = (tx+col, ty+row);
3637 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3638 }}
3639 return Ok(Value::Unit);
3640 }
3641
3642 "tex_mandelbrot" | "ลายแมนเดลบรอต" => {
3644 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3645 let zoom = self.arg_num(&args, 4, 1.0)?;
3646 let mcx = self.arg_num(&args, 5, -0.5)?;
3647 let mcy = self.arg_num(&args, 6, 0.0)?;
3648 let max_iter = self.arg_num(&args, 7, 64.0)? as u32;
3649 let palette = self.arg_str(&args, 8, "psychedelic");
3650 let mut gfx = self.gfx.borrow_mut();
3651 let (bw, bh) = (gfx.width, gfx.height);
3652 for row in 0..th { for col in 0..tw {
3653 let zx0 = (col as f64/tw as f64 - 0.5)/zoom + mcx;
3654 let zy0 = (row as f64/th as f64 - 0.5)/zoom + mcy;
3655 let mut x = 0.0f64; let mut y = 0.0f64; let mut i = 0u32;
3656 while i < max_iter && x*x+y*y < 4.0 { let t=x*x-y*y+zx0; y=2.0*x*y+zy0; x=t; i+=1; }
3657 let t = if i==max_iter { 0.0f32 } else {
3658 (i as f32 - (x as f32*x as f32+y as f32*y as f32).ln().ln()/2.0f32.ln()) / max_iter as f32
3659 };
3660 let [cr,cg,cb] = tex_palette(&palette, t.clamp(0.,1.));
3661 let (dx, dy) = (tx+col, ty+row);
3662 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3663 }}
3664 return Ok(Value::Unit);
3665 }
3666
3667 "tex_julia" | "ลายจูเลีย" => {
3669 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3670 let c_re = self.arg_num(&args, 4, -0.7)?;
3671 let c_im = self.arg_num(&args, 5, 0.27)?;
3672 let max_iter = self.arg_num(&args, 6, 64.0)? as u32;
3673 let palette = self.arg_str(&args, 7, "neon");
3674 let mut gfx = self.gfx.borrow_mut();
3675 let (bw, bh) = (gfx.width, gfx.height);
3676 for row in 0..th { for col in 0..tw {
3677 let mut zx = (col as f64/tw as f64 - 0.5)*3.5;
3678 let mut zy = (row as f64/th as f64 - 0.5)*3.5;
3679 let mut i = 0u32;
3680 while i < max_iter && zx*zx+zy*zy < 4.0 { let t=zx*zx-zy*zy+c_re; zy=2.0*zx*zy+c_im; zx=t; i+=1; }
3681 let t = i as f32 / max_iter as f32;
3682 let [cr,cg,cb] = tex_palette(&palette, t);
3683 let (dx, dy) = (tx+col, ty+row);
3684 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3685 }}
3686 return Ok(Value::Unit);
3687 }
3688
3689 "tex_voronoi" | "ลายโวโรนอย" => {
3691 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3692 let cells = self.arg_num(&args, 4, 16.0)? as u32;
3693 let seed = self.arg_num(&args, 5, 42.0)? as u32;
3694 let palette = self.arg_str(&args, 6, "rainbow");
3695 let pts: Vec<[f32;2]> = (0..cells).map(|i| [tex_hash(i as i32,0,seed), tex_hash(i as i32,1,seed+999)]).collect();
3696 let mut gfx = self.gfx.borrow_mut();
3697 let (bw, bh) = (gfx.width, gfx.height);
3698 for row in 0..th { for col in 0..tw {
3699 let (fx, fy) = (col as f32/tw as f32, row as f32/th as f32);
3700 let (min_d, nearest) = pts.iter().enumerate().fold((f32::MAX,0usize), |(d,idx),(i,&[cx,cy])| {
3701 let dd = (fx-cx).powi(2)+(fy-cy).powi(2);
3702 if dd < d { (dd,i) } else { (d,idx) }
3703 });
3704 let t = (nearest as f32/cells as f32 + min_d*4.0) % 1.0;
3705 let [cr,cg,cb] = tex_palette(&palette, t);
3706 let (dx, dy) = (tx+col, ty+row);
3707 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3708 }}
3709 return Ok(Value::Unit);
3710 }
3711
3712 "tex_halftone" | "ลายฮาล์ฟโทน" => {
3714 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3715 let dot_size = self.arg_num(&args, 4, 0.05)? as f32;
3716 let time = self.arg_num(&args, 5, 0.0)? as f32;
3717 let palette = self.arg_str(&args, 6, "rainbow");
3718 let mut gfx = self.gfx.borrow_mut();
3719 let (bw, bh) = (gfx.width, gfx.height);
3720 for row in 0..th { for col in 0..tw {
3721 let (fx, fy) = (col as f32/tw as f32, row as f32/th as f32);
3722 let gx = (fx/dot_size).floor(); let gy = (fy/dot_size).floor();
3723 let lx = (fx/dot_size - gx - 0.5)*2.0; let ly = (fy/dot_size - gy - 0.5)*2.0;
3724 let r = (lx*lx + ly*ly).sqrt();
3725 let t = (gx/(1.0/dot_size) + time*0.1) % 1.0;
3726 let a = if r < 0.7 { ((0.7-r)/0.7).clamp(0.,1.) } else { 0.0 };
3727 if a > 0.0 {
3728 let [cr,cg,cb] = tex_palette(&palette, t);
3729 let (dx, dy) = (tx+col, ty+row);
3730 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3731 }
3732 }}
3733 return Ok(Value::Unit);
3734 }
3735
3736 "set_shade_mode" | "设置着色" | "シェード設定" | "셰이드모드" | "ตั้งการแรเงา" => {
3741 let m = self.arg_num(&args, 0, 2.0)? as u8;
3742 self.gfx.borrow_mut().shade_mode = m;
3743 return Ok(Value::Unit);
3744 }
3745 "set_cel_bands" | "设置色阶" | "セル段数" | "셀밴드" | "ตั้งระดับสี" => {
3747 let n = (self.arg_num(&args, 0, 4.0)? as u32).max(2);
3748 self.gfx.borrow_mut().shade.bands = n;
3749 return Ok(Value::Unit);
3750 }
3751 "set_shadow_color" | "设置阴影色" | "影の色" | "그림자색" | "ตั้งสีเงา" => {
3753 let r=self.arg_num(&args,0,26.)? as f32/255.0;
3754 let g=self.arg_num(&args,1,33.)? as f32/255.0;
3755 let b=self.arg_num(&args,2,77.)? as f32/255.0;
3756 self.gfx.borrow_mut().shade.shadow = [r,g,b];
3757 return Ok(Value::Unit);
3758 }
3759 #[cfg(not(target_arch = "wasm32"))]
3765 "crypto_hash" | "แฮชเข้ารหัส" | "几何哈希" | "幾何ハッシュ" | "기하해시" => {
3766 let s = self.arg_str(&args, 0, "");
3767 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(s.as_bytes()))));
3768 }
3769 #[cfg(not(target_arch = "wasm32"))]
3771 "knot_points" | "จุดปม" | "结点坐标" | "結び目点" | "매듭점" => {
3772 let s = self.arg_str(&args, 0, "");
3773 let shape = ling_crypto::geo::KnotShape::from_bytes(s.as_bytes());
3774 let mut out = Vec::with_capacity(shape.points.len() * 3);
3775 for p in &shape.points {
3776 out.push(Value::Number(p[0] as f64));
3777 out.push(Value::Number(p[1] as f64));
3778 out.push(Value::Number(p[2] as f64));
3779 }
3780 return Ok(Value::List(out));
3781 }
3782 #[cfg(not(target_arch = "wasm32"))]
3783 "knot_label" | "ป้ายปม" | "结点标签" | "結び目ラベル" | "매듭라벨" => {
3784 let s = self.arg_str(&args, 0, "");
3785 return Ok(Value::Str(ling_crypto::geo::KnotShape::from_bytes(s.as_bytes()).label()));
3786 }
3787 #[cfg(not(target_arch = "wasm32"))]
3789 "knot_keygen" | "hybrid_keygen" | "สร้างกุญแจปม" | "生成密钥" | "鍵生成" | "키생성" => {
3790 self.crypto_ids.push(ling_crypto::KnotIdentity::generate());
3791 return Ok(Value::Number((self.crypto_ids.len() - 1) as f64));
3792 }
3793 #[cfg(not(target_arch = "wasm32"))]
3794 "knot_public" | "hybrid_public" | "กุญแจสาธารณะปม" | "公钥" | "公開鍵" | "공개키" => {
3795 let h = self.arg_num(&args, 0, 0.0)? as usize;
3796 let pk = self.crypto_ids.get(h).map(|id| hex_encode(id.public_key())).unwrap_or_default();
3797 return Ok(Value::Str(pk));
3798 }
3799 #[cfg(not(target_arch = "wasm32"))]
3801 "knot_encapsulate" | "hybrid_encapsulate" | "ห่อกุญแจปม" | "封装密钥" | "カプセル化" | "캡슐화" => {
3802 let pk = hex_decode(&self.arg_str(&args, 0, ""));
3803 match ling_crypto::geo::knot_encapsulate(&pk) {
3804 Ok((ct, ss)) => return Ok(Value::List(vec![Value::Str(hex_encode(&ct)), Value::Str(hex_encode(&ss))])),
3805 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
3806 }
3807 }
3808 #[cfg(not(target_arch = "wasm32"))]
3810 "knot_decapsulate" | "hybrid_decapsulate" | "แกะกุญแจปม" | "解封装密钥" | "カプセル解除" | "캡슐해제" => {
3811 let h = self.arg_num(&args, 0, 0.0)? as usize;
3812 let ct = hex_decode(&self.arg_str(&args, 1, ""));
3813 let ss = self.crypto_ids.get(h)
3814 .and_then(|id| id.decapsulate(&ct).ok())
3815 .map(|s| hex_encode(&s)).unwrap_or_default();
3816 return Ok(Value::Str(ss));
3817 }
3818 #[cfg(not(target_arch = "wasm32"))]
3820 "crypto_seal" | "ผนึก" | "封印" | "封印する" | "봉인" => {
3821 let key = hex_to_32(&self.arg_str(&args, 0, ""));
3822 let pt = self.arg_str(&args, 1, "");
3823 match ling_crypto::geo::holo_seal(key, pt.as_bytes()) {
3824 Ok(ct) => return Ok(Value::Str(hex_encode(&ct))),
3825 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
3826 }
3827 }
3828 #[cfg(not(target_arch = "wasm32"))]
3829 "crypto_open" | "เปิดผนึก" | "解封" | "封印解除" | "봉인해제" => {
3830 let key = hex_to_32(&self.arg_str(&args, 0, ""));
3831 let ct = hex_decode(&self.arg_str(&args, 1, ""));
3832 match ling_crypto::geo::holo_open(key, &ct) {
3833 Ok(pt) => return Ok(Value::Str(String::from_utf8_lossy(&pt).into_owned())),
3834 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
3835 }
3836 }
3837 #[cfg(not(target_arch = "wasm32"))]
3839 "holo_points" | "จุดโฮโลแกรม" | "全息点" | "ホログラム点" | "홀로그램점" => {
3840 let s = self.arg_str(&args, 0, "");
3841 let frags = ling_crypto::geo::scatter(s.as_bytes());
3842 let mut out = Vec::with_capacity(frags.len() * 4);
3843 for f in &frags { for c in f.coord { out.push(Value::Number(c as f64)); } }
3844 return Ok(Value::List(out));
3845 }
3846 #[cfg(not(target_arch = "wasm32"))]
3847 "holo_fragment_count" | "จำนวนชิ้นโฮโลแกรม" | "全息碎片数" | "ホログラム断片数" | "홀로그램조각수" => {
3848 let s = self.arg_str(&args, 0, "");
3849 return Ok(Value::Number(ling_crypto::geo::scatter(s.as_bytes()).len() as f64));
3850 }
3851
3852 "ease" => {
3856 let name = self.arg_str(&args, 0, "ease");
3857 let t = self.arg_num(&args, 1, 0.0)? as f32;
3858 return Ok(Value::Number(ling_ui::Easing::from_name(&name).apply(t) as f64));
3859 }
3860
3861 "tween" | "补间" | "補間" | "트윈" | "แทรกค่า" => {
3866 let a = self.arg_num(&args, 0, 0.0)?;
3867 let b = self.arg_num(&args, 1, 0.0)?;
3868 let t = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 1.0);
3869 return Ok(Value::Number(a + (b - a) * t));
3870 }
3871 "tween_ease" | "缓动补间" | "緩和補間" | "이징트윈" | "แทรกนุ่ม" => {
3872 let a = self.arg_num(&args, 0, 0.0)? as f32;
3873 let b = self.arg_num(&args, 1, 0.0)? as f32;
3874 let t = self.arg_num(&args, 2, 0.0)? as f32;
3875 let kind = self.arg_str(&args, 3, "linear");
3876 let e = ling_animation::EaseFunction::from_name(&kind);
3877 return Ok(Value::Number(ling_animation::ease::tween_ease(&a, &b, t, e) as f64));
3878 }
3879 "breathe" | "呼吸" | "호흡" | "หายใจ" => {
3881 let t = self.arg_num(&args, 0, 0.0)? as f32;
3882 let rate = self.arg_num(&args, 1, 1.0)? as f32;
3883 let depth = self.arg_num(&args, 2, 0.1)? as f32;
3884 return Ok(Value::Number(ling_animation::scalar::breathe(t, rate, depth) as f64));
3885 }
3886 "wobble" | "摆动" | "揺れ" | "흔들림" | "โยก" => {
3887 let t = self.arg_num(&args, 0, 0.0)? as f32;
3888 let freq = self.arg_num(&args, 1, 1.0)? as f32;
3889 let amp = self.arg_num(&args, 2, 1.0)? as f32;
3890 let phase = self.arg_num(&args, 3, 0.0)? as f32;
3891 return Ok(Value::Number(ling_animation::scalar::wobble(t, freq, amp, phase) as f64));
3892 }
3893 "gait_phase" | "步相" | "歩相" | "걸음위상" | "เฟสก้าว" => {
3894 let t = self.arg_num(&args, 0, 0.0)? as f32;
3895 let speed = self.arg_num(&args, 1, 1.0)? as f32;
3896 return Ok(Value::Number(ling_animation::scalar::gait_phase(t, speed) as f64));
3897 }
3898 "gait_swing" | "步摆" | "歩振り" | "걸음흔들" | "ก้าวแกว่ง" => {
3899 let t = self.arg_num(&args, 0, 0.0)? as f32;
3900 let speed = self.arg_num(&args, 1, 1.0)? as f32;
3901 let stride = self.arg_num(&args, 2, 1.0)? as f32;
3902 return Ok(Value::Number(ling_animation::scalar::gait_swing(t, speed, stride) as f64));
3903 }
3904 "gait_lift" | "抬脚" | "足上げ" | "발들기" | "ยกเท้า" => {
3905 let t = self.arg_num(&args, 0, 0.0)? as f32;
3906 let speed = self.arg_num(&args, 1, 1.0)? as f32;
3907 let height = self.arg_num(&args, 2, 1.0)? as f32;
3908 return Ok(Value::Number(ling_animation::scalar::gait_lift(t, speed, height) as f64));
3909 }
3910 "spring_to" | "弹向" | "バネ寄せ" | "스프링이동" | "สปริงไป" => {
3911 let pos = self.arg_num(&args, 0, 0.0)? as f32;
3912 let vel = self.arg_num(&args, 1, 0.0)? as f32;
3913 let target = self.arg_num(&args, 2, 0.0)? as f32;
3914 let stiffness = self.arg_num(&args, 3, 120.0)? as f32;
3915 let damping = self.arg_num(&args, 4, 14.0)? as f32;
3916 let dt = self.arg_num(&args, 5, 1.0 / 60.0)? as f32;
3917 let (np, nv) = ling_animation::scalar::spring_step(pos, vel, target, stiffness, damping, dt);
3918 return Ok(Value::List(vec![Value::Number(np as f64), Value::Number(nv as f64)]));
3919 }
3920 "ik2" | "反解" | "逆運動" | "역운동" | "ไอเค2" => {
3921 let l1 = self.arg_num(&args, 0, 1.0)? as f32;
3922 let l2 = self.arg_num(&args, 1, 1.0)? as f32;
3923 let tx = self.arg_num(&args, 2, 0.0)? as f32;
3924 let ty = self.arg_num(&args, 3, 0.0)? as f32;
3925 let (sh, el) = ling_animation::scalar::two_bone_ik(l1, l2, tx, ty);
3926 return Ok(Value::List(vec![Value::Number(sh as f64), Value::Number(el as f64)]));
3927 }
3928 "gear_couple" | "齿轮联动" | "歯車連動" | "기어연동" | "เฟืองทด" => {
3930 let angle = self.arg_num(&args, 0, 0.0)? as f32;
3931 let ti = self.arg_num(&args, 1, 1.0)? as f32;
3932 let to = self.arg_num(&args, 2, 1.0)? as f32;
3933 return Ok(Value::Number(ling_animation::scalar::gear(angle, ti, to) as f64));
3934 }
3935 "gear_train" | "齿轮组" | "歯車列" | "기어열" | "ชุดเฟือง" => {
3936 let angle = self.arg_num(&args, 0, 0.0)? as f32;
3937 let teeth: Vec<f32> = match args.get(1) {
3938 Some(Value::List(items)) => items.iter()
3939 .filter_map(|v| if let Value::Number(n) = v { Some(*n as f32) } else { None }).collect(),
3940 _ => Vec::new(),
3941 };
3942 let out = ling_animation::mechanism::gear_train(angle, &teeth);
3943 return Ok(Value::List(out.into_iter().map(|a| Value::Number(a as f64)).collect()));
3944 }
3945 "cam_lift" | "凸轮升程" | "カム揚程" | "캠리프트" | "ยกลูกเบี้ยว" => {
3946 let angle = self.arg_num(&args, 0, 0.0)? as f32;
3947 let lift = self.arg_num(&args, 1, 1.0)? as f32;
3948 return Ok(Value::Number(ling_animation::scalar::cam_lift(angle, lift) as f64));
3949 }
3950 "piston" | "活塞" | "ピストン" | "피스톤" | "ลูกสูบ" => {
3951 let angle = self.arg_num(&args, 0, 0.0)? as f32;
3952 let crank = self.arg_num(&args, 1, 1.0)? as f32;
3953 let rod = self.arg_num(&args, 2, 2.0)? as f32;
3954 return Ok(Value::Number(ling_animation::scalar::piston(angle, crank, rod) as f64));
3955 }
3956 "rack" | "齿条" | "ラック" | "랙" | "แร็ค" => {
3957 let angle = self.arg_num(&args, 0, 0.0)? as f32;
3958 let radius = self.arg_num(&args, 1, 1.0)? as f32;
3959 return Ok(Value::Number(ling_animation::scalar::rack(angle, radius) as f64));
3960 }
3961 #[cfg(not(target_arch = "wasm32"))]
3962 "mouse_x" => {
3963 let gfx = self.gfx.borrow();
3964 let v = gfx.window.as_ref().and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp)).map(|p| p.0 as f64).unwrap_or(0.0);
3965 return Ok(Value::Number(v));
3966 }
3967 #[cfg(not(target_arch = "wasm32"))]
3968 "mouse_y" => {
3969 let gfx = self.gfx.borrow();
3970 let v = gfx.window.as_ref().and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp)).map(|p| p.1 as f64).unwrap_or(0.0);
3971 return Ok(Value::Number(v));
3972 }
3973 #[cfg(not(target_arch = "wasm32"))]
3974 "mouse_down" => {
3975 let gfx = self.gfx.borrow();
3976 let d = gfx.window.as_ref().map(|w| w.get_mouse_down(minifb::MouseButton::Left)).unwrap_or(false);
3977 return Ok(Value::Bool(d));
3978 }
3979 #[cfg(not(target_arch = "wasm32"))]
3980 "mouse_down_right" | "เมาส์ขวา" => {
3981 let gfx = self.gfx.borrow();
3982 let d = gfx.window.as_ref().map(|w| w.get_mouse_down(minifb::MouseButton::Right)).unwrap_or(false);
3983 return Ok(Value::Bool(d));
3984 }
3985 #[cfg(not(target_arch = "wasm32"))]
3986 "mouse_down_middle" | "เมาส์กลาง" => {
3987 let gfx = self.gfx.borrow();
3988 let d = gfx.window.as_ref().map(|w| w.get_mouse_down(minifb::MouseButton::Middle)).unwrap_or(false);
3989 return Ok(Value::Bool(d));
3990 }
3991 #[cfg(not(target_arch = "wasm32"))]
3992 "ui_hot" | "热区" | "ホットエリア" | "핫존" | "พื้นที่สัมผัส" => {
3993 let x = self.arg_num(&args,0,0.0)? as f32;
3994 let y = self.arg_num(&args,1,0.0)? as f32;
3995 let w = self.arg_num(&args,2,0.0)? as f32;
3996 let h = self.arg_num(&args,3,0.0)? as f32;
3997 let gfx = self.gfx.borrow();
3998 let (mx,my) = gfx.window.as_ref().and_then(|win| win.get_mouse_pos(minifb::MouseMode::Clamp)).unwrap_or((0.0,0.0));
3999 return Ok(Value::Bool(ling_ui::holo::hit_rect(mx,my,x,y,w,h)));
4000 }
4001 "ui_text" | "界面文字" | "UI文字" | "UI텍스트" | "ข้อความหน้าจอ" => {
4003 let x = self.arg_num(&args,0,0.0)? as f32;
4004 let y = self.arg_num(&args,1,0.0)? as f32;
4005 let scale = self.arg_num(&args,2,16.0)? as f32;
4006 let s = self.arg_str(&args,3,"");
4007 let segs = ling_ui::holo::text_lines(&s, x, y, scale*0.62, scale, scale*0.24);
4008 let mut gfx = self.gfx.borrow_mut();
4009 let (w,h,color) = (gfx.width, gfx.height, gfx.color);
4010 for sg in segs { draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]); }
4011 return Ok(Value::Unit);
4012 }
4013 #[cfg(not(target_arch = "wasm32"))]
4016 "font_load" | "โหลดฟอนต์" | "加载字体" | "フォント読込" | "글꼴로드" => {
4017 let path = self.arg_str(&args, 0, "");
4018 let weight = match self.arg_num(&args, 1, 0.0)? {
4020 w if w > 0.0 => Some(w as f32),
4021 _ => None,
4022 };
4023 let mut loaded = ling_graphics::VectorFont::from_path_weight(&path, weight);
4025 if loaded.is_err() {
4026 if let Some(dir) = &self.source_dir {
4027 let joined = dir.join(&path);
4028 loaded = ling_graphics::VectorFont::from_path_weight(&joined.to_string_lossy(), weight);
4029 }
4030 }
4031 match loaded {
4032 Ok(f) => {
4033 let id = self.fonts.len();
4034 self.fonts.push(f);
4035 return Ok(Value::Number(id as f64));
4036 }
4037 Err(e) => {
4038 eprintln!("font_load failed ({path}): {e}");
4039 return Ok(Value::Number(-1.0));
4040 }
4041 }
4042 }
4043 #[cfg(not(target_arch = "wasm32"))]
4046 "font_text" | "ข้อความฟอนต์" | "字体文本" | "フォント文字" | "글꼴텍스트" => {
4047 let id = self.arg_num(&args, 0, 0.0)? as i64;
4048 let x = self.arg_num(&args, 1, 0.0)? as f32;
4049 let y = self.arg_num(&args, 2, 0.0)? as f32;
4050 let px = self.arg_num(&args, 3, 16.0)? as f32;
4051 let s = self.arg_str(&args, 4, "");
4052 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
4053 let strokes = self.font_layout_2d(id as usize, x, y, px, &s);
4054 let mut gfx = self.gfx.borrow_mut();
4055 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
4056 for pl in &strokes {
4057 for seg in pl.windows(2) {
4058 crate::gfx::raster::draw_line_aa(&mut gfx.buffer, w, h, color, add,
4059 seg[0][0], seg[0][1], seg[1][0], seg[1][1]);
4060 }
4061 }
4062 }
4063 return Ok(Value::Unit);
4064 }
4065 #[cfg(not(target_arch = "wasm32"))]
4067 "font_text_fill" | "เติมฟอนต์" | "填充字体" | "フォント塗り" | "글꼴채움" => {
4068 let id = self.arg_num(&args, 0, 0.0)? as i64;
4069 let x = self.arg_num(&args, 1, 0.0)? as f32;
4070 let y = self.arg_num(&args, 2, 0.0)? as f32;
4071 let px = self.arg_num(&args, 3, 16.0)? as f32;
4072 let s = self.arg_str(&args, 4, "");
4073 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
4074 let glyphs = self.font_layout_2d_glyphs(id as usize, x, y, px, &s);
4076 let mut gfx = self.gfx.borrow_mut();
4077 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
4078 for contours in &glyphs {
4079 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, w, h, color, add, contours);
4080 }
4081 }
4082 return Ok(Value::Unit);
4083 }
4084 #[cfg(not(target_arch = "wasm32"))]
4088 "font_text_3d" | "ข้อความฟอนต์3มิติ" | "字体3D" | "フォント3D" | "글꼴3D" => {
4089 let id = self.arg_num(&args, 0, 0.0)? as i64;
4090 let cx=self.arg_num(&args,1,0.0)? as f32; let cy=self.arg_num(&args,2,0.0)? as f32; let cz=self.arg_num(&args,3,0.0)? as f32;
4091 let ux=self.arg_num(&args,4,1.0)? as f32; let uy=self.arg_num(&args,5,0.0)? as f32; let uz=self.arg_num(&args,6,0.0)? as f32;
4092 let vx=self.arg_num(&args,7,0.0)? as f32; let vy=self.arg_num(&args,8,1.0)? as f32; let vz=self.arg_num(&args,9,0.0)? as f32;
4093 let size=self.arg_num(&args,10,1.0)? as f32;
4094 let s = self.arg_str(&args,11,"");
4095 if id >= 0 && (id as usize) < self.fonts.len() && size > 0.0 {
4096 let font = &mut self.fonts[id as usize];
4098 let asc = font.ascent();
4099 let mut pen = 0.0f32;
4100 let mut lines: Vec<[f32; 6]> = Vec::new();
4101 for ch in s.chars() {
4102 let go = font.glyph_outline(ch, 0.01);
4103 for pl in &go.polylines {
4104 for seg in pl.windows(2) {
4105 let map = |p: [f32; 2]| {
4106 let a = pen + p[0];
4107 let b = p[1] - asc; [cx + a*size*ux + b*size*vx,
4109 cy + a*size*uy + b*size*vy,
4110 cz + a*size*uz + b*size*vz]
4111 };
4112 let p0 = map(seg[0]); let p1 = map(seg[1]);
4113 lines.push([p0[0],p0[1],p0[2], p1[0],p1[1],p1[2]]);
4114 }
4115 }
4116 pen += go.advance;
4117 }
4118 let mut gfx = self.gfx.borrow_mut();
4119 let color = gfx.color;
4120 let near = -gfx.camera.zdist + 0.05;
4121 for l in &lines {
4122 let (mut ax, mut ay, mut az) = (l[0], l[1], l[2]);
4123 let (mut bx, mut by, mut bz) = (l[3], l[4], l[5]);
4124 let da = gfx.camera.depth(ax, ay, az);
4125 let db = gfx.camera.depth(bx, by, bz);
4126 if da <= near && db <= near { continue; }
4127 if da <= near {
4128 let t = (near - da) / (db - da);
4129 ax += t*(bx-ax); ay += t*(by-ay); az += t*(bz-az);
4130 } else if db <= near {
4131 let t = (near - da) / (db - da);
4132 bx = ax + t*(bx-ax); by = ay + t*(by-ay); bz = az + t*(bz-az);
4133 }
4134 let (sax, say, da2) = gfx.camera.project(ax, ay, az);
4135 let (sbx, sby, db2) = gfx.camera.project(bx, by, bz);
4136 let depth = (da2 + db2) / 2.0;
4137 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
4138 }
4139 }
4140 return Ok(Value::Unit);
4141 }
4142 #[cfg(not(target_arch = "wasm32"))]
4144 "font_width" | "ความกว้างฟอนต์" | "字体宽度" | "フォント幅" | "글꼴너비" => {
4145 let id = self.arg_num(&args, 0, 0.0)? as i64;
4146 let px = self.arg_num(&args, 1, 16.0)? as f32;
4147 let s = self.arg_str(&args, 2, "");
4148 if id >= 0 && (id as usize) < self.fonts.len() {
4149 return Ok(Value::Number(self.fonts[id as usize].measure(&s, px) as f64));
4150 }
4151 return Ok(Value::Number(0.0));
4152 }
4153 "ui_frame" | "边框" | "フレーム枠" | "프레임틀" | "กรอบ" => {
4155 let x=self.arg_num(&args,0,0.0)? as f32; let y=self.arg_num(&args,1,0.0)? as f32;
4156 let w0=self.arg_num(&args,2,0.0)? as f32; let h0=self.arg_num(&args,3,0.0)? as f32;
4157 let l=self.arg_num(&args,4,14.0)? as f32;
4158 let segs = ling_ui::holo::corner_brackets(x,y,w0,h0,l);
4159 let mut gfx = self.gfx.borrow_mut();
4160 let (w,h,color)=(gfx.width,gfx.height,gfx.color);
4161 for sg in segs { draw_line(&mut gfx.buffer, w,h,color, sg[0],sg[1],sg[2],sg[3]); }
4162 return Ok(Value::Unit);
4163 }
4164 "ui_bevel" | "斜角框" | "ベベル枠" | "베벨틀" | "กรอบเฉียง" => {
4166 let x=self.arg_num(&args,0,0.0)? as f32; let y=self.arg_num(&args,1,0.0)? as f32;
4167 let w0=self.arg_num(&args,2,0.0)? as f32; let h0=self.arg_num(&args,3,0.0)? as f32;
4168 let bv=self.arg_num(&args,4,10.0)? as f32;
4169 let segs = ling_ui::holo::beveled_rect(x,y,w0,h0,bv);
4170 let mut gfx = self.gfx.borrow_mut();
4171 let (w,h,color)=(gfx.width,gfx.height,gfx.color);
4172 for sg in segs { draw_line(&mut gfx.buffer, w,h,color, sg[0],sg[1],sg[2],sg[3]); }
4173 return Ok(Value::Unit);
4174 }
4175
4176 #[cfg(not(target_arch = "wasm32"))]
4182 "ui_theme" | "界面主题" | "UIテーマ" | "인터페이스테마" | "ธีมส่วนติดต่อ" => {
4183 let cur = self.ui_theme;
4184 let primary = self.color_at(&args, 0, cur.primary);
4185 let accent = self.color_at(&args, 3, cur.accent);
4186 let track = self.color_at(&args, 6, cur.track);
4187 let warn = self.color_at(&args, 9, cur.warn);
4188 let text = self.color_at(&args, 12, cur.text);
4189 let bg = self.color_at(&args, 15, cur.bg);
4190 self.ui_theme = UiTheme { primary, accent, track, warn, text, bg };
4191 return Ok(Value::Unit);
4192 }
4193
4194 #[cfg(not(target_arch = "wasm32"))]
4196 "ui_radar" | "雷达" | "レーダー" | "레이더" | "เรดาร์" => {
4197 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
4198 let r=self.arg_num(&args,2,60.)? as f32; let sweep=self.arg_num(&args,3,0.)? as f32;
4199 let th=self.ui_theme;
4200 let prim=self.color_at(&args,4,th.primary);
4201 self.draw_ui(&ling_ui::widgets::radar(cx,cy,r,sweep, prim, th.accent, th.track));
4202 return Ok(Value::Unit);
4203 }
4204 #[cfg(not(target_arch = "wasm32"))]
4205 "ui_compass" | "罗盘" | "コンパス" | "나침반" | "เข็มทิศ" => {
4206 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4207 let w0=self.arg_num(&args,2,300.)? as f32; let h0=self.arg_num(&args,3,24.)? as f32;
4208 let head=self.arg_num(&args,4,0.)? as f32;
4209 let th=self.ui_theme; let prim=self.color_at(&args,5,th.primary);
4210 self.draw_ui(&ling_ui::widgets::compass(x,y,w0,h0,head, prim, th.track));
4211 return Ok(Value::Unit);
4212 }
4213 #[cfg(not(target_arch = "wasm32"))]
4214 "ui_reticle" | "准星" | "照準" | "조준선" | "เป้าเล็ง" => {
4215 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
4216 let r=self.arg_num(&args,2,30.)? as f32; let spread=self.arg_num(&args,3,0.)? as f32;
4217 let th=self.ui_theme; let prim=self.color_at(&args,4,th.primary);
4218 self.draw_ui(&ling_ui::widgets::reticle(cx,cy,r,spread, prim));
4219 return Ok(Value::Unit);
4220 }
4221 #[cfg(not(target_arch = "wasm32"))]
4222 "ui_target" | "锁定框" | "ターゲット" | "표적" | "กรอบเป้า" => {
4223 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4224 let w0=self.arg_num(&args,2,80.)? as f32; let h0=self.arg_num(&args,3,80.)? as f32;
4225 let lock=self.arg_num(&args,4,0.)? as f32;
4226 let th=self.ui_theme; let prim=self.color_at(&args,5,th.primary);
4227 self.draw_ui(&ling_ui::widgets::target(x,y,w0,h0,lock, prim, th.accent));
4228 return Ok(Value::Unit);
4229 }
4230 #[cfg(not(target_arch = "wasm32"))]
4231 "ui_panel" | "面板" | "パネル" | "패널" | "แผง" => {
4232 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4233 let w0=self.arg_num(&args,2,200.)? as f32; let h0=self.arg_num(&args,3,120.)? as f32;
4234 let bv=self.arg_num(&args,4,12.)? as f32;
4235 let th=self.ui_theme; let prim=self.color_at(&args,5,th.primary);
4236 self.draw_ui(&ling_ui::widgets::panel(x,y,w0,h0,bv, prim, th.bg));
4237 return Ok(Value::Unit);
4238 }
4239 #[cfg(not(target_arch = "wasm32"))]
4240 "ui_scanlines" | "扫描线" | "走査線" | "스캔라인" | "เส้นสแกน" => {
4241 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4242 let w0=self.arg_num(&args,2,200.)? as f32; let h0=self.arg_num(&args,3,120.)? as f32;
4243 let dens=self.arg_num(&args,4,24.)? as usize;
4244 let th=self.ui_theme; let line=self.color_at(&args,5,th.track);
4245 self.draw_ui(&ling_ui::widgets::scanlines(x,y,w0,h0,dens, line));
4246 return Ok(Value::Unit);
4247 }
4248
4249 #[cfg(not(target_arch = "wasm32"))]
4251 "ui_bar" | "进度条" | "バー" | "막대" | "แถบ" => {
4252 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4253 let w0=self.arg_num(&args,2,160.)? as f32; let h0=self.arg_num(&args,3,16.)? as f32;
4254 let val=self.arg_num(&args,4,0.)? as f32; let max=self.arg_num(&args,5,1.)? as f32;
4255 let th=self.ui_theme; let fill=self.color_at(&args,6,th.primary);
4256 self.draw_ui(&ling_ui::widgets::bar(x,y,w0,h0, val/max.max(1e-6), fill, th.track));
4257 return Ok(Value::Unit);
4258 }
4259 #[cfg(not(target_arch = "wasm32"))]
4260 "ui_segbar" | "分段条" | "分割バー" | "분할막대" | "แถบแบ่ง" => {
4261 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4262 let w0=self.arg_num(&args,2,160.)? as f32; let h0=self.arg_num(&args,3,16.)? as f32;
4263 let val=self.arg_num(&args,4,0.)? as f32; let max=self.arg_num(&args,5,1.)? as f32;
4264 let segs=self.arg_num(&args,6,10.)? as usize;
4265 let th=self.ui_theme; let fill=self.color_at(&args,7,th.primary);
4266 self.draw_ui(&ling_ui::widgets::segbar(x,y,w0,h0, val/max.max(1e-6), segs, fill, th.track));
4267 return Ok(Value::Unit);
4268 }
4269 #[cfg(not(target_arch = "wasm32"))]
4270 "ui_gauge" | "仪表" | "ゲージ" | "게이지" | "มาตรวัด" => {
4271 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
4272 let r=self.arg_num(&args,2,50.)? as f32;
4273 let val=self.arg_num(&args,3,0.)? as f32; let max=self.arg_num(&args,4,1.)? as f32;
4274 let th=self.ui_theme; let needle=self.color_at(&args,5,th.warn);
4275 self.draw_ui(&ling_ui::widgets::gauge(cx,cy,r, val/max.max(1e-6), needle, th.accent, th.track));
4276 return Ok(Value::Unit);
4277 }
4278 #[cfg(not(target_arch = "wasm32"))]
4279 "ui_ring" | "环表" | "リングメーター" | "링미터" | "วงแหวนวัด" => {
4280 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
4281 let r=self.arg_num(&args,2,40.)? as f32;
4282 let val=self.arg_num(&args,3,0.)? as f32; let max=self.arg_num(&args,4,1.)? as f32;
4283 let th=self.ui_theme; let fill=self.color_at(&args,5,th.primary);
4284 self.draw_ui(&ling_ui::widgets::ring(cx,cy,r, val/max.max(1e-6), fill, th.track));
4285 return Ok(Value::Unit);
4286 }
4287 #[cfg(not(target_arch = "wasm32"))]
4288 "ui_vu" | "音量条" | "VUメーター" | "음량막대" | "มาตรเสียง" => {
4289 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4290 let w0=self.arg_num(&args,2,160.)? as f32; let h0=self.arg_num(&args,3,60.)? as f32;
4291 let levels=self.arg_list_f32(&args,4);
4292 let th=self.ui_theme; let fill=self.color_at(&args,5,th.primary);
4293 self.draw_ui(&ling_ui::widgets::vu(x,y,w0,h0, &levels, fill, th.warn));
4294 return Ok(Value::Unit);
4295 }
4296 #[cfg(not(target_arch = "wasm32"))]
4297 "ui_spark" | "迷你图" | "スパークライン" | "스파크라인" | "กราฟจิ๋ว" => {
4298 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4299 let w0=self.arg_num(&args,2,160.)? as f32; let h0=self.arg_num(&args,3,40.)? as f32;
4300 let vals=self.arg_list_f32(&args,4);
4301 let th=self.ui_theme; let line=self.color_at(&args,5,th.accent);
4302 self.draw_ui(&ling_ui::widgets::spark(x,y,w0,h0, &vals, line));
4303 return Ok(Value::Unit);
4304 }
4305 #[cfg(not(target_arch = "wasm32"))]
4306 "ui_battery" | "电池" | "バッテリー" | "배터리" | "แบตเตอรี่" => {
4307 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4308 let w0=self.arg_num(&args,2,50.)? as f32; let h0=self.arg_num(&args,3,22.)? as f32;
4309 let val=self.arg_num(&args,4,1.)? as f32; let max=self.arg_num(&args,5,1.)? as f32;
4310 let th=self.ui_theme; let fill=self.color_at(&args,6,th.accent);
4311 self.draw_ui(&ling_ui::widgets::battery(x,y,w0,h0, val/max.max(1e-6), fill, th.track, th.warn));
4312 return Ok(Value::Unit);
4313 }
4314
4315 #[cfg(not(target_arch = "wasm32"))]
4317 "ui_button" | "按钮" | "ボタン" | "버튼" | "ปุ่ม" => {
4318 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4319 let w0=self.arg_num(&args,2,120.)? as f32; let h0=self.arg_num(&args,3,40.)? as f32;
4320 let (mx,my,down)=self.mouse_now();
4321 let hover=ling_ui::holo::hit_rect(mx,my,x,y,w0,h0);
4322 let clicked = hover && down && !self.mouse_was_down;
4323 let th=self.ui_theme; let prim=self.color_at(&args,4,th.primary);
4324 self.draw_ui(&ling_ui::widgets::button(x,y,w0,h0, hover, down&&hover, prim, th.bg));
4325 return Ok(Value::Number(if clicked {1.0} else {0.0}));
4326 }
4327 #[cfg(not(target_arch = "wasm32"))]
4328 "ui_toggle" | "开关" | "トグル" | "토글" | "สวิตช์" => {
4329 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4330 let w0=self.arg_num(&args,2,52.)? as f32; let h0=self.arg_num(&args,3,24.)? as f32;
4331 let mut state=self.arg_num(&args,4,0.)? > 0.5;
4332 let (mx,my,down)=self.mouse_now();
4333 let hover=ling_ui::holo::hit_rect(mx,my,x,y,w0,h0);
4334 if hover && down && !self.mouse_was_down { state = !state; }
4335 let th=self.ui_theme; let on=self.color_at(&args,5,th.accent);
4336 self.draw_ui(&ling_ui::widgets::toggle(x,y,w0,h0, state, on, th.track));
4337 return Ok(Value::Number(if state {1.0} else {0.0}));
4338 }
4339 #[cfg(not(target_arch = "wasm32"))]
4340 "ui_slider" | "滑块" | "スライダー" | "슬라이더" | "แถบเลื่อน" => {
4341 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4342 let w0=self.arg_num(&args,2,160.)? as f32;
4343 let mut val=self.arg_num(&args,3,0.)? as f32;
4344 let mn=self.arg_num(&args,4,0.)? as f32; let mx_=self.arg_num(&args,5,1.)? as f32;
4345 let (mx,my,down)=self.mouse_now();
4346 let hover=ling_ui::holo::hit_rect(mx,my,x-8.0,y-10.0,w0+16.0,20.0);
4347 if hover && down {
4348 let frac=((mx-x)/w0).max(0.0).min(1.0);
4349 val = mn + (mx_-mn)*frac;
4350 }
4351 let frac=((val-mn)/(mx_-mn).abs().max(1e-6)).max(0.0).min(1.0);
4352 let th=self.ui_theme; let fill=self.color_at(&args,6,th.primary);
4353 self.draw_ui(&ling_ui::widgets::slider(x,y,w0, frac, hover, fill, th.track));
4354 return Ok(Value::Number(val as f64));
4355 }
4356 #[cfg(not(target_arch = "wasm32"))]
4357 "ui_checkbox" | "复选框" | "チェックボックス" | "체크박스" | "ช่องเลือก" => {
4358 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4359 let s=self.arg_num(&args,2,20.)? as f32;
4360 let mut checked=self.arg_num(&args,3,0.)? > 0.5;
4361 let (mx,my,down)=self.mouse_now();
4362 let hover=ling_ui::holo::hit_rect(mx,my,x,y,s,s);
4363 if hover && down && !self.mouse_was_down { checked = !checked; }
4364 let th=self.ui_theme; let prim=self.color_at(&args,4,th.primary);
4365 self.draw_ui(&ling_ui::widgets::checkbox(x,y,s, checked, hover, prim, th.track));
4366 return Ok(Value::Number(if checked {1.0} else {0.0}));
4367 }
4368 #[cfg(not(target_arch = "wasm32"))]
4369 "ui_tabs" | "标签页" | "タブ" | "탭" | "แท็บ" => {
4370 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4371 let w0=self.arg_num(&args,2,240.)? as f32; let h0=self.arg_num(&args,3,28.)? as f32;
4372 let count=self.arg_num(&args,4,3.)? as usize;
4373 let mut active=self.arg_num(&args,5,0.)? as i32;
4374 let (mx,my,down)=self.mouse_now();
4375 let mut hover=-1;
4376 if my>=y && my<=y+h0 && mx>=x && mx<=x+w0 && count>0 {
4377 hover = (((mx-x)/(w0/count as f32)) as i32).max(0).min(count as i32-1);
4378 if down && !self.mouse_was_down { active = hover; }
4379 }
4380 let th=self.ui_theme; let prim=self.color_at(&args,6,th.primary);
4381 self.draw_ui(&ling_ui::widgets::tabs(x,y,w0,h0, count, active as usize, hover, prim, th.track));
4382 return Ok(Value::Number(active as f64));
4383 }
4384 #[cfg(not(target_arch = "wasm32"))]
4385 "ui_progress" | "进度" | "プログレス" | "진행바" | "ความคืบหน้า" => {
4386 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4387 let w0=self.arg_num(&args,2,200.)? as f32; let h0=self.arg_num(&args,3,12.)? as f32;
4388 let frac=self.arg_num(&args,4,0.)? as f32;
4389 let th=self.ui_theme; let fill=self.color_at(&args,5,th.accent);
4390 self.draw_ui(&ling_ui::widgets::progress(x,y,w0,h0, frac, fill, th.track));
4391 return Ok(Value::Unit);
4392 }
4393 #[cfg(not(target_arch = "wasm32"))]
4394 "ui_tooltip" | "提示框" | "ツールチップ" | "툴팁" | "คำแนะนำ" => {
4395 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4396 let w0=self.arg_num(&args,2,120.)? as f32; let h0=self.arg_num(&args,3,28.)? as f32;
4397 let th=self.ui_theme; let prim=self.color_at(&args,4,th.primary);
4398 self.draw_ui(&ling_ui::widgets::tooltip(x,y,w0,h0, prim, th.bg));
4399 return Ok(Value::Unit);
4400 }
4401 #[cfg(not(target_arch = "wasm32"))]
4402 "ui_stepper" | "步进器" | "ステッパー" | "스테퍼" | "ตัวปรับค่า" => {
4403 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4404 let w0=self.arg_num(&args,2,120.)? as f32; let h0=self.arg_num(&args,3,28.)? as f32;
4405 let mut val=self.arg_num(&args,4,0.)? as f32; let step=self.arg_num(&args,5,1.)? as f32;
4406 let (mx,my,down)=self.mouse_now();
4407 let hm=ling_ui::holo::hit_rect(mx,my,x,y,h0,h0);
4408 let hp=ling_ui::holo::hit_rect(mx,my,x+w0-h0,y,h0,h0);
4409 if down && !self.mouse_was_down { if hm { val -= step; } if hp { val += step; } }
4410 let th=self.ui_theme; let prim=self.color_at(&args,6,th.primary);
4411 self.draw_ui(&ling_ui::widgets::stepper(x,y,w0,h0, hm, hp, prim, th.track));
4412 return Ok(Value::Number(val as f64));
4413 }
4414
4415 #[cfg(not(target_arch = "wasm32"))]
4417 "ui_healthbar" | "血条" | "体力バー" | "체력바" | "แถบพลังชีวิต" => {
4418 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4419 let w0=self.arg_num(&args,2,180.)? as f32; let h0=self.arg_num(&args,3,16.)? as f32;
4420 let val=self.arg_num(&args,4,1.)? as f32; let max=self.arg_num(&args,5,1.)? as f32;
4421 let pulse=self.arg_num(&args,6,0.)? as f32;
4422 let th=self.ui_theme; let full=self.color_at(&args,7,th.accent);
4423 self.draw_ui(&ling_ui::widgets::healthbar(x,y,w0,h0, val/max.max(1e-6), pulse, full, th.warn, th.track));
4424 return Ok(Value::Unit);
4425 }
4426 #[cfg(not(target_arch = "wasm32"))]
4427 "ui_cooldown" | "冷却" | "クールダウン" | "쿨다운" | "คูลดาวน์" => {
4428 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
4429 let r=self.arg_num(&args,2,28.)? as f32; let frac=self.arg_num(&args,3,0.)? as f32;
4430 let th=self.ui_theme; let fill=self.color_at(&args,4,th.primary);
4431 self.draw_ui(&ling_ui::widgets::cooldown(cx,cy,r, frac, fill, th.track));
4432 return Ok(Value::Unit);
4433 }
4434 #[cfg(not(target_arch = "wasm32"))]
4435 "ui_counter" | "计数器" | "カウンター" | "카운터" | "ตัวนับ" => {
4436 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4437 let dw=self.arg_num(&args,2,14.)? as f32; let dh=self.arg_num(&args,3,24.)? as f32;
4438 let val=self.arg_num(&args,4,0.)? as i64; let digits=self.arg_num(&args,5,4.)? as usize;
4439 let th=self.ui_theme; let on=self.color_at(&args,6,th.primary);
4440 let off=ling_ui::widgets::shade(th.track,0.5);
4441 self.draw_ui(&ling_ui::widgets::counter(x,y,dw,dh, val, digits, on, off));
4442 return Ok(Value::Unit);
4443 }
4444 #[cfg(not(target_arch = "wasm32"))]
4445 "ui_minimap" | "小地图" | "ミニマップ" | "미니맵" | "แผนที่ย่อ" => {
4446 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4447 let w0=self.arg_num(&args,2,140.)? as f32; let h0=self.arg_num(&args,3,140.)? as f32;
4448 let th=self.ui_theme; let prim=self.color_at(&args,4,th.primary);
4449 self.draw_ui(&ling_ui::widgets::minimap(x,y,w0,h0, prim, th.bg));
4450 return Ok(Value::Unit);
4451 }
4452 #[cfg(not(target_arch = "wasm32"))]
4453 "ui_dpad" | "方向键" | "方向パッド" | "방향패드" | "ปุ่มทิศทาง" => {
4454 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
4455 let r=self.arg_num(&args,2,50.)? as f32;
4456 let (mx,my,down)=self.mouse_now();
4457 let mut dir=0;
4458 if down {
4459 let (dx,dy)=(mx-cx, my-cy);
4460 if dx*dx+dy*dy <= r*r {
4461 if dx.abs() > dy.abs() { dir = if dx>0.0 {2} else {4}; }
4462 else { dir = if dy>0.0 {3} else {1}; }
4463 }
4464 }
4465 let th=self.ui_theme; let prim=self.color_at(&args,3,th.primary);
4466 self.draw_ui(&ling_ui::widgets::dpad(cx,cy,r, dir, prim, th.track));
4467 return Ok(Value::Number(dir as f64));
4468 }
4469 #[cfg(not(target_arch = "wasm32"))]
4470 "ui_slotgrid" | "物品格" | "スロットグリッド" | "슬롯격자" | "ช่องไอเทม" => {
4471 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4472 let cols=self.arg_num(&args,2,4.)? as usize; let rows=self.arg_num(&args,3,1.)? as usize;
4473 let cell=self.arg_num(&args,4,36.)? as f32; let sel=self.arg_num(&args,5,-1.)? as i32;
4474 let th=self.ui_theme; let prim=self.color_at(&args,6,th.primary);
4475 self.draw_ui(&ling_ui::widgets::slotgrid(x,y,cols,rows,cell, sel, prim, th.track));
4476 return Ok(Value::Unit);
4477 }
4478 #[cfg(not(target_arch = "wasm32"))]
4479 "ui_vignette" | "暗角" | "ビネット" | "비네트" | "ขอบมืด" => {
4480 let intensity=self.arg_num(&args,0,0.5)? as f32;
4481 let (w,h)={ let g=self.gfx.borrow(); (g.width as f32, g.height as f32) };
4482 let th=self.ui_theme; let col=self.color_at(&args,1,th.warn);
4483 self.draw_ui(&ling_ui::widgets::vignette(w,h, intensity, col));
4484 return Ok(Value::Unit);
4485 }
4486
4487 #[cfg(not(target_arch = "wasm32"))]
4489 "ui_gauge3d" | "立体仪表" | "立体ゲージ" | "입체게이지" | "มาตรวัด3มิติ" => {
4490 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
4491 let r=self.arg_num(&args,2,50.)? as f32;
4492 let val=self.arg_num(&args,3,0.)? as f32; let max=self.arg_num(&args,4,1.)? as f32;
4493 let spin=self.arg_num(&args,5,0.)? as f32;
4494 let th=self.ui_theme; let fill=self.color_at(&args,6,th.primary);
4495 self.draw_ui(&ling_ui::widgets::gauge3d(cx,cy,r, val/max.max(1e-6), spin, fill, th.track));
4496 return Ok(Value::Unit);
4497 }
4498 #[cfg(not(target_arch = "wasm32"))]
4499 "ui_panel3d" | "立体面板" | "立体パネル" | "입체패널" | "แผง3มิติ" => {
4500 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4501 let w0=self.arg_num(&args,2,200.)? as f32; let h0=self.arg_num(&args,3,120.)? as f32;
4502 let depth=self.arg_num(&args,4,14.)? as f32;
4503 let th=self.ui_theme; let prim=self.color_at(&args,5,th.primary);
4504 self.draw_ui(&ling_ui::widgets::panel3d(x,y,w0,h0,depth, prim, th.bg));
4505 return Ok(Value::Unit);
4506 }
4507 #[cfg(not(target_arch = "wasm32"))]
4508 "ui_radar3d" | "立体雷达" | "立体レーダー" | "입체레이더" | "เรดาร์3มิติ" => {
4509 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
4510 let r=self.arg_num(&args,2,60.)? as f32; let tilt=self.arg_num(&args,3,0.9)? as f32;
4511 let sweep=self.arg_num(&args,4,0.)? as f32;
4512 let th=self.ui_theme; let prim=self.color_at(&args,5,th.primary);
4513 self.draw_ui(&ling_ui::widgets::radar3d(cx,cy,r,tilt,sweep, prim, th.track));
4514 return Ok(Value::Unit);
4515 }
4516
4517 #[cfg(not(target_arch = "wasm32"))]
4519 "audio_blip" | "提示音" | "ビープ音" | "효과음" | "เสียงบี๊บ" => {
4520 let freq=self.arg_num(&args,0,660.)? as f32;
4521 let dur=self.arg_num(&args,1,0.08)? as f32;
4522 let wave=Wave::from_name(&self.arg_str(&args,2,"sine"));
4523 let amp=self.arg_num(&args,3,0.25)? as f32;
4524 if let Some(audio)=&self.audio { audio.blip(freq, amp, dur, wave); }
4525 return Ok(Value::Unit);
4526 }
4527 #[cfg(not(target_arch = "wasm32"))]
4528 "ui_sound" | "界面音" | "UI音" | "인터페이스음" | "เสียงปุ่ม" => {
4529 let name=self.arg_str(&args,0,"click");
4530 if let Some(audio)=&self.audio {
4531 match name.as_str() {
4532 "hover" => audio.blip(880.0, 0.10, 0.04, Wave::Sine),
4533 "confirm" => { audio.blip(660.0, 0.22, 0.07, Wave::Square); audio.blip(990.0, 0.18, 0.10, Wave::Square); }
4534 "error" => { audio.blip(180.0, 0.30, 0.16, Wave::Saw); audio.blip(140.0, 0.30, 0.18, Wave::Saw); }
4535 "toggle" => audio.blip(520.0, 0.22, 0.05, Wave::Triangle),
4536 "tick" => audio.blip(1500.0, 0.12, 0.02, Wave::Square),
4537 _ => audio.blip(720.0, 0.26, 0.05, Wave::Square), }
4539 }
4540 return Ok(Value::Unit);
4541 }
4542
4543 #[cfg(not(target_arch = "wasm32"))]
4551 "music_load" | "载入音乐" | "音楽読込" | "음악로드" | "โหลดเพลง" => {
4552 let path = self.arg_str(&args, 0, "");
4553 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4554 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4555 else { path.clone() };
4556 match ling_music::load(&resolved) {
4557 Ok(t) => { let id = self.tracks.len(); self.tracks.push(t); return Ok(Value::Number(id as f64)); }
4558 Err(e) => { eprintln!("music_load failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
4559 }
4560 }
4561 #[cfg(not(target_arch = "wasm32"))]
4562 "music_duration" | "音乐时长" | "音楽長さ" | "음악길이" | "ความยาวเพลง" => {
4563 let id = self.arg_num(&args,0,0.0)? as i64;
4564 let d = self.tracks.get(id as usize).map(|t| t.duration).unwrap_or(0.0);
4565 return Ok(Value::Number(d as f64));
4566 }
4567 #[cfg(not(target_arch = "wasm32"))]
4568 "music_bpm" | "节拍速度" | "テンポ" | "템포" | "จังหวะต่อนาที" => {
4569 let id = self.arg_num(&args,0,0.0)? as i64;
4570 let b = self.tracks.get(id as usize).map(|t| ling_music::analysis::bpm(&t.mono, t.rate)).unwrap_or(0.0);
4571 return Ok(Value::Number(b as f64));
4572 }
4573 #[cfg(not(target_arch = "wasm32"))]
4574 "music_key" | "调性" | "調性" | "조성" | "คีย์เพลง" => {
4575 let id = self.arg_num(&args,0,0.0)? as i64;
4576 let k = self.tracks.get(id as usize).map(|t| ling_music::analysis::key_name(&t.mono, t.rate)).unwrap_or_default();
4577 return Ok(Value::Str(k));
4578 }
4579 #[cfg(not(target_arch = "wasm32"))]
4580 "music_onsets" | "音符起点" | "オンセット" | "온셋" | "จุดเริ่มเสียง" => {
4581 let id = self.arg_num(&args,0,0.0)? as i64;
4582 let v = self.tracks.get(id as usize).map(|t| ling_music::analysis::onsets(&t.mono, t.rate)).unwrap_or_default();
4583 return Ok(Value::List(v.into_iter().map(|x| Value::Number(x as f64)).collect()));
4584 }
4585 #[cfg(not(target_arch = "wasm32"))]
4586 "music_beat_grid" | "节拍网格" | "ビートグリッド" | "비트그리드" | "กริดจังหวะ" => {
4587 let id = self.arg_num(&args,0,0.0)? as i64;
4588 let beats = self.tracks.get(id as usize).map(|t| {
4589 let b = ling_music::analysis::bpm(&t.mono, t.rate);
4590 ling_music::analysis::beat_grid(&t.mono, t.rate, b)
4591 }).unwrap_or_default();
4592 return Ok(Value::List(beats.into_iter().map(|x| Value::Number(x as f64)).collect()));
4593 }
4594
4595 #[cfg(not(target_arch = "wasm32"))]
4597 "music_play" | "播放音乐" | "音楽再生" | "음악재생" | "เล่นเพลง" => {
4598 let id = self.arg_num(&args,0,0.0)? as i64;
4599 if self.ensure_music() {
4600 let track = self.tracks.get(id as usize).map(|t| (t.stereo.clone(), t.rate));
4601 if let (Some((st, rate)), Some(m)) = (track, &self.music) { m.set_track(st, rate); m.play(); }
4602 else if let Some(m) = &self.music { m.play(); }
4603 }
4604 return Ok(Value::Unit);
4605 }
4606 #[cfg(not(target_arch = "wasm32"))]
4607 "music_pause" | "暂停音乐" | "音楽一時停止" | "음악일시정지" | "หยุดเพลงชั่วคราว" => {
4608 if let Some(m) = &self.music { m.pause(); } return Ok(Value::Unit);
4609 }
4610 #[cfg(not(target_arch = "wasm32"))]
4611 "music_stop" | "停止音乐" | "音楽停止" | "음악정지" | "หยุดเพลง" => {
4612 if let Some(m) = &self.music { m.stop(); } return Ok(Value::Unit);
4613 }
4614 #[cfg(not(target_arch = "wasm32"))]
4615 "music_seek" | "定位音乐" | "音楽シーク" | "음악탐색" | "ค้นหาเพลง" => {
4616 let sec = self.arg_num(&args,0,0.0)? as f32;
4617 if let Some(m) = &self.music { m.seek(sec); } return Ok(Value::Unit);
4618 }
4619 #[cfg(not(target_arch = "wasm32"))]
4620 "music_pos" | "音乐位置" | "音楽位置" | "음악위치" | "ตำแหน่งเพลง" => {
4621 let p = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
4622 return Ok(Value::Number(p as f64));
4623 }
4624 #[cfg(not(target_arch = "wasm32"))]
4625 "music_volume" | "音乐音量" | "音楽音量" | "음악음량" | "ระดับเพลง" => {
4626 let v = self.arg_num(&args,0,0.8)? as f32;
4627 if self.ensure_music() { if let Some(m) = &self.music { m.set_volume(v); } }
4628 return Ok(Value::Unit);
4629 }
4630
4631 #[cfg(not(target_arch = "wasm32"))]
4633 "music_patch" | "乐器音色" | "音色読込" | "악기패치" | "แพตช์เครื่องดนตรี" => {
4634 let path = self.arg_str(&args, 0, "");
4635 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4636 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4637 else { path.clone() };
4638 if !self.ensure_music() { return Ok(Value::Number(-1.0)); }
4639 match ling_music::patch::from_path(&resolved) {
4640 Ok(p) => { let id = self.music.as_ref().unwrap().add_patch(p); return Ok(Value::Number(id as f64)); }
4641 Err(e) => { eprintln!("music_patch failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
4642 }
4643 }
4644 #[cfg(not(target_arch = "wasm32"))]
4645 "music_note" | "弹音符" | "音符演奏" | "음표연주" | "เล่นโน้ต" => {
4646 let inst = self.arg_num(&args,0,0.0)? as usize;
4647 let midi = self.pitch_arg(&args, 1, 60);
4648 let dur = self.arg_num(&args,2,0.5)? as f32;
4649 let vel = self.arg_num(&args,3,0.9)? as f32;
4650 if self.ensure_music() { if let Some(m) = &self.music { m.note(inst, midi, vel, dur); } }
4651 return Ok(Value::Unit);
4652 }
4653 #[cfg(not(target_arch = "wasm32"))]
4654 "music_note_on" | "音符开始" | "音符オン" | "음표켜기" | "โน้ตเริ่ม" => {
4655 let inst = self.arg_num(&args,0,0.0)? as usize;
4656 let midi = self.pitch_arg(&args, 1, 60);
4657 let vel = self.arg_num(&args,2,0.9)? as f32;
4658 if self.ensure_music() { if let Some(m) = &self.music { m.note_on(inst, midi, vel); } }
4659 return Ok(Value::Unit);
4660 }
4661 #[cfg(not(target_arch = "wasm32"))]
4662 "music_note_off" | "音符结束" | "音符オフ" | "음표끄기" | "โน้ตจบ" => {
4663 let inst = self.arg_num(&args,0,0.0)? as usize;
4664 let midi = self.pitch_arg(&args, 1, 60);
4665 if let Some(m) = &self.music { m.note_off(inst, midi); }
4666 return Ok(Value::Unit);
4667 }
4668
4669 #[cfg(not(target_arch = "wasm32"))]
4671 "music_judge" | "判定" | "判定する" | "판정" | "ตัดสินจังหวะ" => {
4672 let delta_ms = self.arg_num(&args,0,9999.0)? as f32;
4673 return Ok(Value::Number(ling_music::Grade::judge(delta_ms).index() as f64));
4674 }
4675 #[cfg(not(target_arch = "wasm32"))]
4676 "music_grade_name" | "判定名" | "判定名称" | "판정이름" | "ชื่อการตัดสิน" => {
4677 let idx = self.arg_num(&args,0,4.0)? as i32;
4678 return Ok(Value::Str(ling_music::Grade::from_index(idx).name().to_string()));
4679 }
4680
4681 #[cfg(not(target_arch = "wasm32"))]
4683 "music_lrc" | "载入歌词" | "歌詞読込" | "가사로드" | "โหลดเนื้อเพลง" => {
4684 let path = self.arg_str(&args, 0, "");
4685 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4686 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4687 else { path.clone() };
4688 match std::fs::read_to_string(&resolved) {
4689 Ok(text) => { let id = self.lyrics.len(); self.lyrics.push(ling_music::Lyrics::parse(&text)); return Ok(Value::Number(id as f64)); }
4690 Err(e) => { eprintln!("music_lrc failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
4691 }
4692 }
4693 #[cfg(not(target_arch = "wasm32"))]
4694 "music_lyric" | "当前歌词" | "現在歌詞" | "현재가사" | "เนื้อเพลงปัจจุบัน" => {
4695 let id = self.arg_num(&args,0,0.0)? as i64;
4696 let t = self.arg_num(&args,1,0.0)? as f32;
4697 let line = self.lyrics.get(id as usize).map(|l| l.line_at(t).to_string()).unwrap_or_default();
4698 return Ok(Value::Str(line));
4699 }
4700 #[cfg(not(target_arch = "wasm32"))]
4701 "music_mic_pitch" | "麦克风音高" | "マイク音程" | "마이크음정" | "ระดับเสียงไมค์" => {
4702 let hz = if let Some(mic) = self.mic.as_ref() {
4703 let s = mic.latest_samples();
4704 let rate = mic.sample_rate();
4705 ling_music::pitch::detect(&s, rate).unwrap_or(0.0)
4706 } else { 0.0 };
4707 return Ok(Value::Number(hz as f64));
4708 }
4709 #[cfg(not(target_arch = "wasm32"))]
4710 "music_note_name" | "音名" | "音名称" | "음이름" | "ชื่อโน้ต" => {
4711 let hz = self.arg_num(&args,0,0.0)? as f32;
4712 return Ok(Value::Str(ling_music::note::hz_to_name(hz)));
4713 }
4714 #[cfg(not(target_arch = "wasm32"))]
4715 "music_hz" | "音符频率" | "音符周波数" | "음표주파수" | "ความถี่โน้ต" => {
4716 let midi = self.pitch_arg(&args, 0, 69);
4717 return Ok(Value::Number(ling_music::note::midi_to_hz(midi as f32) as f64));
4718 }
4719 #[cfg(not(target_arch = "wasm32"))]
4720 "music_pitch_score" | "音准评分" | "音程スコア" | "음정점수" | "คะแนนเสียง" => {
4721 let hz = self.arg_num(&args,0,0.0)? as f32;
4722 let target = self.arg_num(&args,1,0.0)? as f32;
4723 return Ok(Value::Number(ling_music::karaoke::pitch_score(hz, target) as f64));
4724 }
4725
4726 #[cfg(not(target_arch = "wasm32"))]
4728 "music_midi_load" | "载入MIDI" | "MIDI読込" | "미디로드" | "โหลดมิดี" => {
4729 let path = self.arg_str(&args, 0, "");
4730 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4731 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4732 else { path.clone() };
4733 match ling_music::midi::load(&resolved) {
4734 Ok(m) => { let id = self.midis.len(); self.midis.push(m); return Ok(Value::Number(id as f64)); }
4735 Err(e) => { eprintln!("music_midi_load failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
4736 }
4737 }
4738 #[cfg(not(target_arch = "wasm32"))]
4739 "music_midi_count" | "MIDI数量" | "MIDI数" | "미디수" | "จำนวนมิดี" => {
4740 let id = self.arg_num(&args,0,0.0)? as i64;
4741 let n = self.midis.get(id as usize).map(|m| m.notes.len()).unwrap_or(0);
4742 return Ok(Value::Number(n as f64));
4743 }
4744 #[cfg(not(target_arch = "wasm32"))]
4746 "music_midi_notes" | "MIDI音符" | "MIDIノート" | "미디음표" | "โน้ตมิดี" => {
4747 let id = self.arg_num(&args,0,0.0)? as i64;
4748 let mut out = Vec::new();
4749 if let Some(m) = self.midis.get(id as usize) {
4750 for n in &m.notes { out.push(Value::Number(n.time as f64)); out.push(Value::Number(n.midi as f64)); }
4751 }
4752 return Ok(Value::List(out));
4753 }
4754 #[cfg(not(target_arch = "wasm32"))]
4756 "music_midi_bars" | "MIDI音条" | "MIDIバー" | "미디바" | "แท่งมิดี" => {
4757 let id = self.arg_num(&args,0,0.0)? as i64;
4758 let mut out = Vec::new();
4759 if let Some(m) = self.midis.get(id as usize) {
4760 for n in &m.notes {
4761 out.push(Value::Number(n.time as f64));
4762 out.push(Value::Number(n.midi as f64));
4763 out.push(Value::Number(n.dur as f64));
4764 }
4765 }
4766 return Ok(Value::List(out));
4767 }
4768
4769 #[cfg(not(target_arch = "wasm32"))]
4771 "music_fft" | "音乐频谱" | "音楽スペクトル" | "음악스펙트럼" | "สเปกตรัมเพลง" => {
4772 let id = self.arg_num(&args,0,0.0)? as i64;
4773 let nbands = self.arg_num(&args,1,16.0)? as usize;
4774 let pos = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
4775 if let Some(t) = self.tracks.get(id as usize) {
4776 let idx = (pos * t.rate as f32) as usize;
4777 let end = (idx + 2048).min(t.mono.len());
4778 if end > idx + 64 {
4779 self.fft.borrow_mut().push_samples(&t.mono[idx..end]);
4780 }
4781 }
4782 let bands = self.fft.borrow().freq_bands(nbands);
4783 return Ok(Value::List(bands.into_iter().map(|x| Value::Number(x as f64)).collect()));
4784 }
4785
4786 #[cfg(not(target_arch = "wasm32"))]
4788 "audio_sfx" | "音效" | "空間効果音" | "공간효과음" | "เสียงเอฟเฟกต์" => {
4789 let x=self.arg_num(&args,0,0.0)? as f32; let y=self.arg_num(&args,1,0.0)? as f32; let z=self.arg_num(&args,2,0.0)? as f32;
4790 let w=self.arg_num(&args,3,1.0)? as f32; let freq=self.arg_num(&args,4,440.0)? as f32;
4791 let amp=self.arg_num(&args,5,0.3)? as f32; let dur=self.arg_num(&args,6,0.15)? as f32;
4792 let wave=Wave::from_name(&self.arg_str(&args,7,"sine"));
4793 if let Some(a)=&self.audio { a.sfx(x,y,z,w,freq,amp,dur,wave); }
4794 return Ok(Value::Unit);
4795 }
4796 #[cfg(not(target_arch = "wasm32"))]
4798 "audio_sample_load" | "载入采样" | "サンプル読込" | "샘플로드" | "โหลดตัวอย่างเสียง" => {
4799 let path = self.arg_str(&args, 0, "");
4800 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4801 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4802 else { path.clone() };
4803 match ling_music::load(&resolved) {
4804 Ok(t) => {
4805 if let Some(a)=&self.audio { return Ok(Value::Number(a.add_sample(t.mono, t.rate) as f64)); }
4806 return Ok(Value::Number(-1.0));
4807 }
4808 Err(e) => { eprintln!("audio_sample_load failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
4809 }
4810 }
4811 #[cfg(not(target_arch = "wasm32"))]
4812 "audio_sample_play" | "播放采样" | "サンプル再生" | "샘플재생" | "เล่นตัวอย่างเสียง" => {
4813 let id=self.arg_num(&args,0,0.0)? as usize;
4814 let x=self.arg_num(&args,1,0.0)? as f32; let y=self.arg_num(&args,2,0.0)? as f32; let z=self.arg_num(&args,3,0.0)? as f32;
4815 let w=self.arg_num(&args,4,1.0)? as f32; let vol=self.arg_num(&args,5,1.0)? as f32;
4816 let looping=self.arg_num(&args,6,0.0)? > 0.5;
4817 let v = self.audio.as_ref().map(|a| a.play_sample(id,x,y,z,w,vol,looping)).unwrap_or(0);
4818 return Ok(Value::Number(v as f64));
4819 }
4820 #[cfg(not(target_arch = "wasm32"))]
4821 "audio_sample_stop" | "停止采样" | "サンプル停止" | "샘플정지" | "หยุดตัวอย่างเสียง" => {
4822 let v=self.arg_num(&args,0,0.0)? as u32;
4823 if let Some(a)=&self.audio { a.stop_sample(v); }
4824 return Ok(Value::Unit);
4825 }
4826 #[cfg(not(target_arch = "wasm32"))]
4828 "audio_fx_delay" | "回声" | "ディレイ効果" | "딜레이" | "เสียงสะท้อน" => {
4829 let time=self.arg_num(&args,0,0.3)? as f32; let fb=self.arg_num(&args,1,0.3)? as f32; let mix=self.arg_num(&args,2,0.3)? as f32;
4830 if let Some(a)=&self.audio { a.fx_delay(time,fb,mix); }
4831 return Ok(Value::Unit);
4832 }
4833 #[cfg(not(target_arch = "wasm32"))]
4834 "audio_fx_reverb" | "混响" | "リバーブ" | "리버브" | "เสียงก้อง" => {
4835 let mix=self.arg_num(&args,0,0.3)? as f32;
4836 if let Some(a)=&self.audio { a.fx_reverb(mix); }
4837 return Ok(Value::Unit);
4838 }
4839 #[cfg(not(target_arch = "wasm32"))]
4840 "audio_fx_lowpass" | "低通滤波" | "ローパス" | "저역통과" | "กรองความถี่ต่ำ" => {
4841 let cutoff=self.arg_num(&args,0,1.0)? as f32;
4842 if let Some(a)=&self.audio { a.fx_lowpass(cutoff); }
4843 return Ok(Value::Unit);
4844 }
4845
4846 #[cfg(not(target_arch = "wasm32"))]
4853 "soft_ball" | "软球" | "ソフトボール" | "소프트볼" | "ลูกบอลนุ่ม" => {
4854 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32; let z=self.arg_num(&args,2,0.)? as f32;
4855 let r=self.arg_num(&args,3,1.0)? as f32;
4856 let b = ling_physics::soft::SoftBody::sphere(ling_physics::Vec3::new(x,y,z), r, 8, 12, 1.0);
4857 let id = self.soft_bodies.len(); self.soft_bodies.push(b);
4858 return Ok(Value::Number(id as f64));
4859 }
4860 #[cfg(not(target_arch = "wasm32"))]
4861 "soft_step" | "软体步进" | "ソフト更新" | "소프트스텝" | "ก้าวนุ่ม" => {
4862 let id=self.arg_num(&args,0,0.)? as usize; let dt=self.arg_num(&args,1,0.016)? as f32;
4863 let gy=self.arg_num(&args,2,15.0)? as f32;
4864 if let Some(b)=self.soft_bodies.get_mut(id) { b.integrate(dt, ling_physics::Vec3::new(0.0,gy,0.0), 4); }
4865 return Ok(Value::Unit);
4866 }
4867 #[cfg(not(target_arch = "wasm32"))]
4868 "soft_bounce" | "软体落地" | "ソフト着地" | "소프트바운스" | "เด้งนุ่ม" => {
4869 let id=self.arg_num(&args,0,0.)? as usize; let fy=self.arg_num(&args,1,0.)? as f32; let rest=self.arg_num(&args,2,0.5)? as f32;
4870 if let Some(b)=self.soft_bodies.get_mut(id) { b.floor_collision(fy, rest); }
4871 return Ok(Value::Unit);
4872 }
4873 #[cfg(not(target_arch = "wasm32"))]
4874 "soft_contain" | "软体边界" | "ソフト箱" | "소프트경계" | "กล่องนุ่ม" => {
4875 let id=self.arg_num(&args,0,0.)? as usize;
4876 let nx=self.arg_num(&args,1,-5.)? as f32; let ny=self.arg_num(&args,2,-5.)? as f32; let nz=self.arg_num(&args,3,-5.)? as f32;
4877 let mx=self.arg_num(&args,4,5.)? as f32; let my=self.arg_num(&args,5,5.)? as f32; let mz=self.arg_num(&args,6,5.)? as f32;
4878 let rest=self.arg_num(&args,7,0.6)? as f32;
4879 if let Some(b)=self.soft_bodies.get_mut(id) { b.contain(ling_physics::Vec3::new(nx,ny,nz), ling_physics::Vec3::new(mx,my,mz), rest); }
4880 return Ok(Value::Unit);
4881 }
4882 #[cfg(not(target_arch = "wasm32"))]
4883 "soft_kick" | "软体踢" | "ソフト衝撃" | "소프트킥" | "เตะนุ่ม" => {
4884 let id=self.arg_num(&args,0,0.)? as usize;
4885 let dx=self.arg_num(&args,1,0.)? as f32; let dy=self.arg_num(&args,2,0.)? as f32; let dz=self.arg_num(&args,3,0.)? as f32;
4886 let s=self.arg_num(&args,4,0.1)? as f32;
4887 if let Some(b)=self.soft_bodies.get_mut(id) { b.kick(ling_physics::Vec3::new(dx,dy,dz), s); }
4888 return Ok(Value::Unit);
4889 }
4890 #[cfg(not(target_arch = "wasm32"))]
4893 "soft_spin" | "软体自旋" | "ソフト回転" | "소프트회전" | "หมุนนุ่ม" => {
4894 let id=self.arg_num(&args,0,0.)? as usize;
4895 let ax=self.arg_num(&args,1,0.)? as f32; let ay=self.arg_num(&args,2,0.)? as f32; let az=self.arg_num(&args,3,0.)? as f32;
4896 let rate=self.arg_num(&args,4,0.1)? as f32;
4897 if let Some(b)=self.soft_bodies.get_mut(id) { b.spin(ling_physics::Vec3::new(ax,ay,az), rate); }
4898 return Ok(Value::Unit);
4899 }
4900 #[cfg(not(target_arch = "wasm32"))]
4901 "soft_deform" | "形变量" | "変形量" | "변형량" | "ความบิดเบี้ยว" => {
4902 let id=self.arg_num(&args,0,0.)? as usize;
4903 let d=self.soft_bodies.get(id).map(|b| b.deformation()).unwrap_or(0.0);
4904 return Ok(Value::Number(d as f64));
4905 }
4906 #[cfg(not(target_arch = "wasm32"))]
4909 "soft_angular_speed" | "软体角速" | "ソフト角速度" | "소프트각속도" | "ความเร็วเชิงมุมนุ่ม" => {
4910 let id=self.arg_num(&args,0,0.)? as usize;
4911 let w=self.soft_bodies.get(id).map(|b| b.angular_speed()).unwrap_or(0.0);
4912 return Ok(Value::Number(w as f64));
4913 }
4914 #[cfg(not(target_arch = "wasm32"))]
4915 "soft_centroid" | "软体质心" | "ソフト重心" | "소프트중심" | "จุดศูนย์กลางนุ่ม" => {
4916 let id=self.arg_num(&args,0,0.)? as usize;
4917 let c=self.soft_bodies.get(id).map(|b| b.centroid()).unwrap_or(ling_physics::Vec3::ZERO);
4918 return Ok(Value::List(vec![Value::Number(c.x as f64),Value::Number(c.y as f64),Value::Number(c.z as f64)]));
4919 }
4920 #[cfg(not(target_arch = "wasm32"))]
4922 "soft_nodes" | "软体节点" | "ソフト節点" | "소프트노드" | "จุดนุ่ม" => {
4923 let id=self.arg_num(&args,0,0.)? as usize;
4924 let mut out=Vec::new();
4925 if let Some(b)=self.soft_bodies.get(id) {
4926 for n in &b.nodes { out.push(Value::Number(n.pos.x as f64)); out.push(Value::Number(n.pos.y as f64)); out.push(Value::Number(n.pos.z as f64)); }
4927 }
4928 return Ok(Value::List(out));
4929 }
4930
4931 #[cfg(not(target_arch = "wasm32"))]
4933 "rb_add" | "刚体添加" | "剛体追加" | "강체추가" | "เพิ่มวัตถุแข็ง" => {
4934 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32; let z=self.arg_num(&args,2,0.)? as f32;
4935 let mass=self.arg_num(&args,3,1.0)? as f32;
4936 let mut b = ling_physics::rigid::RigidBody::new(ling_physics::Vec3::new(x,y,z), mass);
4937 b.restitution = 0.6;
4938 return Ok(Value::Number(self.rigid_world.add(b) as f64));
4939 }
4940 #[cfg(not(target_arch = "wasm32"))]
4941 "rb_torque" | "扭矩" | "トルク" | "토크" | "แรงบิด" => {
4942 let i=self.arg_num(&args,0,0.)? as usize;
4943 let tx=self.arg_num(&args,1,0.)? as f32; let ty=self.arg_num(&args,2,0.)? as f32; let tz=self.arg_num(&args,3,0.)? as f32;
4944 if let Some(b)=self.rigid_world.bodies.get_mut(i) { b.apply_torque(ling_physics::Vec3::new(tx,ty,tz)); }
4945 return Ok(Value::Unit);
4946 }
4947 #[cfg(not(target_arch = "wasm32"))]
4948 "rb_spin" | "自旋" | "スピン" | "스핀" | "หมุน" => {
4949 let i=self.arg_num(&args,0,0.)? as usize;
4950 let wx=self.arg_num(&args,1,0.)? as f32; let wy=self.arg_num(&args,2,0.)? as f32; let wz=self.arg_num(&args,3,0.)? as f32;
4951 if let Some(b)=self.rigid_world.bodies.get_mut(i) { b.apply_spin(ling_physics::Vec3::new(wx,wy,wz)); }
4952 return Ok(Value::Unit);
4953 }
4954 #[cfg(not(target_arch = "wasm32"))]
4955 "rb_impulse" | "刚体冲量" | "剛体インパルス" | "강체충격" | "แรงดลแข็ง" => {
4956 let i=self.arg_num(&args,0,0.)? as usize;
4957 let ix=self.arg_num(&args,1,0.)? as f32; let iy=self.arg_num(&args,2,0.)? as f32; let iz=self.arg_num(&args,3,0.)? as f32;
4958 if let Some(b)=self.rigid_world.bodies.get_mut(i) { b.apply_impulse(ling_physics::Vec3::new(ix,iy,iz)); }
4959 return Ok(Value::Unit);
4960 }
4961 #[cfg(not(target_arch = "wasm32"))]
4962 "rb_floor" | "刚体落地" | "剛体着地" | "강체바닥" | "พื้นแข็ง" => {
4963 let i=self.arg_num(&args,0,0.)? as usize; let fy=self.arg_num(&args,1,0.)? as f32;
4964 let rest=self.arg_num(&args,2,0.6)? as f32; let fric=self.arg_num(&args,3,0.6)? as f32;
4965 if let Some(b)=self.rigid_world.bodies.get_mut(i) { b.bounce_floor(fy, rest, fric); }
4966 return Ok(Value::Unit);
4967 }
4968 #[cfg(not(target_arch = "wasm32"))]
4969 "rb_gravity" | "刚体重力" | "剛体重力" | "강체중력" | "แรงโน้มถ่วงแข็ง" => {
4970 let gx=self.arg_num(&args,0,0.)? as f32; let gy=self.arg_num(&args,1,9.81)? as f32; let gz=self.arg_num(&args,2,0.)? as f32;
4971 self.rigid_world.gravity = ling_physics::Vec3::new(gx,gy,gz);
4972 return Ok(Value::Unit);
4973 }
4974 #[cfg(not(target_arch = "wasm32"))]
4975 "rb_step" | "刚体步进" | "剛体更新" | "강체스텝" | "ก้าวแข็ง" => {
4976 let dt=self.arg_num(&args,0,0.016)? as f32;
4977 self.rigid_world.step(dt);
4978 return Ok(Value::Unit);
4979 }
4980 #[cfg(not(target_arch = "wasm32"))]
4981 "rb_pos" | "刚体位置" | "剛体位置" | "강체위치" | "ตำแหน่งแข็ง" => {
4982 let i=self.arg_num(&args,0,0.)? as usize;
4983 let p=self.rigid_world.bodies.get(i).map(|b| b.pos).unwrap_or(ling_physics::Vec3::ZERO);
4984 return Ok(Value::List(vec![Value::Number(p.x as f64),Value::Number(p.y as f64),Value::Number(p.z as f64)]));
4985 }
4986 #[cfg(not(target_arch = "wasm32"))]
4987 "rb_rot" | "刚体旋转" | "剛体回転" | "강체회전" | "การหมุนแข็ง" => {
4988 let i=self.arg_num(&args,0,0.)? as usize;
4989 let q=self.rigid_world.bodies.get(i).map(|b| b.orientation).unwrap_or(ling_physics::Quat::IDENTITY);
4990 return Ok(Value::List(vec![Value::Number(q.x as f64),Value::Number(q.y as f64),Value::Number(q.z as f64),Value::Number(q.w as f64)]));
4991 }
4992
4993 #[cfg(not(target_arch = "wasm32"))]
4995 "mesh_load" | "โหลดเมช" | "载入网格" | "メッシュ読込" | "메시로드" => {
4996 let path = self.arg_str(&args, 0, "");
4997 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4998 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4999 else { path.clone() };
5000 let bytes = match std::fs::read(&resolved) {
5001 Ok(b) => b,
5002 Err(e) => { eprintln!("mesh_load failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
5003 };
5004 if bytes.len() < 16 || &bytes[0..4] != b"LMSH" { eprintln!("mesh_load: bad header ({path})"); return Ok(Value::Number(-1.0)); }
5005 let rd4 = |o: usize| -> [u8;4] { [bytes[o],bytes[o+1],bytes[o+2],bytes[o+3]] };
5006 let height = f32::from_le_bytes(rd4(8));
5007 let ntri = u32::from_le_bytes(rd4(12)) as usize;
5008 let need = 16usize.saturating_add(ntri.saturating_mul(9*4 + 3));
5009 if bytes.len() < need { eprintln!("mesh_load: truncated ({path})"); return Ok(Value::Number(-1.0)); }
5010 let mut pos = Vec::with_capacity(ntri*3);
5011 let mut col = Vec::with_capacity(ntri);
5012 let mut off = 16usize;
5013 for _ in 0..ntri {
5014 for _k in 0..3 {
5015 let x = f32::from_le_bytes(rd4(off)); let y = f32::from_le_bytes(rd4(off+4)); let z = f32::from_le_bytes(rd4(off+8));
5016 off += 12; pos.push([x,y,z]);
5017 }
5018 col.push([bytes[off],bytes[off+1],bytes[off+2]]); off += 3;
5019 }
5020 eprintln!("mesh_load: {} ({} tris, h={:.2})", path, ntri, height);
5021 let id = self.meshes.len();
5022 self.meshes.push(crate::gfx::shapes::ColorMesh{ pos, col, height });
5023 return Ok(Value::Number(id as f64));
5024 }
5025 #[cfg(not(target_arch = "wasm32"))]
5026 "mesh_draw" | "วาดเมชสี" => { let id = self.arg_num(&args,0,0.)? as usize;
5028 let cx=self.arg_num(&args,1,0.)? as f32; let cy=self.arg_num(&args,2,0.)? as f32; let cz=self.arg_num(&args,3,0.)? as f32;
5029 let sc=self.arg_num(&args,4,1.)? as f32; let yaw=self.arg_num(&args,5,0.)? as f32;
5030 let sway=self.arg_num(&args,6,0.)? as f32; let arm=self.arg_num(&args,7,0.)? as f32;
5031 let lean=self.arg_num(&args,8,0.)? as f32;
5032 let leg=self.arg_num(&args,9,0.)? as f32; let tuck=self.arg_num(&args,10,0.)? as f32;
5033 if id < self.meshes.len() {
5034 let m = &self.meshes[id];
5035 let mut gfx = self.gfx.borrow_mut();
5036 gfx.draw_color_mesh(m, cx,cy,cz, sc, yaw, sway, arm, lean, leg, tuck);
5037 }
5038 return Ok(Value::Unit);
5039 }
5040
5041 #[cfg(not(target_arch = "wasm32"))]
5043 "liquid_new" | "新建液体" | "液体新規" | "액체생성" | "สร้างของเหลว" => {
5044 let w=self.arg_num(&args,0,64.)? as usize; let h=self.arg_num(&args,1,64.)? as usize;
5045 let id=self.liquids.len(); self.liquids.push(ling_physics::liquid::LiquidGrid::new(w,h));
5046 return Ok(Value::Number(id as f64));
5047 }
5048 #[cfg(not(target_arch = "wasm32"))]
5049 "liquid_set_colors" | "液体颜色" | "液体配色" | "액체색상" | "สีของเหลว" => {
5050 let id=self.arg_num(&args,0,0.)? as usize;
5051 let wr=self.arg_num(&args,1,40.)? as f32; let wg=self.arg_num(&args,2,110.)? as f32; let wb=self.arg_num(&args,3,235.)? as f32;
5052 let or_=self.arg_num(&args,4,240.)? as f32; let og=self.arg_num(&args,5,175.)? as f32; let ob=self.arg_num(&args,6,45.)? as f32;
5053 if let Some(g)=self.liquids.get_mut(id) { g.set_colors(wr,wg,wb,or_,og,ob); }
5054 return Ok(Value::Unit);
5055 }
5056 #[cfg(not(target_arch = "wasm32"))]
5057 "liquid_splat" | "液体注入" | "液体追加" | "액체분사" | "หยดของเหลว" => {
5058 let id=self.arg_num(&args,0,0.)? as usize;
5059 let x=self.arg_num(&args,1,0.)? as f32; let y=self.arg_num(&args,2,0.)? as f32;
5060 let kind=self.arg_num(&args,3,0.)? as i32; let amt=self.arg_num(&args,4,1.0)? as f32; let rad=self.arg_num(&args,5,4.0)? as f32;
5061 if let Some(g)=self.liquids.get_mut(id) { g.splat(x,y,kind,amt,rad); }
5062 return Ok(Value::Unit);
5063 }
5064 #[cfg(not(target_arch = "wasm32"))]
5065 "liquid_gravity" | "液体重力" | "液体重力ベクトル" | "액체중력" | "แรงโน้มถ่วงเหลว" => {
5066 let id=self.arg_num(&args,0,0.)? as usize;
5067 let gx=self.arg_num(&args,1,0.)? as f32; let gy=self.arg_num(&args,2,60.)? as f32;
5068 if let Some(g)=self.liquids.get_mut(id) { g.set_gravity(gx,gy); }
5069 return Ok(Value::Unit);
5070 }
5071 #[cfg(not(target_arch = "wasm32"))]
5072 "liquid_step" | "液体步进" | "液体更新" | "액체스텝" | "ก้าวของเหลว" => {
5073 let id=self.arg_num(&args,0,0.)? as usize; let dt=self.arg_num(&args,1,0.016)? as f32;
5074 if let Some(g)=self.liquids.get_mut(id) { g.step(dt); }
5075 return Ok(Value::Unit);
5076 }
5077 #[cfg(not(target_arch = "wasm32"))]
5079 "liquid_rainbow" | "液体彩虹" | "液体虹" | "액체무지개" | "ของเหลวสายรุ้ง" => {
5080 let id=self.arg_num(&args,0,0.)? as usize;
5081 let on=self.arg_num(&args,1,1.0)? > 0.5;
5082 if let Some(g)=self.liquids.get_mut(id) { g.rainbow = on; }
5083 return Ok(Value::Unit);
5084 }
5085 #[cfg(not(target_arch = "wasm32"))]
5087 "liquid_mix" | "液体混合" | "液体混合度" | "액체혼합" | "การผสมของเหลว" => {
5088 let id=self.arg_num(&args,0,0.)? as usize;
5089 let m=self.liquids.get(id).map(|g| g.mix_amount()).unwrap_or(0.0);
5090 return Ok(Value::Number(m as f64));
5091 }
5092 #[cfg(not(target_arch = "wasm32"))]
5094 "liquid_draw" | "绘制液体" | "液体描画" | "액체그리기" | "วาดของเหลว" => {
5095 let id=self.arg_num(&args,0,0.)? as usize;
5096 let sx=self.arg_num(&args,1,0.)? as i32; let sy=self.arg_num(&args,2,0.)? as i32;
5097 let scale=(self.arg_num(&args,3,4.)? as i32).max(1);
5098 if id < self.liquids.len() {
5099 let (gw,gh)={ let g=&self.liquids[id]; (g.w,g.h) };
5100 let mut gfx=self.gfx.borrow_mut(); let (w,h)=(gfx.width as i32, gfx.height as i32);
5101 let g=&self.liquids[id];
5102 for cy in 0..gh { for cx in 0..gw {
5103 let col=g.sample_rgb(cx,cy);
5104 let bx=sx + cx as i32*scale; let by=sy + cy as i32*scale;
5105 for dy in 0..scale { for dx in 0..scale {
5106 let px=bx+dx; let py=by+dy;
5107 if px>=0 && py>=0 && px<w && py<h { gfx.buffer[(py*w+px) as usize]=col; }
5108 }}
5109 }}
5110 }
5111 return Ok(Value::Unit);
5112 }
5113 #[cfg(not(target_arch = "wasm32"))]
5116 "liquid_draw_surface" | "液体贴面" | "液体曲面" | "액체곡면" | "ของเหลวบนพื้นผิว" => {
5117 let id=self.arg_num(&args,0,0.)? as usize;
5118 let kind=self.arg_num(&args,1,1.)? as i32;
5119 let cx=self.arg_num(&args,2,0.)? as f32; let cy=self.arg_num(&args,3,0.)? as f32; let cz=self.arg_num(&args,4,0.)? as f32;
5120 let radius=self.arg_num(&args,5,2.0)? as f32; let height=self.arg_num(&args,6,3.0)? as f32;
5121 if id < self.liquids.len() {
5122 let (gw,gh)={ let g=&self.liquids[id]; (g.w,g.h) };
5123 let mut gfx=self.gfx.borrow_mut();
5124 let (w,h,add)=(gfx.width, gfx.height, gfx.blend==1);
5125 let cam=gfx.camera.clone();
5126 let near = -cam.zdist + 0.05;
5127 let g=&self.liquids[id];
5128 let tau=std::f32::consts::TAU; let pi=std::f32::consts::PI;
5129 let sp = |u:f32, v:f32| -> [f32;3] {
5131 if kind==0 { [cx+(u-0.5)*2.0*radius, cy, cz+(v-0.5)*2.0*radius] }
5132 else if kind==2 { let th=u*tau; [cx+th.cos()*radius, cy+(v-0.5)*height, cz+th.sin()*radius] }
5133 else if kind==3 { let th=u*tau; let rr=radius*(1.0-v); [cx+th.cos()*rr, cy+(v-0.5)*height, cz+th.sin()*rr] }
5134 else if kind==4 { let th=u*tau; let ph=v*pi*0.5; [cx+ph.sin()*th.cos()*radius, cy-ph.cos()*radius, cz+ph.sin()*th.sin()*radius] }
5135 else { let th=u*tau; let ph=v*pi; [cx+ph.sin()*th.cos()*radius, cy+ph.cos()*radius, cz+ph.sin()*th.sin()*radius] }
5136 };
5137 let nrm = |u:f32, v:f32| -> [f32;3] {
5138 if kind==0 { [0.0,-1.0,0.0] }
5139 else if kind==2 { let th=u*tau; [th.cos(),0.0,th.sin()] }
5140 else if kind==3 { let th=u*tau; let s=(radius/height.max(0.01)).atan(); [th.cos()*s.cos(), s.sin(), th.sin()*s.cos()] }
5141 else if kind==4 { let th=u*tau; let ph=v*pi*0.5; [ph.sin()*th.cos(),-ph.cos(),ph.sin()*th.sin()] }
5142 else { let th=u*tau; let ph=v*pi; [ph.sin()*th.cos(),ph.cos(),ph.sin()*th.sin()] }
5143 };
5144 let gwf=gw as f32; let ghf=gh as f32;
5145 let mut cyc=0usize;
5146 while cyc<gh {
5147 let mut cxc=0usize;
5148 while cxc<gw {
5149 let uc=(cxc as f32+0.5)/gwf; let vc=(cyc as f32+0.5)/ghf;
5151 let c=sp(uc,vc); let n=nrm(uc,vc);
5152 let dc=cam.depth(c[0],c[1],c[2]);
5153 if dc>near {
5154 let cull = kind!=0 && cam.depth(c[0]+n[0]*0.06,c[1]+n[1]*0.06,c[2]+n[2]*0.06) > dc;
5155 if !cull {
5156 let u0=cxc as f32/gwf; let u1=(cxc+1) as f32/gwf;
5158 let v0=cyc as f32/ghf; let v1=(cyc+1) as f32/ghf;
5159 let q=[sp(u0,v0),sp(u1,v0),sp(u1,v1),sp(u0,v1)];
5160 let mut poly: Vec<[f32;2]> = Vec::with_capacity(5);
5161 let mut ok=true;
5162 for p in &q { if cam.depth(p[0],p[1],p[2])<=near { ok=false; break; } let (sx,sy,_)=cam.project(p[0],p[1],p[2]); poly.push([sx,sy]); }
5163 if ok { let p0=poly[0]; poly.push(p0);
5164 let col=g.sample_rgb(cxc,cyc);
5165 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, w, h, col, add, std::slice::from_ref(&poly));
5166 }
5167 }
5168 }
5169 cxc+=1;
5170 }
5171 cyc+=1;
5172 }
5173 }
5174 return Ok(Value::Unit);
5175 }
5176 #[cfg(not(target_arch = "wasm32"))]
5179 "sparkle" | "闪光" | "きらめき" | "반짝임" | "ประกาย" => {
5180 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
5181 let ww=self.arg_num(&args,2,200.)? as f32; let hh=self.arg_num(&args,3,200.)? as f32;
5182 let count=self.arg_num(&args,4,40.)? as i32;
5183 let t=self.arg_num(&args,5,0.)? as f32;
5184 let mut gfx=self.gfx.borrow_mut();
5185 let (w,h,add,color)=(gfx.width, gfx.height, gfx.blend==1, gfx.color);
5186 let (cr,cg,cb)=((color>>16&0xFF) as f32,(color>>8&0xFF) as f32,(color&0xFF) as f32);
5187 let mut n=0i32;
5188 while n<count {
5189 let hsh=(n as u32).wrapping_mul(2654435761).wrapping_add(0x9E3779B9);
5190 let u=((hsh>>8)&1023) as f32/1023.0;
5191 let v=((hsh>>18)&1023) as f32/1023.0;
5192 let phase=(hsh&255) as f32/255.0;
5193 let tw=(t*3.0 + phase*6.2831 + n as f32).sin()*0.5+0.5;
5194 let sz=1.5+tw*5.0;
5195 let px=x+u*ww; let py=y+v*hh;
5196 let b=tw*tw; let col=(((cr*b)as u32)<<16)|(((cg*b)as u32)<<8)|((cb*b)as u32);
5198 crate::gfx::raster::draw_line_aa(&mut gfx.buffer,w,h,col,add, px-sz,py, px+sz,py);
5199 crate::gfx::raster::draw_line_aa(&mut gfx.buffer,w,h,col,add, px,py-sz, px,py+sz);
5200 let d=sz*0.55;
5201 crate::gfx::raster::draw_line_aa(&mut gfx.buffer,w,h,col,add, px-d,py-d, px+d,py+d);
5202 crate::gfx::raster::draw_line_aa(&mut gfx.buffer,w,h,col,add, px-d,py+d, px+d,py-d);
5203 n+=1;
5204 }
5205 return Ok(Value::Unit);
5206 }
5207
5208 #[cfg(not(target_arch = "wasm32"))]
5214 "dialog_show" | "对话显示" | "会話表示" | "대화표시" | "แสดงบทสนทนา" => {
5215 let text = self.arg_str(&args, 0, "");
5216 let cps = self.arg_num(&args, 1, 32.0)? as f32;
5217 self.dialog = Some(ling_game::dialog::Dialog::new(&text, cps));
5218 return Ok(Value::Unit);
5219 }
5220 #[cfg(not(target_arch = "wasm32"))]
5221 "dialog_step" | "对话步进" | "会話更新" | "대화스텝" | "ก้าวบทสนทนา" => {
5222 let dt = self.arg_num(&args, 0, 0.016)? as f32;
5223 if let Some(d) = self.dialog.as_mut() { d.update(dt); }
5224 return Ok(Value::Unit);
5225 }
5226 #[cfg(not(target_arch = "wasm32"))]
5227 "dialog_advance" | "对话推进" | "会話送り" | "대화진행" | "เลื่อนบทสนทนา" => {
5228 if let Some(d) = self.dialog.as_mut() { d.advance(); }
5229 return Ok(Value::Unit);
5230 }
5231 #[cfg(not(target_arch = "wasm32"))]
5232 "dialog_active" | "对话激活" | "会話中" | "대화중" | "บทสนทนาทำงาน" => {
5233 let a = self.dialog.as_ref().map(|d| !d.is_closed()).unwrap_or(false);
5234 return Ok(Value::Bool(a));
5235 }
5236 #[cfg(not(target_arch = "wasm32"))]
5237 "dialog_typing" | "对话打字" | "会話タイプ中" | "대화타이핑" | "กำลังพิมพ์บทสนทนา" => {
5238 use ling_game::dialog::Dialog;
5239
5240 let a = self.dialog.as_ref().map(|d: &Dialog | !d.is_closed() && d.is_typing()).unwrap_or(false);
5241 return Ok(Value::Bool(a))
5242 }
5243 #[cfg(not(target_arch = "wasm32"))]
5244 "dialog_close" | "对话关闭" | "会話閉じる" | "대화닫기" | "ปิดบทสนทนา" => {
5245 self.dialog = None;
5246 return Ok(Value::Unit);
5247 }
5248 #[cfg(not(target_arch = "wasm32"))]
5250 "dialog_color" | "对话颜色" | "会話色" | "대화색" | "สีบทสนทนา" => {
5251 let role = (self.arg_num(&args,0,0.0)? as usize).min(3);
5252 let r = self.arg_num(&args,1,255.0)? as u32 & 0xFF;
5253 let g = self.arg_num(&args,2,255.0)? as u32 & 0xFF;
5254 let b = self.arg_num(&args,3,255.0)? as u32 & 0xFF;
5255 self.dialog_colors[role] = (r<<16)|(g<<8)|b;
5256 return Ok(Value::Unit);
5257 }
5258 #[cfg(not(target_arch = "wasm32"))]
5260 "dialog_draw" | "对话绘制" | "会話描画" | "대화그리기" | "วาดบทสนทนา" => {
5261 let x=self.arg_num(&args,0,40.0)? as f32; let y=self.arg_num(&args,1,0.0)? as f32;
5262 let ww=self.arg_num(&args,2,720.0)? as f32; let hh=self.arg_num(&args,3,150.0)? as f32;
5263 let font = self.arg_num(&args,4,-1.0)? as i64;
5264 let t = self.start_time.elapsed().as_secs_f32();
5265 self.render_dialog(x, y, ww, hh, font, t);
5266 return Ok(Value::Unit);
5267 }
5268
5269 #[cfg(not(target_arch = "wasm32"))]
5271 "text_poll" => {
5272 let keys = { let gfx = self.gfx.borrow(); gfx.window.as_ref().map(|w| w.get_keys_pressed(minifb::KeyRepeat::No)).unwrap_or_default() };
5273 for k in keys {
5274 if k == minifb::Key::Backspace { self.text_buffer.pop(); }
5275 else if let Some(c) = key_char(k) { self.text_buffer.push(c); }
5276 }
5277 return Ok(Value::Str(self.text_buffer.clone()));
5278 }
5279 "text_get" => return Ok(Value::Str(self.text_buffer.clone())),
5280 "text_set" => { self.text_buffer = self.arg_str(&args,0,""); return Ok(Value::Unit); }
5281 "text_clear" => { self.text_buffer.clear(); return Ok(Value::Unit); }
5282 #[cfg(not(target_arch = "wasm32"))]
5284 "record_frame" => {
5285 let n = self.record_n;
5286 let (buf, w, h) = { let gfx = self.gfx.borrow(); (gfx.buffer.clone(), gfx.width, gfx.height) };
5287 let _ = std::fs::create_dir_all("recordings");
5288 let mut out = Vec::with_capacity(w*h*3 + 32);
5289 out.extend_from_slice(format!("P6\n{w} {h}\n255\n").as_bytes());
5290 for px in &buf { let p = *px; out.push((p>>16) as u8); out.push((p>>8) as u8); out.push(p as u8); }
5291 let _ = std::fs::write(format!("recordings/frame_{n:05}.ppm"), out);
5292 self.record_n += 1;
5293 return Ok(Value::Number(n as f64));
5294 }
5295 "record_count" => return Ok(Value::Number(self.record_n as f64)),
5296 #[cfg(not(target_arch = "wasm32"))]
5298 "screenshot" | "บันทึกภาพ" => {
5299 let mode = self.arg_str(&args, 0, "game");
5300 let (buf, w, h) = { let gfx = self.gfx.borrow(); (gfx.buffer.clone(), gfx.width, gfx.height) };
5301 let _ = std::fs::create_dir_all("screenshots");
5302 let ts = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0);
5303 let safe: String = mode.chars().map(|c| if c.is_alphanumeric() { c } else { '_' }).collect();
5304 let path = format!("screenshots/ss_{ts}_{safe}_{w}x{h}.png");
5305 let mut rgb = Vec::with_capacity(w * h * 3);
5306 for px in &buf { let p = *px; rgb.push((p >> 16) as u8); rgb.push((p >> 8) as u8); rgb.push(p as u8); }
5307 if let Some(img) = image::RgbImage::from_raw(w as u32, h as u32, rgb) {
5308 let _ = img.save(&path);
5309 }
5310 return Ok(Value::Str(path));
5311 }
5312 #[cfg(not(target_arch = "wasm32"))]
5316 "mic_capture" => {
5317 if let Some(mic) = self.mic.as_ref() {
5318 let s = mic.latest_samples();
5319 self.mic_buffer.extend_from_slice(&s);
5320 let cap = 96_000usize; if self.mic_buffer.len() > cap {
5322 let drop = self.mic_buffer.len() - cap;
5323 self.mic_buffer.drain(0..drop);
5324 }
5325 }
5326 return Ok(Value::Number(self.mic_buffer.len() as f64));
5327 }
5328 #[cfg(not(target_arch = "wasm32"))]
5330 "mic_seed" => {
5331 let mut bytes = Vec::with_capacity(self.mic_buffer.len() * 4);
5332 for f in &self.mic_buffer { bytes.extend_from_slice(&f.to_le_bytes()); }
5333 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(&bytes))));
5334 }
5335 #[cfg(not(target_arch = "wasm32"))]
5336 "mic_clear" => { self.mic_buffer.clear(); return Ok(Value::Number(0.0)); }
5337 #[cfg(not(target_arch = "wasm32"))]
5340 "flush_3d" | "render_3d" => {
5341 let mut gfx = self.gfx.borrow_mut();
5342 if !gfx.depth_queue.is_empty() {
5343 let w = gfx.width; let h = gfx.height;
5344 let queue = std::mem::take(&mut gfx.depth_queue);
5345 queue.flush(&mut gfx.buffer, w, h);
5346 }
5347 return Ok(Value::Unit);
5348 }
5349
5350 #[cfg(not(target_arch = "wasm32"))]
5353 "screen_distort" | "บิดจอ" | "屏幕扭曲" | "画面歪み" | "화면왜곡" => {
5354 let amount = self.arg_num(&args, 0, 8.0)? as f32;
5355 let t = self.arg_num(&args, 1, 0.0)? as f32;
5356 self.gfx.borrow_mut().distort(amount, t);
5357 return Ok(Value::Unit);
5358 }
5359
5360 "set_rim" | "设置边缘光" | "リム設定" | "림라이트" | "ตั้งขอบเรือง" => {
5361 let s=self.arg_num(&args,0,0.6)? as f32;
5362 let r=self.arg_num(&args,1,115.)? as f32/255.0;
5363 let g=self.arg_num(&args,2,217.)? as f32/255.0;
5364 let b=self.arg_num(&args,3,255.)? as f32/255.0;
5365 let mut gfx=self.gfx.borrow_mut();
5366 gfx.shade.rim = s; gfx.shade.rim_color = [r,g,b];
5367 return Ok(Value::Unit);
5368 }
5369
5370 n if crate::gfx::shapes::canon(n).is_some() => {
5379 let kind = crate::gfx::shapes::canon(n).unwrap();
5380 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32; let cz=self.arg_num(&args,2,0.)? as f32;
5381 let sx=self.arg_num(&args,3,1.)? as f32; let sy=self.arg_num(&args,4,1.)? as f32; let sz=self.arg_num(&args,5,1.)? as f32;
5382 let rx=self.arg_num(&args,6,0.)? as f32; let ry=self.arg_num(&args,7,0.)? as f32; let rz=self.arg_num(&args,8,0.)? as f32;
5383 let mode=self.arg_num(&args,9,0.)? as i32;
5384 let e0=self.arg_num(&args,10,0.)? as f32; let e1=self.arg_num(&args,11,0.)? as f32; let e2=self.arg_num(&args,12,0.)? as f32;
5385 if let Some(mesh)=crate::gfx::shapes::build(kind,[cx,cy,cz,sx,sy,sz,rx,ry,rz],e0,e1,e2){
5386 let mut gfx=self.gfx.borrow_mut();
5387 gfx.emit_mesh(&mesh,mode);
5388 }
5389 return Ok(Value::Unit);
5390 }
5391
5392 _ => {}
5393 }
5394
5395 if let Some(def) = self.functions.get(name).cloned() {
5397 let mut call_env = self.global_seed.clone();
5401 let _ = env; for (param, arg) in def.params.iter().zip(args) {
5403 call_env.insert(param.clone(), arg);
5404 }
5405 return match self.framed(name, |me| me.exec_block(&def.body, &mut call_env)) {
5406 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
5407 Err(EvalErr::Return(v)) => Ok(v),
5408 Err(e) => Err(e),
5409 };
5410 }
5411
5412 if let Some(field_names) = self.structs.get(name).cloned() {
5414 if args.len() != field_names.len() {
5415 return Err(EvalErr::from(format!(
5416 "{name} expects {} field(s), got {}", field_names.len(), args.len())));
5417 }
5418 let fields = field_names.into_iter().zip(args).collect();
5419 return Ok(Value::Struct { name: name.to_string(), fields });
5420 }
5421
5422 if let Some((enum_name, arity)) = self.enum_variants.get(name).cloned() {
5424 if args.len() != arity {
5425 return Err(EvalErr::from(format!(
5426 "{name} expects {arity} value(s), got {}", args.len())));
5427 }
5428 let variant = name.rsplit("::").next().unwrap_or(name).to_string();
5429 return Ok(Value::Variant { enum_name, variant, payload: args });
5430 }
5431
5432 Err(EvalErr::from(format!("unknown function '{name}'")))
5433 }
5434
5435 fn call_value(&mut self, v: Value, args: Vec<Value>) -> EvalResult {
5436 match v {
5437 Value::Fn(params, body, mut captured) => {
5438 for (p, a) in params.iter().zip(args) {
5439 captured.insert(p.clone(), a);
5440 }
5441 match self.framed("<closure>", |me| me.exec_block(&body, &mut captured)) {
5442 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
5443 Err(EvalErr::Return(v)) => Ok(v),
5444 Err(e) => Err(e),
5445 }
5446 }
5447 other => Err(EvalErr::from(format!("cannot call {:?}", other))),
5448 }
5449 }
5450
5451 fn call_method(&self, recv: Value, method: &str, args: Vec<Value>) -> EvalResult {
5452 match (&recv, method) {
5453 (Value::Str(s), "is_empty" | "是空") => Ok(Value::Bool(s.is_empty())),
5454 (Value::Str(s), "len" | "长") => Ok(Value::Number(s.len() as f64)),
5455 (Value::Str(s), "to_string" | "转文") => Ok(Value::Str(s.clone())),
5456 (Value::Str(s), "contains" | "包含") => {
5457 if let Some(Value::Str(sub)) = args.first() {
5458 Ok(Value::Bool(s.contains(sub.as_str())))
5459 } else { Ok(Value::Bool(false)) }
5460 }
5461 (Value::Str(s), "push_str" | "推_文") => {
5462 let mut s2 = s.clone();
5463 if let Some(Value::Str(a)) = args.first() { s2.push_str(a); }
5464 Ok(Value::Str(s2))
5465 }
5466 (Value::List(v), "len" | "长") => Ok(Value::Number(v.len() as f64)),
5467 (Value::List(v), "push" | "推") => {
5468 let mut v2 = v.clone();
5469 if let Some(a) = args.first() { v2.push(a.clone()); }
5470 Ok(Value::List(v2))
5471 }
5472 (Value::Struct { fields, .. }, _) if args.is_empty() => {
5474 fields.iter().find(|(k, _)| k == method).map(|(_, v)| v.clone())
5475 .ok_or_else(|| EvalErr::from(format!("no field '{method}' on {recv}")))
5476 }
5477 (Value::Variant { variant, .. }, "tag" | "标签" | "タグ" | "태그" | "ป้าย") if args.is_empty() =>
5479 Ok(Value::Str(variant.clone())),
5480 (Value::Ok(inner), _) | (Value::Err(inner), _) => Ok(*inner.clone()),
5481 _ => Err(EvalErr::from(format!("no method '{method}' on {recv}"))),
5482 }
5483 }
5484
5485 fn match_pattern(&self, pat: &Pattern, val: &Value) -> Option<Env> {
5488 match (pat, val) {
5489 (Pattern::Wildcard, _) => Some(Env::new()),
5490 (Pattern::Str(s), Value::Str(v)) if s == v => Some(Env::new()),
5491 (Pattern::Number(n), Value::Number(v)) if (n - v).abs() < 1e-12 => Some(Env::new()),
5492 (Pattern::Bool(b), Value::Bool(v)) if b == v => Some(Env::new()),
5493 (Pattern::Ident(name), _) => {
5494 let mut e = Env::new();
5495 e.insert(name.clone(), val.clone());
5496 Some(e)
5497 }
5498 (Pattern::Constructor(ctor, inner_pat), _) => {
5499 let (matches, inner_val) = match (ctor.as_str(), val) {
5500 ("ok" | "好", Value::Ok(v)) => (true, Some(v.as_ref().clone())),
5501 ("bad" | "坏", Value::Err(v)) => (true, Some(v.as_ref().clone())),
5502 ("ok" | "好", v) if !matches!(v, Value::Err(_)) => (true, Some(v.clone())),
5503 _ => (false, None),
5504 };
5505 if !matches { return None; }
5506 match (inner_pat, inner_val) {
5507 (Some(p), Some(v)) => self.match_pattern(p, &v),
5508 (None, _) => Some(Env::new()),
5509 (Some(p), None) => self.match_pattern(p, &Value::Unit),
5510 }
5511 }
5512 (Pattern::Variant(vname, sub_pats), Value::Variant { variant, payload, .. }) => {
5514 if vname != variant || sub_pats.len() != payload.len() { return None; }
5515 let mut bindings = Env::new();
5516 for (p, v) in sub_pats.iter().zip(payload.iter()) {
5517 bindings.extend(self.match_pattern(p, v)?);
5518 }
5519 Some(bindings)
5520 }
5521 (Pattern::Variant(vname, sub), Value::Ok(v)) if (vname == "ok" || vname == "好") => {
5524 match sub.as_slice() {
5525 [] => Some(Env::new()),
5526 [p] => self.match_pattern(p, v),
5527 _ => None,
5528 }
5529 }
5530 (Pattern::Variant(vname, sub), Value::Err(v)) if (vname == "bad" || vname == "坏" || vname == "err") => {
5531 match sub.as_slice() {
5532 [] => Some(Env::new()),
5533 [p] => self.match_pattern(p, v),
5534 _ => None,
5535 }
5536 }
5537 _ => None,
5538 }
5539 }
5540
5541 fn value_to_iter(&self, val: Value) -> Result<Vec<Value>, EvalErr> {
5544 match val {
5545 Value::List(v) => Ok(v),
5546 Value::Str(s) => Ok(s.chars().map(|c| Value::Str(c.to_string())).collect()),
5547 Value::Number(n) => Ok((0..n as i64).map(|i| Value::Number(i as f64)).collect()),
5548 other => Err(EvalErr::from(format!("cannot iterate over {:?}", other))),
5549 }
5550 }
5551
5552 fn is_truthy(&self, val: &Value) -> bool {
5553 match val {
5554 Value::Bool(b) => *b,
5555 Value::Unit => false,
5556 Value::Number(n) => *n != 0.0,
5557 Value::Str(s) => !s.is_empty(),
5558 Value::List(v) => !v.is_empty(),
5559 Value::Ok(_) => true,
5560 Value::Err(_) => false,
5561 Value::Fn(_, _, _) => true,
5562 Value::Struct { .. } => true,
5563 Value::Variant { .. } => true,
5564 }
5565 }
5566
5567 fn to_number(&self, val: &Value) -> Result<f64, EvalErr> {
5568 match val {
5569 Value::Number(n) => Ok(*n),
5570 Value::Str(s) => s.parse().map_err(|_| EvalErr::from(format!("cannot convert '{s}' to number"))),
5571 other => Err(EvalErr::from(format!("expected number, got {:?}", other))),
5572 }
5573 }
5574
5575 fn arg_num(&self, args: &[Value], n: usize, default: f64) -> Result<f64, EvalErr> {
5577 match args.get(n) {
5578 Some(v) => self.to_number(v),
5579 None => Ok(default),
5580 }
5581 }
5582
5583 fn arg_str(&self, args: &[Value], n: usize, default: &str) -> String {
5584 args.get(n).map(|v| v.to_string()).unwrap_or_else(|| default.to_string())
5585 }
5586
5587 #[allow(dead_code)]
5589 fn arg_list_f32(&self, args: &[Value], n: usize) -> Vec<f32> {
5590 match args.get(n) {
5591 Some(Value::List(v)) => v.iter().filter_map(|x| match x {
5592 Value::Number(n) => Some(*n as f32),
5593 _ => None,
5594 }).collect(),
5595 _ => Vec::new(),
5596 }
5597 }
5598
5599 #[cfg(not(target_arch = "wasm32"))]
5602 fn color_at(&self, args: &[Value], i: usize, default: u32) -> u32 {
5603 match (args.get(i), args.get(i + 1), args.get(i + 2)) {
5604 (Some(a), Some(b), Some(c)) => match (self.to_number(a), self.to_number(b), self.to_number(c)) {
5605 (Ok(r), Ok(g), Ok(bl)) =>
5606 ((r as u32 & 0xFF) << 16) | ((g as u32 & 0xFF) << 8) | (bl as u32 & 0xFF),
5607 _ => default,
5608 },
5609 _ => default,
5610 }
5611 }
5612
5613 #[cfg(not(target_arch = "wasm32"))]
5615 fn pitch_arg(&self, args: &[Value], i: usize, default: i32) -> i32 {
5616 match args.get(i) {
5617 Some(Value::Str(s)) => ling_music::note::parse_pitch(s).unwrap_or(default),
5618 Some(Value::Number(n)) => *n as i32,
5619 _ => default,
5620 }
5621 }
5622
5623 #[cfg(not(target_arch = "wasm32"))]
5625 fn mouse_now(&self) -> (f32, f32, bool) {
5626 let gfx = self.gfx.borrow();
5627 let (mx, my) = gfx.window.as_ref()
5628 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp)).unwrap_or((0.0, 0.0));
5629 let down = gfx.window.as_ref()
5630 .map(|w| w.get_mouse_down(minifb::MouseButton::Left)).unwrap_or(false);
5631 (mx, my, down)
5632 }
5633
5634 #[cfg(not(target_arch = "wasm32"))]
5638 fn draw_ui(&self, d: &ling_ui::widgets::Draw) {
5639 let mut gfx = self.gfx.borrow_mut();
5640 let (w, h, add) = (gfx.width, gfx.height, gfx.blend == 1);
5641 for (c, poly) in &d.fills {
5642 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, w, h, *c, add, std::slice::from_ref(poly));
5643 }
5644 for (c, pl) in &d.strokes {
5645 for s in pl.windows(2) {
5646 crate::gfx::raster::draw_line_aa(&mut gfx.buffer, w, h, *c, add, s[0][0], s[0][1], s[1][0], s[1][1]);
5647 }
5648 }
5649 }
5650
5651 fn tex_rect(&self, args: &[Value]) -> Result<(usize, usize, usize, usize), EvalErr> {
5653 let tx = self.arg_num(args, 0, 0.0)? as usize;
5654 let ty = self.arg_num(args, 1, 0.0)? as usize;
5655 let tw = self.arg_num(args, 2, 256.0)? as usize;
5656 let th = self.arg_num(args, 3, 256.0)? as usize;
5657 Ok((tx, ty, tw.max(1), th.max(1)))
5658 }
5659
5660 fn apply_binop(&self, op: &BinOp, l: Value, r: Value) -> EvalResult {
5661 match op {
5662 BinOp::Add => match (l, r) {
5663 (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
5664 (Value::Str(a), Value::Str(b)) => Ok(Value::Str(a + &b)),
5665 (Value::Str(a), b) => Ok(Value::Str(a + &b.to_string())),
5666 (a, Value::Str(b)) => Ok(Value::Str(a.to_string() + &b)),
5667 (a, b) => Err(EvalErr::from(format!("cannot add {:?} and {:?}", a, b))),
5668 },
5669 BinOp::Sub => Ok(Value::Number(self.to_number(&l)? - self.to_number(&r)?)),
5670 BinOp::Mul => Ok(Value::Number(self.to_number(&l)? * self.to_number(&r)?)),
5671 BinOp::Div => Ok(Value::Number(self.to_number(&l)? / self.to_number(&r)?)),
5672 BinOp::Rem => Ok(Value::Number(self.to_number(&l)? % self.to_number(&r)?)),
5673 BinOp::Eq => Ok(Value::Bool(values_equal(&l, &r))),
5674 BinOp::Ne => Ok(Value::Bool(!values_equal(&l, &r))),
5675 BinOp::Lt => Ok(Value::Bool(self.to_number(&l)? < self.to_number(&r)?)),
5676 BinOp::Gt => Ok(Value::Bool(self.to_number(&l)? > self.to_number(&r)?)),
5677 BinOp::Le => Ok(Value::Bool(self.to_number(&l)? <= self.to_number(&r)?)),
5678 BinOp::Ge => Ok(Value::Bool(self.to_number(&l)? >= self.to_number(&r)?)),
5679 BinOp::And => Ok(Value::Bool(self.is_truthy(&l) && self.is_truthy(&r))),
5680 BinOp::Or => Ok(Value::Bool(self.is_truthy(&l) || self.is_truthy(&r))),
5681 }
5682 }
5683
5684 fn builtin_format(&self, args: &[Value]) -> Result<String, EvalErr> {
5685 if args.is_empty() { return Ok(String::new()); }
5686 let fmt = match &args[0] {
5687 Value::Str(s) => s.clone(),
5688 other => return Ok(other.to_string()),
5689 };
5690
5691 let mut result = String::new();
5692 let mut arg_idx = 1usize;
5693 let mut chars = fmt.chars().peekable();
5694 while let Some(c) = chars.next() {
5695 if c == '{' {
5696 if chars.peek() == Some(&'}') {
5697 chars.next();
5698 if arg_idx < args.len() {
5699 result.push_str(&args[arg_idx].to_string());
5700 arg_idx += 1;
5701 }
5702 } else {
5703 let mut spec = String::new();
5704 for ch in chars.by_ref() {
5705 if ch == '}' { break; }
5706 spec.push(ch);
5707 }
5708 if arg_idx < args.len() {
5709 if spec.starts_with(":.") {
5710 if let Value::Number(n) = &args[arg_idx] {
5711 let prec: usize = spec[2..].trim_end_matches('f')
5712 .parse().unwrap_or(2);
5713 result.push_str(&format!("{:.prec$}", n));
5714 arg_idx += 1;
5715 continue;
5716 }
5717 }
5718 result.push_str(&args[arg_idx].to_string());
5719 arg_idx += 1;
5720 }
5721 }
5722 } else {
5723 result.push(c);
5724 }
5725 }
5726 Ok(result)
5727 }
5728}
5729
5730#[cfg(not(target_arch = "wasm32"))]
5731#[cfg(not(target_arch = "wasm32"))]
5733fn parse_pad_button(name: &str) -> Option<ling_input::GamepadButton> {
5734 use ling_input::GamepadButton as B;
5735 Some(match name.to_ascii_lowercase().as_str() {
5736 "a" | "south" | "cross" => B::South,
5737 "b" | "east" | "circle" => B::East,
5738 "x" | "west" | "square" => B::West,
5739 "y" | "north" | "triangle" => B::North,
5740 "lb" | "l1" | "left_shoulder" => B::LeftShoulder,
5741 "rb" | "r1" | "right_shoulder" => B::RightShoulder,
5742 "lt" | "l2" | "left_trigger" => B::LeftTrigger,
5743 "rt" | "r2" | "right_trigger" => B::RightTrigger,
5744 "start" | "menu" | "options" => B::Start,
5745 "select" | "back" | "share" | "view" => B::Select,
5746 "guide" | "home" => B::Guide,
5747 "l3" | "left_stick" => B::LeftStick,
5748 "r3" | "right_stick" => B::RightStick,
5749 "up" | "dpad_up" => B::DpadUp,
5750 "down" | "dpad_down" => B::DpadDown,
5751 "left" | "dpad_left" => B::DpadLeft,
5752 "right" | "dpad_right" => B::DpadRight,
5753 _ => return None,
5754 })
5755}
5756
5757#[cfg(not(target_arch = "wasm32"))]
5758fn str_to_minifb_key(name: &str) -> Option<minifb::Key> {
5759 use minifb::Key;
5760 Some(match name {
5761 "numpad0" | "kp0" => Key::NumPad0,
5762 "numpad1" | "kp1" => Key::NumPad1,
5763 "numpad2" | "kp2" => Key::NumPad2,
5764 "numpad3" | "kp3" => Key::NumPad3,
5765 "numpad4" | "kp4" => Key::NumPad4,
5766 "numpad5" | "kp5" => Key::NumPad5,
5767 "numpad6" | "kp6" => Key::NumPad6,
5768 "numpad7" | "kp7" => Key::NumPad7,
5769 "numpad8" | "kp8" => Key::NumPad8,
5770 "numpad9" | "kp9" => Key::NumPad9,
5771 "numpad+" | "kp+" => Key::NumPadPlus,
5772 "numpad-" | "kp-" => Key::NumPadMinus,
5773 "numpad*" | "kp*" => Key::NumPadAsterisk,
5774 "numpad/" | "kp/" => Key::NumPadSlash,
5775 "left" => Key::Left,
5776 "right" => Key::Right,
5777 "up" => Key::Up,
5778 "down" => Key::Down,
5779 "space" => Key::Space,
5780 "enter" => Key::Enter,
5781 "escape" => Key::Escape,
5782 "pageup" => Key::PageUp,
5783 "pagedown" => Key::PageDown,
5784 "lshift" | "leftshift" => Key::LeftShift,
5785 "rshift" | "rightshift" => Key::RightShift,
5786 "lctrl" | "leftctrl" => Key::LeftCtrl,
5787 "rctrl" | "rightctrl" => Key::RightCtrl,
5788 "lalt" | "leftalt" => Key::LeftAlt,
5789 "ralt" | "rightalt" => Key::RightAlt,
5790 "tab" => Key::Tab,
5791 "backspace" => Key::Backspace,
5792 "delete" => Key::Delete,
5793 "insert" => Key::Insert,
5794 "home" => Key::Home,
5795 "end" => Key::End,
5796 "a" => Key::A, "b" => Key::B, "c" => Key::C, "d" => Key::D,
5797 "e" => Key::E, "f" => Key::F, "g" => Key::G, "h" => Key::H,
5798 "i" => Key::I, "j" => Key::J, "k" => Key::K, "l" => Key::L,
5799 "m" => Key::M, "n" => Key::N, "o" => Key::O, "p" => Key::P,
5800 "q" => Key::Q, "r" => Key::R, "s" => Key::S, "t" => Key::T,
5801 "u" => Key::U, "v" => Key::V, "w" => Key::W, "x" => Key::X,
5802 "y" => Key::Y, "z" => Key::Z,
5803 "0" => Key::Key0, "1" => Key::Key1, "2" => Key::Key2,
5804 "3" => Key::Key3, "4" => Key::Key4, "5" => Key::Key5,
5805 "6" => Key::Key6, "7" => Key::Key7, "8" => Key::Key8,
5806 "9" => Key::Key9,
5807 _ => return None,
5808 })
5809}
5810
5811fn values_equal(a: &Value, b: &Value) -> bool {
5812 match (a, b) {
5813 (Value::Number(x), Value::Number(y)) => (x - y).abs() < 1e-12,
5814 (Value::Str(x), Value::Str(y)) => x == y,
5815 (Value::Bool(x), Value::Bool(y)) => x == y,
5816 (Value::Unit, Value::Unit) => true,
5817 _ => false,
5818 }
5819}
5820
5821#[cfg(not(target_arch = "wasm32"))]
5828fn hide_console_window() {
5829 #[cfg(windows)]
5830 unsafe {
5831 extern "system" {
5832 fn GetConsoleWindow() -> isize;
5833 fn ShowWindow(hwnd: isize, nCmdShow: i32) -> i32;
5834 }
5835 let hwnd = GetConsoleWindow();
5836 if hwnd != 0 {
5837 ShowWindow(hwnd, 0); }
5839 }
5840}
5841
5842#[cfg(all(not(target_arch = "wasm32"), windows))]
5847fn make_borderless_fullscreen(hwnd: isize, screen_w: i32, screen_h: i32) {
5848 if hwnd == 0 {
5849 return;
5850 }
5851 unsafe {
5852 extern "system" {
5853 fn SetWindowLongPtrW(hwnd: isize, index: i32, new: isize) -> isize;
5854 fn SetWindowPos(hwnd: isize, insert_after: isize,
5855 x: i32, y: i32, cx: i32, cy: i32,
5856 flags: u32) -> i32;
5857 fn ShowWindow(hwnd: isize, cmd: i32) -> i32;
5858 }
5859 const GWL_STYLE: i32 = -16;
5860 const GWL_EXSTYLE: i32 = -20;
5861 SetWindowLongPtrW(hwnd, GWL_STYLE, 0x9000_0000isize);
5864 SetWindowLongPtrW(hwnd, GWL_EXSTYLE, 0);
5866 SetWindowPos(hwnd, -1isize, 0, 0, screen_w, screen_h, 0x0020 | 0x0040);
5868 ShowWindow(hwnd, 3); }
5870}
5871
5872#[cfg(all(not(target_arch = "wasm32"), windows))]
5875fn monitor_info() -> (i32, i32, i32) {
5876 unsafe {
5877 extern "system" {
5878 fn GetSystemMetrics(index: i32) -> i32;
5879 fn GetDC(hwnd: isize) -> isize;
5880 fn ReleaseDC(hwnd: isize, hdc: isize) -> i32;
5881 fn GetDeviceCaps(hdc: isize, index: i32) -> i32;
5882 }
5883 let w = GetSystemMetrics(0).max(1); let h = GetSystemMetrics(1).max(1); let hdc = GetDC(0);
5886 let mut hz = if hdc != 0 { GetDeviceCaps(hdc, 116) } else { 0 }; if hdc != 0 {
5888 ReleaseDC(0, hdc);
5889 }
5890 if hz <= 1 {
5891 hz = 60; }
5893 (w, h, hz)
5894 }
5895}
5896
5897#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
5899fn monitor_info() -> (i32, i32, i32) {
5900 let (w, h) = native_screen_size();
5901 (w as i32, h as i32, 60)
5902}
5903
5904#[cfg(target_arch = "wasm32")]
5906fn monitor_info() -> (i32, i32, i32) {
5907 let (w, h) = crate::gfx::webgl::canvas_size();
5908 (w as i32, h as i32, 60)
5909}
5910
5911#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
5914fn native_screen_size() -> (f64, f64) {
5915 (1920.0, 1080.0)
5918}