1#[cfg(not(target_arch = "wasm32"))]
3mod net;
4#[cfg(not(target_arch = "wasm32"))]
5mod ai;
6use std::cell::RefCell;
7use std::collections::HashMap;
8use crate::parser::ast::*;
9use crate::gfx::{GfxState, Light};
10#[cfg(not(target_arch = "wasm32"))]
11use crate::gfx::raster::{fill_triangle, draw_line};
12#[cfg(not(target_arch = "wasm32"))]
13use ling_audio::{AudioEngine, ToneParams, Wave};
14
15#[cfg(not(target_arch = "wasm32"))]
16use ling_audio::FftAnalyzer;
17
18#[cfg(not(target_arch = "wasm32"))]
19use ling_mic;
20
21#[derive(Debug, Clone)]
24pub enum Value {
25 Str(String),
26 Number(f64),
27 Bool(bool),
28 Unit,
29 List(Vec<Value>),
30 Ok(Box<Value>),
31 Err(Box<Value>),
32 Fn(Vec<String>, Vec<Stmt>, Env),
33}
34
35type Env = HashMap<String, Value>;
36
37impl std::fmt::Display for Value {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 match self {
40 Value::Str(s) => write!(f, "{s}"),
41 Value::Number(n) => {
42 if n.fract() == 0.0 && n.abs() < 1e15 { write!(f, "{}", *n as i64) }
43 else { write!(f, "{n}") }
44 }
45 Value::Bool(b) => write!(f, "{b}"),
46 Value::Unit => write!(f, "()"),
47 Value::List(v) => {
48 write!(f, "[")?;
49 for (i, x) in v.iter().enumerate() {
50 if i > 0 { write!(f, ", ")?; }
51 write!(f, "{x}")?;
52 }
53 write!(f, "]")
54 }
55 Value::Ok(v) => write!(f, "Ok({v})"),
56 Value::Err(v) => write!(f, "Err({v})"),
57 Value::Fn(_, _, _) => write!(f, "<fn>"),
58 }
59 }
60}
61
62#[derive(Debug)]
65enum EvalErr {
66 Runtime(String),
67 Return(Value),
68 #[allow(dead_code)] Break,
70}
71
72impl From<String> for EvalErr {
73 fn from(s: String) -> Self { EvalErr::Runtime(s) }
74}
75
76type EvalResult = Result<Value, EvalErr>;
77
78struct SvgWriter {
83 path: String,
84 width: f64,
85 height: f64,
86 elements: Vec<String>,
87}
88
89impl SvgWriter {
90 fn new(path: String, width: f64, height: f64) -> Self {
91 let bg = format!(
92 "<rect width=\"{width}\" height=\"{height}\" fill=\"#0a0a0a\"/>"
93 );
94 Self { path, width, height, elements: vec![bg] }
95 }
96
97 fn save(&self) -> std::io::Result<()> {
98 let w = self.width;
99 let h = self.height;
100 let mut out = format!(
101 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
102 <svg xmlns=\"http://www.w3.org/2000/svg\" \
103 width=\"{w}\" height=\"{h}\" viewBox=\"0 0 {w} {h}\">\n"
104 );
105 for elem in &self.elements {
106 out.push_str(" ");
107 out.push_str(elem);
108 out.push('\n');
109 }
110 out.push_str("</svg>\n");
111 if let Some(parent) = std::path::Path::new(&self.path).parent() {
113 if !parent.as_os_str().is_empty() {
114 let _ = std::fs::create_dir_all(parent);
115 }
116 }
117 std::fs::write(&self.path, out.as_bytes())
118 }
119}
120
121fn hsl_to_hex(h: f64, s: f64, l: f64) -> String {
122 let s = s / 100.0;
123 let l = l / 100.0;
124 let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
125 let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
126 let m = l - c / 2.0;
127 let (r1, g1, b1) = if h < 60.0 { (c, x, 0.0) }
128 else if h < 120.0 { (x, c, 0.0) }
129 else if h < 180.0 { (0.0, c, x) }
130 else if h < 240.0 { (0.0, x, c) }
131 else if h < 300.0 { (x, 0.0, c) }
132 else { (c, 0.0, x) };
133 let r = ((r1 + m) * 255.0).round() as u8;
134 let g = ((g1 + m) * 255.0).round() as u8;
135 let b = ((b1 + m) * 255.0).round() as u8;
136 format!("#{r:02x}{g:02x}{b:02x}")
137}
138
139fn tex_hash(x: i32, y: i32, seed: u32) -> f32 {
142 let mut h = (x as u32).wrapping_add((y as u32).wrapping_mul(2654435769)).wrapping_add(seed.wrapping_mul(1234567891));
143 h ^= h >> 16; h = h.wrapping_mul(0x45d9f3b); h ^= h >> 16;
144 h as f32 / u32::MAX as f32
145}
146
147fn tex_vnoise(x: f32, y: f32, seed: u32) -> f32 {
148 let xi = x.floor() as i32; let yi = y.floor() as i32;
149 let sm = |t: f32| t * t * (3.0 - 2.0 * t);
150 let xf = sm(x - xi as f32); let yf = sm(y - yi as f32);
151 let a = tex_hash(xi, yi, seed); let b = tex_hash(xi+1, yi, seed);
152 let c = tex_hash(xi, yi+1, seed); let d = tex_hash(xi+1, yi+1, seed);
153 a + (b-a)*xf + (c-a)*yf + (a-b-c+d)*xf*yf
154}
155
156fn tex_fbm(x: f32, y: f32, octaves: u32, seed: u32) -> f32 {
157 let mut v = 0.0f32; let mut amp = 0.5f32; let mut f = 1.0f32;
158 for i in 0..octaves {
159 v += tex_vnoise(x * f, y * f, seed.wrapping_add(i * 7919)) * amp;
160 amp *= 0.5; f *= 2.0;
161 }
162 v
163}
164
165fn tex_palette(name: &str, t: f32) -> [f32; 3] {
166 let (a, b, c, d): ([f32;3],[f32;3],[f32;3],[f32;3]) = match name {
167 "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]),
168 "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]),
169 "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]),
170 "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]),
171 "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]),
172 _ => ([0.5,0.5,0.5],[0.5,0.5,0.5],[1.0,1.0,1.0],[0.0,0.333,0.667]),
173 };
174 [0,1,2].map(|i| (a[i] + b[i] * (std::f32::consts::TAU * (c[i] * t + d[i])).cos()).clamp(0.0, 1.0))
175}
176
177#[cfg(not(target_arch = "wasm32"))]
179fn key_char(k: minifb::Key) -> Option<char> {
180 use minifb::Key::*;
181 Some(match k {
182 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',
183 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',
184 Key0=>'0',Key1=>'1',Key2=>'2',Key3=>'3',Key4=>'4',Key5=>'5',Key6=>'6',Key7=>'7',Key8=>'8',Key9=>'9',
185 Space=>' ', Minus=>'-', Period=>'.',
186 _ => return None,
187 })
188}
189
190fn hex_encode(bytes: &[u8]) -> String {
192 let mut s = String::with_capacity(bytes.len() * 2);
193 for b in bytes { s.push_str(&format!("{b:02x}")); }
194 s
195}
196
197#[cfg(not(target_arch = "wasm32"))]
199fn decode_blob(s: &str) -> Result<Vec<u8>, String> {
200 use base64::Engine as _;
201 use std::io::Read as _;
202 let comp = base64::engine::general_purpose::STANDARD
203 .decode(s.trim())
204 .map_err(|e| format!("base64: {e}"))?;
205 let mut out = Vec::new();
206 flate2::read::ZlibDecoder::new(&comp[..])
207 .read_to_end(&mut out)
208 .map_err(|e| format!("inflate: {e}"))?;
209 Ok(out)
210}
211
212fn hex_decode(s: &str) -> Vec<u8> {
214 let s = s.trim();
215 (0..s.len() / 2)
216 .filter_map(|i| u8::from_str_radix(s.get(i * 2..i * 2 + 2)?, 16).ok())
217 .collect()
218}
219
220fn hex_to_32(s: &str) -> [u8; 32] {
222 let v = hex_decode(s);
223 let mut out = [0u8; 32];
224 let n = v.len().min(32);
225 out[..n].copy_from_slice(&v[..n]);
226 out
227}
228
229fn tex_rgb(r: f32, g: f32, b: f32) -> u32 {
230 ((r * 255.0) as u32) << 16 | ((g * 255.0) as u32) << 8 | (b * 255.0) as u32
231}
232
233const PERM: [u8; 512] = [
236 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,
237 140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,
238 247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,
239 57,177,33,88,237,149,56,87,174,35,63,189,114,56,42,123,
240 165,38,72,93,69,139,138,78,149,159,56,89,152,78,61,140,
241 63,26,142,76,124,132,72,11,90,44,82,59,96,41,148,126,
242 157,13,49,27,176,33,47,14,97,78,71,40,87,183,4,122,
243 92,7,72,3,246,17,225,87,91,106,203,190,57,74,76,88,
244 207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,
245 168,81,163,64,143,146,157,56,245,188,182,218,33,16,255,243,
246 210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,25,
247 115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,
248 219,224,50,58,10,73,6,36,92,194,211,172,98,145,149,228,
249 121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,
250 8,186,120,37,46,28,166,180,198,232,221,116,31,75,189,139,
251 138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,
252 158,225,248,152,17,105,217,142,148,155,30,135,233,206,85,40,
253 223,140,161,137,13,191,230,66,104,153,199,167,147,99,179,92,
254 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,
256 140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,
257 247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,
258 57,177,33,88,237,149,56,87,174,35,63,189,114,56,42,123,
259 165,38,72,93,69,139,138,78,149,159,56,89,152,78,61,140,
260 63,26,142,76,124,132,72,11,90,44,82,59,96,41,148,126,
261 157,13,49,27,176,33,47,14,97,78,71,40,87,183,4,122,
262 92,7,72,3,246,17,225,87,91,106,203,190,57,74,76,88,
263 207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,
264 168,81,163,64,143,146,157,56,245,188,182,218,33,16,255,243,
265 210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,25,
266 115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,
267 219,224,50,58,10,73,6,36,92,194,211,172,98,145,149,228,
268 121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,
269];
270
271fn fade(t: f32) -> f32 {
272 t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
273}
274
275fn grad(hash: u8, x: f32, y: f32, z: f32) -> f32 {
276 let h = hash & 15;
277 let u = if h < 8 { x } else { y };
278 let v = if h < 8 { y } else { z };
279 (if (h & 1) == 0 { u } else { -u }) + (if (h & 2) == 0 { v } else { -v })
280}
281
282fn perlin3(x: f32, y: f32, z: f32) -> f32 {
283 let xi = (x.floor() as i32) & 255;
284 let yi = (y.floor() as i32) & 255;
285 let zi = (z.floor() as i32) & 255;
286
287 let xf = x - x.floor();
288 let yf = y - y.floor();
289 let zf = z - z.floor();
290
291 let u = fade(xf);
292 let v = fade(yf);
293 let w = fade(zf);
294
295 let p0 = PERM[xi as usize] as usize;
296 let p1 = PERM[((xi + 1) & 255) as usize] as usize;
297 let pa = PERM[(p0 + yi as usize) & 255] as usize;
298 let pb = PERM[(p0 + ((yi + 1) & 255) as usize) & 255] as usize;
299 let pc = PERM[(p1 + yi as usize) & 255] as usize;
300 let pd = PERM[(p1 + ((yi + 1) & 255) as usize) & 255] as usize;
301
302 let g000 = grad(PERM[(pa + zi as usize) & 255], xf, yf, zf);
303 let g001 = grad(PERM[(pa + ((zi + 1) & 255) as usize) & 255], xf, yf, zf - 1.0);
304 let g010 = grad(PERM[(pb + zi as usize) & 255], xf, yf - 1.0, zf);
305 let g011 = grad(PERM[(pb + ((zi + 1) & 255) as usize) & 255], xf, yf - 1.0, zf - 1.0);
306 let g100 = grad(PERM[(pc + zi as usize) & 255], xf - 1.0, yf, zf);
307 let g101 = grad(PERM[(pc + ((zi + 1) & 255) as usize) & 255], xf - 1.0, yf, zf - 1.0);
308 let g110 = grad(PERM[(pd + zi as usize) & 255], xf - 1.0, yf - 1.0, zf);
309 let g111 = grad(PERM[(pd + ((zi + 1) & 255) as usize) & 255], xf - 1.0, yf - 1.0, zf - 1.0);
310
311 let l00 = g000 + u * (g100 - g000);
312 let l01 = g001 + u * (g101 - g001);
313 let l10 = g010 + u * (g110 - g010);
314 let l11 = g011 + u * (g111 - g011);
315
316 let l0 = l00 + v * (l10 - l00);
317 let l1 = l01 + v * (l11 - l01);
318
319 l0 + w * (l1 - l0)
320}
321
322fn fast_rand_f64(state: &mut u64) -> f64 {
323 *state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
324 ((*state >> 32) as u32) as f64 / 4294967296.0
325}
326
327#[inline]
331fn put_px(buf: &mut [u32], idx: usize, color: u32, blend: u8) {
332 if idx >= buf.len() { return; }
333 if blend == 0 {
334 buf[idx] = color;
335 } else {
336 let old = buf[idx];
337 let r = (((old >> 16) & 255) + ((color >> 16) & 255)).min(255);
338 let g = (((old >> 8) & 255) + ((color >> 8) & 255)).min(255);
339 let b = ((old & 255) + (color & 255)).min(255);
340 buf[idx] = (r << 16) | (g << 8) | b;
341 }
342}
343
344fn draw_circle_outline(buf: &mut [u32], w: i32, h: i32, cx: i32, cy: i32, r: i32, color: u32, blend: u8) {
345 let r = r.clamp(0, 20000); if r == 0 { return; }
347 let mut x = 0;
348 let mut y = r;
349 let mut d = 3 - 2 * r;
350 while x <= y {
351 plot_circle_points(buf, w, h, cx, cy, x, y, color, blend);
352 if d < 0 {
353 d += 4 * x + 6;
354 } else {
355 d += 4 * (x - y) + 10;
356 y -= 1;
357 }
358 x += 1;
359 }
360}
361
362fn plot_circle_points(buf: &mut [u32], w: i32, h: i32, cx: i32, cy: i32, x: i32, y: i32, color: u32, blend: u8) {
363 let points = [(cx+x, cy+y), (cx-x, cy+y), (cx+x, cy-y), (cx-x, cy-y),
364 (cx+y, cy+x), (cx-y, cy+x), (cx+y, cy-x), (cx-y, cy-x)];
365 for &(px, py) in &points {
366 if px >= 0 && px < w && py >= 0 && py < h {
367 put_px(buf, (py * w + px) as usize, color, blend);
368 }
369 }
370}
371
372fn draw_circle_filled(buf: &mut [u32], w: i32, h: i32, cx: i32, cy: i32, r: i32, color: u32, blend: u8) {
373 if r <= 0 { return; }
374 for dy in -r..=r {
375 let dx_max = ((r*r - dy*dy) as f64).sqrt() as i32;
376 let py = cy + dy;
377 if py < 0 || py >= h { continue; }
378 for dx in -dx_max..=dx_max {
379 let px = cx + dx;
380 if px >= 0 && px < w {
381 put_px(buf, (py * w + px) as usize, color, blend);
382 }
383 }
384 }
385}
386
387#[cfg(test)]
388mod draw_tests {
389 use super::*;
390
391 #[test]
392 fn filled_circle_actually_writes_pixels() {
393 let mut buf = vec![0u32; 100 * 100];
394 draw_circle_filled(&mut buf, 100, 100, 50, 50, 10, 0xFF00FF, 0);
395 assert_eq!(buf[50 * 100 + 50], 0xFF00FF, "centre pixel must be filled");
396 assert_eq!(buf[0], 0, "far corner must stay clear");
397 let n = buf.iter().filter(|&&p| p != 0).count();
398 assert!(n > 200 && n < 500, "r=10 disc area ≈ 314, got {n}");
399 }
400
401 #[test]
402 fn circle_outline_writes_a_ring() {
403 let mut buf = vec![0u32; 100 * 100];
404 draw_circle_outline(&mut buf, 100, 100, 50, 50, 20, 0x00FF00, 0);
405 assert_eq!(buf[50 * 100 + 50], 0, "outline must NOT fill the centre");
406 assert!(buf.iter().any(|&p| p == 0x00FF00), "outline must draw a ring");
407 }
408
409 #[test]
410 fn additive_blend_accumulates_channels() {
411 let mut buf = vec![0x202020u32; 1];
412 put_px(&mut buf, 0, 0x404040, 1);
413 assert_eq!(buf[0], 0x606060);
414 }
415}
416
417#[derive(Clone, Copy)]
423pub struct UiTheme {
424 pub primary: u32,
425 pub accent: u32,
426 pub track: u32,
427 pub warn: u32,
428 pub text: u32,
429 pub bg: u32,
430}
431
432impl Default for UiTheme {
433 fn default() -> Self {
434 Self {
435 primary: 0x00D2FF, accent: 0x28FFB4, track: 0x2C3E64, warn: 0xFF5A5A, text: 0xBEEBFF, bg: 0x0A1018, }
442 }
443}
444
445pub struct Interpreter {
446 globals: HashMap<String, Expr>,
447 global_seed: Env,
450 functions: HashMap<String, FnDef>,
451 _modules: HashMap<String, Vec<FnDef>>,
452 gfx: RefCell<GfxState>,
453 svg: RefCell<Option<SvgWriter>>,
454 pub source_dir: Option<std::path::PathBuf>,
456 loaded_files: std::collections::HashSet<String>,
458 #[cfg(not(target_arch = "wasm32"))]
460 audio: Option<AudioEngine>,
461 #[cfg(not(target_arch = "wasm32"))]
462 fft: RefCell<FftAnalyzer>,
463 fft_bands_cache: RefCell<Vec<f32>>,
464 start_time: std::time::Instant,
466 frame_num: u64,
468 rand_state: u64,
470 #[cfg(not(target_arch = "wasm32"))]
472 mic: Option<ling_mic::MicInput>,
473 #[cfg(not(target_arch = "wasm32"))]
475 crypto_ids: Vec<ling_crypto::KnotIdentity>,
476 text_buffer: String,
478 record_n: u32,
480 #[cfg(not(target_arch = "wasm32"))]
482 mic_buffer: Vec<f32>,
483 #[cfg(not(target_arch = "wasm32"))]
485 fonts: Vec<ling_graphics::VectorFont>,
486 ui_theme: UiTheme,
488 mouse_was_down: bool,
490 #[cfg(not(target_arch = "wasm32"))]
492 music: Option<ling_music::MusicEngine>,
493 #[cfg(not(target_arch = "wasm32"))]
494 music_init: bool,
495 #[cfg(not(target_arch = "wasm32"))]
497 tracks: Vec<ling_music::DecodedAudio>,
498 #[cfg(not(target_arch = "wasm32"))]
500 lyrics: Vec<ling_music::Lyrics>,
501 #[cfg(not(target_arch = "wasm32"))]
503 midis: Vec<ling_music::MidiSong>,
504 soft_bodies: Vec<ling_physics::soft::SoftBody>,
506 rigid_world: ling_physics::rigid::PhysicsWorld,
508 liquids: Vec<ling_physics::liquid::LiquidGrid>,
510 dialog: Option<ling_game::dialog::Dialog>,
512 dialog_colors: [u32; 4],
514 frames: Vec<String>,
516 error_trace: Option<Vec<String>>,
519}
520
521impl Interpreter {
522 pub fn new() -> Self {
523 #[cfg(not(target_arch = "wasm32"))]
524 let audio = AudioEngine::new()
525 .map_err(|e| eprintln!("audio init failed (no sound): {e}"))
526 .ok();
527 Self {
528 globals: HashMap::new(),
529 global_seed: HashMap::new(),
530 functions: HashMap::new(),
531 _modules: HashMap::new(),
532 gfx: RefCell::new(GfxState::new()),
533 svg: RefCell::new(None),
534 source_dir: None,
535 loaded_files: std::collections::HashSet::new(),
536 #[cfg(not(target_arch = "wasm32"))]
537 audio,
538 #[cfg(not(target_arch = "wasm32"))]
539 fft: RefCell::new(FftAnalyzer::new(2048, 44100)),
540 fft_bands_cache: RefCell::new(vec![]),
541 start_time: std::time::Instant::now(),
542 frame_num: 0,
543 rand_state: 0x123456789ABCDEF,
544 #[cfg(not(target_arch = "wasm32"))]
545 mic: None,
546 #[cfg(not(target_arch = "wasm32"))]
547 crypto_ids: Vec::new(),
548 text_buffer: String::new(),
549 record_n: 0,
550 #[cfg(not(target_arch = "wasm32"))]
551 mic_buffer: Vec::new(),
552 #[cfg(not(target_arch = "wasm32"))]
553 fonts: Vec::new(),
554 ui_theme: UiTheme::default(),
555 mouse_was_down: false,
556 #[cfg(not(target_arch = "wasm32"))]
557 music: None,
558 #[cfg(not(target_arch = "wasm32"))]
559 music_init: false,
560 #[cfg(not(target_arch = "wasm32"))]
561 tracks: Vec::new(),
562 #[cfg(not(target_arch = "wasm32"))]
563 lyrics: Vec::new(),
564 #[cfg(not(target_arch = "wasm32"))]
565 midis: Vec::new(),
566 soft_bodies: Vec::new(),
567 rigid_world: ling_physics::rigid::PhysicsWorld::new(),
568 liquids: Vec::new(),
569 dialog: None,
570 dialog_colors: [0xE6F2FF, 0xFFD24A, 0x4AD2FF, 0x6CFF8C], frames: Vec::new(),
572 error_trace: None,
573 }
574 }
575
576 pub fn take_error_trace(&mut self) -> Vec<String> {
579 self.error_trace.take().unwrap_or_default()
580 }
581
582 fn framed<T, F>(&mut self, name: &str, body: F) -> Result<T, EvalErr>
585 where
586 F: FnOnce(&mut Self) -> Result<T, EvalErr>,
587 {
588 self.frames.push(name.to_string());
589 let result = body(self);
590 if matches!(result, Err(EvalErr::Runtime(_))) && self.error_trace.is_none() {
591 self.error_trace = Some(self.frames.clone());
592 }
593 self.frames.pop();
594 result
595 }
596
597 #[cfg(not(target_arch = "wasm32"))]
601 fn render_dialog(&mut self, x: f32, y: f32, w: f32, h: f32, font: i64, t: f32) {
602 let (runs, typing) = match &self.dialog {
603 Some(d) if !d.is_closed() => {
604 let runs: Vec<(String, usize, bool)> = d.visible_runs().into_iter()
605 .map(|r| (r.text, r.role.index(), r.newline_before)).collect();
606 (runs, d.is_typing())
607 }
608 _ => return,
609 };
610 let colors = self.dialog_colors;
611 let b = 12.0;
613 let corners: Vec<[f32; 2]> = vec![
614 [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],
615 ];
616 {
617 let mut gfx = self.gfx.borrow_mut();
618 let (bw, bh) = (gfx.width, gfx.height);
619 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, bw, bh, 0x0A1018, false, std::slice::from_ref(&corners));
620 for seg in corners.windows(2) {
621 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]);
622 }
623 }
624 let px = 22.0f32;
626 let pad = 20.0f32;
627 let line_h = px * 1.45;
628 let mut cx = x + pad;
629 let mut cy = y + pad;
630 let use_font = font >= 0 && (font as usize) < self.fonts.len();
631 for (text, role, nl) in &runs {
632 if *nl { cx = x + pad; cy += line_h; }
633 for word in text.split_inclusive(' ') {
634 let wpx = if use_font { self.fonts[font as usize].measure(word, px) }
635 else { ling_ui::holo::text_width(word, px * 0.6, px * 0.24) };
636 if cx + wpx > x + w - pad && cx > x + pad + 1.0 { cx = x + pad; cy += line_h; }
637 if cy + line_h > y + h { break; }
638 let col = colors[(*role).min(3)];
639 if use_font {
640 let glyphs = self.font_layout_2d_glyphs(font as usize, cx, cy, px, word);
641 let mut gfx = self.gfx.borrow_mut();
642 let (bw, bh, add) = (gfx.width, gfx.height, gfx.blend == 1);
643 for contours in &glyphs {
644 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, bw, bh, col, add, contours);
645 }
646 } else {
647 let segs = ling_ui::holo::text_lines(word, cx, cy, px * 0.6, px, px * 0.24);
648 let mut gfx = self.gfx.borrow_mut();
649 let (bw, bh) = (gfx.width, gfx.height);
650 for s in segs { draw_line(&mut gfx.buffer, bw, bh, col, s[0], s[1], s[2], s[3]); }
651 }
652 cx += wpx;
653 }
654 }
655 if !typing && (t * 3.0).sin() > 0.0 {
657 let ax = x + w - 26.0; let ay = y + h - 22.0;
658 let mut gfx = self.gfx.borrow_mut();
659 let (bw, bh) = (gfx.width, gfx.height);
660 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, bw, bh, 0x00D2FF, false,
661 std::slice::from_ref(&vec![[ax-7.0,ay],[ax+7.0,ay],[ax,ay+9.0],[ax-7.0,ay]]));
662 }
663 }
664
665 #[cfg(not(target_arch = "wasm32"))]
668 fn ensure_music(&mut self) -> bool {
669 if self.music.is_some() { return true; }
670 if self.music_init { return false; }
671 self.music_init = true;
672 match ling_music::MusicEngine::new() {
673 Ok(m) => { self.music = Some(m); true }
674 Err(e) => { eprintln!("music engine init failed (no music playback): {e}"); false }
675 }
676 }
677
678 #[cfg(not(target_arch = "wasm32"))]
682 fn font_layout_2d(&mut self, id: usize, x: f32, y: f32, px: f32, text: &str) -> Vec<Vec<[f32; 2]>> {
683 let mut out = Vec::new();
684 for g in self.font_layout_2d_glyphs(id, x, y, px, text) { out.extend(g); }
685 out
686 }
687
688 #[cfg(not(target_arch = "wasm32"))]
691 fn font_layout_2d_glyphs(&mut self, id: usize, x: f32, y: f32, px: f32, text: &str) -> Vec<Vec<Vec<[f32; 2]>>> {
692 let font = &mut self.fonts[id];
693 let asc = font.ascent();
694 let tol = 0.3 / px;
695 let mut pen = 0.0f32;
696 let mut glyphs = Vec::new();
697 for ch in text.chars() {
698 let go = font.glyph_outline(ch, tol);
699 let mut contours = Vec::with_capacity(go.polylines.len());
700 for pl in &go.polylines {
701 let mapped: Vec<[f32; 2]> = pl.iter()
702 .map(|p| [x + (pen + p[0]) * px, y + (asc - p[1]) * px])
703 .collect();
704 contours.push(mapped);
705 }
706 glyphs.push(contours);
707 pen += go.advance;
708 }
709 glyphs
710 }
711
712 pub fn run_program(&mut self, program: &Program) -> Result<(), String> {
713 for item in &program.items {
714 self.register_item("", item)?;
715 }
716 let entry = self.find_entry()
717 .ok_or("no entry point — need `bind start = do {...}` or `ผูก เริ่ม = ทำ {...}`")?;
718 let mut env = Env::new();
721 let non_do: Vec<_> = self.globals.iter()
722 .filter(|(_, e)| !matches!(e, Expr::Do(_)))
723 .map(|(k, e)| (k.clone(), e.clone()))
724 .collect();
725 let mut pending: Vec<(String, Expr)> = Vec::new();
726 for (k, expr) in &non_do {
727 let mut tmp = Env::new();
728 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
729 env.insert(k.clone(), v);
730 } else {
731 pending.push((k.clone(), expr.clone()));
732 }
733 }
734 for (k, expr) in &pending {
735 let mut tmp = env.clone();
736 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
737 env.insert(k.clone(), v);
738 }
739 }
740 self.global_seed = env.clone();
743 self.framed("start", |me| me.eval_expr(&entry, &mut env)).map(|_| ()).map_err(|e| match e {
744 EvalErr::Runtime(s) => s,
745 EvalErr::Return(_) => "unexpected top-level return".to_string(),
746 EvalErr::Break => "unexpected break at top level".to_string(),
747 })
748 }
749
750 fn register_item(&mut self, ns: &str, item: &Item) -> Result<(), String> {
751 match item {
752 Item::Bind(name, expr) => {
753 let key = if ns.is_empty() { name.clone() } else { format!("{ns}::{name}") };
754 self.globals.insert(key, expr.clone());
755 }
756 Item::Fn(def) => {
757 let key = if ns.is_empty() { def.name.clone() } else { format!("{ns}::{}", def.name) };
758 self.functions.insert(key, def.clone());
759 }
760 Item::Mod(name, body) => {
761 let child_ns = if ns.is_empty() { name.clone() } else { format!("{ns}::{name}") };
762 for child in body {
763 self.register_item(&child_ns, child)?;
764 }
765 }
766 Item::TypeAlias(_, _) => {}
767 Item::Use { path, alias } => {
768 self.load_module(path, alias.as_deref(), ns)?;
769 }
770 }
771 Ok(())
772 }
773
774 fn load_module(&mut self, path: &str, alias: Option<&str>, parent_ns: &str) -> Result<(), String> {
779 let base_dir = self.source_dir.clone().unwrap_or_else(|| std::path::PathBuf::from("."));
781 let raw = std::path::Path::new(path);
782 let candidates: Vec<std::path::PathBuf> = vec![
783 base_dir.join(format!("{}.ling", path)),
784 base_dir.join(format!("{}.灵", path)),
785 base_dir.join(format!("{}.령", path)),
786 base_dir.join(format!("{}.霊", path)),
787 base_dir.join(format!("{}.ลิง", path)),
788 base_dir.join(raw),
790 std::path::PathBuf::from(format!("{}.ling", path)),
791 std::path::PathBuf::from(path),
792 ];
793
794 let resolved = candidates.into_iter().find(|p| p.exists())
795 .ok_or_else(|| format!("use: cannot find module '{path}'"))?;
796
797 let canonical = resolved.canonicalize()
798 .unwrap_or_else(|_| resolved.clone())
799 .to_string_lossy()
800 .to_string();
801
802 if self.loaded_files.contains(&canonical) {
804 return Ok(());
805 }
806 self.loaded_files.insert(canonical.clone());
807
808 let source = std::fs::read_to_string(&resolved)
809 .map_err(|e| format!("use: failed to read '{path}': {e}"))?;
810
811 let prev_dir = self.source_dir.clone();
813 self.source_dir = resolved.parent().map(|p| p.to_path_buf());
814
815 let program = crate::parser::parse(&source)
816 .map_err(|e| format!("use: parse error in '{path}': {e}"))?;
817
818 let target_ns = match (parent_ns.is_empty(), alias) {
820 (_, Some(a)) if !parent_ns.is_empty() => format!("{parent_ns}::{a}"),
821 (_, Some(a)) => a.to_string(),
822 (false, None) => parent_ns.to_string(),
823 (true, None) => String::new(),
824 };
825
826 for item in &program.items {
827 self.register_item(&target_ns, item)?;
828 }
829
830 self.source_dir = prev_dir;
831 Ok(())
832 }
833
834 fn find_entry(&self) -> Option<Expr> {
835 for key in &[
837 "start", "main",
838 "启",
839 "เริ่ม", "시작",
841 "начать", "начало",
842 "inicio", "comenzar",
843 "début", "commencer",
844 "anfang", "starten",
845 "início",
846 "शुरू",
847 "ابدأ",
848 ] {
849 if let Some(e) = self.globals.get(*key) { return Some(e.clone()); }
850 }
851 self.globals.values().find(|e| matches!(e, Expr::Do(_))).cloned()
852 }
853
854 fn eval_expr(&mut self, expr: &Expr, env: &mut Env) -> EvalResult {
857 match expr {
858 Expr::Str(s) => Ok(Value::Str(s.clone())),
859 Expr::Number(n) => Ok(Value::Number(*n)),
860 Expr::Bool(b) => Ok(Value::Bool(*b)),
861 Expr::Unit => Ok(Value::Unit),
862 Expr::Array(elems) => {
863 let vs: Vec<_> = elems.iter()
864 .map(|e| self.eval_expr(e, env))
865 .collect::<Result<_,_>>()?;
866 Ok(Value::List(vs))
867 }
868
869 Expr::Ident(name) => self.lookup(name, env),
870
871 Expr::Path(segs) => {
872 if segs.len() == 1 { return self.lookup(&segs[0], env); }
873 Ok(Value::Str(segs.join("::")))
874 }
875
876 Expr::Ref(inner) => self.eval_expr(inner, env),
877 Expr::Await(inner) => self.eval_expr(inner, env),
878
879 Expr::Do(stmts) => {
880 let mut local = env.clone();
881 Ok(self.exec_block(stmts, &mut local)?.unwrap_or(Value::Unit))
882 }
883
884 Expr::BinOp(op, lhs, rhs) => {
885 let l = self.eval_expr(lhs, env)?;
886 let r = self.eval_expr(rhs, env)?;
887 self.apply_binop(op, l, r)
888 }
889
890 Expr::If { cond, then, elseifs, else_body } => {
891 let cond_val = self.eval_expr(cond, env)?;
892 if self.is_truthy(&cond_val) {
893 return Ok(self.exec_block(then, env)?.unwrap_or(Value::Unit));
894 }
895 for (ei_cond, ei_body) in elseifs {
896 let ei_cond_val = self.eval_expr(ei_cond, env)?;
897 if self.is_truthy(&ei_cond_val) {
898 return Ok(self.exec_block(ei_body, env)?.unwrap_or(Value::Unit));
899 }
900 }
901 if let Some(eb) = else_body {
902 return Ok(self.exec_block(eb, env)?.unwrap_or(Value::Unit));
903 }
904 Ok(Value::Unit)
905 }
906
907 Expr::While { cond, body } => {
908 loop {
912 let cv = self.eval_expr(cond, env)?;
913 if !self.is_truthy(&cv) { break; }
914 match self.exec_block(body, env) {
915 Ok(_) => {}
916 Err(EvalErr::Break) => break,
917 Err(e) => return Err(e),
918 }
919 }
920 Ok(Value::Unit)
921 }
922
923 Expr::For { var, iter, body } => {
924 let iter_val = self.eval_expr(iter, env)?;
925 let items = self.value_to_iter(iter_val)?;
926 for item in items {
927 let mut local = env.clone();
928 local.insert(var.clone(), item);
929 match self.exec_block(body, &mut local) {
930 Ok(_) => {}
931 Err(EvalErr::Break) => break,
932 Err(e) => return Err(e),
933 }
934 }
935 Ok(Value::Unit)
936 }
937
938 Expr::Match(subject, arms) => {
939 let subj = self.eval_expr(subject, env)?;
940 for arm in arms {
941 if let Some(bindings) = self.match_pattern(&arm.pattern, &subj) {
942 let mut local = env.clone();
943 local.extend(bindings);
944 return self.eval_expr(&arm.body, &mut local);
945 }
946 }
947 Ok(Value::Unit)
948 }
949
950 Expr::Range(lo, hi) => {
951 let lo_v = self.eval_expr(lo, env)?;
952 let hi_v = self.eval_expr(hi, env)?;
953 let lo_n = self.to_number(&lo_v)? as i64;
954 let hi_n = self.to_number(&hi_v)? as i64;
955 Ok(Value::List((lo_n..hi_n).map(|i| Value::Number(i as f64)).collect()))
956 }
957
958 Expr::Index(base, idx) => {
959 let b = self.eval_expr(base, env)?;
960 let i = self.eval_expr(idx, env)?;
961 let n = self.to_number(&i)? as usize;
962 match b {
963 Value::List(v) => v.get(n).cloned()
964 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
965 Value::Str(s) => s.chars().nth(n)
966 .map(|c| Value::Str(c.to_string()))
967 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
968 other => Err(EvalErr::from(format!("cannot index {:?}", other))),
969 }
970 }
971
972 Expr::Call(callee, args) => {
973 let arg_vals: Vec<Value> = args.iter()
974 .map(|a| self.eval_expr(a, env))
975 .collect::<Result<_,_>>()?;
976 match callee.as_ref() {
977 Expr::Ident(name) => self.call_named(name, arg_vals, env),
978 Expr::Path(segs) => self.call_named(&segs.join("::"), arg_vals, env),
979 _ => {
980 let v = self.eval_expr(callee, env)?;
981 self.call_value(v, arg_vals)
982 }
983 }
984 }
985
986 Expr::MethodCall { receiver, method, args } => {
987 let recv = self.eval_expr(receiver, env)?;
988 let arg_vals: Vec<Value> = args.iter()
989 .map(|a| self.eval_expr(a, env))
990 .collect::<Result<_,_>>()?;
991 self.call_method(recv, method, arg_vals)
992 }
993
994 Expr::Closure(params, body) => {
995 Ok(Value::Fn(params.clone(), vec![Stmt::Expr(*body.clone())], env.clone()))
996 }
997 }
998 }
999
1000 fn exec_block(&mut self, stmts: &[Stmt], env: &mut Env) -> Result<Option<Value>, EvalErr> {
1003 let mut last: Option<Value> = None;
1004 for stmt in stmts {
1005 match stmt {
1006 Stmt::Bind(name, expr) => {
1007 let v = self.eval_expr(expr, env)?;
1008 env.insert(name.clone(), v);
1009 last = None;
1010 }
1011 Stmt::Return(expr) => {
1012 let v = self.eval_expr(expr, env)?;
1013 return Err(EvalErr::Return(v));
1014 }
1015 Stmt::Expr(expr) => {
1016 last = Some(self.eval_expr(expr, env)?);
1017 }
1018 }
1019 }
1020 Ok(last)
1021 }
1022
1023 fn lookup(&self, name: &str, env: &Env) -> EvalResult {
1026 if let Some(v) = env.get(name) { return Ok(v.clone()); }
1027 if self.functions.contains_key(name) {
1028 let def = &self.functions[name];
1029 return Ok(Value::Fn(def.params.clone(), def.body.clone(), Env::new()));
1030 }
1031 match name {
1033 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" => return Ok(Value::Number(std::f64::consts::PI)),
1034 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" => return Ok(Value::Number(std::f64::consts::TAU)),
1035 _ => {}
1036 }
1037 Err(EvalErr::from(format!("undefined: '{name}'")))
1038 }
1039
1040 fn call_named(&mut self, name: &str, args: Vec<Value>, env: &Env) -> EvalResult {
1041 match name {
1042 "print" | "println" | "印" | "打印" | "印刷" | "พิมพ์" | "출력" | "вывести" | "imprimir" | "afficher" => {
1044 let s = args.iter().map(|v| v.to_string()).collect::<Vec<_>>().join("");
1045 println!("{s}");
1046 return Ok(Value::Unit);
1047 }
1048 "format" | "格式" | "フォーマット" | "서식" | "รูปแบบ" | "форматировать" | "formatear" | "formater" => {
1050 return Ok(Value::Str(self.builtin_format(&args)?));
1051 }
1052 "格式::拼接" | "format::join" => {
1054 match args.first() {
1055 Some(Value::List(items)) => {
1056 return Ok(Value::Str(items.iter().map(|v| v.to_string()).collect()));
1057 }
1058 _ => return Ok(Value::Str(self.builtin_format(&args)?)),
1059 }
1060 }
1061 "ok" | "好" | "良し" | "좋아" | "โอเค" => {
1063 let val = args.into_iter().next().unwrap_or(Value::Unit);
1064 return Ok(Value::Ok(Box::new(val)));
1065 }
1066 "bad" | "坏" | "err" | "悪い" | "나쁨" | "ผิด" => {
1067 let val = args.into_iter().next().unwrap_or(Value::Unit);
1068 return Ok(Value::Err(Box::new(val)));
1069 }
1070 "向量::从" | "Vec::from" => {
1072 if let Some(Value::List(v)) = args.first() {
1073 return Ok(Value::List(v.clone()));
1074 }
1075 return Ok(Value::List(args));
1076 }
1077 "向量::有容量" | "Vec::with_capacity" => return Ok(Value::List(Vec::new())),
1078 "计时::获取当前小时" | "Timer::hour" => return Ok(Value::Number(14.0)),
1080 "计时::现在" | "Timer::now" => return Ok(Value::Number(1000.0)),
1081 "sleep" | "หยุด" | "นอน" | "sleep_ms" | "睡眠" | "眠る" | "スリープ" | "잠자기" | "잠" | "流水::睡眠" | "Flow::sleep" => {
1083 if let Some(ms_val) = args.first() {
1084 if let Ok(ms) = self.to_number(ms_val) {
1085 std::thread::sleep(std::time::Duration::from_millis(ms as u64));
1086 }
1087 }
1088 return Ok(Value::Unit);
1089 }
1090 "流水::并行" | "Flow::parallel" => {
1092 if let Some(Value::Fn(params, body, mut cap)) = args.first().cloned() {
1093 let _ = params;
1094 match self.exec_block(&body, &mut cap) {
1095 Ok(Some(v)) => return Ok(v),
1096 Ok(None) => return Ok(Value::Unit),
1097 Err(EvalErr::Return(v)) => return Ok(v),
1098 Err(e) => return Err(e),
1099 }
1100 }
1101 return Ok(Value::Unit);
1102 }
1103
1104 "sin" | "ไซน์" | "正弦" | "サイン" | "사인" => {
1113 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sin()));
1114 }
1115 "cos" | "โคไซน์" | "余弦" | "コサイン" | "코사인" => {
1116 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cos()));
1117 }
1118
1119 "tanh" | "tanhf" | "双曲正切" | "双曲線正接" | "쌍곡탄젠트" => {
1122 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tanh()));
1123 }
1124
1125
1126 "tan" | "แทนเจนต์" | "正切" | "タンジェント" | "탄젠트" => {
1127 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tan()));
1128 }
1129 "asin" | "arcsin" | "反正弦" | "アークサイン" | "아크사인" | "อาร์กไซน์" => {
1130 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.asin()));
1131 }
1132 "acos" | "arccos" | "反余弦" | "アークコサイン" | "아크코사인" | "อาร์กโคไซน์" => {
1133 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.acos()));
1134 }
1135 "atan" | "arctan" | "反正切" | "アークタンジェント" | "아크탄젠트" | "อาร์กแทนเจนต์" => {
1136 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.atan()));
1137 }
1138 "atan2" | "arctan2" | "反正切2" | "アークタンジェント2" | "아크탄젠트2" => {
1139 let y = self.arg_num(&args, 0, 0.0)?;
1140 let x = self.arg_num(&args, 1, 1.0)?;
1141 return Ok(Value::Number(y.atan2(x)));
1142 }
1143
1144 "sqrt" | "รากที่สอง" | "平方根" | "根" | "제곱근" => {
1146 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sqrt()));
1147 }
1148 "cbrt" | "立方根" | "세제곱근" | "รากที่สาม" => {
1149 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cbrt()));
1150 }
1151 "pow" | "ยกกำลัง" | "幂" | "べき乗" | "거듭제곱" => {
1152 let base = self.arg_num(&args, 0, 0.0)?;
1153 let exp = self.arg_num(&args, 1, 1.0)?;
1154 return Ok(Value::Number(base.powf(exp)));
1155 }
1156 "exp" | "指数" | "指数関数" | "지수" => {
1157 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.exp()));
1158 }
1159 "hypot" | "斜边" | "斜辺" | "빗변" => {
1160 let x = self.arg_num(&args, 0, 0.0)?;
1161 let y = self.arg_num(&args, 1, 0.0)?;
1162 return Ok(Value::Number(x.hypot(y)));
1163 }
1164
1165 "ln" | "log" | "ลอการิทึม" | "对数" | "対数" | "로그" => {
1167 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.ln()));
1168 }
1169 "log2" | "对数2" | "対数2" | "로그2" => {
1170 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log2()));
1171 }
1172 "log10" | "对数10" | "対数10" | "로그10" => {
1173 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log10()));
1174 }
1175
1176 "abs" | "ค่าสัมบูรณ์" | "绝对值" | "绝对" | "絶対値" | "절댓값" | "절대값" => {
1178 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.abs()));
1179 }
1180 "floor" | "ปัดลง" | "向下取整" | "下整" | "床関数" | "내림" => {
1181 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.floor()));
1182 }
1183 "ceil" | "ปัดขึ้น" | "向上取整" | "上整" | "天井関数" | "올림" => {
1184 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.ceil()));
1185 }
1186 "round" | "ปัดเศษ" | "四舍五入" | "四舍" | "四捨五入" | "반올림" => {
1187 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.round()));
1188 }
1189 "trunc" | "int" | "ตัดทศนิยม" | "取整" | "整数化" | "整数" | "截整"
1190 | "정수화" | "정수" | "切り捨て" | "버림" => {
1191 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.trunc()));
1192 }
1193 "fract" | "小数部分" | "小数部" | "소수부" => {
1194 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.fract()));
1195 }
1196
1197 "min" | "ต่ำสุด" | "最小" | "최솟값" => {
1199 let a = self.arg_num(&args, 0, 0.0)?;
1200 let b = self.arg_num(&args, 1, 0.0)?;
1201 return Ok(Value::Number(a.min(b)));
1202 }
1203 "max" | "สูงสุด" | "最大" | "최댓값" => {
1204 let a = self.arg_num(&args, 0, 0.0)?;
1205 let b = self.arg_num(&args, 1, 0.0)?;
1206 return Ok(Value::Number(a.max(b)));
1207 }
1208 "clamp" | "จำกัด" | "截取" | "範囲制限" | "범위제한" => {
1209 let x = self.arg_num(&args, 0, 0.0)?;
1210 let lo = self.arg_num(&args, 1, 0.0)?;
1211 let hi = self.arg_num(&args, 2, 1.0)?;
1212 return Ok(Value::Number(x.clamp(lo, hi)));
1213 }
1214
1215 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" => return Ok(Value::Number(std::f64::consts::PI)),
1217 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" => return Ok(Value::Number(std::f64::consts::TAU)),
1218
1219 "vnoise" | "noise2" | "นอยส์2ดี" | "柏林噪声2D" | "バリューノイズ2D" | "값노이즈2D" => {
1225 let x = self.arg_num(&args, 0, 0.0)? as f32;
1226 let y = self.arg_num(&args, 1, 0.0)? as f32;
1227 let seed = self.arg_num(&args, 2, 0.0)? as u32;
1228 return Ok(Value::Number(tex_vnoise(x, y, seed) as f64));
1229 }
1230
1231 "fbm" | "นอยส์ออร์แกนิก" | "分形噪声" | "フラクタルノイズ" | "프랙탈노이즈" => {
1232 let x = self.arg_num(&args, 0, 0.0)? as f32;
1233 let y = self.arg_num(&args, 1, 0.0)? as f32;
1234 let octaves = self.arg_num(&args, 2, 4.0)? as u32;
1235 let seed = self.arg_num(&args, 3, 0.0)? as u32;
1236 return Ok(Value::Number(tex_fbm(x, y, octaves, seed) as f64));
1237 }
1238
1239 "perlin" | "perlin3" | "เพอร์ลิน3ดี" | "柏林噪声3D" | "パーリンノイズ3D" | "펄린노이즈3D" => {
1240 let x = self.arg_num(&args, 0, 0.0)? as f32;
1241 let y = self.arg_num(&args, 1, 0.0)? as f32;
1242 let z = self.arg_num(&args, 2, 0.0)? as f32;
1243 return Ok(Value::Number(perlin3(x, y, z) as f64));
1244 }
1245
1246 "lerp" | "ค่าระหว่าง" | "线性插值" | "線形補間" | "선형보간" => {
1248 let a = self.arg_num(&args, 0, 0.0)?;
1249 let b = self.arg_num(&args, 1, 1.0)?;
1250 let t = self.arg_num(&args, 2, 0.0)?;
1251 return Ok(Value::Number(a + (b - a) * t));
1252 }
1253
1254 "smoothstep" | "เปลี่ยนแบบนุ่ม" | "平滑步进" | "スムーズステップ" | "스무스스텝" => {
1255 let lo = self.arg_num(&args, 0, 0.0)?;
1256 let hi = self.arg_num(&args, 1, 1.0)?;
1257 let x = self.arg_num(&args, 2, 0.5)?;
1258 let t = ((x - lo) / (hi - lo)).clamp(0.0, 1.0);
1259 return Ok(Value::Number(t * t * (3.0 - 2.0 * t)));
1260 }
1261
1262 "rand" | "สุ่ม" | "随机" | "乱数" | "난수" => {
1263 let val = fast_rand_f64(&mut self.rand_state);
1264 return Ok(Value::Number(val));
1265 }
1266
1267 "sign" | "เครื่องหมาย" | "符号" | "符号関数" | "부호" => {
1268 let x = self.arg_num(&args, 0, 0.0)?;
1269 return Ok(Value::Number(x.signum()));
1270 }
1271
1272 "hsv_to_rgb" | "เอชเอสวีเป็นRGB" | "HSV转RGB" | "HSV変換RGB" | "HSV변환RGB" => {
1273 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;
1277 let x = c * (1.0 - (((h / 60.0) % 2.0) - 1.0).abs());
1278 let m = v - c;
1279 let (r1, g1, b1) = if h < 60.0 { (c, x, 0.0) }
1280 else if h < 120.0 { (x, c, 0.0) }
1281 else if h < 180.0 { (0.0, c, x) }
1282 else if h < 240.0 { (0.0, x, c) }
1283 else if h < 300.0 { (x, 0.0, c) }
1284 else { (c, 0.0, x) };
1285 let r = ((r1 + m) * 255.0).round();
1286 let g = ((g1 + m) * 255.0).round();
1287 let b = ((b1 + m) * 255.0).round();
1288 return Ok(Value::List(vec![Value::Number(r), Value::Number(g), Value::Number(b)]));
1289 }
1290
1291 "lerp_color" | "ไล่สี" | "颜色插值" | "色補間" | "색보간" => {
1292 let r1 = self.arg_num(&args, 0, 0.0)?;
1293 let g1 = self.arg_num(&args, 1, 0.0)?;
1294 let b1 = self.arg_num(&args, 2, 0.0)?;
1295 let r2 = self.arg_num(&args, 3, 255.0)?;
1296 let g2 = self.arg_num(&args, 4, 255.0)?;
1297 let b2 = self.arg_num(&args, 5, 255.0)?;
1298 let t = self.arg_num(&args, 6, 0.0)?;
1299 let r = r1 + (r2 - r1) * t;
1300 let g = g1 + (g2 - g1) * t;
1301 let b = b1 + (b2 - b1) * t;
1302 let c = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
1303 self.gfx.borrow_mut().color = c;
1304 return Ok(Value::Unit);
1305 }
1306
1307 "time_now" | "เวลาปัจจุบัน" | "当前时间" | "経過時間" | "현재시간" => {
1309 return Ok(Value::Number(self.start_time.elapsed().as_secs_f64()));
1310 }
1311
1312 "frame_count" | "เฟรม" | "帧数" | "フレーム数" | "프레임수" => {
1313 return Ok(Value::Number(self.frame_num as f64));
1314 }
1315
1316 "mic_open" | "เปิดไมค์" | "开麦克风" | "マイク開く" | "마이크열기" => {
1318 #[cfg(not(target_arch = "wasm32"))]
1319 {
1320 match ling_mic::MicInput::open(Default::default()) {
1321 Ok(mic) => {
1322 let _ = mic.start(|_samples: &[f32]| {}); self.mic = Some(mic);
1324 return Ok(Value::Unit);
1325 }
1326 Err(e) => return Err(EvalErr::from(format!("mic_open failed: {e}"))),
1327 }
1328 }
1329 #[cfg(target_arch = "wasm32")]
1330 return Ok(Value::Unit);
1331 }
1332
1333 "mic_rms" | "เสียงRMS" | "麦克风音量" | "マイクRMS" | "마이크RMS" => {
1334 #[cfg(not(target_arch = "wasm32"))]
1335 {
1336 let rms = self.mic.as_ref().map(|m: &ling_mic::MicInput| m.rms()).unwrap_or(0.0);
1337 return Ok(Value::Number(rms as f64));
1338 }
1339 #[cfg(target_arch = "wasm32")]
1340 return Ok(Value::Number(0.0));
1341 }
1342
1343 "mic_peak" | "เสียงพีค" | "麦克风峰值" | "マイクピーク" | "마이크피크" => {
1344 #[cfg(not(target_arch = "wasm32"))]
1345 {
1346 let peak = self.mic.as_ref().map(|m: &ling_mic::MicInput| m.peak()).unwrap_or(0.0);
1347 return Ok(Value::Number(peak as f64));
1348 }
1349 #[cfg(target_arch = "wasm32")]
1350 return Ok(Value::Number(0.0));
1351 }
1352
1353 "mic_fft" | "วิเคราะห์เสียงสด" | "实时频谱" | "リアルタイムFFT" | "실시간FFT" => {
1354 #[cfg(not(target_arch = "wasm32"))]
1355 {
1356 let n = self.arg_num(&args, 0, 8.0)? as usize;
1357 if let Some(mic) = self.mic.as_ref() {
1358 let samples = mic.latest_samples();
1359 self.fft.borrow_mut().push_samples(&samples);
1360 }
1361 let bands = self.fft.borrow().freq_bands(n);
1362 let result = bands.iter().map(|&v| Value::Number(v as f64)).collect();
1363 return Ok(Value::List(result));
1364 }
1365 #[cfg(target_arch = "wasm32")]
1366 return Ok(Value::List(vec![]));
1367 }
1368
1369 "set_blend" | "โหมดผสม" | "混合模式" | "ブレンドモード" | "블렌드모드" => {
1371 let mode = self.arg_num(&args, 0, 0.0)? as u8;
1372 self.gfx.borrow_mut().blend = mode;
1373 return Ok(Value::Unit);
1374 }
1375
1376 "draw_circle" | "วาดวงกลม" | "画圆" | "円描画" | "원그리기" => {
1378 let cx = self.arg_num(&args, 0, 0.0)? as i32;
1379 let cy = self.arg_num(&args, 1, 0.0)? as i32;
1380 let r = self.arg_num(&args, 2, 10.0)? as i32;
1381 let mut gfx = self.gfx.borrow_mut();
1382 let (w, h, color, blend) = (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
1383 draw_circle_outline(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
1384 return Ok(Value::Unit);
1385 }
1386
1387 "draw_filled_circle" | "draw_disc" | "วาดวงกลมทึบ" | "画实心圆" | "塗りつぶし円" | "원채우기" => {
1388 let cx = self.arg_num(&args, 0, 0.0)? as i32;
1389 let cy = self.arg_num(&args, 1, 0.0)? as i32;
1390 let r = self.arg_num(&args, 2, 10.0)? as i32;
1391 let mut gfx = self.gfx.borrow_mut();
1392 let (w, h, color, blend) = (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
1393 draw_circle_filled(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
1394 return Ok(Value::Unit);
1395 }
1396
1397 "เปิดหน้าต่าง" | "open_window" | "gfx_window" | "开窗" | "ウィンドウ開く" | "창열기" => {
1404 let w = self.arg_num(&args, 0, 800.0)? as usize;
1405 let h = self.arg_num(&args, 1, 600.0)? as usize;
1406 #[cfg(not(target_arch = "wasm32"))]
1407 {
1408 let title = args.get(2).map(|v| v.to_string()).unwrap_or_else(|| "Ling".into());
1409 let mut gfx = self.gfx.borrow_mut();
1410 let mut win = minifb::Window::new(
1411 &title, w, h,
1412 minifb::WindowOptions {
1413 resize: false,
1414 scale: minifb::Scale::X1,
1415 ..Default::default()
1416 },
1417 ).map_err(|e| EvalErr::from(format!("cannot open window: {e}")))?;
1418 #[allow(deprecated)]
1419 win.limit_update_rate(Some(std::time::Duration::from_millis(8)));
1420 gfx.buffer = vec![0u32; w * h];
1421 gfx.width = w;
1422 gfx.height = h;
1423 gfx.window = Some(win);
1424 gfx.sync_projection();
1425 hide_console_window();
1426 }
1427 #[cfg(target_arch = "wasm32")]
1428 {
1429 let mut gfx = self.gfx.borrow_mut();
1430 gfx.width = w;
1431 gfx.height = h;
1432 gfx.sync_projection();
1433 crate::gfx::webgl::resize(w as u32, h as u32);
1434 }
1435 return Ok(Value::Unit);
1436 }
1437
1438 "เติม" | "fill" | "gfx_fill" | "clear" | "填" | "塗り潰し" | "채우기" | "清" | "消去" | "지우기" => {
1440 let r = self.arg_num(&args, 0, 0.0)? as u32;
1441 let g = self.arg_num(&args, 1, 0.0)? as u32;
1442 let b = self.arg_num(&args, 2, 0.0)? as u32;
1443 #[cfg(not(target_arch = "wasm32"))]
1444 {
1445 let c = (r << 16) | (g << 8) | b;
1446 self.gfx.borrow_mut().buffer.fill(c);
1447 }
1448 #[cfg(target_arch = "wasm32")]
1449 {
1450 let mut gfx = self.gfx.borrow_mut();
1451 gfx.fill_r = r as f32 / 255.0;
1452 gfx.fill_g = g as f32 / 255.0;
1453 gfx.fill_b = b as f32 / 255.0;
1454 }
1455 return Ok(Value::Unit);
1456 }
1457
1458 "set_color_hsl" | "颜色HSL" | "色相" | "HSL色" | "HSL색설정" | "สีHSLวาด" => {
1461 let h = self.arg_num(&args, 0, 0.0)?;
1462 let s = self.arg_num(&args, 1, 70.0)?;
1463 let l = self.arg_num(&args, 2, 50.0)?;
1464 let hex = hsl_to_hex(h, s, l);
1465 let r = u32::from_str_radix(&hex[1..3], 16).unwrap_or(255);
1466 let g = u32::from_str_radix(&hex[3..5], 16).unwrap_or(255);
1467 let b = u32::from_str_radix(&hex[5..7], 16).unwrap_or(255);
1468 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
1469 return Ok(Value::Unit);
1470 }
1471
1472 "สีดินสอ" | "set_color" | "gfx_color" | "color" | "设色" | "色設定" | "색설정" => {
1474 let r = self.arg_num(&args, 0, 255.0)? as u32;
1475 let g = self.arg_num(&args, 1, 255.0)? as u32;
1476 let b = self.arg_num(&args, 2, 255.0)? as u32;
1477 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
1478 return Ok(Value::Unit);
1479 }
1480
1481 "วาดสามเหลี่ยม" | "draw_triangle" | "gfx_triangle" | "triangle" | "画三角" | "三角形描画" | "삼각형그리기" => {
1483 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
1484 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
1485 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
1486 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
1487 let x2 = self.arg_num(&args, 4, 0.0)? as f32;
1488 let y2 = self.arg_num(&args, 5, 0.0)? as f32;
1489 let mut gfx = self.gfx.borrow_mut();
1490 let color = gfx.color;
1491 #[cfg(not(target_arch = "wasm32"))]
1492 {
1493 let w = gfx.width;
1494 let h = gfx.height;
1495 fill_triangle(&mut gfx.buffer, w, h, color, x0, y0, x1, y1, x2, y2);
1496 }
1497 #[cfg(target_arch = "wasm32")]
1498 gfx.depth_queue.push_triangle(0.0, color, x0, y0, x1, y1, x2, y2);
1499 return Ok(Value::Unit);
1500 }
1501
1502 "วาดเส้น" | "draw_line" | "gfx_line" | "line" | "画线" | "線描く" | "선그리기" => {
1504 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
1505 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
1506 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
1507 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
1508 let mut gfx = self.gfx.borrow_mut();
1509 let color = gfx.color;
1510 #[cfg(not(target_arch = "wasm32"))]
1511 {
1512 let w = gfx.width;
1513 let h = gfx.height;
1514 draw_line(&mut gfx.buffer, w, h, color, x0, y0, x1, y1);
1515 }
1516 #[cfg(target_arch = "wasm32")]
1517 gfx.depth_queue.push_line(0.0, color, x0, y0, x1, y1);
1518 return Ok(Value::Unit);
1519 }
1520
1521 "วาดจุด" | "draw_pixel" | "gfx_pixel" | "pixel" | "画点" | "点描く" | "점그리기" => {
1523 let px = self.arg_num(&args, 0, 0.0)? as i32;
1524 let py = self.arg_num(&args, 1, 0.0)? as i32;
1525 #[cfg(not(target_arch = "wasm32"))]
1526 {
1527 let mut gfx = self.gfx.borrow_mut();
1528 let color = gfx.color;
1529 let w = gfx.width;
1530 let h = gfx.height;
1531 if px >= 0 && py >= 0 && (px as usize) < w && (py as usize) < h {
1532 gfx.buffer[py as usize * w + px as usize] = color;
1533 }
1534 }
1535 #[cfg(target_arch = "wasm32")]
1536 {
1537 let mut gfx = self.gfx.borrow_mut();
1539 let color = gfx.color;
1540 let x = px as f32; let y = py as f32;
1541 gfx.depth_queue.push_triangle(0.0, color, x, y, x+1.0, y, x+1.0, y+1.0);
1542 gfx.depth_queue.push_triangle(0.0, color, x, y, x+1.0, y+1.0, x, y+1.0);
1543 }
1544 return Ok(Value::Unit);
1545 }
1546
1547 "แสดงผล" | "present" | "gfx_present" | "show" | "显" | "呈现" | "表示" | "표시" => {
1549 #[cfg(not(target_arch = "wasm32"))]
1550 {
1551 {
1553 let mut gfx = self.gfx.borrow_mut();
1554 if !gfx.depth_queue.is_empty() {
1555 let w = gfx.width;
1556 let h = gfx.height;
1557 let queue = std::mem::take(&mut gfx.depth_queue);
1558 queue.flush(&mut gfx.buffer, w, h);
1559 }
1560 let buf = gfx.buffer.clone();
1561 let w = gfx.width;
1562 let h = gfx.height;
1563 if let Some(win) = gfx.window.as_mut() {
1564 win.update_with_buffer(&buf, w, h)
1565 .map_err(|e| EvalErr::from(format!("present error: {e}")))?;
1566 }
1567 }
1568 let mouse_pos = {
1570 let gfx = self.gfx.borrow();
1571 gfx.window.as_ref()
1572 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
1573 };
1574 let mut gfx = self.gfx.borrow_mut();
1575 if gfx.mouse_captured {
1576 let mut cur_my = gfx.last_my;
1577 if let Some((mx, my)) = mouse_pos {
1578 cur_my = my;
1579 if gfx.last_mx.is_nan() {
1580 gfx.mouse_dx = 0.0; gfx.mouse_dy = 0.0;
1581 } else {
1582 gfx.mouse_dx = mx - gfx.last_mx;
1583 gfx.mouse_dy = my - gfx.last_my;
1584 }
1585 } else {
1586 gfx.mouse_dx = 0.0; gfx.mouse_dy = 0.0;
1587 }
1588 #[cfg(windows)]
1591 unsafe {
1592 #[repr(C)]
1593 struct RECT { left: i32, top: i32, right: i32, bottom: i32 }
1594 #[repr(C)]
1595 struct POINT { x: i32, y: i32 }
1596 extern "system" {
1597 fn ClipCursor(lpRect: *const std::ffi::c_void) -> i32;
1598 fn GetForegroundWindow() -> isize;
1599 fn GetWindowRect(hwnd: isize, lpRect: *mut RECT) -> i32;
1600 fn SetCursorPos(x: i32, y: i32) -> i32;
1601 fn GetCursorPos(lpPoint: *mut POINT) -> i32;
1602 }
1603 let hwnd = GetForegroundWindow();
1604 let mut rect = RECT { left: 0, top: 0, right: 0, bottom: 0 };
1605 if GetWindowRect(hwnd, &mut rect) != 0 {
1606 ClipCursor(&rect as *const RECT as *const std::ffi::c_void);
1607 let mid_x = (rect.left + rect.right) / 2;
1608 let mut p = POINT { x: 0, y: 0 };
1609 GetCursorPos(&mut p);
1610 SetCursorPos(mid_x, p.y); }
1612 }
1613 gfx.last_mx = gfx.width as f32 / 2.0;
1615 gfx.last_my = cur_my;
1616 } else if let Some((mx, my)) = mouse_pos {
1617 if gfx.last_mx.is_nan() {
1618 gfx.mouse_dx = 0.0; gfx.mouse_dy = 0.0;
1619 } else {
1620 gfx.mouse_dx = mx - gfx.last_mx;
1621 gfx.mouse_dy = my - gfx.last_my;
1622 }
1623 gfx.last_mx = mx; gfx.last_my = my;
1624 } else {
1625 gfx.mouse_dx = 0.0; gfx.mouse_dy = 0.0;
1626 }
1627 }
1628 #[cfg(target_arch = "wasm32")]
1629 {
1630 let mut gfx = self.gfx.borrow_mut();
1631 let w = gfx.width;
1632 let h = gfx.height;
1633 let fr = gfx.fill_r;
1634 let fg = gfx.fill_g;
1635 let fb = gfx.fill_b;
1636 let queue = std::mem::take(&mut gfx.depth_queue);
1637 queue.flush_to_webgl(fr, fg, fb, w, h);
1638 }
1639 #[cfg(not(target_arch = "wasm32"))]
1641 {
1642 let (_, _, down) = self.mouse_now();
1643 self.mouse_was_down = down;
1644 }
1645 self.frame_num += 1;
1647 return Ok(Value::Unit);
1648 }
1649
1650 "เปิดหน้าต่างเต็มจอ" | "open_fullscreen" | "fullscreen" | "全屏" | "全画面" | "전체화면" => {
1652 #[cfg(target_arch = "wasm32")]
1655 let (default_w, default_h) = {
1656 let (cw, ch) = crate::gfx::webgl::canvas_size();
1657 (cw as f64, ch as f64)
1658 };
1659 #[cfg(all(not(target_arch = "wasm32"), windows))]
1661 let (default_w, default_h) = unsafe {
1662 extern "system" { fn GetSystemMetrics(nIndex: i32) -> i32; }
1663 (GetSystemMetrics(0) as f64, GetSystemMetrics(1) as f64)
1664 };
1665 #[cfg(all(not(target_arch = "wasm32"), not(windows)))]
1666 let (default_w, default_h) = native_screen_size();
1667
1668 let w = args.get(1).map(|v| self.to_number(v).unwrap_or(default_w) as usize).unwrap_or(default_w as usize);
1669 let h = args.get(2).map(|v| self.to_number(v).unwrap_or(default_h) as usize).unwrap_or(default_h as usize);
1670 #[cfg(not(target_arch = "wasm32"))]
1671 {
1672 let title = args.get(0).map(|v| v.to_string()).unwrap_or_else(|| "Ling".into());
1673 let mut gfx = self.gfx.borrow_mut();
1674 let mut win = minifb::Window::new(
1675 &title, w, h,
1676 minifb::WindowOptions {
1677 borderless: true,
1678 title: false,
1679 resize: false,
1680 topmost: true,
1681 scale: minifb::Scale::X1,
1682 ..Default::default()
1683 },
1684 ).map_err(|e| EvalErr::from(format!("cannot open fullscreen: {e}")))?;
1685 win.set_target_fps(monitor_info().2.max(30) as usize);
1688 #[cfg(windows)]
1690 let hwnd = win.get_window_handle() as isize;
1691 gfx.buffer = vec![0u32; w * h];
1692 gfx.width = w;
1693 gfx.height = h;
1694 gfx.window = Some(win);
1695 gfx.sync_projection();
1696 #[cfg(windows)]
1698 make_borderless_fullscreen(hwnd, w as i32, h as i32);
1699 hide_console_window();
1700 }
1701 #[cfg(target_arch = "wasm32")]
1702 {
1703 let mut gfx = self.gfx.borrow_mut();
1704 gfx.width = w;
1705 gfx.height = h;
1706 gfx.sync_projection();
1707 crate::gfx::webgl::resize(w as u32, h as u32);
1708 }
1709 return Ok(Value::Unit);
1710 }
1711
1712 "get_width" | "ความกว้าง" | "宽" | "幅取得" | "너비" => {
1714 return Ok(Value::Number(self.gfx.borrow().width as f64));
1715 }
1716 "get_height" | "ความสูง" | "高" | "高取得" | "높이" => {
1717 return Ok(Value::Number(self.gfx.borrow().height as f64));
1718 }
1719
1720 "monitor_width" | "screen_width" | "屏宽" | "画面幅" | "화면너비" | "ความกว้างจอ" => {
1723 return Ok(Value::Number(monitor_info().0 as f64));
1724 }
1725 "monitor_height" | "screen_height" | "屏高" | "画面高" | "화면높이" | "ความสูงจอ" => {
1727 return Ok(Value::Number(monitor_info().1 as f64));
1728 }
1729 "monitor_refresh" | "monitor_hz" | "monitor_fps" | "refresh_rate"
1731 | "刷新率" | "リフレッシュレート" | "주사율" | "อัตรารีเฟรช" => {
1732 return Ok(Value::Number(monitor_info().2 as f64));
1733 }
1734 "monitor_info" | "screen_info" | "屏幕信息" | "画面情報" | "화면정보" | "ข้อมูลจอ" => {
1736 let (w, h, hz) = monitor_info();
1737 return Ok(Value::List(vec![
1738 Value::Number(w as f64),
1739 Value::Number(h as f64),
1740 Value::Number(hz as f64),
1741 ]));
1742 }
1743 "set_fps" | "set_target_fps" | "target_fps" | "设帧率" | "フレームレート設定" | "프레임설정" | "ตั้งเฟรมเรต" => {
1745 let fps = self.arg_num(&args, 0, 60.0)?.max(1.0) as usize;
1746 #[cfg(not(target_arch = "wasm32"))]
1747 {
1748 let mut gfx = self.gfx.borrow_mut();
1749 if let Some(win) = gfx.window.as_mut() {
1750 win.set_target_fps(fps);
1751 }
1752 }
1753 return Ok(Value::Unit);
1754 }
1755
1756 "หน้าต่างเปิดอยู่" | "window_is_open" | "gfx_is_open" | "is_open" | "窗开" | "開いている" | "창열림" => {
1758 #[cfg(not(target_arch = "wasm32"))]
1759 {
1760 let gfx = self.gfx.borrow();
1761 let open = gfx.window.as_ref()
1762 .map(|w| w.is_open() && !w.is_key_down(minifb::Key::Escape))
1763 .unwrap_or(false);
1764 return Ok(Value::Bool(open));
1765 }
1766 #[cfg(target_arch = "wasm32")]
1767 return Ok(Value::Bool(true));
1768 }
1769
1770 "key_down" | "กดค้าง" | "按键" | "キー押す" | "키누름" => {
1772 #[cfg(not(target_arch = "wasm32"))]
1773 {
1774 let name = self.arg_str(&args, 0, "");
1775 let gfx = self.gfx.borrow();
1776 let down = gfx.window.as_ref()
1777 .and_then(|w| str_to_minifb_key(&name).map(|k| w.is_key_down(k)))
1778 .unwrap_or(false);
1779 return Ok(Value::Bool(down));
1780 }
1781 #[cfg(target_arch = "wasm32")]
1782 return Ok(Value::Bool(false));
1783 }
1784
1785 "key_pressed" | "กดปุ่ม" | "键按" | "キー押した" | "키눌림" => {
1787 #[cfg(not(target_arch = "wasm32"))]
1788 {
1789 let name = self.arg_str(&args, 0, "");
1790 let gfx = self.gfx.borrow();
1791 let pressed = gfx.window.as_ref()
1792 .and_then(|w| str_to_minifb_key(&name)
1793 .map(|k| w.is_key_pressed(k, minifb::KeyRepeat::No)))
1794 .unwrap_or(false);
1795 return Ok(Value::Bool(pressed));
1796 }
1797 #[cfg(target_arch = "wasm32")]
1798 return Ok(Value::Bool(false));
1799 }
1800
1801 "mouse_dx" | "เมาส์X" | "鼠ΔX" | "マウスΔX" | "마우스ΔX" => {
1803 #[cfg(not(target_arch = "wasm32"))]
1804 return Ok(Value::Number(self.gfx.borrow().mouse_dx as f64));
1805 #[cfg(target_arch = "wasm32")]
1806 return Ok(Value::Number(0.0));
1807 }
1808 #[cfg(not(target_arch = "wasm32"))]
1810 "mouse_scroll" | "ล้อเมาส์" | "滚轮" | "ホイール" | "스크롤" => {
1811 let gfx = self.gfx.borrow();
1812 let s = gfx.window.as_ref()
1813 .and_then(|w| w.get_scroll_wheel())
1814 .map(|(_, y)| y as f64).unwrap_or(0.0);
1815 return Ok(Value::Number(s));
1816 }
1817 "mouse_dy" | "เมาส์Y" | "鼠ΔY" | "マウスΔY" | "마우스ΔY" => {
1818 #[cfg(not(target_arch = "wasm32"))]
1819 return Ok(Value::Number(self.gfx.borrow().mouse_dy as f64));
1820 #[cfg(target_arch = "wasm32")]
1821 return Ok(Value::Number(0.0));
1822 }
1823
1824 "set_camera_pos" | "ตั้งตำแหน่งกล้อง" | "镜坐标" | "カメラ座標" | "카메라좌표" => {
1826 let x = self.arg_num(&args, 0, 0.0)? as f32;
1827 let y = self.arg_num(&args, 1, 0.0)? as f32;
1828 let z = self.arg_num(&args, 2, 0.0)? as f32;
1829 let mut gfx = self.gfx.borrow_mut();
1830 gfx.camera.tx = x; gfx.camera.ty = y; gfx.camera.tz = z;
1831 return Ok(Value::Unit);
1832 }
1833
1834 "move_camera" => {
1836 let dx = self.arg_num(&args, 0, 0.0)? as f32;
1837 let dy = self.arg_num(&args, 1, 0.0)? as f32;
1838 let dz = self.arg_num(&args, 2, 0.0)? as f32;
1839 let mut gfx = self.gfx.borrow_mut();
1840 gfx.camera.tx += dx; gfx.camera.ty += dy; gfx.camera.tz += dz;
1841 return Ok(Value::Unit);
1842 }
1843
1844 "set_zdist" | "ตั้งระยะห่าง" | "镜距" | "Z距離設定" | "Z거리설정" => {
1846 let d = self.arg_num(&args, 0, 5.0)? as f32;
1847 self.gfx.borrow_mut().camera.zdist = d;
1848 return Ok(Value::Unit);
1849 }
1850
1851 "capture_mouse" | "จับเมาส์" | "捕鼠" | "マウス捕捉" | "마우스잡기" => {
1853 #[cfg(not(target_arch = "wasm32"))]
1854 {
1855 let mut gfx = self.gfx.borrow_mut();
1856 gfx.mouse_captured = true;
1857 gfx.last_mx = f32::NAN;
1858 if let Some(win) = gfx.window.as_mut() {
1859 win.set_cursor_visibility(false);
1860 }
1861 }
1862 return Ok(Value::Unit);
1863 }
1864
1865 "release_mouse" => {
1867 #[cfg(not(target_arch = "wasm32"))]
1868 {
1869 let mut gfx = self.gfx.borrow_mut();
1870 gfx.mouse_captured = false;
1871 gfx.last_mx = f32::NAN;
1872 if let Some(win) = gfx.window.as_mut() {
1873 win.set_cursor_visibility(true);
1874 }
1875 #[cfg(windows)]
1876 unsafe {
1877 extern "system" { fn ClipCursor(lpRect: *const std::ffi::c_void) -> i32; }
1879 ClipCursor(std::ptr::null());
1880 }
1881 }
1882 return Ok(Value::Unit);
1883 }
1884
1885 "set_camera" | "ตั้งกล้อง" | "设镜" | "设置摄像机" | "カメラ設定" | "카메라설정" => {
1892 let cry = self.arg_num(&args, 0, 1.0)? as f32;
1893 let sry = self.arg_num(&args, 1, 0.0)? as f32;
1894 let crx = self.arg_num(&args, 2, 1.0)? as f32;
1895 let srx = self.arg_num(&args, 3, 0.0)? as f32;
1896 let mut gfx = self.gfx.borrow_mut();
1897 gfx.camera.cry = cry; gfx.camera.sry = sry;
1898 gfx.camera.crx = crx; gfx.camera.srx = srx;
1899 return Ok(Value::Unit);
1900 }
1901
1902 "set_projection" | "ตั้งโปรเจกชัน" | "投影" | "投影設定" | "투영설정" => {
1905 let cx = self.arg_num(&args, 0, 960.0)? as f32;
1906 let cy = self.arg_num(&args, 1, 540.0)? as f32;
1907 let focal = self.arg_num(&args, 2, 1080.0)? as f32;
1908 let zdist = self.arg_num(&args, 3, 5.0)? as f32;
1909 let mut gfx = self.gfx.borrow_mut();
1910 gfx.camera.cx = cx;
1911 gfx.camera.cy = cy;
1912 gfx.camera.focal = focal;
1913 gfx.camera.zdist = zdist;
1914 return Ok(Value::Unit);
1915 }
1916
1917 "add_light" | "เพิ่มแสง" | "加灯" | "ライト追加" | "조명추가" => {
1921 let x = self.arg_num(&args, 0, 0.0)? as f32;
1922 let y = self.arg_num(&args, 1, -3.0)? as f32;
1923 let z = self.arg_num(&args, 2, 3.0)? as f32;
1924 let mut r = self.arg_num(&args, 3, 1.0)? as f32;
1925 let mut g = self.arg_num(&args, 4, 1.0)? as f32;
1926 let mut b = self.arg_num(&args, 5, 1.0)? as f32;
1927 if r > 1.5 || g > 1.5 || b > 1.5 { r/=255.0; g/=255.0; b/=255.0; }
1930 let intensity = self.arg_num(&args, 6, 1.0)? as f32;
1931 let radius = self.arg_num(&args, 7, 0.0)? as f32;
1932 self.gfx.borrow_mut().lights.push(Light { x, y, z, r, g, b, intensity, radius });
1933 return Ok(Value::Unit);
1934 }
1935
1936 "clear_lights" | "ล้างแสง" | "清灯" | "ライト消去" | "조명초기화" => {
1938 self.gfx.borrow_mut().lights.clear();
1939 return Ok(Value::Unit);
1940 }
1941
1942 "set_ambient" | "ตั้งแสงรอบข้าง" | "环境光" | "環境光設定" | "환경광설정" => {
1944 let v = self.arg_num(&args, 0, 0.15)? as f32;
1945 self.gfx.borrow_mut().ambient = v;
1946 return Ok(Value::Unit);
1947 }
1948
1949 "set_fog" | "ตั้งหมอก" | "雾" | "霧設定" | "안개설정" => {
1952 let r = self.arg_num(&args, 0, 0.0)?.clamp(0.0, 255.0) as u32;
1953 let g = self.arg_num(&args, 1, 0.0)?.clamp(0.0, 255.0) as u32;
1954 let b = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 255.0) as u32;
1955 let start = self.arg_num(&args, 3, 0.0)? as f32;
1956 let end = self.arg_num(&args, 4, 0.0)? as f32;
1957 let mut gfx = self.gfx.borrow_mut();
1958 gfx.fog_color = (r << 16) | (g << 8) | b;
1959 gfx.fog_start = start;
1960 gfx.fog_end = end;
1961 return Ok(Value::Unit);
1962 }
1963
1964 "วาดสามเหลี่ยม3มิติ" | "draw_triangle_3d" | "triangle3d" => {
1968 let ax = self.arg_num(&args, 0, 0.0)? as f32;
1969 let ay = self.arg_num(&args, 1, 0.0)? as f32;
1970 let az = self.arg_num(&args, 2, 0.0)? as f32;
1971 let bx = self.arg_num(&args, 3, 0.0)? as f32;
1972 let by = self.arg_num(&args, 4, 0.0)? as f32;
1973 let bz = self.arg_num(&args, 5, 0.0)? as f32;
1974 let cx = self.arg_num(&args, 6, 0.0)? as f32;
1975 let cy = self.arg_num(&args, 7, 0.0)? as f32;
1976 let cz = self.arg_num(&args, 8, 0.0)? as f32;
1977
1978 let mut gfx = self.gfx.borrow_mut();
1979
1980 let ux = bx-ax; let uy = by-ay; let uz = bz-az;
1982 let vx = cx-ax; let vy = cy-ay; let vz = cz-az;
1983 let normal = [
1984 uy*vz - uz*vy,
1985 uz*vx - ux*vz,
1986 ux*vy - uy*vx,
1987 ];
1988 let centroid = [
1990 (ax+bx+cx)/3.0,
1991 (ay+by+cy)/3.0,
1992 (az+bz+cz)/3.0,
1993 ];
1994
1995 let lit_color = crate::gfx::light::compute_lit_color(
1997 gfx.color, normal, centroid, &gfx.lights, gfx.ambient,
1998 );
1999
2000 let near = -gfx.camera.zdist + 0.05;
2007 let vw = [
2008 (ax, ay, az, gfx.camera.depth(ax, ay, az)),
2009 (bx, by, bz, gfx.camera.depth(bx, by, bz)),
2010 (cx, cy, cz, gfx.camera.depth(cx, cy, cz)),
2011 ];
2012 let mut poly: Vec<(f32, f32, f32)> = Vec::with_capacity(4);
2013 let mut ei = 0;
2014 while ei < 3 {
2015 let a = vw[ei];
2016 let b = vw[(ei + 1) % 3];
2017 let ain = a.3 > near;
2018 let bin = b.3 > near;
2019 if ain { poly.push((a.0, a.1, a.2)); }
2020 if ain != bin {
2021 let tt = (near - a.3) / (b.3 - a.3);
2022 poly.push((a.0 + (b.0 - a.0) * tt, a.1 + (b.1 - a.1) * tt, a.2 + (b.2 - a.2) * tt));
2023 }
2024 ei += 1;
2025 }
2026 if poly.len() < 3 { return Ok(Value::Unit); }
2027 let proj: Vec<(f32, f32, f32)> =
2029 poly.iter().map(|p| gfx.camera.project(p.0, p.1, p.2)).collect();
2030 let mut dsum = 0.0f32;
2031 for p in &proj { dsum += p.2; }
2032 let depth = dsum / proj.len() as f32;
2033 let lit_color = gfx.fog_apply(lit_color, depth);
2034 let mut fk = 1;
2035 while fk + 1 < proj.len() {
2036 gfx.depth_queue.push_triangle(
2037 depth, lit_color,
2038 proj[0].0, proj[0].1, proj[fk].0, proj[fk].1, proj[fk + 1].0, proj[fk + 1].1,
2039 );
2040 fk += 1;
2041 }
2042 return Ok(Value::Unit);
2043 }
2044
2045 "วาดเส้น3มิติ" | "draw_line_3d" | "line3d" | "画3D线" | "3D線描く" | "3D선그리기" => {
2049 let ax = self.arg_num(&args, 0, 0.0)? as f32;
2050 let ay = self.arg_num(&args, 1, 0.0)? as f32;
2051 let az = self.arg_num(&args, 2, 0.0)? as f32;
2052 let bx = self.arg_num(&args, 3, 0.0)? as f32;
2053 let by = self.arg_num(&args, 4, 0.0)? as f32;
2054 let bz = self.arg_num(&args, 5, 0.0)? as f32;
2055
2056 let mut gfx = self.gfx.borrow_mut();
2057 let color = gfx.color;
2058 let near = -gfx.camera.zdist + 0.05;
2060 let mut lax = ax; let mut lay = ay; let mut laz = az;
2061 let mut lbx = bx; let mut lby = by; let mut lbz = bz;
2062 let da_raw = gfx.camera.depth(lax, lay, laz);
2063 let db_raw = gfx.camera.depth(lbx, lby, lbz);
2064 if da_raw <= near && db_raw <= near {
2065 return Ok(Value::Unit);
2066 }
2067 if da_raw <= near {
2068 let t = (near - da_raw) / (db_raw - da_raw);
2069 lax += t * (lbx - lax);
2070 lay += t * (lby - lay);
2071 laz += t * (lbz - laz);
2072 } else if db_raw <= near {
2073 let t = (near - da_raw) / (db_raw - da_raw);
2074 lbx = lax + t * (lbx - lax);
2075 lby = lay + t * (lby - lay);
2076 lbz = laz + t * (lbz - laz);
2077 }
2078 let (sax, say, da) = gfx.camera.project(lax, lay, laz);
2079 let (sbx, sby, db) = gfx.camera.project(lbx, lby, lbz);
2080 let depth = (da + db) / 2.0;
2081 let color = gfx.fog_apply(color, depth);
2082 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
2083 return Ok(Value::Unit);
2084 }
2085
2086 #[cfg(not(target_arch = "wasm32"))]
2096 "orb_shell" | "球壳" | "オーブ殻" | "오브껍질" | "เปลือกทรงกลม" => {
2097 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;
2098 let radius=self.arg_num(&args,3,1.0)? as f32;
2099 let ry=self.arg_num(&args,4,0.)? as f32; let rx=self.arg_num(&args,5,0.)? as f32;
2100 let density=(self.arg_num(&args,6,10.)? as i32).clamp(1, 48);
2101 let tr=(self.arg_num(&args,7,230.)? as f32).clamp(0.,255.);
2102 let tg=(self.arg_num(&args,8,230.)? as f32).clamp(0.,255.);
2103 let tb=(self.arg_num(&args,9,235.)? as f32).clamp(0.,255.);
2104 let (cyr, syr) = (ry.cos(), ry.sin());
2105 let (cxr, sxr) = (rx.cos(), rx.sin());
2106 let tau = std::f32::consts::TAU;
2107 let pi = std::f32::consts::PI;
2108 let turns = 6.0_f32; let nseg = 96; let inv_r = if radius.abs() > 1e-5 { 1.0 / radius } else { 0.0 };
2111 let pt = |u: f32, theta0: f32, dir: f32| -> ([f32;3], f32) {
2114 let phi = pi * u; let th = dir * turns * tau * u + theta0;
2116 let (mut x, y, mut z) = (phi.sin()*th.cos()*radius, phi.cos()*radius, phi.sin()*th.sin()*radius);
2117 let x1 = x*cyr + z*syr; let z1 = -x*syr + z*cyr;
2119 x = x1; z = z1;
2120 let y2 = y*cxr - z*sxr; let z2 = y*sxr + z*cxr;
2122 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.0, 1.0);
2124 ([cx + x, cy + y2, cz + z2], facing)
2125 };
2126 let mut gfx = self.gfx.borrow_mut();
2127 let near = -gfx.camera.zdist + 0.05;
2128 let seg = |gfx: &mut crate::gfx::GfxState, a: [f32;3], b: [f32;3], lum: f32| {
2130 let (mut lax,mut lay,mut laz)=(a[0],a[1],a[2]);
2131 let (mut lbx,mut lby,mut lbz)=(b[0],b[1],b[2]);
2132 let da=gfx.camera.depth(lax,lay,laz); let db=gfx.camera.depth(lbx,lby,lbz);
2133 if da<=near && db<=near { return; }
2134 if da<=near { let t=(near-da)/(db-da); lax+=t*(lbx-lax); lay+=t*(lby-lay); laz+=t*(lbz-laz); }
2135 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); }
2136 let (sax,say,da2)=gfx.camera.project(lax,lay,laz);
2137 let (sbx,sby,db2)=gfx.camera.project(lbx,lby,lbz);
2138 let l = (0.12 + 0.88 * lum).clamp(0.0, 1.0);
2140 let cr=(tr*l) as u32; let cg=(tg*l) as u32; let cb=(tb*l) as u32;
2141 let color=(cr<<16)|(cg<<8)|cb;
2142 gfx.depth_queue.push_line((da2+db2)*0.5, color, sax,say, sbx,sby);
2143 };
2144 for &dir in &[1.0_f32, -1.0_f32] {
2146 for s in 0..density {
2147 let theta0 = s as f32 * tau / density as f32;
2148 let mut prev = pt(0.0, theta0, dir);
2149 for k in 1..=nseg {
2150 let cur = pt(k as f32 / nseg as f32, theta0, dir);
2151 seg(&mut gfx, prev.0, cur.0, (prev.1 + cur.1) * 0.5);
2152 prev = cur;
2153 }
2154 }
2155 }
2156 return Ok(Value::Unit);
2157 }
2158
2159 #[cfg(not(target_arch = "wasm32"))]
2167 "orb_particles" | "球内粒子" | "オーブ粒子" | "오브입자" | "อนุภาคทรงกลม" => {
2168 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;
2169 let radius=self.arg_num(&args,3,1.0)? as f32;
2170 let count=(self.arg_num(&args,4,160.)? as i32).clamp(1, 4000);
2171 let t=self.arg_num(&args,5,0.)? as f32;
2172 let tr=(self.arg_num(&args,6,255.)? as f32).clamp(0.,255.);
2173 let tg=(self.arg_num(&args,7,255.)? as f32).clamp(0.,255.);
2174 let tb=(self.arg_num(&args,8,255.)? as f32).clamp(0.,255.);
2175 let inv_r = if radius.abs() > 1e-5 { 1.0/radius } else { 0.0 };
2176 let h = |mut x: u32| -> f32 {
2178 x = x.wrapping_mul(747796405).wrapping_add(2891336453);
2179 x = ((x >> ((x >> 28).wrapping_add(4))) ^ x).wrapping_mul(277803737);
2180 (((x >> 22) ^ x) & 0xFFFFFF) as f32 / 16_777_216.0
2181 };
2182 let tau = std::f32::consts::TAU;
2183 let (cyr, syr) = ((t*0.5).cos(), (t*0.5).sin());
2185 let (cxr, sxr) = ((t*0.23).cos(), (t*0.23).sin());
2186 let mut gfx = self.gfx.borrow_mut();
2187 let near = -gfx.camera.zdist + 0.05;
2188 let (sw, sh) = (gfx.width as i32, gfx.height as i32);
2189 for i in 0..count {
2190 let i = i as u32;
2191 let u = h(i.wrapping_mul(3) + 1);
2193 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());
2197 let x1 = x*cyr + z*syr; let z1 = -x*syr + z*cyr; x = x1; z = z1;
2199 let y2 = y*cxr - z*sxr; let z2 = y*sxr + z*cxr;
2200 let (wx, wy, wz) = (cx + x, cy + y2, cz + z2);
2201 if gfx.camera.depth(wx, wy, wz) <= near { continue; }
2202 let (sx, sy, dep) = gfx.camera.project(wx, wy, wz);
2203 let sxi = sx as i32; let syi = sy as i32;
2204 if sxi < 0 || syi < 0 || sxi >= sw || syi >= sh { continue; }
2205 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.15, 1.0);
2207 let l = facing;
2208 let cr=(tr*l) as u32; let cg=(tg*l) as u32; let cb=(tb*l) as u32;
2209 let color=(cr<<16)|(cg<<8)|cb;
2210 let len = if facing > 0.7 { 1.0 } else { 0.0 };
2212 gfx.depth_queue.push_line(dep, color, sx, sy, sx + len, sy);
2213 }
2214 return Ok(Value::Unit);
2215 }
2216
2217 "project_3d" | "投影3D" | "3D投影" | "3D투영" | "ฉาย3มิติ" => {
2221 let x = self.arg_num(&args,0,0.0)? as f32;
2222 let y = self.arg_num(&args,1,0.0)? as f32;
2223 let z = self.arg_num(&args,2,0.0)? as f32;
2224 let gfx = self.gfx.borrow();
2225 let near = -gfx.camera.zdist + 0.05;
2226 let d = gfx.camera.depth(x, y, z);
2227 if d <= near {
2228 return Ok(Value::List(vec![Value::Number(-99999.0), Value::Number(-99999.0), Value::Number(d as f64)]));
2229 }
2230 let (sx, sy, depth) = gfx.camera.project(x, y, z);
2231 return Ok(Value::List(vec![Value::Number(sx as f64), Value::Number(sy as f64), Value::Number(depth as f64)]));
2232 }
2233 #[cfg(not(target_arch = "wasm32"))]
2236 "draw_poly" | "填充多边形" | "ポリゴン塗り" | "다각형채우기" | "เติมรูปหลายเหลี่ยม" => {
2237 let mut pts: Vec<[f32; 2]> = Vec::new();
2238 if let Some(Value::List(v)) = args.first() {
2239 let mut i = 0;
2240 while i + 1 < v.len() {
2241 let x = self.to_number(&v[i]).unwrap_or(0.0) as f32;
2242 let y = self.to_number(&v[i + 1]).unwrap_or(0.0) as f32;
2243 pts.push([x, y]);
2244 i += 2;
2245 }
2246 }
2247 if pts.len() >= 3 {
2248 if pts[0] != pts[pts.len() - 1] { let p0 = pts[0]; pts.push(p0); } let mut gfx = self.gfx.borrow_mut();
2250 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
2251 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, w, h, color, add, std::slice::from_ref(&pts));
2252 }
2253 return Ok(Value::Unit);
2254 }
2255
2256 "vtex_grid" | "ลายตาราง" | "纹格" | "格子模様" | "격자무늬" => {
2265 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;
2266 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;
2267 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;
2268 let cols=self.arg_num(&args,9,10.)?as usize; let rows=self.arg_num(&args,10,10.)?as usize;
2269 let cw=self.arg_num(&args,11,1.)?as f32; let ch=self.arg_num(&args,12,1.)?as f32;
2270 let fr=self.arg_num(&args,13,0.)?as f32; let hue=self.arg_num(&args,14,0.)?as f32;
2271 let mut gfx = self.gfx.borrow_mut();
2272 let cam = gfx.camera.clone();
2273 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);
2274 return Ok(Value::Unit);
2275 }
2276
2277 "vtex_rings" | "ลายวงซ้อน" | "纹环" | "同心円" | "동심원" => {
2279 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;
2280 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;
2281 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;
2282 let nr=self.arg_num(&args,9,6.)?as usize; let ns=self.arg_num(&args,10,6.)?as usize;
2283 let mr=self.arg_num(&args,11,3.)?as f32; let tw=self.arg_num(&args,12,0.)?as f32;
2284 let fr=self.arg_num(&args,13,0.)?as f32; let hue=self.arg_num(&args,14,0.)?as f32;
2285 let mut gfx = self.gfx.borrow_mut();
2286 let cam = gfx.camera.clone();
2287 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);
2288 return Ok(Value::Unit);
2289 }
2290
2291 "vtex_star" | "ลายดาว" | "纹星" | "星模様" | "별무늬" => {
2293 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;
2294 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;
2295 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;
2296 let np=self.arg_num(&args,9,6.)?as usize;
2297 let ro=self.arg_num(&args,10,2.)?as f32; let ri=self.arg_num(&args,11,1.)?as f32;
2298 let rs=self.arg_num(&args,12,0.01)?as f32;
2299 let fr=self.arg_num(&args,13,0.)?as f32; let hue=self.arg_num(&args,14,0.)?as f32;
2300 let mut gfx = self.gfx.borrow_mut();
2301 let cam = gfx.camera.clone();
2302 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);
2303 return Ok(Value::Unit);
2304 }
2305
2306 "vtex_spiral" | "ลายเกลียว" | "纹螺" | "螺旋" | "나선" => {
2308 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;
2309 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;
2310 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;
2311 let nt=self.arg_num(&args,9,3.)?as f32; let mr=self.arg_num(&args,10,3.)?as f32;
2312 let st=self.arg_num(&args,11,120.)?as usize;
2313 let fr=self.arg_num(&args,12,0.)?as f32; let hue=self.arg_num(&args,13,0.)?as f32;
2314 let mut gfx = self.gfx.borrow_mut();
2315 let cam = gfx.camera.clone();
2316 crate::gfx::vtex::draw_spiral(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, nt,mr,st, fr,hue);
2317 return Ok(Value::Unit);
2318 }
2319
2320 "vtex_flower" | "ลายดอก" | "纹花" | "花模様" | "꽃무늬" => {
2322 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;
2323 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;
2324 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;
2325 let r=self.arg_num(&args,9,1.)?as f32; let ns=self.arg_num(&args,10,24.)?as usize;
2326 let fr=self.arg_num(&args,11,0.)?as f32; let hue=self.arg_num(&args,12,0.)?as f32;
2327 let mut gfx = self.gfx.borrow_mut();
2328 let cam = gfx.camera.clone();
2329 crate::gfx::vtex::draw_flower(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, r,ns, fr,hue);
2330 return Ok(Value::Unit);
2331 }
2332
2333 "vtex_letter_rain" | "ลายอักษรไหล" | "纹字雨" | "文字雨" | "글자비" => {
2335 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;
2336 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;
2337 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;
2338 let nc=self.arg_num(&args,9,16.)?as usize; let nv=self.arg_num(&args,10,14.)?as usize;
2339 let cw=self.arg_num(&args,11,0.65)?as f32; let rh=self.arg_num(&args,12,0.60)?as f32;
2340 let sp=self.arg_num(&args,13,0.025)?as f32;
2341 let fr=self.arg_num(&args,14,0.)?as f32; let hue=self.arg_num(&args,15,0.)?as f32;
2342 let mut gfx = self.gfx.borrow_mut();
2343 let cam = gfx.camera.clone();
2344 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);
2345 return Ok(Value::Unit);
2346 }
2347
2348 "vtex_hyperbolic_uv" | "ลายไฮเพอร์โบลิก" | "纹曲面" | "双曲線" | "쌍곡선" => {
2350 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;
2351 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;
2352 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;
2353 let mr=self.arg_num(&args,9,5.)?as f32;
2354 let nc=self.arg_num(&args,10,12.)?as usize; let nr=self.arg_num(&args,11,18.)?as usize;
2355 let fr=self.arg_num(&args,12,0.)?as f32; let hue=self.arg_num(&args,13,0.)?as f32;
2356 let mut gfx = self.gfx.borrow_mut();
2357 let cam = gfx.camera.clone();
2358 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);
2359 return Ok(Value::Unit);
2360 }
2361
2362 "vtex_halftone" | "ลายจุด" | "纹半调" | "網点模様" | "망점" => {
2364 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;
2365 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;
2366 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;
2367 let cols=self.arg_num(&args,9,16.)?as usize; let rows=self.arg_num(&args,10,12.)?as usize;
2368 let cw=self.arg_num(&args,11,0.5)?as f32; let ch=self.arg_num(&args,12,0.5)?as f32;
2369 let dens=self.arg_num(&args,13,0.4)?as f32;
2370 let fr=self.arg_num(&args,14,0.)?as f32; let hue=self.arg_num(&args,15,0.)?as f32;
2371 let mut gfx = self.gfx.borrow_mut();
2372 let cam = gfx.camera.clone();
2373 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);
2374 return Ok(Value::Unit);
2375 }
2376
2377 "vtex_tessellated" | "ลายตาข่าย" | "纹镶嵌" | "網目模様" | "격자망" => {
2379 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;
2380 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;
2381 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;
2382 let cols=self.arg_num(&args,9,14.)?as usize; let rows=self.arg_num(&args,10,10.)?as usize;
2383 let cell=self.arg_num(&args,11,0.6)?as f32;
2384 let amp=self.arg_num(&args,12,0.25)?as f32; let freq=self.arg_num(&args,13,4.)?as f32;
2385 let fr=self.arg_num(&args,14,0.)?as f32; let hue=self.arg_num(&args,15,0.)?as f32;
2386 let mut gfx = self.gfx.borrow_mut();
2387 let cam = gfx.camera.clone();
2388 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);
2389 return Ok(Value::Unit);
2390 }
2391
2392 "vtex_lotus" | "ลายดอกบัว" | "纹莲" | "蓮模様" | "연꽃무늬" => {
2394 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;
2395 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;
2396 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;
2397 let ri=self.arg_num(&args,9,1.)?as f32; let ro=self.arg_num(&args,10,2.)?as f32;
2398 let np=self.arg_num(&args,11,12.)?as usize;
2399 let fr=self.arg_num(&args,12,0.)?as f32; let hue=self.arg_num(&args,13,0.)?as f32;
2400 let mut gfx = self.gfx.borrow_mut();
2401 let cam = gfx.camera.clone();
2402 crate::gfx::vtex::draw_lotus(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, ri,ro,np, fr,hue);
2403 return Ok(Value::Unit);
2404 }
2405
2406 "vtex_chakra" | "ลายจักร" | "纹轮" | "輪模様" | "바퀴무늬" => {
2408 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;
2409 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;
2410 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;
2411 let r=self.arg_num(&args,9,2.)?as f32; let ns=self.arg_num(&args,10,8.)?as usize;
2412 let fr=self.arg_num(&args,11,0.)?as f32; let hue=self.arg_num(&args,12,0.)?as f32;
2413 let mut gfx = self.gfx.borrow_mut();
2414 let cam = gfx.camera.clone();
2415 crate::gfx::vtex::draw_chakra(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, r,ns, fr,hue);
2416 return Ok(Value::Unit);
2417 }
2418
2419 "vtex_yantra" | "ลายยันต์" | "纹咒" | "護符模様" | "부적무늬" => {
2421 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;
2422 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;
2423 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;
2424 let nl=self.arg_num(&args,9,4.)?as usize; let mr=self.arg_num(&args,10,3.)?as f32;
2425 let fr=self.arg_num(&args,11,0.)?as f32; let hue=self.arg_num(&args,12,0.)?as f32;
2426 let mut gfx = self.gfx.borrow_mut();
2427 let cam = gfx.camera.clone();
2428 crate::gfx::vtex::draw_yantra(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, nl,mr, fr,hue);
2429 return Ok(Value::Unit);
2430 }
2431
2432 "vtex_spiked_cog" | "ฟันเฟืองหนาม" | "纹棘轮" | "歯車模様" | "톱니바퀴" => {
2434 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;
2435 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;
2436 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;
2437 let nt=self.arg_num(&args,9,12.)?as usize; let rb=self.arg_num(&args,10,1.)?as f32;
2438 let rs=self.arg_num(&args,11,1.3)?as f32; let rh=self.arg_num(&args,12,0.2)?as f32;
2439 let ns=self.arg_num(&args,13,6.)?as usize;
2440 let fr=self.arg_num(&args,14,0.)?as f32; let hue=self.arg_num(&args,15,0.)?as f32;
2441 let mut gfx = self.gfx.borrow_mut();
2442 let cam = gfx.camera.clone();
2443 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);
2444 return Ok(Value::Unit);
2445 }
2446
2447 "vtex_torii" | "ประตูโทริอิ" | "纹鸟居" | "鳥居" | "도리이" => {
2449 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;
2450 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;
2451 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;
2452 let w=self.arg_num(&args,9,4.)?as f32; let h=self.arg_num(&args,10,5.)?as f32;
2453 let fr=self.arg_num(&args,11,0.)?as f32; let hue=self.arg_num(&args,12,0.)?as f32;
2454 let mut gfx = self.gfx.borrow_mut();
2455 let cam = gfx.camera.clone();
2456 crate::gfx::vtex::draw_torii(&mut gfx.depth_queue,&cam, cx,cy,cz, ux,uy,uz, vx,vy,vz, w,h, fr,hue);
2457 return Ok(Value::Unit);
2458 }
2459
2460 "vtex_pagoda" | "เจดีย์" | "纹塔" | "塔" | "탑" => {
2462 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;
2463 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;
2464 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;
2465 let nt=self.arg_num(&args,9,5.)?as usize; let bw=self.arg_num(&args,10,2.)?as f32;
2466 let th=self.arg_num(&args,11,1.)?as f32; let tp=self.arg_num(&args,12,0.72)?as f32;
2467 let eo=self.arg_num(&args,13,0.28)?as f32;
2468 let fr=self.arg_num(&args,14,0.)?as f32; let hue=self.arg_num(&args,15,0.)?as f32;
2469 let mut gfx = self.gfx.borrow_mut();
2470 let cam = gfx.camera.clone();
2471 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);
2472 return Ok(Value::Unit);
2473 }
2474
2475 #[cfg(not(target_arch = "wasm32"))]
2481 "audio_tone" | "เสียงโทน" | "音调" | "音調" | "음조" | "空间音" | "空間音" | "공간음" => {
2482 let idx = self.arg_num(&args, 0, 0.0)? as usize;
2483 let x = self.arg_num(&args, 1, 0.0)? as f32;
2484 let y = self.arg_num(&args, 2, 0.0)? as f32;
2485 let z = self.arg_num(&args, 3, 0.0)? as f32;
2486 let w = self.arg_num(&args, 4, 1.0)? as f32;
2487 let freq = self.arg_num(&args, 5, 220.0)? as f32;
2488 let amp = self.arg_num(&args, 6, 0.15)? as f32;
2489 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
2490 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
2491 if let Some(audio) = &self.audio {
2492 audio.set_tone(idx, ToneParams { x, y, z, w, freq, amp, lfo_rate, lfo_depth });
2493 }
2494 return Ok(Value::Unit);
2495 }
2496
2497 #[cfg(not(target_arch = "wasm32"))]
2498 "audio_listener" | "ผู้ฟัง" | "音频监听" | "音声リスナー" | "오디오리스너" => {
2499 let cry = self.arg_num(&args, 0, 1.0)? as f32;
2500 let sry = self.arg_num(&args, 1, 0.0)? as f32;
2501 let crx = self.arg_num(&args, 2, 1.0)? as f32;
2502 let srx = self.arg_num(&args, 3, 0.0)? as f32;
2503 if let Some(audio) = &self.audio {
2504 audio.set_listener(cry, sry, crx, srx);
2505 }
2506 return Ok(Value::Unit);
2507 }
2508
2509 #[cfg(not(target_arch = "wasm32"))]
2510 "audio_bgm" | "เพลงพื้นหลัง" | "เพลงประกอบ" | "背景乐" | "BGM" | "배경음악" => {
2511 let path = match args.first() {
2512 Some(Value::Str(s)) => s.clone(),
2513 _ => return Ok(Value::Unit),
2514 };
2515 let vol = self.arg_num(&args, 1, 0.5)? as f32;
2516 if let Some(audio) = &self.audio {
2517 audio.load_bgm(&path, vol);
2518 }
2519 return Ok(Value::Unit);
2520 }
2521
2522 #[cfg(not(target_arch = "wasm32"))]
2523 "audio_bgm_volume" | "ระดับเสียงพื้นหลัง" | "ระดับเพลงประกอบ" | "背景乐音量" | "BGM音量" | "배경음악음량" => {
2524 let vol = self.arg_num(&args, 0, 0.5)? as f32;
2525 if let Some(audio) = &self.audio {
2526 audio.set_bgm_volume(vol);
2527 }
2528 return Ok(Value::Unit);
2529 }
2530
2531 #[cfg(not(target_arch = "wasm32"))]
2532 "audio_volume" | "ระดับเสียง" | "音量" | "음량" => {
2533 let vol = self.arg_num(&args, 0, 0.7)? as f32;
2534 if let Some(audio) = &self.audio {
2535 audio.set_master_volume(vol);
2536 }
2537 return Ok(Value::Unit);
2538 }
2539
2540 #[cfg(target_arch = "wasm32")]
2542 "audio_tone" | "เสียงโทน" | "音调" | "音調" | "음조" | "空间音" | "空間音" | "공간음" => {
2543 let idx = self.arg_num(&args, 0, 0.0)? as usize;
2544 let x = self.arg_num(&args, 1, 0.0)? as f32;
2545 let y = self.arg_num(&args, 2, 0.0)? as f32;
2546 let z = self.arg_num(&args, 3, 0.0)? as f32;
2547 let w = self.arg_num(&args, 4, 1.0)? as f32;
2548 let freq = self.arg_num(&args, 5, 220.0)? as f32;
2549 let amp = self.arg_num(&args, 6, 0.15)? as f32;
2550 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
2551 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
2552 crate::gfx::audio_web::set_tone(idx, x, y, z, w, freq, amp, lfo_rate, lfo_depth);
2553 return Ok(Value::Unit);
2554 }
2555
2556 #[cfg(target_arch = "wasm32")]
2557 "audio_listener" | "ผู้ฟัง" | "音频监听" | "音声リスナー" | "오디오리스너" => {
2558 let cry = self.arg_num(&args, 0, 1.0)? as f32;
2559 let sry = self.arg_num(&args, 1, 0.0)? as f32;
2560 let crx = self.arg_num(&args, 2, 1.0)? as f32;
2561 let srx = self.arg_num(&args, 3, 0.0)? as f32;
2562 crate::gfx::audio_web::set_listener(cry, sry, crx, srx);
2563 return Ok(Value::Unit);
2564 }
2565
2566 #[cfg(target_arch = "wasm32")]
2567 "audio_bgm" | "เพลงพื้นหลัง" | "เพลงประกอบ" | "背景乐" | "BGM" | "배경음악" => {
2568 let path = self.arg_str(&args, 0, "");
2569 let vol = self.arg_num(&args, 1, 0.5)? as f32;
2570 crate::gfx::audio_web::load_bgm(&path, vol);
2571 return Ok(Value::Unit);
2572 }
2573
2574 #[cfg(target_arch = "wasm32")]
2575 "audio_bgm_volume" | "ระดับเสียงพื้นหลัง" | "ระดับเพลงประกอบ" | "背景乐音量" | "BGM音量" | "배경음악음량" => {
2576 let vol = self.arg_num(&args, 0, 0.5)? as f32;
2577 crate::gfx::audio_web::set_bgm_volume(vol);
2578 return Ok(Value::Unit);
2579 }
2580
2581 #[cfg(target_arch = "wasm32")]
2582 "audio_volume" | "ระดับเสียง" | "音量" | "음량" => {
2583 let vol = self.arg_num(&args, 0, 0.7)? as f32;
2584 crate::gfx::audio_web::set_master_volume(vol);
2585 return Ok(Value::Unit);
2586 }
2587
2588 "รอหน้าต่าง" | "wait_window" | "gfx_wait" => {
2590 #[cfg(not(target_arch = "wasm32"))]
2591 loop {
2592 let still_open = {
2593 let gfx = self.gfx.borrow();
2594 gfx.window.as_ref()
2595 .map(|w| w.is_open() && !w.is_key_down(minifb::Key::Escape))
2596 .unwrap_or(false)
2597 };
2598 if !still_open { break; }
2599 let (buf, w, h) = {
2600 let gfx = self.gfx.borrow();
2601 (gfx.buffer.clone(), gfx.width, gfx.height)
2602 };
2603 let mut gfx = self.gfx.borrow_mut();
2604 if let Some(win) = gfx.window.as_mut() {
2605 if win.update_with_buffer(&buf, w, h).is_err() { break; }
2606 }
2607 }
2608 return Ok(Value::Unit);
2609 }
2610
2611 "read_file" | "อ่านไฟล์" => {
2613 let path = self.arg_str(&args, 0, "");
2614 return std::fs::read_to_string(&path)
2615 .map(Value::Str)
2616 .map_err(|e| EvalErr::from(format!("read_file '{path}': {e}")));
2617 }
2618 #[cfg(not(target_arch = "wasm32"))]
2620 "net_host" | "เน็ตโฮสต์" => {
2621 let port = self.arg_num(&args, 0, 7777.0)? as u16;
2622 net::host(port);
2623 return Ok(Value::Unit);
2624 }
2625 #[cfg(not(target_arch = "wasm32"))]
2626 "net_join" | "เน็ตจอย" => {
2627 let ip = self.arg_str(&args, 0, "127.0.0.1");
2628 let port = self.arg_num(&args, 1, 7777.0)? as u16;
2629 net::join(&ip, port);
2630 return Ok(Value::Unit);
2631 }
2632 #[cfg(not(target_arch = "wasm32"))]
2633 "net_send" | "เน็ตส่ง" => {
2634 let s = self.arg_str(&args, 0, "");
2635 net::send(&s);
2636 return Ok(Value::Unit);
2637 }
2638 #[cfg(not(target_arch = "wasm32"))]
2639 "net_recv" | "เน็ตรับ" => {
2640 return Ok(Value::Str(net::recv()));
2641 }
2642 #[cfg(not(target_arch = "wasm32"))]
2643 "net_status" | "เน็ตสถานะ" => {
2644 return Ok(Value::Number(net::status() as f64));
2645 }
2646
2647 #[cfg(not(target_arch = "wasm32"))]
2650 "nn_new" | "建神经网" | "ニューラル作成" | "신경망생성" | "สร้างโครงข่าย" => {
2651 let n_in = self.arg_num(&args, 0, 1.0)?.max(0.0) as usize;
2652 let seed = self.arg_num(&args, 1, 1.0)? as u64;
2653 return Ok(Value::Number(ai::nn_new(n_in, seed) as f64));
2654 }
2655 #[cfg(not(target_arch = "wasm32"))]
2657 "nn_dense" | "密集层" | "密層追加" | "밀집층" | "ชั้นหนาแน่น" => {
2658 let id = self.arg_num(&args, 0, -1.0)? as i64;
2659 let units = self.arg_num(&args, 1, 1.0)?.max(1.0) as usize;
2660 let act = self.arg_str(&args, 2, "relu");
2661 ai::nn_dense(id, units, &act);
2662 return Ok(Value::Unit);
2663 }
2664 #[cfg(not(target_arch = "wasm32"))]
2666 "nn_forward" | "神经前向" | "順伝播" | "순전파" | "ส่งต่อโครงข่าย" => {
2667 let id = self.arg_num(&args, 0, -1.0)? as i64;
2668 let input = self.arg_list_f32(&args, 1);
2669 let out = ai::nn_forward(id, &input);
2670 return Ok(Value::List(out.into_iter().map(|v| Value::Number(v as f64)).collect()));
2671 }
2672 #[cfg(not(target_arch = "wasm32"))]
2674 "nn_train" | "训练网" | "ニューラル学習" | "신경망학습" | "ฝึกโครงข่าย" => {
2675 let id = self.arg_num(&args, 0, -1.0)? as i64;
2676 let input = self.arg_list_f32(&args, 1);
2677 let target = self.arg_list_f32(&args, 2);
2678 let lr = self.arg_num(&args, 3, 0.01)? as f32;
2679 return Ok(Value::Number(ai::nn_train(id, &input, &target, lr) as f64));
2680 }
2681 #[cfg(not(target_arch = "wasm32"))]
2683 "nn_save" | "保存网" | "網保存" | "신경망저장" | "บันทึกโครงข่าย" => {
2684 let id = self.arg_num(&args, 0, -1.0)? as i64;
2685 let path = self.arg_str(&args, 1, "model.lnn");
2686 return Ok(Value::Bool(ai::nn_save(id, &path)));
2687 }
2688 #[cfg(not(target_arch = "wasm32"))]
2690 "nn_load" | "载入网" | "網読込" | "신경망불러오기" | "โหลดโครงข่าย" => {
2691 let path = self.arg_str(&args, 0, "model.lnn");
2692 return Ok(Value::Number(ai::nn_load(&path) as f64));
2693 }
2694
2695 #[cfg(not(target_arch = "wasm32"))]
2698 "bt_build" | "建行为树" | "行動木構築" | "행동트리구성" | "สร้างต้นไม้พฤติกรรม" => {
2699 let spec = self.arg_str(&args, 0, "");
2700 return Ok(Value::Number(ai::bt_build(&spec) as f64));
2701 }
2702 #[cfg(not(target_arch = "wasm32"))]
2704 "bt_set" | "设事实" | "事実設定" | "사실설정" | "ตั้งข้อเท็จจริง" => {
2705 let id = self.arg_num(&args, 0, -1.0)? as i64;
2706 let key = self.arg_str(&args, 1, "");
2707 let val = self.arg_num(&args, 2, 0.0)? as f32;
2708 ai::bt_set(id, &key, val);
2709 return Ok(Value::Unit);
2710 }
2711 #[cfg(not(target_arch = "wasm32"))]
2713 "bt_tick" | "行为树滴答" | "行動木更新" | "행동트리틱" | "เดินต้นไม้พฤติกรรม" => {
2714 let id = self.arg_num(&args, 0, -1.0)? as i64;
2715 return Ok(Value::Str(ai::bt_tick(id)));
2716 }
2717 #[cfg(not(target_arch = "wasm32"))]
2719 "bt_status" | "行为树状态" | "行動木状態" | "행동트리상태" | "สถานะต้นไม้พฤติกรรม" => {
2720 let id = self.arg_num(&args, 0, -1.0)? as i64;
2721 return Ok(Value::Number(ai::bt_status(id) as f64));
2722 }
2723
2724 #[cfg(not(target_arch = "wasm32"))]
2727 "dialog_new" | "建对话模型" | "対話モデル作成" | "대화모델생성" | "สร้างโมเดลสนทนา" => {
2728 let ctx = self.arg_num(&args, 0, 3.0)?.max(1.0) as usize;
2729 let embed = self.arg_num(&args, 1, 32.0)?.max(1.0) as usize;
2730 let hidden = self.arg_num(&args, 2, 64.0)?.max(1.0) as usize;
2731 let seed = self.arg_num(&args, 3, 1.0)? as u64;
2732 return Ok(Value::Number(ai::dialog_new(ctx, embed, hidden, seed) as f64));
2733 }
2734 #[cfg(not(target_arch = "wasm32"))]
2736 "dialog_learn" | "对话学习" | "対話学習" | "대화학습" | "เรียนรู้สนทนา" => {
2737 let id = self.arg_num(&args, 0, -1.0)? as i64;
2738 let text = self.arg_str(&args, 1, "");
2739 ai::dialog_learn(id, &text);
2740 return Ok(Value::Unit);
2741 }
2742 #[cfg(not(target_arch = "wasm32"))]
2744 "dialog_load" | "对话载入" | "対話読込" | "대화불러오기" | "โหลดชุดสนทนา" => {
2745 let id = self.arg_num(&args, 0, -1.0)? as i64;
2746 let path = self.arg_str(&args, 1, "");
2747 return Ok(Value::Number(ai::dialog_load(id, &path) as f64));
2748 }
2749 #[cfg(not(target_arch = "wasm32"))]
2751 "dialog_train" | "对话训练" | "対話訓練" | "대화훈련" | "ฝึกสนทนา" => {
2752 let id = self.arg_num(&args, 0, -1.0)? as i64;
2753 let epochs = self.arg_num(&args, 1, 20.0)?.max(1.0) as usize;
2754 let lr = self.arg_num(&args, 2, 0.1)? as f32;
2755 return Ok(Value::Number(ai::dialog_train(id, epochs, lr) as f64));
2756 }
2757 #[cfg(not(target_arch = "wasm32"))]
2759 "dialog_say" | "对话生成" | "対話生成" | "대화생성" | "พูดสนทนา" => {
2760 let id = self.arg_num(&args, 0, -1.0)? as i64;
2761 let prompt = self.arg_str(&args, 1, "");
2762 let max = self.arg_num(&args, 2, 24.0)?.max(1.0) as usize;
2763 let temp = self.arg_num(&args, 3, 0.8)? as f32;
2764 return Ok(Value::Str(ai::dialog_say(id, &prompt, max, temp)));
2765 }
2766 #[cfg(not(target_arch = "wasm32"))]
2768 "dialog_save" | "对话存模" | "対話モデル保存" | "대화모델저장" | "บันทึกโมเดลสนทนา" => {
2769 let id = self.arg_num(&args, 0, -1.0)? as i64;
2770 let path = self.arg_str(&args, 1, "model.llm");
2771 return Ok(Value::Bool(ai::dialog_save(id, &path)));
2772 }
2773 #[cfg(not(target_arch = "wasm32"))]
2775 "dialog_load_model" | "对话载模" | "対話モデル読込" | "대화모델불러오기" | "โหลดโมเดลสนทนา" => {
2776 let path = self.arg_str(&args, 0, "model.llm");
2777 return Ok(Value::Number(ai::dialog_load_model(&path) as f64));
2778 }
2779
2780 "write_file" | "เขียนไฟล์" => {
2781 let path = self.arg_str(&args, 0, "");
2782 let content = self.arg_str(&args, 1, "");
2783 std::fs::write(&path, content.as_bytes())
2784 .map_err(|e| EvalErr::from(format!("write_file '{path}': {e}")))?;
2785 return Ok(Value::Unit);
2786 }
2787 "print_file" | "พิมพ์ไฟล์" => {
2788 let content = self.arg_str(&args, 0, "");
2789 print!("{content}");
2790 return Ok(Value::Unit);
2791 }
2792
2793 "get_args" | "รับอาร์กิวเมนต์" => {
2795 let v: Vec<Value> = std::env::args().map(Value::Str).collect();
2796 return Ok(Value::List(v));
2797 }
2798
2799 "split" | "str_split" | "แยก" => {
2801 let s = self.arg_str(&args, 0, "");
2802 let sep = self.arg_str(&args, 1, "\n");
2803 let sep = if sep.is_empty() { "\n".into() } else { sep };
2804 let parts: Vec<Value> = s.split(sep.as_str())
2805 .map(|p| Value::Str(p.to_string())).collect();
2806 return Ok(Value::List(parts));
2807 }
2808 "trim" | "str_trim" | "ตัดช่องว่าง" => {
2809 let s = self.arg_str(&args, 0, "");
2810 return Ok(Value::Str(s.trim().to_string()));
2811 }
2812 "starts_with" | "str_starts_with" | "เริ่มด้วย" => {
2813 let s = self.arg_str(&args, 0, "");
2814 let prefix = self.arg_str(&args, 1, "");
2815 return Ok(Value::Bool(s.starts_with(prefix.as_str())));
2816 }
2817 "ends_with" | "str_ends_with" | "ลงท้ายด้วย" => {
2818 let s = self.arg_str(&args, 0, "");
2819 let suffix = self.arg_str(&args, 1, "");
2820 return Ok(Value::Bool(s.ends_with(suffix.as_str())));
2821 }
2822 "str_replace" | "แทนสตริง" => {
2823 let s = self.arg_str(&args, 0, "");
2824 let from = self.arg_str(&args, 1, "");
2825 let to = self.arg_str(&args, 2, "");
2826 return Ok(Value::Str(s.replace(from.as_str(), to.as_str())));
2827 }
2828 "str_find" | "หาในสตริง" => {
2829 let s = self.arg_str(&args, 0, "");
2830 let needle = self.arg_str(&args, 1, "");
2831 let pos = s.find(needle.as_str())
2833 .map(|byte_i| s[..byte_i].chars().count() as f64)
2834 .unwrap_or(-1.0);
2835 return Ok(Value::Number(pos));
2836 }
2837 "substr" | "str_slice" | "ส่วนสตริง" => {
2838 let s = self.arg_str(&args, 0, "");
2839 let start = self.arg_num(&args, 1, 0.0)? as usize;
2840 let len = args.get(2)
2841 .map(|v| self.to_number(v).unwrap_or(999999.0) as usize)
2842 .unwrap_or_else(|| s.chars().count().saturating_sub(start));
2843 let chars: Vec<char> = s.chars().collect();
2844 let end = (start + len).min(chars.len());
2845 let slice: String = chars.get(start..end).unwrap_or(&[]).iter().collect();
2846 return Ok(Value::Str(slice));
2847 }
2848 "to_str" | "str" | "num_str" | "แปลงสตริง" => {
2849 let v = args.into_iter().next().unwrap_or(Value::Unit);
2850 return Ok(Value::Str(v.to_string()));
2851 }
2852 "str_repeat" | "ทำซ้ำสตริง" => {
2853 let s = self.arg_str(&args, 0, "");
2854 let n = self.arg_num(&args, 1, 1.0)? as usize;
2855 return Ok(Value::Str(s.repeat(n)));
2856 }
2857 "str_upper" => {
2858 let s = self.arg_str(&args, 0, "");
2859 return Ok(Value::Str(s.to_uppercase()));
2860 }
2861 "str_lower" => {
2862 let s = self.arg_str(&args, 0, "");
2863 return Ok(Value::Str(s.to_lowercase()));
2864 }
2865 "str_len" | "len" | "ความยาว" | "长度" | "長さ" | "길이" => {
2866 match args.first() {
2867 Some(Value::Str(s)) => return Ok(Value::Number(s.chars().count() as f64)),
2868 Some(Value::List(v)) => return Ok(Value::Number(v.len() as f64)),
2869 _ => return Ok(Value::Number(0.0)),
2870 }
2871 }
2872
2873 "hash_str" | "แฮช" => {
2875 let s = self.arg_str(&args, 0, "");
2876 let mut h: u64 = 14695981039346656037_u64;
2877 for b in s.bytes() { h ^= b as u64; h = h.wrapping_mul(1099511628211); }
2878 return Ok(Value::Number((h & 0xFFFFFF) as f64 / 16777215.0));
2879 }
2880 "hash_int" | "แฮชจำนวน" => {
2881 let s = self.arg_str(&args, 0, "");
2882 let n = self.arg_num(&args, 1, 100.0)? as u64;
2883 let mut h: u64 = 14695981039346656037_u64;
2884 for b in s.bytes() { h ^= b as u64; h = h.wrapping_mul(1099511628211); }
2885 return Ok(Value::Number((h % n.max(1)) as f64));
2886 }
2887
2888 "list_new" | "รายการใหม่" | "新建列表" | "新規リスト" | "새목록" => {
2890 return Ok(Value::List(Vec::new()));
2891 }
2892 "list_push" | "เพิ่มรายการ" | "列表添加" | "リスト追加" | "목록추가" => {
2893 let lst = args.first().cloned().unwrap_or(Value::List(vec![]));
2894 let val = args.get(1).cloned().unwrap_or(Value::Unit);
2895 if let Value::List(mut v) = lst { v.push(val); return Ok(Value::List(v)); }
2896 return Ok(Value::List(vec![val]));
2897 }
2898 "list_get" | "รับรายการ" | "取元素" | "要素取得" | "요소가져오기" => {
2899 let lst = args.first().cloned().unwrap_or(Value::List(vec![]));
2900 let i = self.arg_num(&args, 1, 0.0)? as usize;
2901 if let Value::List(v) = lst {
2902 return Ok(v.get(i).cloned().unwrap_or(Value::Str(String::new())));
2903 }
2904 return Ok(Value::Str(String::new()));
2905 }
2906 "list_join" | "join" | "รวมรายการ" | "连接" | "連結" | "연결" => {
2907 let lst = args.first().cloned().unwrap_or(Value::List(vec![]));
2908 let sep = args.get(1).map(|v| v.to_string()).unwrap_or_default();
2909 if let Value::List(v) = lst {
2910 return Ok(Value::Str(v.iter().map(|x| x.to_string())
2911 .collect::<Vec<_>>().join(&sep)));
2912 }
2913 return Ok(Value::Str(String::new()));
2914 }
2915 #[cfg(not(target_arch = "wasm32"))]
2919 "blob_f32" | "blob_i32" => {
2920 let s = self.arg_str(&args, 0, "");
2921 let is_i32 = name == "blob_i32";
2922 match decode_blob(&s) {
2923 Ok(bytes) => {
2924 let mut out = Vec::with_capacity(bytes.len() / 4);
2925 for ch in bytes.chunks_exact(4) {
2926 let arr = [ch[0], ch[1], ch[2], ch[3]];
2927 let n = if is_i32 {
2928 i32::from_le_bytes(arr) as f64
2929 } else {
2930 f32::from_le_bytes(arr) as f64
2931 };
2932 out.push(Value::Number(n));
2933 }
2934 return Ok(Value::List(out));
2935 }
2936 Err(e) => {
2937 eprintln!("blob decode failed: {e}");
2938 return Ok(Value::List(vec![]));
2939 }
2940 }
2941 }
2942
2943 "svg_begin" | "开始SVG" | "เริ่มSVG" => {
2951 let path = self.arg_str(&args, 0, "output.svg");
2952 let width = self.arg_num(&args, 1, 800.0)?;
2953 let height = self.arg_num(&args, 2, 600.0)?;
2954 *self.svg.borrow_mut() = Some(SvgWriter::new(path, width, height));
2955 return Ok(Value::Unit);
2956 }
2957
2958 "svg_rect" | "SVG矩形" | "SVGสี่เหลี่ยม" => {
2959 let x = self.arg_num(&args, 0, 0.0)?;
2960 let y = self.arg_num(&args, 1, 0.0)?;
2961 let w = self.arg_num(&args, 2, 10.0)?;
2962 let h = self.arg_num(&args, 3, 10.0)?;
2963 let fill = self.arg_str(&args, 4, "#ffffff");
2964 if let Some(svg) = self.svg.borrow_mut().as_mut() {
2965 svg.elements.push(format!(
2966 "<rect x=\"{x:.1}\" y=\"{y:.1}\" width=\"{w:.1}\" \
2967 height=\"{h:.1}\" fill=\"{fill}\"/>"));
2968 }
2969 return Ok(Value::Unit);
2970 }
2971
2972 "svg_circle" | "SVG圆形" | "SVGวงกลม" => {
2973 let cx = self.arg_num(&args, 0, 0.0)?;
2974 let cy = self.arg_num(&args, 1, 0.0)?;
2975 let r = self.arg_num(&args, 2, 5.0)?;
2976 let fill = self.arg_str(&args, 3, "#ffffff");
2977 if let Some(svg) = self.svg.borrow_mut().as_mut() {
2978 svg.elements.push(format!(
2979 "<circle cx=\"{cx:.1}\" cy=\"{cy:.1}\" r=\"{r:.1}\" fill=\"{fill}\"/>"));
2980 }
2981 return Ok(Value::Unit);
2982 }
2983
2984 "svg_line" | "SVG线段" | "SVGเส้น" => {
2985 let x1 = self.arg_num(&args, 0, 0.0)?;
2986 let y1 = self.arg_num(&args, 1, 0.0)?;
2987 let x2 = self.arg_num(&args, 2, 0.0)?;
2988 let y2 = self.arg_num(&args, 3, 0.0)?;
2989 let stroke = self.arg_str(&args, 4, "#ffffff");
2990 let sw = self.arg_num(&args, 5, 1.0)?;
2991 if let Some(svg) = self.svg.borrow_mut().as_mut() {
2992 svg.elements.push(format!(
2993 "<line x1=\"{x1:.1}\" y1=\"{y1:.1}\" x2=\"{x2:.1}\" y2=\"{y2:.1}\" \
2994 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"));
2995 }
2996 return Ok(Value::Unit);
2997 }
2998
2999 "svg_polyline" | "SVG折线" | "SVGเส้นหัก" => {
3000 let pts = self.arg_str(&args, 0, "");
3001 let stroke = self.arg_str(&args, 1, "#ffffff");
3002 let sw = self.arg_num(&args, 2, 1.0)?;
3003 if let Some(svg) = self.svg.borrow_mut().as_mut() {
3004 svg.elements.push(format!(
3005 "<polyline points=\"{pts}\" fill=\"none\" \
3006 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"));
3007 }
3008 return Ok(Value::Unit);
3009 }
3010
3011 "svg_text" | "SVG文本" | "SVGข้อความ" => {
3012 let x = self.arg_num(&args, 0, 0.0)?;
3013 let y = self.arg_num(&args, 1, 0.0)?;
3014 let text = self.arg_str(&args, 2, "");
3015 let fill = self.arg_str(&args, 3, "#ffffff");
3016 let size = self.arg_num(&args, 4, 12.0)?;
3017 if let Some(svg) = self.svg.borrow_mut().as_mut() {
3018 let safe = text.replace('&', "&").replace('<', "<").replace('>', ">");
3019 svg.elements.push(format!(
3020 "<text x=\"{x:.1}\" y=\"{y:.1}\" fill=\"{fill}\" \
3021 font-family=\"monospace\" font-size=\"{size:.0}\">{safe}</text>"));
3022 }
3023 return Ok(Value::Unit);
3024 }
3025
3026 "svg_end" | "结束SVG" | "จบSVG" => {
3027 {
3028 let borrow = self.svg.borrow();
3029 if let Some(svg) = borrow.as_ref() {
3030 svg.save().map_err(|e| EvalErr::from(format!("svg_end: {e}")))?;
3031 }
3032 }
3033 *self.svg.borrow_mut() = None;
3034 return Ok(Value::Unit);
3035 }
3036
3037 "hsl_color" | "HSL颜色" | "สีHSL" => {
3038 let h = self.arg_num(&args, 0, 0.0)?;
3039 let s = self.arg_num(&args, 1, 70.0)?;
3040 let l = self.arg_num(&args, 2, 50.0)?;
3041 return Ok(Value::Str(hsl_to_hex(h, s, l)));
3042 }
3043
3044 #[cfg(not(target_arch = "wasm32"))]
3050 "fft_push" | "วิเคราะห์เสียง" | "频谱输入" | "FFT入力" | "FFT입력" => {
3051 if let Some(Value::List(v)) = args.first() {
3052 let samples: Vec<f32> = v.iter()
3053 .filter_map(|x| if let Value::Number(n) = x { Some(*n as f32) } else { None })
3054 .collect();
3055 self.fft.borrow_mut().push_samples(&samples);
3056 }
3057 return Ok(Value::Unit);
3058 }
3059
3060 #[cfg(not(target_arch = "wasm32"))]
3062 "fft_bands" | "แถบความถี่" | "频段" | "周波数帯" | "주파수대" => {
3063 let n = self.arg_num(&args, 0, 32.0)? as usize;
3064 let bands = self.fft.borrow().freq_bands(n);
3065 *self.fft_bands_cache.borrow_mut() = bands.clone();
3066 return Ok(Value::List(bands.into_iter().map(|v| Value::Number(v as f64)).collect()));
3067 }
3068
3069 #[cfg(not(target_arch = "wasm32"))]
3071 "fft_beat" | "จังหวะเสียง" | "节拍检测" | "ビート検出" | "비트" => {
3072 return Ok(Value::Bool(self.fft.borrow().is_beat()));
3073 }
3074
3075 #[cfg(not(target_arch = "wasm32"))]
3077 "fft_beat_ratio" | "อัตราจังหวะ" | "节拍比" | "ビート比" | "비트비율" => {
3078 return Ok(Value::Number(self.fft.borrow().beat_ratio() as f64));
3079 }
3080
3081 #[cfg(not(target_arch = "wasm32"))]
3083 "fft_rms" | "ระดับRMS" | "均方根" | "二乗平均" | "RMS레벨" => {
3084 return Ok(Value::Number(self.fft.borrow().rms() as f64));
3085 }
3086
3087 #[cfg(not(target_arch = "wasm32"))]
3089 "fft_dominant_freq" | "ความถี่หลัก" | "主频" | "主要周波数" | "주파수" => {
3090 return Ok(Value::Number(self.fft.borrow().dominant_freq() as f64));
3091 }
3092
3093 #[cfg(target_arch = "wasm32")]
3095 "fft_push" | "วิเคราะห์เสียง" | "频谱输入" | "FFT入力" | "FFT입력" => { return Ok(Value::Unit); }
3096 #[cfg(target_arch = "wasm32")]
3097 "fft_bands" | "แถบความถี่" | "频段" | "周波数帯" | "주파수대" => {
3098 let n = self.arg_num(&args, 0, 32.0)? as usize;
3099 return Ok(Value::List(vec![Value::Number(0.0); n]));
3100 }
3101 #[cfg(target_arch = "wasm32")]
3102 "fft_beat" | "จังหวะเสียง" | "节拍检测" | "ビート検出" | "비트" => { return Ok(Value::Bool(false)); }
3103 #[cfg(target_arch = "wasm32")]
3104 "fft_beat_ratio" | "อัตราจังหวะ" | "节拍比" | "ビート比" | "비트비율" => { return Ok(Value::Number(1.0)); }
3105 #[cfg(target_arch = "wasm32")]
3106 "fft_rms" | "ระดับRMS" | "均方根" | "二乗平均" | "RMS레벨" => { return Ok(Value::Number(0.0)); }
3107 #[cfg(target_arch = "wasm32")]
3108 "fft_dominant_freq" | "ความถี่หลัก" | "主频" | "主要周波数" | "주파수" => { return Ok(Value::Number(0.0)); }
3109
3110 "tex_checkerboard" | "ลายตารางหมากรุก" => {
3118 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3119 let tiles = self.arg_num(&args, 4, 8.0)? as u32;
3120 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);
3121 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);
3122 let c1 = (r1<<16)|(g1<<8)|b1; let c2 = (r2<<16)|(g2<<8)|b2;
3123 let mut gfx = self.gfx.borrow_mut();
3124 let (bw, bh) = (gfx.width, gfx.height);
3125 for row in 0..th { for col in 0..tw {
3126 let cx = col as u32 * tiles / tw as u32;
3127 let cy = row as u32 * tiles / th as u32;
3128 let (dx, dy) = (tx+col, ty+row);
3129 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = if (cx+cy)%2==0 { c1 } else { c2 }; }
3130 }}
3131 return Ok(Value::Unit);
3132 }
3133
3134 "tex_gradient" | "ลายไล่สี" => {
3136 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3137 let angle = self.arg_num(&args, 4, 0.0)? as f32;
3138 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.);
3139 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.);
3140 let (ca, sa) = (angle.to_radians().cos(), angle.to_radians().sin());
3141 let mut gfx = self.gfx.borrow_mut();
3142 let (bw, bh) = (gfx.width, gfx.height);
3143 for row in 0..th { for col in 0..tw {
3144 let nx = col as f32/tw as f32 - 0.5; let ny = row as f32/th as f32 - 0.5;
3145 let t = ((nx*ca + ny*sa + 0.707)/1.414).clamp(0.,1.);
3146 let (dx, dy) = (tx+col, ty+row);
3147 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(r1+(r2-r1)*t, g1+(g2-g1)*t, b1+(b2-b1)*t); }
3148 }}
3149 return Ok(Value::Unit);
3150 }
3151
3152 "tex_noise" | "ลายนอยส์" => {
3154 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3155 let scale = self.arg_num(&args, 4, 4.0)? as f32;
3156 let octaves = self.arg_num(&args, 5, 4.0)? as u32;
3157 let seed = self.arg_num(&args, 6, 0.0)? as u32;
3158 let palette = self.arg_str(&args, 7, "rainbow");
3159 let mut gfx = self.gfx.borrow_mut();
3160 let (bw, bh) = (gfx.width, gfx.height);
3161 for row in 0..th { for col in 0..tw {
3162 let v = tex_fbm(col as f32*scale/tw as f32, row as f32*scale/th as f32, octaves, seed);
3163 let [r,g,b] = tex_palette(&palette, v);
3164 let (dx, dy) = (tx+col, ty+row);
3165 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(r, g, b); }
3166 }}
3167 return Ok(Value::Unit);
3168 }
3169
3170 "tex_freq_map" | "ลายความถี่" => {
3173 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3174 let time = self.arg_num(&args, 4, 0.0)? as f32;
3175 let speed = self.arg_num(&args, 5, 0.3)? as f32;
3176 let palette = self.arg_str(&args, 6, "rainbow");
3177 let bands: Vec<f32> = {
3178 let c = self.fft_bands_cache.borrow();
3179 if c.is_empty() { vec![0.0; 32] } else { c.clone() }
3180 };
3181 let n = bands.len().max(1);
3182 let mut gfx = self.gfx.borrow_mut();
3183 let (bw, bh) = (gfx.width, gfx.height);
3184 for row in 0..th { for col in 0..tw {
3185 let band_idx = (col * n / tw.max(1)).min(n-1);
3186 let mag = bands[band_idx].clamp(0.,1.);
3187 let fill_y = (mag * th as f32) as usize;
3188 if row >= th.saturating_sub(fill_y) {
3189 let t = (col as f32/tw as f32 + time*speed) % 1.0;
3190 let [r,g,b] = tex_palette(&palette, t);
3191 let bright = mag * (1.0 - row as f32/th as f32 * 0.5);
3192 let (dx, dy) = (tx+col, ty+row);
3193 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(r*bright, g*bright, b*bright); }
3194 }
3195 }}
3196 return Ok(Value::Unit);
3197 }
3198
3199 "tex_spiral" | "ลายเกลียวหมุน" => {
3201 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3202 let freq = self.arg_num(&args, 4, 5.0)? as f32;
3203 let n_bands = self.arg_num(&args, 5, 8.0)? as f32;
3204 let time = self.arg_num(&args, 6, 0.0)? as f32;
3205 let palette = self.arg_str(&args, 7, "rainbow");
3206 let mut gfx = self.gfx.borrow_mut();
3207 let (bw, bh) = (gfx.width, gfx.height);
3208 for row in 0..th { for col in 0..tw {
3209 let nx = col as f32/tw as f32 - 0.5; let ny = row as f32/th as f32 - 0.5;
3210 let r = (nx*nx + ny*ny).sqrt();
3211 let theta = ny.atan2(nx);
3212 let t = ((r*freq - theta/std::f32::consts::TAU + time*0.5) * n_bands % 1.0).abs();
3213 let [cr,cg,cb] = tex_palette(&palette, t);
3214 let (dx, dy) = (tx+col, ty+row);
3215 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3216 }}
3217 return Ok(Value::Unit);
3218 }
3219
3220 "tex_ripple" | "ลายระลอก" => {
3222 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3223 let freq = self.arg_num(&args, 4, 10.0)? as f32;
3224 let rcx = self.arg_num(&args, 5, 0.5)? as f32;
3225 let rcy = self.arg_num(&args, 6, 0.5)? as f32;
3226 let time = self.arg_num(&args, 7, 0.0)? as f32;
3227 let palette = self.arg_str(&args, 8, "ocean");
3228 let mut gfx = self.gfx.borrow_mut();
3229 let (bw, bh) = (gfx.width, gfx.height);
3230 for row in 0..th { for col in 0..tw {
3231 let nx = col as f32/tw as f32 - rcx; let ny = row as f32/th as f32 - rcy;
3232 let r = (nx*nx + ny*ny).sqrt();
3233 let t = ((r*freq - time) % 1.0).abs();
3234 let [cr,cg,cb] = tex_palette(&palette, t);
3235 let (dx, dy) = (tx+col, ty+row);
3236 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3237 }}
3238 return Ok(Value::Unit);
3239 }
3240
3241 "tex_mandelbrot" | "ลายแมนเดลบรอต" => {
3243 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3244 let zoom = self.arg_num(&args, 4, 1.0)?;
3245 let mcx = self.arg_num(&args, 5, -0.5)?;
3246 let mcy = self.arg_num(&args, 6, 0.0)?;
3247 let max_iter = self.arg_num(&args, 7, 64.0)? as u32;
3248 let palette = self.arg_str(&args, 8, "psychedelic");
3249 let mut gfx = self.gfx.borrow_mut();
3250 let (bw, bh) = (gfx.width, gfx.height);
3251 for row in 0..th { for col in 0..tw {
3252 let zx0 = (col as f64/tw as f64 - 0.5)/zoom + mcx;
3253 let zy0 = (row as f64/th as f64 - 0.5)/zoom + mcy;
3254 let mut x = 0.0f64; let mut y = 0.0f64; let mut i = 0u32;
3255 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; }
3256 let t = if i==max_iter { 0.0f32 } else {
3257 (i as f32 - (x as f32*x as f32+y as f32*y as f32).ln().ln()/2.0f32.ln()) / max_iter as f32
3258 };
3259 let [cr,cg,cb] = tex_palette(&palette, t.clamp(0.,1.));
3260 let (dx, dy) = (tx+col, ty+row);
3261 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3262 }}
3263 return Ok(Value::Unit);
3264 }
3265
3266 "tex_julia" | "ลายจูเลีย" => {
3268 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3269 let c_re = self.arg_num(&args, 4, -0.7)?;
3270 let c_im = self.arg_num(&args, 5, 0.27)?;
3271 let max_iter = self.arg_num(&args, 6, 64.0)? as u32;
3272 let palette = self.arg_str(&args, 7, "neon");
3273 let mut gfx = self.gfx.borrow_mut();
3274 let (bw, bh) = (gfx.width, gfx.height);
3275 for row in 0..th { for col in 0..tw {
3276 let mut zx = (col as f64/tw as f64 - 0.5)*3.5;
3277 let mut zy = (row as f64/th as f64 - 0.5)*3.5;
3278 let mut i = 0u32;
3279 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; }
3280 let t = i as f32 / max_iter as f32;
3281 let [cr,cg,cb] = tex_palette(&palette, t);
3282 let (dx, dy) = (tx+col, ty+row);
3283 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3284 }}
3285 return Ok(Value::Unit);
3286 }
3287
3288 "tex_voronoi" | "ลายโวโรนอย" => {
3290 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3291 let cells = self.arg_num(&args, 4, 16.0)? as u32;
3292 let seed = self.arg_num(&args, 5, 42.0)? as u32;
3293 let palette = self.arg_str(&args, 6, "rainbow");
3294 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();
3295 let mut gfx = self.gfx.borrow_mut();
3296 let (bw, bh) = (gfx.width, gfx.height);
3297 for row in 0..th { for col in 0..tw {
3298 let (fx, fy) = (col as f32/tw as f32, row as f32/th as f32);
3299 let (min_d, nearest) = pts.iter().enumerate().fold((f32::MAX,0usize), |(d,idx),(i,&[cx,cy])| {
3300 let dd = (fx-cx).powi(2)+(fy-cy).powi(2);
3301 if dd < d { (dd,i) } else { (d,idx) }
3302 });
3303 let t = (nearest as f32/cells as f32 + min_d*4.0) % 1.0;
3304 let [cr,cg,cb] = tex_palette(&palette, t);
3305 let (dx, dy) = (tx+col, ty+row);
3306 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3307 }}
3308 return Ok(Value::Unit);
3309 }
3310
3311 "tex_halftone" | "ลายฮาล์ฟโทน" => {
3313 let (tx,ty,tw,th) = self.tex_rect(&args)?;
3314 let dot_size = self.arg_num(&args, 4, 0.05)? as f32;
3315 let time = self.arg_num(&args, 5, 0.0)? as f32;
3316 let palette = self.arg_str(&args, 6, "rainbow");
3317 let mut gfx = self.gfx.borrow_mut();
3318 let (bw, bh) = (gfx.width, gfx.height);
3319 for row in 0..th { for col in 0..tw {
3320 let (fx, fy) = (col as f32/tw as f32, row as f32/th as f32);
3321 let gx = (fx/dot_size).floor(); let gy = (fy/dot_size).floor();
3322 let lx = (fx/dot_size - gx - 0.5)*2.0; let ly = (fy/dot_size - gy - 0.5)*2.0;
3323 let r = (lx*lx + ly*ly).sqrt();
3324 let t = (gx/(1.0/dot_size) + time*0.1) % 1.0;
3325 let a = if r < 0.7 { ((0.7-r)/0.7).clamp(0.,1.) } else { 0.0 };
3326 if a > 0.0 {
3327 let [cr,cg,cb] = tex_palette(&palette, t);
3328 let (dx, dy) = (tx+col, ty+row);
3329 if dx < bw && dy < bh { gfx.buffer[dy*bw+dx] = tex_rgb(cr, cg, cb); }
3330 }
3331 }}
3332 return Ok(Value::Unit);
3333 }
3334
3335 "set_shade_mode" | "设置着色" | "シェード設定" | "셰이드모드" | "ตั้งการแรเงา" => {
3340 let m = self.arg_num(&args, 0, 2.0)? as u8;
3341 self.gfx.borrow_mut().shade_mode = m;
3342 return Ok(Value::Unit);
3343 }
3344 "set_cel_bands" | "设置色阶" | "セル段数" | "셀밴드" | "ตั้งระดับสี" => {
3346 let n = (self.arg_num(&args, 0, 4.0)? as u32).max(2);
3347 self.gfx.borrow_mut().shade.bands = n;
3348 return Ok(Value::Unit);
3349 }
3350 "set_shadow_color" | "设置阴影色" | "影の色" | "그림자색" | "ตั้งสีเงา" => {
3352 let r=self.arg_num(&args,0,26.)? as f32/255.0;
3353 let g=self.arg_num(&args,1,33.)? as f32/255.0;
3354 let b=self.arg_num(&args,2,77.)? as f32/255.0;
3355 self.gfx.borrow_mut().shade.shadow = [r,g,b];
3356 return Ok(Value::Unit);
3357 }
3358 #[cfg(not(target_arch = "wasm32"))]
3364 "crypto_hash" | "แฮชเข้ารหัส" | "几何哈希" | "幾何ハッシュ" | "기하해시" => {
3365 let s = self.arg_str(&args, 0, "");
3366 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(s.as_bytes()))));
3367 }
3368 #[cfg(not(target_arch = "wasm32"))]
3370 "knot_points" | "จุดปม" | "结点坐标" | "結び目点" | "매듭점" => {
3371 let s = self.arg_str(&args, 0, "");
3372 let shape = ling_crypto::geo::KnotShape::from_bytes(s.as_bytes());
3373 let mut out = Vec::with_capacity(shape.points.len() * 3);
3374 for p in &shape.points {
3375 out.push(Value::Number(p[0] as f64));
3376 out.push(Value::Number(p[1] as f64));
3377 out.push(Value::Number(p[2] as f64));
3378 }
3379 return Ok(Value::List(out));
3380 }
3381 #[cfg(not(target_arch = "wasm32"))]
3382 "knot_label" | "ป้ายปม" | "结点标签" | "結び目ラベル" | "매듭라벨" => {
3383 let s = self.arg_str(&args, 0, "");
3384 return Ok(Value::Str(ling_crypto::geo::KnotShape::from_bytes(s.as_bytes()).label()));
3385 }
3386 #[cfg(not(target_arch = "wasm32"))]
3388 "knot_keygen" | "hybrid_keygen" | "สร้างกุญแจปม" | "生成密钥" | "鍵生成" | "키생성" => {
3389 self.crypto_ids.push(ling_crypto::KnotIdentity::generate());
3390 return Ok(Value::Number((self.crypto_ids.len() - 1) as f64));
3391 }
3392 #[cfg(not(target_arch = "wasm32"))]
3393 "knot_public" | "hybrid_public" | "กุญแจสาธารณะปม" | "公钥" | "公開鍵" | "공개키" => {
3394 let h = self.arg_num(&args, 0, 0.0)? as usize;
3395 let pk = self.crypto_ids.get(h).map(|id| hex_encode(id.public_key())).unwrap_or_default();
3396 return Ok(Value::Str(pk));
3397 }
3398 #[cfg(not(target_arch = "wasm32"))]
3400 "knot_encapsulate" | "hybrid_encapsulate" | "ห่อกุญแจปม" | "封装密钥" | "カプセル化" | "캡슐화" => {
3401 let pk = hex_decode(&self.arg_str(&args, 0, ""));
3402 match ling_crypto::geo::knot_encapsulate(&pk) {
3403 Ok((ct, ss)) => return Ok(Value::List(vec![Value::Str(hex_encode(&ct)), Value::Str(hex_encode(&ss))])),
3404 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
3405 }
3406 }
3407 #[cfg(not(target_arch = "wasm32"))]
3409 "knot_decapsulate" | "hybrid_decapsulate" | "แกะกุญแจปม" | "解封装密钥" | "カプセル解除" | "캡슐해제" => {
3410 let h = self.arg_num(&args, 0, 0.0)? as usize;
3411 let ct = hex_decode(&self.arg_str(&args, 1, ""));
3412 let ss = self.crypto_ids.get(h)
3413 .and_then(|id| id.decapsulate(&ct).ok())
3414 .map(|s| hex_encode(&s)).unwrap_or_default();
3415 return Ok(Value::Str(ss));
3416 }
3417 #[cfg(not(target_arch = "wasm32"))]
3419 "crypto_seal" | "ผนึก" | "封印" | "封印する" | "봉인" => {
3420 let key = hex_to_32(&self.arg_str(&args, 0, ""));
3421 let pt = self.arg_str(&args, 1, "");
3422 match ling_crypto::geo::holo_seal(key, pt.as_bytes()) {
3423 Ok(ct) => return Ok(Value::Str(hex_encode(&ct))),
3424 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
3425 }
3426 }
3427 #[cfg(not(target_arch = "wasm32"))]
3428 "crypto_open" | "เปิดผนึก" | "解封" | "封印解除" | "봉인해제" => {
3429 let key = hex_to_32(&self.arg_str(&args, 0, ""));
3430 let ct = hex_decode(&self.arg_str(&args, 1, ""));
3431 match ling_crypto::geo::holo_open(key, &ct) {
3432 Ok(pt) => return Ok(Value::Str(String::from_utf8_lossy(&pt).into_owned())),
3433 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
3434 }
3435 }
3436 #[cfg(not(target_arch = "wasm32"))]
3438 "holo_points" | "จุดโฮโลแกรม" | "全息点" | "ホログラム点" | "홀로그램점" => {
3439 let s = self.arg_str(&args, 0, "");
3440 let frags = ling_crypto::geo::scatter(s.as_bytes());
3441 let mut out = Vec::with_capacity(frags.len() * 4);
3442 for f in &frags { for c in f.coord { out.push(Value::Number(c as f64)); } }
3443 return Ok(Value::List(out));
3444 }
3445 #[cfg(not(target_arch = "wasm32"))]
3446 "holo_fragment_count" | "จำนวนชิ้นโฮโลแกรม" | "全息碎片数" | "ホログラム断片数" | "홀로그램조각수" => {
3447 let s = self.arg_str(&args, 0, "");
3448 return Ok(Value::Number(ling_crypto::geo::scatter(s.as_bytes()).len() as f64));
3449 }
3450
3451 "ease" => {
3455 let name = self.arg_str(&args, 0, "ease");
3456 let t = self.arg_num(&args, 1, 0.0)? as f32;
3457 return Ok(Value::Number(ling_ui::Easing::from_name(&name).apply(t) as f64));
3458 }
3459 #[cfg(not(target_arch = "wasm32"))]
3460 "mouse_x" => {
3461 let gfx = self.gfx.borrow();
3462 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);
3463 return Ok(Value::Number(v));
3464 }
3465 #[cfg(not(target_arch = "wasm32"))]
3466 "mouse_y" => {
3467 let gfx = self.gfx.borrow();
3468 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);
3469 return Ok(Value::Number(v));
3470 }
3471 #[cfg(not(target_arch = "wasm32"))]
3472 "mouse_down" => {
3473 let gfx = self.gfx.borrow();
3474 let d = gfx.window.as_ref().map(|w| w.get_mouse_down(minifb::MouseButton::Left)).unwrap_or(false);
3475 return Ok(Value::Bool(d));
3476 }
3477 "mouse_down_right" | "เมาส์ขวา" => {
3478 let gfx = self.gfx.borrow();
3479 let d = gfx.window.as_ref().map(|w| w.get_mouse_down(minifb::MouseButton::Right)).unwrap_or(false);
3480 return Ok(Value::Bool(d));
3481 }
3482 "mouse_down_middle" | "เมาส์กลาง" => {
3483 let gfx = self.gfx.borrow();
3484 let d = gfx.window.as_ref().map(|w| w.get_mouse_down(minifb::MouseButton::Middle)).unwrap_or(false);
3485 return Ok(Value::Bool(d));
3486 }
3487 #[cfg(not(target_arch = "wasm32"))]
3488 "ui_hot" | "热区" | "ホットエリア" | "핫존" | "พื้นที่สัมผัส" => {
3489 let x = self.arg_num(&args,0,0.0)? as f32;
3490 let y = self.arg_num(&args,1,0.0)? as f32;
3491 let w = self.arg_num(&args,2,0.0)? as f32;
3492 let h = self.arg_num(&args,3,0.0)? as f32;
3493 let gfx = self.gfx.borrow();
3494 let (mx,my) = gfx.window.as_ref().and_then(|win| win.get_mouse_pos(minifb::MouseMode::Clamp)).unwrap_or((0.0,0.0));
3495 return Ok(Value::Bool(ling_ui::holo::hit_rect(mx,my,x,y,w,h)));
3496 }
3497 "ui_text" | "界面文字" | "UI文字" | "UI텍스트" | "ข้อความหน้าจอ" => {
3499 let x = self.arg_num(&args,0,0.0)? as f32;
3500 let y = self.arg_num(&args,1,0.0)? as f32;
3501 let scale = self.arg_num(&args,2,16.0)? as f32;
3502 let s = self.arg_str(&args,3,"");
3503 let segs = ling_ui::holo::text_lines(&s, x, y, scale*0.62, scale, scale*0.24);
3504 let mut gfx = self.gfx.borrow_mut();
3505 let (w,h,color) = (gfx.width, gfx.height, gfx.color);
3506 for sg in segs { draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]); }
3507 return Ok(Value::Unit);
3508 }
3509 #[cfg(not(target_arch = "wasm32"))]
3512 "font_load" | "โหลดฟอนต์" | "加载字体" | "フォント読込" | "글꼴로드" => {
3513 let path = self.arg_str(&args, 0, "");
3514 let weight = match self.arg_num(&args, 1, 0.0)? {
3516 w if w > 0.0 => Some(w as f32),
3517 _ => None,
3518 };
3519 let mut loaded = ling_graphics::VectorFont::from_path_weight(&path, weight);
3521 if loaded.is_err() {
3522 if let Some(dir) = &self.source_dir {
3523 let joined = dir.join(&path);
3524 loaded = ling_graphics::VectorFont::from_path_weight(&joined.to_string_lossy(), weight);
3525 }
3526 }
3527 match loaded {
3528 Ok(f) => {
3529 let id = self.fonts.len();
3530 self.fonts.push(f);
3531 return Ok(Value::Number(id as f64));
3532 }
3533 Err(e) => {
3534 eprintln!("font_load failed ({path}): {e}");
3535 return Ok(Value::Number(-1.0));
3536 }
3537 }
3538 }
3539 #[cfg(not(target_arch = "wasm32"))]
3542 "font_text" | "ข้อความฟอนต์" | "字体文本" | "フォント文字" | "글꼴텍스트" => {
3543 let id = self.arg_num(&args, 0, 0.0)? as i64;
3544 let x = self.arg_num(&args, 1, 0.0)? as f32;
3545 let y = self.arg_num(&args, 2, 0.0)? as f32;
3546 let px = self.arg_num(&args, 3, 16.0)? as f32;
3547 let s = self.arg_str(&args, 4, "");
3548 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
3549 let strokes = self.font_layout_2d(id as usize, x, y, px, &s);
3550 let mut gfx = self.gfx.borrow_mut();
3551 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
3552 for pl in &strokes {
3553 for seg in pl.windows(2) {
3554 crate::gfx::raster::draw_line_aa(&mut gfx.buffer, w, h, color, add,
3555 seg[0][0], seg[0][1], seg[1][0], seg[1][1]);
3556 }
3557 }
3558 }
3559 return Ok(Value::Unit);
3560 }
3561 #[cfg(not(target_arch = "wasm32"))]
3563 "font_text_fill" | "เติมฟอนต์" | "填充字体" | "フォント塗り" | "글꼴채움" => {
3564 let id = self.arg_num(&args, 0, 0.0)? as i64;
3565 let x = self.arg_num(&args, 1, 0.0)? as f32;
3566 let y = self.arg_num(&args, 2, 0.0)? as f32;
3567 let px = self.arg_num(&args, 3, 16.0)? as f32;
3568 let s = self.arg_str(&args, 4, "");
3569 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
3570 let glyphs = self.font_layout_2d_glyphs(id as usize, x, y, px, &s);
3572 let mut gfx = self.gfx.borrow_mut();
3573 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
3574 for contours in &glyphs {
3575 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, w, h, color, add, contours);
3576 }
3577 }
3578 return Ok(Value::Unit);
3579 }
3580 #[cfg(not(target_arch = "wasm32"))]
3584 "font_text_3d" | "ข้อความฟอนต์3มิติ" | "字体3D" | "フォント3D" | "글꼴3D" => {
3585 let id = self.arg_num(&args, 0, 0.0)? as i64;
3586 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;
3587 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;
3588 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;
3589 let size=self.arg_num(&args,10,1.0)? as f32;
3590 let s = self.arg_str(&args,11,"");
3591 if id >= 0 && (id as usize) < self.fonts.len() && size > 0.0 {
3592 let font = &mut self.fonts[id as usize];
3594 let asc = font.ascent();
3595 let mut pen = 0.0f32;
3596 let mut lines: Vec<[f32; 6]> = Vec::new();
3597 for ch in s.chars() {
3598 let go = font.glyph_outline(ch, 0.01);
3599 for pl in &go.polylines {
3600 for seg in pl.windows(2) {
3601 let map = |p: [f32; 2]| {
3602 let a = pen + p[0];
3603 let b = p[1] - asc; [cx + a*size*ux + b*size*vx,
3605 cy + a*size*uy + b*size*vy,
3606 cz + a*size*uz + b*size*vz]
3607 };
3608 let p0 = map(seg[0]); let p1 = map(seg[1]);
3609 lines.push([p0[0],p0[1],p0[2], p1[0],p1[1],p1[2]]);
3610 }
3611 }
3612 pen += go.advance;
3613 }
3614 let mut gfx = self.gfx.borrow_mut();
3615 let color = gfx.color;
3616 let near = -gfx.camera.zdist + 0.05;
3617 for l in &lines {
3618 let (mut ax, mut ay, mut az) = (l[0], l[1], l[2]);
3619 let (mut bx, mut by, mut bz) = (l[3], l[4], l[5]);
3620 let da = gfx.camera.depth(ax, ay, az);
3621 let db = gfx.camera.depth(bx, by, bz);
3622 if da <= near && db <= near { continue; }
3623 if da <= near {
3624 let t = (near - da) / (db - da);
3625 ax += t*(bx-ax); ay += t*(by-ay); az += t*(bz-az);
3626 } else if db <= near {
3627 let t = (near - da) / (db - da);
3628 bx = ax + t*(bx-ax); by = ay + t*(by-ay); bz = az + t*(bz-az);
3629 }
3630 let (sax, say, da2) = gfx.camera.project(ax, ay, az);
3631 let (sbx, sby, db2) = gfx.camera.project(bx, by, bz);
3632 let depth = (da2 + db2) / 2.0;
3633 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
3634 }
3635 }
3636 return Ok(Value::Unit);
3637 }
3638 #[cfg(not(target_arch = "wasm32"))]
3640 "font_width" | "ความกว้างฟอนต์" | "字体宽度" | "フォント幅" | "글꼴너비" => {
3641 let id = self.arg_num(&args, 0, 0.0)? as i64;
3642 let px = self.arg_num(&args, 1, 16.0)? as f32;
3643 let s = self.arg_str(&args, 2, "");
3644 if id >= 0 && (id as usize) < self.fonts.len() {
3645 return Ok(Value::Number(self.fonts[id as usize].measure(&s, px) as f64));
3646 }
3647 return Ok(Value::Number(0.0));
3648 }
3649 "ui_frame" | "边框" | "フレーム枠" | "프레임틀" | "กรอบ" => {
3651 let x=self.arg_num(&args,0,0.0)? as f32; let y=self.arg_num(&args,1,0.0)? as f32;
3652 let w0=self.arg_num(&args,2,0.0)? as f32; let h0=self.arg_num(&args,3,0.0)? as f32;
3653 let l=self.arg_num(&args,4,14.0)? as f32;
3654 let segs = ling_ui::holo::corner_brackets(x,y,w0,h0,l);
3655 let mut gfx = self.gfx.borrow_mut();
3656 let (w,h,color)=(gfx.width,gfx.height,gfx.color);
3657 for sg in segs { draw_line(&mut gfx.buffer, w,h,color, sg[0],sg[1],sg[2],sg[3]); }
3658 return Ok(Value::Unit);
3659 }
3660 "ui_bevel" | "斜角框" | "ベベル枠" | "베벨틀" | "กรอบเฉียง" => {
3662 let x=self.arg_num(&args,0,0.0)? as f32; let y=self.arg_num(&args,1,0.0)? as f32;
3663 let w0=self.arg_num(&args,2,0.0)? as f32; let h0=self.arg_num(&args,3,0.0)? as f32;
3664 let bv=self.arg_num(&args,4,10.0)? as f32;
3665 let segs = ling_ui::holo::beveled_rect(x,y,w0,h0,bv);
3666 let mut gfx = self.gfx.borrow_mut();
3667 let (w,h,color)=(gfx.width,gfx.height,gfx.color);
3668 for sg in segs { draw_line(&mut gfx.buffer, w,h,color, sg[0],sg[1],sg[2],sg[3]); }
3669 return Ok(Value::Unit);
3670 }
3671
3672 #[cfg(not(target_arch = "wasm32"))]
3678 "ui_theme" | "界面主题" | "UIテーマ" | "인터페이스테마" | "ธีมส่วนติดต่อ" => {
3679 let cur = self.ui_theme;
3680 let primary = self.color_at(&args, 0, cur.primary);
3681 let accent = self.color_at(&args, 3, cur.accent);
3682 let track = self.color_at(&args, 6, cur.track);
3683 let warn = self.color_at(&args, 9, cur.warn);
3684 let text = self.color_at(&args, 12, cur.text);
3685 let bg = self.color_at(&args, 15, cur.bg);
3686 self.ui_theme = UiTheme { primary, accent, track, warn, text, bg };
3687 return Ok(Value::Unit);
3688 }
3689
3690 #[cfg(not(target_arch = "wasm32"))]
3692 "ui_radar" | "雷达" | "レーダー" | "레이더" | "เรดาร์" => {
3693 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
3694 let r=self.arg_num(&args,2,60.)? as f32; let sweep=self.arg_num(&args,3,0.)? as f32;
3695 let th=self.ui_theme;
3696 let prim=self.color_at(&args,4,th.primary);
3697 self.draw_ui(&ling_ui::widgets::radar(cx,cy,r,sweep, prim, th.accent, th.track));
3698 return Ok(Value::Unit);
3699 }
3700 #[cfg(not(target_arch = "wasm32"))]
3701 "ui_compass" | "罗盘" | "コンパス" | "나침반" | "เข็มทิศ" => {
3702 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3703 let w0=self.arg_num(&args,2,300.)? as f32; let h0=self.arg_num(&args,3,24.)? as f32;
3704 let head=self.arg_num(&args,4,0.)? as f32;
3705 let th=self.ui_theme; let prim=self.color_at(&args,5,th.primary);
3706 self.draw_ui(&ling_ui::widgets::compass(x,y,w0,h0,head, prim, th.track));
3707 return Ok(Value::Unit);
3708 }
3709 #[cfg(not(target_arch = "wasm32"))]
3710 "ui_reticle" | "准星" | "照準" | "조준선" | "เป้าเล็ง" => {
3711 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
3712 let r=self.arg_num(&args,2,30.)? as f32; let spread=self.arg_num(&args,3,0.)? as f32;
3713 let th=self.ui_theme; let prim=self.color_at(&args,4,th.primary);
3714 self.draw_ui(&ling_ui::widgets::reticle(cx,cy,r,spread, prim));
3715 return Ok(Value::Unit);
3716 }
3717 #[cfg(not(target_arch = "wasm32"))]
3718 "ui_target" | "锁定框" | "ターゲット" | "표적" | "กรอบเป้า" => {
3719 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3720 let w0=self.arg_num(&args,2,80.)? as f32; let h0=self.arg_num(&args,3,80.)? as f32;
3721 let lock=self.arg_num(&args,4,0.)? as f32;
3722 let th=self.ui_theme; let prim=self.color_at(&args,5,th.primary);
3723 self.draw_ui(&ling_ui::widgets::target(x,y,w0,h0,lock, prim, th.accent));
3724 return Ok(Value::Unit);
3725 }
3726 #[cfg(not(target_arch = "wasm32"))]
3727 "ui_panel" | "面板" | "パネル" | "패널" | "แผง" => {
3728 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3729 let w0=self.arg_num(&args,2,200.)? as f32; let h0=self.arg_num(&args,3,120.)? as f32;
3730 let bv=self.arg_num(&args,4,12.)? as f32;
3731 let th=self.ui_theme; let prim=self.color_at(&args,5,th.primary);
3732 self.draw_ui(&ling_ui::widgets::panel(x,y,w0,h0,bv, prim, th.bg));
3733 return Ok(Value::Unit);
3734 }
3735 #[cfg(not(target_arch = "wasm32"))]
3736 "ui_scanlines" | "扫描线" | "走査線" | "스캔라인" | "เส้นสแกน" => {
3737 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3738 let w0=self.arg_num(&args,2,200.)? as f32; let h0=self.arg_num(&args,3,120.)? as f32;
3739 let dens=self.arg_num(&args,4,24.)? as usize;
3740 let th=self.ui_theme; let line=self.color_at(&args,5,th.track);
3741 self.draw_ui(&ling_ui::widgets::scanlines(x,y,w0,h0,dens, line));
3742 return Ok(Value::Unit);
3743 }
3744
3745 #[cfg(not(target_arch = "wasm32"))]
3747 "ui_bar" | "进度条" | "バー" | "막대" | "แถบ" => {
3748 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3749 let w0=self.arg_num(&args,2,160.)? as f32; let h0=self.arg_num(&args,3,16.)? as f32;
3750 let val=self.arg_num(&args,4,0.)? as f32; let max=self.arg_num(&args,5,1.)? as f32;
3751 let th=self.ui_theme; let fill=self.color_at(&args,6,th.primary);
3752 self.draw_ui(&ling_ui::widgets::bar(x,y,w0,h0, val/max.max(1e-6), fill, th.track));
3753 return Ok(Value::Unit);
3754 }
3755 #[cfg(not(target_arch = "wasm32"))]
3756 "ui_segbar" | "分段条" | "分割バー" | "분할막대" | "แถบแบ่ง" => {
3757 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3758 let w0=self.arg_num(&args,2,160.)? as f32; let h0=self.arg_num(&args,3,16.)? as f32;
3759 let val=self.arg_num(&args,4,0.)? as f32; let max=self.arg_num(&args,5,1.)? as f32;
3760 let segs=self.arg_num(&args,6,10.)? as usize;
3761 let th=self.ui_theme; let fill=self.color_at(&args,7,th.primary);
3762 self.draw_ui(&ling_ui::widgets::segbar(x,y,w0,h0, val/max.max(1e-6), segs, fill, th.track));
3763 return Ok(Value::Unit);
3764 }
3765 #[cfg(not(target_arch = "wasm32"))]
3766 "ui_gauge" | "仪表" | "ゲージ" | "게이지" | "มาตรวัด" => {
3767 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
3768 let r=self.arg_num(&args,2,50.)? as f32;
3769 let val=self.arg_num(&args,3,0.)? as f32; let max=self.arg_num(&args,4,1.)? as f32;
3770 let th=self.ui_theme; let needle=self.color_at(&args,5,th.warn);
3771 self.draw_ui(&ling_ui::widgets::gauge(cx,cy,r, val/max.max(1e-6), needle, th.accent, th.track));
3772 return Ok(Value::Unit);
3773 }
3774 #[cfg(not(target_arch = "wasm32"))]
3775 "ui_ring" | "环表" | "リングメーター" | "링미터" | "วงแหวนวัด" => {
3776 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
3777 let r=self.arg_num(&args,2,40.)? as f32;
3778 let val=self.arg_num(&args,3,0.)? as f32; let max=self.arg_num(&args,4,1.)? as f32;
3779 let th=self.ui_theme; let fill=self.color_at(&args,5,th.primary);
3780 self.draw_ui(&ling_ui::widgets::ring(cx,cy,r, val/max.max(1e-6), fill, th.track));
3781 return Ok(Value::Unit);
3782 }
3783 #[cfg(not(target_arch = "wasm32"))]
3784 "ui_vu" | "音量条" | "VUメーター" | "음량막대" | "มาตรเสียง" => {
3785 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3786 let w0=self.arg_num(&args,2,160.)? as f32; let h0=self.arg_num(&args,3,60.)? as f32;
3787 let levels=self.arg_list_f32(&args,4);
3788 let th=self.ui_theme; let fill=self.color_at(&args,5,th.primary);
3789 self.draw_ui(&ling_ui::widgets::vu(x,y,w0,h0, &levels, fill, th.warn));
3790 return Ok(Value::Unit);
3791 }
3792 #[cfg(not(target_arch = "wasm32"))]
3793 "ui_spark" | "迷你图" | "スパークライン" | "스파크라인" | "กราฟจิ๋ว" => {
3794 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3795 let w0=self.arg_num(&args,2,160.)? as f32; let h0=self.arg_num(&args,3,40.)? as f32;
3796 let vals=self.arg_list_f32(&args,4);
3797 let th=self.ui_theme; let line=self.color_at(&args,5,th.accent);
3798 self.draw_ui(&ling_ui::widgets::spark(x,y,w0,h0, &vals, line));
3799 return Ok(Value::Unit);
3800 }
3801 #[cfg(not(target_arch = "wasm32"))]
3802 "ui_battery" | "电池" | "バッテリー" | "배터리" | "แบตเตอรี่" => {
3803 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3804 let w0=self.arg_num(&args,2,50.)? as f32; let h0=self.arg_num(&args,3,22.)? as f32;
3805 let val=self.arg_num(&args,4,1.)? as f32; let max=self.arg_num(&args,5,1.)? as f32;
3806 let th=self.ui_theme; let fill=self.color_at(&args,6,th.accent);
3807 self.draw_ui(&ling_ui::widgets::battery(x,y,w0,h0, val/max.max(1e-6), fill, th.track, th.warn));
3808 return Ok(Value::Unit);
3809 }
3810
3811 #[cfg(not(target_arch = "wasm32"))]
3813 "ui_button" | "按钮" | "ボタン" | "버튼" | "ปุ่ม" => {
3814 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3815 let w0=self.arg_num(&args,2,120.)? as f32; let h0=self.arg_num(&args,3,40.)? as f32;
3816 let (mx,my,down)=self.mouse_now();
3817 let hover=ling_ui::holo::hit_rect(mx,my,x,y,w0,h0);
3818 let clicked = hover && down && !self.mouse_was_down;
3819 let th=self.ui_theme; let prim=self.color_at(&args,4,th.primary);
3820 self.draw_ui(&ling_ui::widgets::button(x,y,w0,h0, hover, down&&hover, prim, th.bg));
3821 return Ok(Value::Number(if clicked {1.0} else {0.0}));
3822 }
3823 #[cfg(not(target_arch = "wasm32"))]
3824 "ui_toggle" | "开关" | "トグル" | "토글" | "สวิตช์" => {
3825 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3826 let w0=self.arg_num(&args,2,52.)? as f32; let h0=self.arg_num(&args,3,24.)? as f32;
3827 let mut state=self.arg_num(&args,4,0.)? > 0.5;
3828 let (mx,my,down)=self.mouse_now();
3829 let hover=ling_ui::holo::hit_rect(mx,my,x,y,w0,h0);
3830 if hover && down && !self.mouse_was_down { state = !state; }
3831 let th=self.ui_theme; let on=self.color_at(&args,5,th.accent);
3832 self.draw_ui(&ling_ui::widgets::toggle(x,y,w0,h0, state, on, th.track));
3833 return Ok(Value::Number(if state {1.0} else {0.0}));
3834 }
3835 #[cfg(not(target_arch = "wasm32"))]
3836 "ui_slider" | "滑块" | "スライダー" | "슬라이더" | "แถบเลื่อน" => {
3837 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3838 let w0=self.arg_num(&args,2,160.)? as f32;
3839 let mut val=self.arg_num(&args,3,0.)? as f32;
3840 let mn=self.arg_num(&args,4,0.)? as f32; let mx_=self.arg_num(&args,5,1.)? as f32;
3841 let (mx,my,down)=self.mouse_now();
3842 let hover=ling_ui::holo::hit_rect(mx,my,x-8.0,y-10.0,w0+16.0,20.0);
3843 if hover && down {
3844 let frac=((mx-x)/w0).max(0.0).min(1.0);
3845 val = mn + (mx_-mn)*frac;
3846 }
3847 let frac=((val-mn)/(mx_-mn).abs().max(1e-6)).max(0.0).min(1.0);
3848 let th=self.ui_theme; let fill=self.color_at(&args,6,th.primary);
3849 self.draw_ui(&ling_ui::widgets::slider(x,y,w0, frac, hover, fill, th.track));
3850 return Ok(Value::Number(val as f64));
3851 }
3852 #[cfg(not(target_arch = "wasm32"))]
3853 "ui_checkbox" | "复选框" | "チェックボックス" | "체크박스" | "ช่องเลือก" => {
3854 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3855 let s=self.arg_num(&args,2,20.)? as f32;
3856 let mut checked=self.arg_num(&args,3,0.)? > 0.5;
3857 let (mx,my,down)=self.mouse_now();
3858 let hover=ling_ui::holo::hit_rect(mx,my,x,y,s,s);
3859 if hover && down && !self.mouse_was_down { checked = !checked; }
3860 let th=self.ui_theme; let prim=self.color_at(&args,4,th.primary);
3861 self.draw_ui(&ling_ui::widgets::checkbox(x,y,s, checked, hover, prim, th.track));
3862 return Ok(Value::Number(if checked {1.0} else {0.0}));
3863 }
3864 #[cfg(not(target_arch = "wasm32"))]
3865 "ui_tabs" | "标签页" | "タブ" | "탭" | "แท็บ" => {
3866 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3867 let w0=self.arg_num(&args,2,240.)? as f32; let h0=self.arg_num(&args,3,28.)? as f32;
3868 let count=self.arg_num(&args,4,3.)? as usize;
3869 let mut active=self.arg_num(&args,5,0.)? as i32;
3870 let (mx,my,down)=self.mouse_now();
3871 let mut hover=-1;
3872 if my>=y && my<=y+h0 && mx>=x && mx<=x+w0 && count>0 {
3873 hover = (((mx-x)/(w0/count as f32)) as i32).max(0).min(count as i32-1);
3874 if down && !self.mouse_was_down { active = hover; }
3875 }
3876 let th=self.ui_theme; let prim=self.color_at(&args,6,th.primary);
3877 self.draw_ui(&ling_ui::widgets::tabs(x,y,w0,h0, count, active as usize, hover, prim, th.track));
3878 return Ok(Value::Number(active as f64));
3879 }
3880 #[cfg(not(target_arch = "wasm32"))]
3881 "ui_progress" | "进度" | "プログレス" | "진행바" | "ความคืบหน้า" => {
3882 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3883 let w0=self.arg_num(&args,2,200.)? as f32; let h0=self.arg_num(&args,3,12.)? as f32;
3884 let frac=self.arg_num(&args,4,0.)? as f32;
3885 let th=self.ui_theme; let fill=self.color_at(&args,5,th.accent);
3886 self.draw_ui(&ling_ui::widgets::progress(x,y,w0,h0, frac, fill, th.track));
3887 return Ok(Value::Unit);
3888 }
3889 #[cfg(not(target_arch = "wasm32"))]
3890 "ui_tooltip" | "提示框" | "ツールチップ" | "툴팁" | "คำแนะนำ" => {
3891 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3892 let w0=self.arg_num(&args,2,120.)? as f32; let h0=self.arg_num(&args,3,28.)? as f32;
3893 let th=self.ui_theme; let prim=self.color_at(&args,4,th.primary);
3894 self.draw_ui(&ling_ui::widgets::tooltip(x,y,w0,h0, prim, th.bg));
3895 return Ok(Value::Unit);
3896 }
3897 #[cfg(not(target_arch = "wasm32"))]
3898 "ui_stepper" | "步进器" | "ステッパー" | "스테퍼" | "ตัวปรับค่า" => {
3899 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3900 let w0=self.arg_num(&args,2,120.)? as f32; let h0=self.arg_num(&args,3,28.)? as f32;
3901 let mut val=self.arg_num(&args,4,0.)? as f32; let step=self.arg_num(&args,5,1.)? as f32;
3902 let (mx,my,down)=self.mouse_now();
3903 let hm=ling_ui::holo::hit_rect(mx,my,x,y,h0,h0);
3904 let hp=ling_ui::holo::hit_rect(mx,my,x+w0-h0,y,h0,h0);
3905 if down && !self.mouse_was_down { if hm { val -= step; } if hp { val += step; } }
3906 let th=self.ui_theme; let prim=self.color_at(&args,6,th.primary);
3907 self.draw_ui(&ling_ui::widgets::stepper(x,y,w0,h0, hm, hp, prim, th.track));
3908 return Ok(Value::Number(val as f64));
3909 }
3910
3911 #[cfg(not(target_arch = "wasm32"))]
3913 "ui_healthbar" | "血条" | "体力バー" | "체력바" | "แถบพลังชีวิต" => {
3914 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3915 let w0=self.arg_num(&args,2,180.)? as f32; let h0=self.arg_num(&args,3,16.)? as f32;
3916 let val=self.arg_num(&args,4,1.)? as f32; let max=self.arg_num(&args,5,1.)? as f32;
3917 let pulse=self.arg_num(&args,6,0.)? as f32;
3918 let th=self.ui_theme; let full=self.color_at(&args,7,th.accent);
3919 self.draw_ui(&ling_ui::widgets::healthbar(x,y,w0,h0, val/max.max(1e-6), pulse, full, th.warn, th.track));
3920 return Ok(Value::Unit);
3921 }
3922 #[cfg(not(target_arch = "wasm32"))]
3923 "ui_cooldown" | "冷却" | "クールダウン" | "쿨다운" | "คูลดาวน์" => {
3924 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
3925 let r=self.arg_num(&args,2,28.)? as f32; let frac=self.arg_num(&args,3,0.)? as f32;
3926 let th=self.ui_theme; let fill=self.color_at(&args,4,th.primary);
3927 self.draw_ui(&ling_ui::widgets::cooldown(cx,cy,r, frac, fill, th.track));
3928 return Ok(Value::Unit);
3929 }
3930 #[cfg(not(target_arch = "wasm32"))]
3931 "ui_counter" | "计数器" | "カウンター" | "카운터" | "ตัวนับ" => {
3932 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3933 let dw=self.arg_num(&args,2,14.)? as f32; let dh=self.arg_num(&args,3,24.)? as f32;
3934 let val=self.arg_num(&args,4,0.)? as i64; let digits=self.arg_num(&args,5,4.)? as usize;
3935 let th=self.ui_theme; let on=self.color_at(&args,6,th.primary);
3936 let off=ling_ui::widgets::shade(th.track,0.5);
3937 self.draw_ui(&ling_ui::widgets::counter(x,y,dw,dh, val, digits, on, off));
3938 return Ok(Value::Unit);
3939 }
3940 #[cfg(not(target_arch = "wasm32"))]
3941 "ui_minimap" | "小地图" | "ミニマップ" | "미니맵" | "แผนที่ย่อ" => {
3942 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3943 let w0=self.arg_num(&args,2,140.)? as f32; let h0=self.arg_num(&args,3,140.)? as f32;
3944 let th=self.ui_theme; let prim=self.color_at(&args,4,th.primary);
3945 self.draw_ui(&ling_ui::widgets::minimap(x,y,w0,h0, prim, th.bg));
3946 return Ok(Value::Unit);
3947 }
3948 #[cfg(not(target_arch = "wasm32"))]
3949 "ui_dpad" | "方向键" | "方向パッド" | "방향패드" | "ปุ่มทิศทาง" => {
3950 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
3951 let r=self.arg_num(&args,2,50.)? as f32;
3952 let (mx,my,down)=self.mouse_now();
3953 let mut dir=0;
3954 if down {
3955 let (dx,dy)=(mx-cx, my-cy);
3956 if dx*dx+dy*dy <= r*r {
3957 if dx.abs() > dy.abs() { dir = if dx>0.0 {2} else {4}; }
3958 else { dir = if dy>0.0 {3} else {1}; }
3959 }
3960 }
3961 let th=self.ui_theme; let prim=self.color_at(&args,3,th.primary);
3962 self.draw_ui(&ling_ui::widgets::dpad(cx,cy,r, dir, prim, th.track));
3963 return Ok(Value::Number(dir as f64));
3964 }
3965 #[cfg(not(target_arch = "wasm32"))]
3966 "ui_slotgrid" | "物品格" | "スロットグリッド" | "슬롯격자" | "ช่องไอเทม" => {
3967 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3968 let cols=self.arg_num(&args,2,4.)? as usize; let rows=self.arg_num(&args,3,1.)? as usize;
3969 let cell=self.arg_num(&args,4,36.)? as f32; let sel=self.arg_num(&args,5,-1.)? as i32;
3970 let th=self.ui_theme; let prim=self.color_at(&args,6,th.primary);
3971 self.draw_ui(&ling_ui::widgets::slotgrid(x,y,cols,rows,cell, sel, prim, th.track));
3972 return Ok(Value::Unit);
3973 }
3974 #[cfg(not(target_arch = "wasm32"))]
3975 "ui_vignette" | "暗角" | "ビネット" | "비네트" | "ขอบมืด" => {
3976 let intensity=self.arg_num(&args,0,0.5)? as f32;
3977 let (w,h)={ let g=self.gfx.borrow(); (g.width as f32, g.height as f32) };
3978 let th=self.ui_theme; let col=self.color_at(&args,1,th.warn);
3979 self.draw_ui(&ling_ui::widgets::vignette(w,h, intensity, col));
3980 return Ok(Value::Unit);
3981 }
3982
3983 #[cfg(not(target_arch = "wasm32"))]
3985 "ui_gauge3d" | "立体仪表" | "立体ゲージ" | "입체게이지" | "มาตรวัด3มิติ" => {
3986 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
3987 let r=self.arg_num(&args,2,50.)? as f32;
3988 let val=self.arg_num(&args,3,0.)? as f32; let max=self.arg_num(&args,4,1.)? as f32;
3989 let spin=self.arg_num(&args,5,0.)? as f32;
3990 let th=self.ui_theme; let fill=self.color_at(&args,6,th.primary);
3991 self.draw_ui(&ling_ui::widgets::gauge3d(cx,cy,r, val/max.max(1e-6), spin, fill, th.track));
3992 return Ok(Value::Unit);
3993 }
3994 #[cfg(not(target_arch = "wasm32"))]
3995 "ui_panel3d" | "立体面板" | "立体パネル" | "입체패널" | "แผง3มิติ" => {
3996 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
3997 let w0=self.arg_num(&args,2,200.)? as f32; let h0=self.arg_num(&args,3,120.)? as f32;
3998 let depth=self.arg_num(&args,4,14.)? as f32;
3999 let th=self.ui_theme; let prim=self.color_at(&args,5,th.primary);
4000 self.draw_ui(&ling_ui::widgets::panel3d(x,y,w0,h0,depth, prim, th.bg));
4001 return Ok(Value::Unit);
4002 }
4003 #[cfg(not(target_arch = "wasm32"))]
4004 "ui_radar3d" | "立体雷达" | "立体レーダー" | "입체레이더" | "เรดาร์3มิติ" => {
4005 let cx=self.arg_num(&args,0,0.)? as f32; let cy=self.arg_num(&args,1,0.)? as f32;
4006 let r=self.arg_num(&args,2,60.)? as f32; let tilt=self.arg_num(&args,3,0.9)? as f32;
4007 let sweep=self.arg_num(&args,4,0.)? as f32;
4008 let th=self.ui_theme; let prim=self.color_at(&args,5,th.primary);
4009 self.draw_ui(&ling_ui::widgets::radar3d(cx,cy,r,tilt,sweep, prim, th.track));
4010 return Ok(Value::Unit);
4011 }
4012
4013 #[cfg(not(target_arch = "wasm32"))]
4015 "audio_blip" | "提示音" | "ビープ音" | "효과음" | "เสียงบี๊บ" => {
4016 let freq=self.arg_num(&args,0,660.)? as f32;
4017 let dur=self.arg_num(&args,1,0.08)? as f32;
4018 let wave=Wave::from_name(&self.arg_str(&args,2,"sine"));
4019 let amp=self.arg_num(&args,3,0.25)? as f32;
4020 if let Some(audio)=&self.audio { audio.blip(freq, amp, dur, wave); }
4021 return Ok(Value::Unit);
4022 }
4023 #[cfg(not(target_arch = "wasm32"))]
4024 "ui_sound" | "界面音" | "UI音" | "인터페이스음" | "เสียงปุ่ม" => {
4025 let name=self.arg_str(&args,0,"click");
4026 if let Some(audio)=&self.audio {
4027 match name.as_str() {
4028 "hover" => audio.blip(880.0, 0.10, 0.04, Wave::Sine),
4029 "confirm" => { audio.blip(660.0, 0.22, 0.07, Wave::Square); audio.blip(990.0, 0.18, 0.10, Wave::Square); }
4030 "error" => { audio.blip(180.0, 0.30, 0.16, Wave::Saw); audio.blip(140.0, 0.30, 0.18, Wave::Saw); }
4031 "toggle" => audio.blip(520.0, 0.22, 0.05, Wave::Triangle),
4032 "tick" => audio.blip(1500.0, 0.12, 0.02, Wave::Square),
4033 _ => audio.blip(720.0, 0.26, 0.05, Wave::Square), }
4035 }
4036 return Ok(Value::Unit);
4037 }
4038
4039 #[cfg(not(target_arch = "wasm32"))]
4047 "music_load" | "载入音乐" | "音楽読込" | "음악로드" | "โหลดเพลง" => {
4048 let path = self.arg_str(&args, 0, "");
4049 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4050 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4051 else { path.clone() };
4052 match ling_music::load(&resolved) {
4053 Ok(t) => { let id = self.tracks.len(); self.tracks.push(t); return Ok(Value::Number(id as f64)); }
4054 Err(e) => { eprintln!("music_load failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
4055 }
4056 }
4057 #[cfg(not(target_arch = "wasm32"))]
4058 "music_duration" | "音乐时长" | "音楽長さ" | "음악길이" | "ความยาวเพลง" => {
4059 let id = self.arg_num(&args,0,0.0)? as i64;
4060 let d = self.tracks.get(id as usize).map(|t| t.duration).unwrap_or(0.0);
4061 return Ok(Value::Number(d as f64));
4062 }
4063 #[cfg(not(target_arch = "wasm32"))]
4064 "music_bpm" | "节拍速度" | "テンポ" | "템포" | "จังหวะต่อนาที" => {
4065 let id = self.arg_num(&args,0,0.0)? as i64;
4066 let b = self.tracks.get(id as usize).map(|t| ling_music::analysis::bpm(&t.mono, t.rate)).unwrap_or(0.0);
4067 return Ok(Value::Number(b as f64));
4068 }
4069 #[cfg(not(target_arch = "wasm32"))]
4070 "music_key" | "调性" | "調性" | "조성" | "คีย์เพลง" => {
4071 let id = self.arg_num(&args,0,0.0)? as i64;
4072 let k = self.tracks.get(id as usize).map(|t| ling_music::analysis::key_name(&t.mono, t.rate)).unwrap_or_default();
4073 return Ok(Value::Str(k));
4074 }
4075 #[cfg(not(target_arch = "wasm32"))]
4076 "music_onsets" | "音符起点" | "オンセット" | "온셋" | "จุดเริ่มเสียง" => {
4077 let id = self.arg_num(&args,0,0.0)? as i64;
4078 let v = self.tracks.get(id as usize).map(|t| ling_music::analysis::onsets(&t.mono, t.rate)).unwrap_or_default();
4079 return Ok(Value::List(v.into_iter().map(|x| Value::Number(x as f64)).collect()));
4080 }
4081 #[cfg(not(target_arch = "wasm32"))]
4082 "music_beat_grid" | "节拍网格" | "ビートグリッド" | "비트그리드" | "กริดจังหวะ" => {
4083 let id = self.arg_num(&args,0,0.0)? as i64;
4084 let beats = self.tracks.get(id as usize).map(|t| {
4085 let b = ling_music::analysis::bpm(&t.mono, t.rate);
4086 ling_music::analysis::beat_grid(&t.mono, t.rate, b)
4087 }).unwrap_or_default();
4088 return Ok(Value::List(beats.into_iter().map(|x| Value::Number(x as f64)).collect()));
4089 }
4090
4091 #[cfg(not(target_arch = "wasm32"))]
4093 "music_play" | "播放音乐" | "音楽再生" | "음악재생" | "เล่นเพลง" => {
4094 let id = self.arg_num(&args,0,0.0)? as i64;
4095 if self.ensure_music() {
4096 let track = self.tracks.get(id as usize).map(|t| (t.stereo.clone(), t.rate));
4097 if let (Some((st, rate)), Some(m)) = (track, &self.music) { m.set_track(st, rate); m.play(); }
4098 else if let Some(m) = &self.music { m.play(); }
4099 }
4100 return Ok(Value::Unit);
4101 }
4102 #[cfg(not(target_arch = "wasm32"))]
4103 "music_pause" | "暂停音乐" | "音楽一時停止" | "음악일시정지" | "หยุดเพลงชั่วคราว" => {
4104 if let Some(m) = &self.music { m.pause(); } return Ok(Value::Unit);
4105 }
4106 #[cfg(not(target_arch = "wasm32"))]
4107 "music_stop" | "停止音乐" | "音楽停止" | "음악정지" | "หยุดเพลง" => {
4108 if let Some(m) = &self.music { m.stop(); } return Ok(Value::Unit);
4109 }
4110 #[cfg(not(target_arch = "wasm32"))]
4111 "music_seek" | "定位音乐" | "音楽シーク" | "음악탐색" | "ค้นหาเพลง" => {
4112 let sec = self.arg_num(&args,0,0.0)? as f32;
4113 if let Some(m) = &self.music { m.seek(sec); } return Ok(Value::Unit);
4114 }
4115 #[cfg(not(target_arch = "wasm32"))]
4116 "music_pos" | "音乐位置" | "音楽位置" | "음악위치" | "ตำแหน่งเพลง" => {
4117 let p = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
4118 return Ok(Value::Number(p as f64));
4119 }
4120 #[cfg(not(target_arch = "wasm32"))]
4121 "music_volume" | "音乐音量" | "音楽音量" | "음악음량" | "ระดับเพลง" => {
4122 let v = self.arg_num(&args,0,0.8)? as f32;
4123 if self.ensure_music() { if let Some(m) = &self.music { m.set_volume(v); } }
4124 return Ok(Value::Unit);
4125 }
4126
4127 #[cfg(not(target_arch = "wasm32"))]
4129 "music_patch" | "乐器音色" | "音色読込" | "악기패치" | "แพตช์เครื่องดนตรี" => {
4130 let path = self.arg_str(&args, 0, "");
4131 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4132 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4133 else { path.clone() };
4134 if !self.ensure_music() { return Ok(Value::Number(-1.0)); }
4135 match ling_music::patch::from_path(&resolved) {
4136 Ok(p) => { let id = self.music.as_ref().unwrap().add_patch(p); return Ok(Value::Number(id as f64)); }
4137 Err(e) => { eprintln!("music_patch failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
4138 }
4139 }
4140 #[cfg(not(target_arch = "wasm32"))]
4141 "music_note" | "弹音符" | "音符演奏" | "음표연주" | "เล่นโน้ต" => {
4142 let inst = self.arg_num(&args,0,0.0)? as usize;
4143 let midi = self.pitch_arg(&args, 1, 60);
4144 let dur = self.arg_num(&args,2,0.5)? as f32;
4145 let vel = self.arg_num(&args,3,0.9)? as f32;
4146 if self.ensure_music() { if let Some(m) = &self.music { m.note(inst, midi, vel, dur); } }
4147 return Ok(Value::Unit);
4148 }
4149 #[cfg(not(target_arch = "wasm32"))]
4150 "music_note_on" | "音符开始" | "音符オン" | "음표켜기" | "โน้ตเริ่ม" => {
4151 let inst = self.arg_num(&args,0,0.0)? as usize;
4152 let midi = self.pitch_arg(&args, 1, 60);
4153 let vel = self.arg_num(&args,2,0.9)? as f32;
4154 if self.ensure_music() { if let Some(m) = &self.music { m.note_on(inst, midi, vel); } }
4155 return Ok(Value::Unit);
4156 }
4157 #[cfg(not(target_arch = "wasm32"))]
4158 "music_note_off" | "音符结束" | "音符オフ" | "음표끄기" | "โน้ตจบ" => {
4159 let inst = self.arg_num(&args,0,0.0)? as usize;
4160 let midi = self.pitch_arg(&args, 1, 60);
4161 if let Some(m) = &self.music { m.note_off(inst, midi); }
4162 return Ok(Value::Unit);
4163 }
4164
4165 #[cfg(not(target_arch = "wasm32"))]
4167 "music_judge" | "判定" | "判定する" | "판정" | "ตัดสินจังหวะ" => {
4168 let delta_ms = self.arg_num(&args,0,9999.0)? as f32;
4169 return Ok(Value::Number(ling_music::Grade::judge(delta_ms).index() as f64));
4170 }
4171 #[cfg(not(target_arch = "wasm32"))]
4172 "music_grade_name" | "判定名" | "判定名称" | "판정이름" | "ชื่อการตัดสิน" => {
4173 let idx = self.arg_num(&args,0,4.0)? as i32;
4174 return Ok(Value::Str(ling_music::Grade::from_index(idx).name().to_string()));
4175 }
4176
4177 #[cfg(not(target_arch = "wasm32"))]
4179 "music_lrc" | "载入歌词" | "歌詞読込" | "가사로드" | "โหลดเนื้อเพลง" => {
4180 let path = self.arg_str(&args, 0, "");
4181 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4182 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4183 else { path.clone() };
4184 match std::fs::read_to_string(&resolved) {
4185 Ok(text) => { let id = self.lyrics.len(); self.lyrics.push(ling_music::Lyrics::parse(&text)); return Ok(Value::Number(id as f64)); }
4186 Err(e) => { eprintln!("music_lrc failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
4187 }
4188 }
4189 #[cfg(not(target_arch = "wasm32"))]
4190 "music_lyric" | "当前歌词" | "現在歌詞" | "현재가사" | "เนื้อเพลงปัจจุบัน" => {
4191 let id = self.arg_num(&args,0,0.0)? as i64;
4192 let t = self.arg_num(&args,1,0.0)? as f32;
4193 let line = self.lyrics.get(id as usize).map(|l| l.line_at(t).to_string()).unwrap_or_default();
4194 return Ok(Value::Str(line));
4195 }
4196 #[cfg(not(target_arch = "wasm32"))]
4197 "music_mic_pitch" | "麦克风音高" | "マイク音程" | "마이크음정" | "ระดับเสียงไมค์" => {
4198 let hz = if let Some(mic) = self.mic.as_ref() {
4199 let s = mic.latest_samples();
4200 let rate = mic.sample_rate();
4201 ling_music::pitch::detect(&s, rate).unwrap_or(0.0)
4202 } else { 0.0 };
4203 return Ok(Value::Number(hz as f64));
4204 }
4205 #[cfg(not(target_arch = "wasm32"))]
4206 "music_note_name" | "音名" | "音名称" | "음이름" | "ชื่อโน้ต" => {
4207 let hz = self.arg_num(&args,0,0.0)? as f32;
4208 return Ok(Value::Str(ling_music::note::hz_to_name(hz)));
4209 }
4210 #[cfg(not(target_arch = "wasm32"))]
4211 "music_hz" | "音符频率" | "音符周波数" | "음표주파수" | "ความถี่โน้ต" => {
4212 let midi = self.pitch_arg(&args, 0, 69);
4213 return Ok(Value::Number(ling_music::note::midi_to_hz(midi as f32) as f64));
4214 }
4215 #[cfg(not(target_arch = "wasm32"))]
4216 "music_pitch_score" | "音准评分" | "音程スコア" | "음정점수" | "คะแนนเสียง" => {
4217 let hz = self.arg_num(&args,0,0.0)? as f32;
4218 let target = self.arg_num(&args,1,0.0)? as f32;
4219 return Ok(Value::Number(ling_music::karaoke::pitch_score(hz, target) as f64));
4220 }
4221
4222 #[cfg(not(target_arch = "wasm32"))]
4224 "music_midi_load" | "载入MIDI" | "MIDI読込" | "미디로드" | "โหลดมิดี" => {
4225 let path = self.arg_str(&args, 0, "");
4226 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4227 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4228 else { path.clone() };
4229 match ling_music::midi::load(&resolved) {
4230 Ok(m) => { let id = self.midis.len(); self.midis.push(m); return Ok(Value::Number(id as f64)); }
4231 Err(e) => { eprintln!("music_midi_load failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
4232 }
4233 }
4234 #[cfg(not(target_arch = "wasm32"))]
4235 "music_midi_count" | "MIDI数量" | "MIDI数" | "미디수" | "จำนวนมิดี" => {
4236 let id = self.arg_num(&args,0,0.0)? as i64;
4237 let n = self.midis.get(id as usize).map(|m| m.notes.len()).unwrap_or(0);
4238 return Ok(Value::Number(n as f64));
4239 }
4240 #[cfg(not(target_arch = "wasm32"))]
4242 "music_midi_notes" | "MIDI音符" | "MIDIノート" | "미디음표" | "โน้ตมิดี" => {
4243 let id = self.arg_num(&args,0,0.0)? as i64;
4244 let mut out = Vec::new();
4245 if let Some(m) = self.midis.get(id as usize) {
4246 for n in &m.notes { out.push(Value::Number(n.time as f64)); out.push(Value::Number(n.midi as f64)); }
4247 }
4248 return Ok(Value::List(out));
4249 }
4250 #[cfg(not(target_arch = "wasm32"))]
4252 "music_midi_bars" | "MIDI音条" | "MIDIバー" | "미디바" | "แท่งมิดี" => {
4253 let id = self.arg_num(&args,0,0.0)? as i64;
4254 let mut out = Vec::new();
4255 if let Some(m) = self.midis.get(id as usize) {
4256 for n in &m.notes {
4257 out.push(Value::Number(n.time as f64));
4258 out.push(Value::Number(n.midi as f64));
4259 out.push(Value::Number(n.dur as f64));
4260 }
4261 }
4262 return Ok(Value::List(out));
4263 }
4264
4265 #[cfg(not(target_arch = "wasm32"))]
4267 "music_fft" | "音乐频谱" | "音楽スペクトル" | "음악스펙트럼" | "สเปกตรัมเพลง" => {
4268 let id = self.arg_num(&args,0,0.0)? as i64;
4269 let nbands = self.arg_num(&args,1,16.0)? as usize;
4270 let pos = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
4271 if let Some(t) = self.tracks.get(id as usize) {
4272 let idx = (pos * t.rate as f32) as usize;
4273 let end = (idx + 2048).min(t.mono.len());
4274 if end > idx + 64 {
4275 self.fft.borrow_mut().push_samples(&t.mono[idx..end]);
4276 }
4277 }
4278 let bands = self.fft.borrow().freq_bands(nbands);
4279 return Ok(Value::List(bands.into_iter().map(|x| Value::Number(x as f64)).collect()));
4280 }
4281
4282 #[cfg(not(target_arch = "wasm32"))]
4284 "audio_sfx" | "音效" | "空間効果音" | "공간효과음" | "เสียงเอฟเฟกต์" => {
4285 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;
4286 let w=self.arg_num(&args,3,1.0)? as f32; let freq=self.arg_num(&args,4,440.0)? as f32;
4287 let amp=self.arg_num(&args,5,0.3)? as f32; let dur=self.arg_num(&args,6,0.15)? as f32;
4288 let wave=Wave::from_name(&self.arg_str(&args,7,"sine"));
4289 if let Some(a)=&self.audio { a.sfx(x,y,z,w,freq,amp,dur,wave); }
4290 return Ok(Value::Unit);
4291 }
4292 #[cfg(not(target_arch = "wasm32"))]
4294 "audio_sample_load" | "载入采样" | "サンプル読込" | "샘플로드" | "โหลดตัวอย่างเสียง" => {
4295 let path = self.arg_str(&args, 0, "");
4296 let resolved = if std::path::Path::new(&path).exists() { path.clone() }
4297 else if let Some(d) = &self.source_dir { d.join(&path).to_string_lossy().into_owned() }
4298 else { path.clone() };
4299 match ling_music::load(&resolved) {
4300 Ok(t) => {
4301 if let Some(a)=&self.audio { return Ok(Value::Number(a.add_sample(t.mono, t.rate) as f64)); }
4302 return Ok(Value::Number(-1.0));
4303 }
4304 Err(e) => { eprintln!("audio_sample_load failed ({path}): {e}"); return Ok(Value::Number(-1.0)); }
4305 }
4306 }
4307 #[cfg(not(target_arch = "wasm32"))]
4308 "audio_sample_play" | "播放采样" | "サンプル再生" | "샘플재생" | "เล่นตัวอย่างเสียง" => {
4309 let id=self.arg_num(&args,0,0.0)? as usize;
4310 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;
4311 let w=self.arg_num(&args,4,1.0)? as f32; let vol=self.arg_num(&args,5,1.0)? as f32;
4312 let looping=self.arg_num(&args,6,0.0)? > 0.5;
4313 let v = self.audio.as_ref().map(|a| a.play_sample(id,x,y,z,w,vol,looping)).unwrap_or(0);
4314 return Ok(Value::Number(v as f64));
4315 }
4316 #[cfg(not(target_arch = "wasm32"))]
4317 "audio_sample_stop" | "停止采样" | "サンプル停止" | "샘플정지" | "หยุดตัวอย่างเสียง" => {
4318 let v=self.arg_num(&args,0,0.0)? as u32;
4319 if let Some(a)=&self.audio { a.stop_sample(v); }
4320 return Ok(Value::Unit);
4321 }
4322 #[cfg(not(target_arch = "wasm32"))]
4324 "audio_fx_delay" | "回声" | "ディレイ効果" | "딜레이" | "เสียงสะท้อน" => {
4325 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;
4326 if let Some(a)=&self.audio { a.fx_delay(time,fb,mix); }
4327 return Ok(Value::Unit);
4328 }
4329 #[cfg(not(target_arch = "wasm32"))]
4330 "audio_fx_reverb" | "混响" | "リバーブ" | "리버브" | "เสียงก้อง" => {
4331 let mix=self.arg_num(&args,0,0.3)? as f32;
4332 if let Some(a)=&self.audio { a.fx_reverb(mix); }
4333 return Ok(Value::Unit);
4334 }
4335 #[cfg(not(target_arch = "wasm32"))]
4336 "audio_fx_lowpass" | "低通滤波" | "ローパス" | "저역통과" | "กรองความถี่ต่ำ" => {
4337 let cutoff=self.arg_num(&args,0,1.0)? as f32;
4338 if let Some(a)=&self.audio { a.fx_lowpass(cutoff); }
4339 return Ok(Value::Unit);
4340 }
4341
4342 #[cfg(not(target_arch = "wasm32"))]
4349 "soft_ball" | "软球" | "ソフトボール" | "소프트볼" | "ลูกบอลนุ่ม" => {
4350 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;
4351 let r=self.arg_num(&args,3,1.0)? as f32;
4352 let b = ling_physics::soft::SoftBody::sphere(ling_physics::Vec3::new(x,y,z), r, 8, 12, 1.0);
4353 let id = self.soft_bodies.len(); self.soft_bodies.push(b);
4354 return Ok(Value::Number(id as f64));
4355 }
4356 #[cfg(not(target_arch = "wasm32"))]
4357 "soft_step" | "软体步进" | "ソフト更新" | "소프트스텝" | "ก้าวนุ่ม" => {
4358 let id=self.arg_num(&args,0,0.)? as usize; let dt=self.arg_num(&args,1,0.016)? as f32;
4359 let gy=self.arg_num(&args,2,15.0)? as f32;
4360 if let Some(b)=self.soft_bodies.get_mut(id) { b.integrate(dt, ling_physics::Vec3::new(0.0,gy,0.0), 4); }
4361 return Ok(Value::Unit);
4362 }
4363 #[cfg(not(target_arch = "wasm32"))]
4364 "soft_bounce" | "软体落地" | "ソフト着地" | "소프트바운스" | "เด้งนุ่ม" => {
4365 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;
4366 if let Some(b)=self.soft_bodies.get_mut(id) { b.floor_collision(fy, rest); }
4367 return Ok(Value::Unit);
4368 }
4369 #[cfg(not(target_arch = "wasm32"))]
4370 "soft_contain" | "软体边界" | "ソフト箱" | "소프트경계" | "กล่องนุ่ม" => {
4371 let id=self.arg_num(&args,0,0.)? as usize;
4372 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;
4373 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;
4374 let rest=self.arg_num(&args,7,0.6)? as f32;
4375 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); }
4376 return Ok(Value::Unit);
4377 }
4378 #[cfg(not(target_arch = "wasm32"))]
4379 "soft_kick" | "软体踢" | "ソフト衝撃" | "소프트킥" | "เตะนุ่ม" => {
4380 let id=self.arg_num(&args,0,0.)? as usize;
4381 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;
4382 let s=self.arg_num(&args,4,0.1)? as f32;
4383 if let Some(b)=self.soft_bodies.get_mut(id) { b.kick(ling_physics::Vec3::new(dx,dy,dz), s); }
4384 return Ok(Value::Unit);
4385 }
4386 #[cfg(not(target_arch = "wasm32"))]
4389 "soft_spin" | "软体自旋" | "ソフト回転" | "소프트회전" | "หมุนนุ่ม" => {
4390 let id=self.arg_num(&args,0,0.)? as usize;
4391 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;
4392 let rate=self.arg_num(&args,4,0.1)? as f32;
4393 if let Some(b)=self.soft_bodies.get_mut(id) { b.spin(ling_physics::Vec3::new(ax,ay,az), rate); }
4394 return Ok(Value::Unit);
4395 }
4396 #[cfg(not(target_arch = "wasm32"))]
4397 "soft_deform" | "形变量" | "変形量" | "변형량" | "ความบิดเบี้ยว" => {
4398 let id=self.arg_num(&args,0,0.)? as usize;
4399 let d=self.soft_bodies.get(id).map(|b| b.deformation()).unwrap_or(0.0);
4400 return Ok(Value::Number(d as f64));
4401 }
4402 #[cfg(not(target_arch = "wasm32"))]
4405 "soft_angular_speed" | "软体角速" | "ソフト角速度" | "소프트각속도" | "ความเร็วเชิงมุมนุ่ม" => {
4406 let id=self.arg_num(&args,0,0.)? as usize;
4407 let w=self.soft_bodies.get(id).map(|b| b.angular_speed()).unwrap_or(0.0);
4408 return Ok(Value::Number(w as f64));
4409 }
4410 #[cfg(not(target_arch = "wasm32"))]
4411 "soft_centroid" | "软体质心" | "ソフト重心" | "소프트중심" | "จุดศูนย์กลางนุ่ม" => {
4412 let id=self.arg_num(&args,0,0.)? as usize;
4413 let c=self.soft_bodies.get(id).map(|b| b.centroid()).unwrap_or(ling_physics::Vec3::ZERO);
4414 return Ok(Value::List(vec![Value::Number(c.x as f64),Value::Number(c.y as f64),Value::Number(c.z as f64)]));
4415 }
4416 #[cfg(not(target_arch = "wasm32"))]
4418 "soft_nodes" | "软体节点" | "ソフト節点" | "소프트노드" | "จุดนุ่ม" => {
4419 let id=self.arg_num(&args,0,0.)? as usize;
4420 let mut out=Vec::new();
4421 if let Some(b)=self.soft_bodies.get(id) {
4422 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)); }
4423 }
4424 return Ok(Value::List(out));
4425 }
4426
4427 #[cfg(not(target_arch = "wasm32"))]
4429 "rb_add" | "刚体添加" | "剛体追加" | "강체추가" | "เพิ่มวัตถุแข็ง" => {
4430 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;
4431 let mass=self.arg_num(&args,3,1.0)? as f32;
4432 let mut b = ling_physics::rigid::RigidBody::new(ling_physics::Vec3::new(x,y,z), mass);
4433 b.restitution = 0.6;
4434 return Ok(Value::Number(self.rigid_world.add(b) as f64));
4435 }
4436 #[cfg(not(target_arch = "wasm32"))]
4437 "rb_torque" | "扭矩" | "トルク" | "토크" | "แรงบิด" => {
4438 let i=self.arg_num(&args,0,0.)? as usize;
4439 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;
4440 if let Some(b)=self.rigid_world.bodies.get_mut(i) { b.apply_torque(ling_physics::Vec3::new(tx,ty,tz)); }
4441 return Ok(Value::Unit);
4442 }
4443 #[cfg(not(target_arch = "wasm32"))]
4444 "rb_spin" | "自旋" | "スピン" | "스핀" | "หมุน" => {
4445 let i=self.arg_num(&args,0,0.)? as usize;
4446 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;
4447 if let Some(b)=self.rigid_world.bodies.get_mut(i) { b.apply_spin(ling_physics::Vec3::new(wx,wy,wz)); }
4448 return Ok(Value::Unit);
4449 }
4450 #[cfg(not(target_arch = "wasm32"))]
4451 "rb_impulse" | "刚体冲量" | "剛体インパルス" | "강체충격" | "แรงดลแข็ง" => {
4452 let i=self.arg_num(&args,0,0.)? as usize;
4453 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;
4454 if let Some(b)=self.rigid_world.bodies.get_mut(i) { b.apply_impulse(ling_physics::Vec3::new(ix,iy,iz)); }
4455 return Ok(Value::Unit);
4456 }
4457 #[cfg(not(target_arch = "wasm32"))]
4458 "rb_floor" | "刚体落地" | "剛体着地" | "강체바닥" | "พื้นแข็ง" => {
4459 let i=self.arg_num(&args,0,0.)? as usize; let fy=self.arg_num(&args,1,0.)? as f32;
4460 let rest=self.arg_num(&args,2,0.6)? as f32; let fric=self.arg_num(&args,3,0.6)? as f32;
4461 if let Some(b)=self.rigid_world.bodies.get_mut(i) { b.bounce_floor(fy, rest, fric); }
4462 return Ok(Value::Unit);
4463 }
4464 #[cfg(not(target_arch = "wasm32"))]
4465 "rb_gravity" | "刚体重力" | "剛体重力" | "강체중력" | "แรงโน้มถ่วงแข็ง" => {
4466 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;
4467 self.rigid_world.gravity = ling_physics::Vec3::new(gx,gy,gz);
4468 return Ok(Value::Unit);
4469 }
4470 #[cfg(not(target_arch = "wasm32"))]
4471 "rb_step" | "刚体步进" | "剛体更新" | "강체스텝" | "ก้าวแข็ง" => {
4472 let dt=self.arg_num(&args,0,0.016)? as f32;
4473 self.rigid_world.step(dt);
4474 return Ok(Value::Unit);
4475 }
4476 #[cfg(not(target_arch = "wasm32"))]
4477 "rb_pos" | "刚体位置" | "剛体位置" | "강체위치" | "ตำแหน่งแข็ง" => {
4478 let i=self.arg_num(&args,0,0.)? as usize;
4479 let p=self.rigid_world.bodies.get(i).map(|b| b.pos).unwrap_or(ling_physics::Vec3::ZERO);
4480 return Ok(Value::List(vec![Value::Number(p.x as f64),Value::Number(p.y as f64),Value::Number(p.z as f64)]));
4481 }
4482 #[cfg(not(target_arch = "wasm32"))]
4483 "rb_rot" | "刚体旋转" | "剛体回転" | "강체회전" | "การหมุนแข็ง" => {
4484 let i=self.arg_num(&args,0,0.)? as usize;
4485 let q=self.rigid_world.bodies.get(i).map(|b| b.orientation).unwrap_or(ling_physics::Quat::IDENTITY);
4486 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)]));
4487 }
4488
4489 #[cfg(not(target_arch = "wasm32"))]
4491 "liquid_new" | "新建液体" | "液体新規" | "액체생성" | "สร้างของเหลว" => {
4492 let w=self.arg_num(&args,0,64.)? as usize; let h=self.arg_num(&args,1,64.)? as usize;
4493 let id=self.liquids.len(); self.liquids.push(ling_physics::liquid::LiquidGrid::new(w,h));
4494 return Ok(Value::Number(id as f64));
4495 }
4496 #[cfg(not(target_arch = "wasm32"))]
4497 "liquid_splat" | "液体注入" | "液体追加" | "액체분사" | "หยดของเหลว" => {
4498 let id=self.arg_num(&args,0,0.)? as usize;
4499 let x=self.arg_num(&args,1,0.)? as f32; let y=self.arg_num(&args,2,0.)? as f32;
4500 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;
4501 if let Some(g)=self.liquids.get_mut(id) { g.splat(x,y,kind,amt,rad); }
4502 return Ok(Value::Unit);
4503 }
4504 #[cfg(not(target_arch = "wasm32"))]
4505 "liquid_gravity" | "液体重力" | "液体重力ベクトル" | "액체중력" | "แรงโน้มถ่วงเหลว" => {
4506 let id=self.arg_num(&args,0,0.)? as usize;
4507 let gx=self.arg_num(&args,1,0.)? as f32; let gy=self.arg_num(&args,2,60.)? as f32;
4508 if let Some(g)=self.liquids.get_mut(id) { g.set_gravity(gx,gy); }
4509 return Ok(Value::Unit);
4510 }
4511 #[cfg(not(target_arch = "wasm32"))]
4512 "liquid_step" | "液体步进" | "液体更新" | "액체스텝" | "ก้าวของเหลว" => {
4513 let id=self.arg_num(&args,0,0.)? as usize; let dt=self.arg_num(&args,1,0.016)? as f32;
4514 if let Some(g)=self.liquids.get_mut(id) { g.step(dt); }
4515 return Ok(Value::Unit);
4516 }
4517 #[cfg(not(target_arch = "wasm32"))]
4519 "liquid_rainbow" | "液体彩虹" | "液体虹" | "액체무지개" | "ของเหลวสายรุ้ง" => {
4520 let id=self.arg_num(&args,0,0.)? as usize;
4521 let on=self.arg_num(&args,1,1.0)? > 0.5;
4522 if let Some(g)=self.liquids.get_mut(id) { g.rainbow = on; }
4523 return Ok(Value::Unit);
4524 }
4525 #[cfg(not(target_arch = "wasm32"))]
4527 "liquid_mix" | "液体混合" | "液体混合度" | "액체혼합" | "การผสมของเหลว" => {
4528 let id=self.arg_num(&args,0,0.)? as usize;
4529 let m=self.liquids.get(id).map(|g| g.mix_amount()).unwrap_or(0.0);
4530 return Ok(Value::Number(m as f64));
4531 }
4532 #[cfg(not(target_arch = "wasm32"))]
4534 "liquid_draw" | "绘制液体" | "液体描画" | "액체그리기" | "วาดของเหลว" => {
4535 let id=self.arg_num(&args,0,0.)? as usize;
4536 let sx=self.arg_num(&args,1,0.)? as i32; let sy=self.arg_num(&args,2,0.)? as i32;
4537 let scale=(self.arg_num(&args,3,4.)? as i32).max(1);
4538 if id < self.liquids.len() {
4539 let (gw,gh)={ let g=&self.liquids[id]; (g.w,g.h) };
4540 let mut gfx=self.gfx.borrow_mut(); let (w,h)=(gfx.width as i32, gfx.height as i32);
4541 let g=&self.liquids[id];
4542 for cy in 0..gh { for cx in 0..gw {
4543 let col=g.sample_rgb(cx,cy);
4544 let bx=sx + cx as i32*scale; let by=sy + cy as i32*scale;
4545 for dy in 0..scale { for dx in 0..scale {
4546 let px=bx+dx; let py=by+dy;
4547 if px>=0 && py>=0 && px<w && py<h { gfx.buffer[(py*w+px) as usize]=col; }
4548 }}
4549 }}
4550 }
4551 return Ok(Value::Unit);
4552 }
4553 #[cfg(not(target_arch = "wasm32"))]
4556 "liquid_draw_surface" | "液体贴面" | "液体曲面" | "액체곡면" | "ของเหลวบนพื้นผิว" => {
4557 let id=self.arg_num(&args,0,0.)? as usize;
4558 let kind=self.arg_num(&args,1,1.)? as i32;
4559 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;
4560 let radius=self.arg_num(&args,5,2.0)? as f32; let height=self.arg_num(&args,6,3.0)? as f32;
4561 if id < self.liquids.len() {
4562 let (gw,gh)={ let g=&self.liquids[id]; (g.w,g.h) };
4563 let mut gfx=self.gfx.borrow_mut();
4564 let (w,h,add)=(gfx.width, gfx.height, gfx.blend==1);
4565 let cam=gfx.camera.clone();
4566 let near = -cam.zdist + 0.05;
4567 let g=&self.liquids[id];
4568 let tau=std::f32::consts::TAU; let pi=std::f32::consts::PI;
4569 let sp = |u:f32, v:f32| -> [f32;3] {
4571 if kind==0 { [cx+(u-0.5)*2.0*radius, cy, cz+(v-0.5)*2.0*radius] }
4572 else if kind==2 { let th=u*tau; [cx+th.cos()*radius, cy+(v-0.5)*height, cz+th.sin()*radius] }
4573 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] }
4574 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] }
4575 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] }
4576 };
4577 let nrm = |u:f32, v:f32| -> [f32;3] {
4578 if kind==0 { [0.0,-1.0,0.0] }
4579 else if kind==2 { let th=u*tau; [th.cos(),0.0,th.sin()] }
4580 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()] }
4581 else if kind==4 { let th=u*tau; let ph=v*pi*0.5; [ph.sin()*th.cos(),-ph.cos(),ph.sin()*th.sin()] }
4582 else { let th=u*tau; let ph=v*pi; [ph.sin()*th.cos(),ph.cos(),ph.sin()*th.sin()] }
4583 };
4584 let gwf=gw as f32; let ghf=gh as f32;
4585 let mut cyc=0usize;
4586 while cyc<gh {
4587 let mut cxc=0usize;
4588 while cxc<gw {
4589 let uc=(cxc as f32+0.5)/gwf; let vc=(cyc as f32+0.5)/ghf;
4591 let c=sp(uc,vc); let n=nrm(uc,vc);
4592 let dc=cam.depth(c[0],c[1],c[2]);
4593 if dc>near {
4594 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;
4595 if !cull {
4596 let u0=cxc as f32/gwf; let u1=(cxc+1) as f32/gwf;
4598 let v0=cyc as f32/ghf; let v1=(cyc+1) as f32/ghf;
4599 let q=[sp(u0,v0),sp(u1,v0),sp(u1,v1),sp(u0,v1)];
4600 let mut poly: Vec<[f32;2]> = Vec::with_capacity(5);
4601 let mut ok=true;
4602 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]); }
4603 if ok { let p0=poly[0]; poly.push(p0);
4604 let col=g.sample_rgb(cxc,cyc);
4605 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, w, h, col, add, std::slice::from_ref(&poly));
4606 }
4607 }
4608 }
4609 cxc+=1;
4610 }
4611 cyc+=1;
4612 }
4613 }
4614 return Ok(Value::Unit);
4615 }
4616 #[cfg(not(target_arch = "wasm32"))]
4619 "sparkle" | "闪光" | "きらめき" | "반짝임" | "ประกาย" => {
4620 let x=self.arg_num(&args,0,0.)? as f32; let y=self.arg_num(&args,1,0.)? as f32;
4621 let ww=self.arg_num(&args,2,200.)? as f32; let hh=self.arg_num(&args,3,200.)? as f32;
4622 let count=self.arg_num(&args,4,40.)? as i32;
4623 let t=self.arg_num(&args,5,0.)? as f32;
4624 let mut gfx=self.gfx.borrow_mut();
4625 let (w,h,add,color)=(gfx.width, gfx.height, gfx.blend==1, gfx.color);
4626 let (cr,cg,cb)=((color>>16&0xFF) as f32,(color>>8&0xFF) as f32,(color&0xFF) as f32);
4627 let mut n=0i32;
4628 while n<count {
4629 let hsh=(n as u32).wrapping_mul(2654435761).wrapping_add(0x9E3779B9);
4630 let u=((hsh>>8)&1023) as f32/1023.0;
4631 let v=((hsh>>18)&1023) as f32/1023.0;
4632 let phase=(hsh&255) as f32/255.0;
4633 let tw=(t*3.0 + phase*6.2831 + n as f32).sin()*0.5+0.5;
4634 let sz=1.5+tw*5.0;
4635 let px=x+u*ww; let py=y+v*hh;
4636 let b=tw*tw; let col=(((cr*b)as u32)<<16)|(((cg*b)as u32)<<8)|((cb*b)as u32);
4638 crate::gfx::raster::draw_line_aa(&mut gfx.buffer,w,h,col,add, px-sz,py, px+sz,py);
4639 crate::gfx::raster::draw_line_aa(&mut gfx.buffer,w,h,col,add, px,py-sz, px,py+sz);
4640 let d=sz*0.55;
4641 crate::gfx::raster::draw_line_aa(&mut gfx.buffer,w,h,col,add, px-d,py-d, px+d,py+d);
4642 crate::gfx::raster::draw_line_aa(&mut gfx.buffer,w,h,col,add, px-d,py+d, px+d,py-d);
4643 n+=1;
4644 }
4645 return Ok(Value::Unit);
4646 }
4647
4648 #[cfg(not(target_arch = "wasm32"))]
4654 "dialog_show" | "对话显示" | "会話表示" | "대화표시" | "แสดงบทสนทนา" => {
4655 let text = self.arg_str(&args, 0, "");
4656 let cps = self.arg_num(&args, 1, 32.0)? as f32;
4657 self.dialog = Some(ling_game::dialog::Dialog::new(&text, cps));
4658 return Ok(Value::Unit);
4659 }
4660 #[cfg(not(target_arch = "wasm32"))]
4661 "dialog_step" | "对话步进" | "会話更新" | "대화스텝" | "ก้าวบทสนทนา" => {
4662 let dt = self.arg_num(&args, 0, 0.016)? as f32;
4663 if let Some(d) = self.dialog.as_mut() { d.update(dt); }
4664 return Ok(Value::Unit);
4665 }
4666 #[cfg(not(target_arch = "wasm32"))]
4667 "dialog_advance" | "对话推进" | "会話送り" | "대화진행" | "เลื่อนบทสนทนา" => {
4668 if let Some(d) = self.dialog.as_mut() { d.advance(); }
4669 return Ok(Value::Unit);
4670 }
4671 #[cfg(not(target_arch = "wasm32"))]
4672 "dialog_active" | "对话激活" | "会話中" | "대화중" | "บทสนทนาทำงาน" => {
4673 let a = self.dialog.as_ref().map(|d| !d.is_closed()).unwrap_or(false);
4674 return Ok(Value::Bool(a));
4675 }
4676 #[cfg(not(target_arch = "wasm32"))]
4677 "dialog_typing" | "对话打字" | "会話タイプ中" | "대화타이핑" | "กำลังพิมพ์บทสนทนา" => {
4678 use ling_game::dialog::Dialog;
4679
4680 let a = self.dialog.as_ref().map(|d: &Dialog | !d.is_closed() && d.is_typing()).unwrap_or(false);
4681 return Ok(Value::Bool(a))
4682 }
4683 #[cfg(not(target_arch = "wasm32"))]
4684 "dialog_close" | "对话关闭" | "会話閉じる" | "대화닫기" | "ปิดบทสนทนา" => {
4685 self.dialog = None;
4686 return Ok(Value::Unit);
4687 }
4688 #[cfg(not(target_arch = "wasm32"))]
4690 "dialog_color" | "对话颜色" | "会話色" | "대화색" | "สีบทสนทนา" => {
4691 let role = (self.arg_num(&args,0,0.0)? as usize).min(3);
4692 let r = self.arg_num(&args,1,255.0)? as u32 & 0xFF;
4693 let g = self.arg_num(&args,2,255.0)? as u32 & 0xFF;
4694 let b = self.arg_num(&args,3,255.0)? as u32 & 0xFF;
4695 self.dialog_colors[role] = (r<<16)|(g<<8)|b;
4696 return Ok(Value::Unit);
4697 }
4698 #[cfg(not(target_arch = "wasm32"))]
4700 "dialog_draw" | "对话绘制" | "会話描画" | "대화그리기" | "วาดบทสนทนา" => {
4701 let x=self.arg_num(&args,0,40.0)? as f32; let y=self.arg_num(&args,1,0.0)? as f32;
4702 let ww=self.arg_num(&args,2,720.0)? as f32; let hh=self.arg_num(&args,3,150.0)? as f32;
4703 let font = self.arg_num(&args,4,-1.0)? as i64;
4704 let t = self.start_time.elapsed().as_secs_f32();
4705 self.render_dialog(x, y, ww, hh, font, t);
4706 return Ok(Value::Unit);
4707 }
4708
4709 #[cfg(not(target_arch = "wasm32"))]
4711 "text_poll" => {
4712 let keys = { let gfx = self.gfx.borrow(); gfx.window.as_ref().map(|w| w.get_keys_pressed(minifb::KeyRepeat::No)).unwrap_or_default() };
4713 for k in keys {
4714 if k == minifb::Key::Backspace { self.text_buffer.pop(); }
4715 else if let Some(c) = key_char(k) { self.text_buffer.push(c); }
4716 }
4717 return Ok(Value::Str(self.text_buffer.clone()));
4718 }
4719 "text_get" => return Ok(Value::Str(self.text_buffer.clone())),
4720 "text_set" => { self.text_buffer = self.arg_str(&args,0,""); return Ok(Value::Unit); }
4721 "text_clear" => { self.text_buffer.clear(); return Ok(Value::Unit); }
4722 #[cfg(not(target_arch = "wasm32"))]
4724 "record_frame" => {
4725 let n = self.record_n;
4726 let (buf, w, h) = { let gfx = self.gfx.borrow(); (gfx.buffer.clone(), gfx.width, gfx.height) };
4727 let _ = std::fs::create_dir_all("recordings");
4728 let mut out = Vec::with_capacity(w*h*3 + 32);
4729 out.extend_from_slice(format!("P6\n{w} {h}\n255\n").as_bytes());
4730 for px in &buf { let p = *px; out.push((p>>16) as u8); out.push((p>>8) as u8); out.push(p as u8); }
4731 let _ = std::fs::write(format!("recordings/frame_{n:05}.ppm"), out);
4732 self.record_n += 1;
4733 return Ok(Value::Number(n as f64));
4734 }
4735 "record_count" => return Ok(Value::Number(self.record_n as f64)),
4736 #[cfg(not(target_arch = "wasm32"))]
4740 "mic_capture" => {
4741 if let Some(mic) = self.mic.as_ref() {
4742 let s = mic.latest_samples();
4743 self.mic_buffer.extend_from_slice(&s);
4744 let cap = 96_000usize; if self.mic_buffer.len() > cap {
4746 let drop = self.mic_buffer.len() - cap;
4747 self.mic_buffer.drain(0..drop);
4748 }
4749 }
4750 return Ok(Value::Number(self.mic_buffer.len() as f64));
4751 }
4752 #[cfg(not(target_arch = "wasm32"))]
4754 "mic_seed" => {
4755 let mut bytes = Vec::with_capacity(self.mic_buffer.len() * 4);
4756 for f in &self.mic_buffer { bytes.extend_from_slice(&f.to_le_bytes()); }
4757 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(&bytes))));
4758 }
4759 #[cfg(not(target_arch = "wasm32"))]
4760 "mic_clear" => { self.mic_buffer.clear(); return Ok(Value::Number(0.0)); }
4761 #[cfg(not(target_arch = "wasm32"))]
4764 "flush_3d" | "render_3d" => {
4765 let mut gfx = self.gfx.borrow_mut();
4766 if !gfx.depth_queue.is_empty() {
4767 let w = gfx.width; let h = gfx.height;
4768 let queue = std::mem::take(&mut gfx.depth_queue);
4769 queue.flush(&mut gfx.buffer, w, h);
4770 }
4771 return Ok(Value::Unit);
4772 }
4773
4774 "set_rim" | "设置边缘光" | "リム設定" | "림라이트" | "ตั้งขอบเรือง" => {
4775 let s=self.arg_num(&args,0,0.6)? as f32;
4776 let r=self.arg_num(&args,1,115.)? as f32/255.0;
4777 let g=self.arg_num(&args,2,217.)? as f32/255.0;
4778 let b=self.arg_num(&args,3,255.)? as f32/255.0;
4779 let mut gfx=self.gfx.borrow_mut();
4780 gfx.shade.rim = s; gfx.shade.rim_color = [r,g,b];
4781 return Ok(Value::Unit);
4782 }
4783
4784 n if crate::gfx::shapes::canon(n).is_some() => {
4793 let kind = crate::gfx::shapes::canon(n).unwrap();
4794 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;
4795 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;
4796 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;
4797 let mode=self.arg_num(&args,9,0.)? as i32;
4798 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;
4799 if let Some(mesh)=crate::gfx::shapes::build(kind,[cx,cy,cz,sx,sy,sz,rx,ry,rz],e0,e1,e2){
4800 let mut gfx=self.gfx.borrow_mut();
4801 gfx.emit_mesh(&mesh,mode);
4802 }
4803 return Ok(Value::Unit);
4804 }
4805
4806 _ => {}
4807 }
4808
4809 if let Some(def) = self.functions.get(name).cloned() {
4811 let mut call_env = self.global_seed.clone();
4815 let _ = env; for (param, arg) in def.params.iter().zip(args) {
4817 call_env.insert(param.clone(), arg);
4818 }
4819 return match self.framed(name, |me| me.exec_block(&def.body, &mut call_env)) {
4820 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
4821 Err(EvalErr::Return(v)) => Ok(v),
4822 Err(e) => Err(e),
4823 };
4824 }
4825
4826 Err(EvalErr::from(format!("unknown function '{name}'")))
4827 }
4828
4829 fn call_value(&mut self, v: Value, args: Vec<Value>) -> EvalResult {
4830 match v {
4831 Value::Fn(params, body, mut captured) => {
4832 for (p, a) in params.iter().zip(args) {
4833 captured.insert(p.clone(), a);
4834 }
4835 match self.framed("<closure>", |me| me.exec_block(&body, &mut captured)) {
4836 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
4837 Err(EvalErr::Return(v)) => Ok(v),
4838 Err(e) => Err(e),
4839 }
4840 }
4841 other => Err(EvalErr::from(format!("cannot call {:?}", other))),
4842 }
4843 }
4844
4845 fn call_method(&self, recv: Value, method: &str, args: Vec<Value>) -> EvalResult {
4846 match (&recv, method) {
4847 (Value::Str(s), "is_empty" | "是空") => Ok(Value::Bool(s.is_empty())),
4848 (Value::Str(s), "len" | "长") => Ok(Value::Number(s.len() as f64)),
4849 (Value::Str(s), "to_string" | "转文") => Ok(Value::Str(s.clone())),
4850 (Value::Str(s), "contains" | "包含") => {
4851 if let Some(Value::Str(sub)) = args.first() {
4852 Ok(Value::Bool(s.contains(sub.as_str())))
4853 } else { Ok(Value::Bool(false)) }
4854 }
4855 (Value::Str(s), "push_str" | "推_文") => {
4856 let mut s2 = s.clone();
4857 if let Some(Value::Str(a)) = args.first() { s2.push_str(a); }
4858 Ok(Value::Str(s2))
4859 }
4860 (Value::List(v), "len" | "长") => Ok(Value::Number(v.len() as f64)),
4861 (Value::List(v), "push" | "推") => {
4862 let mut v2 = v.clone();
4863 if let Some(a) = args.first() { v2.push(a.clone()); }
4864 Ok(Value::List(v2))
4865 }
4866 (Value::Ok(inner), _) | (Value::Err(inner), _) => Ok(*inner.clone()),
4867 _ => Err(EvalErr::from(format!("no method '{method}' on {recv}"))),
4868 }
4869 }
4870
4871 fn match_pattern(&self, pat: &Pattern, val: &Value) -> Option<Env> {
4874 match (pat, val) {
4875 (Pattern::Wildcard, _) => Some(Env::new()),
4876 (Pattern::Str(s), Value::Str(v)) if s == v => Some(Env::new()),
4877 (Pattern::Number(n), Value::Number(v)) if (n - v).abs() < 1e-12 => Some(Env::new()),
4878 (Pattern::Bool(b), Value::Bool(v)) if b == v => Some(Env::new()),
4879 (Pattern::Ident(name), _) => {
4880 let mut e = Env::new();
4881 e.insert(name.clone(), val.clone());
4882 Some(e)
4883 }
4884 (Pattern::Constructor(ctor, inner_pat), _) => {
4885 let (matches, inner_val) = match (ctor.as_str(), val) {
4886 ("ok" | "好", Value::Ok(v)) => (true, Some(v.as_ref().clone())),
4887 ("bad" | "坏", Value::Err(v)) => (true, Some(v.as_ref().clone())),
4888 ("ok" | "好", v) if !matches!(v, Value::Err(_)) => (true, Some(v.clone())),
4889 _ => (false, None),
4890 };
4891 if !matches { return None; }
4892 match (inner_pat, inner_val) {
4893 (Some(p), Some(v)) => self.match_pattern(p, &v),
4894 (None, _) => Some(Env::new()),
4895 (Some(p), None) => self.match_pattern(p, &Value::Unit),
4896 }
4897 }
4898 _ => None,
4899 }
4900 }
4901
4902 fn value_to_iter(&self, val: Value) -> Result<Vec<Value>, EvalErr> {
4905 match val {
4906 Value::List(v) => Ok(v),
4907 Value::Str(s) => Ok(s.chars().map(|c| Value::Str(c.to_string())).collect()),
4908 Value::Number(n) => Ok((0..n as i64).map(|i| Value::Number(i as f64)).collect()),
4909 other => Err(EvalErr::from(format!("cannot iterate over {:?}", other))),
4910 }
4911 }
4912
4913 fn is_truthy(&self, val: &Value) -> bool {
4914 match val {
4915 Value::Bool(b) => *b,
4916 Value::Unit => false,
4917 Value::Number(n) => *n != 0.0,
4918 Value::Str(s) => !s.is_empty(),
4919 Value::List(v) => !v.is_empty(),
4920 Value::Ok(_) => true,
4921 Value::Err(_) => false,
4922 Value::Fn(_, _, _) => true,
4923 }
4924 }
4925
4926 fn to_number(&self, val: &Value) -> Result<f64, EvalErr> {
4927 match val {
4928 Value::Number(n) => Ok(*n),
4929 Value::Str(s) => s.parse().map_err(|_| EvalErr::from(format!("cannot convert '{s}' to number"))),
4930 other => Err(EvalErr::from(format!("expected number, got {:?}", other))),
4931 }
4932 }
4933
4934 fn arg_num(&self, args: &[Value], n: usize, default: f64) -> Result<f64, EvalErr> {
4936 match args.get(n) {
4937 Some(v) => self.to_number(v),
4938 None => Ok(default),
4939 }
4940 }
4941
4942 fn arg_str(&self, args: &[Value], n: usize, default: &str) -> String {
4943 args.get(n).map(|v| v.to_string()).unwrap_or_else(|| default.to_string())
4944 }
4945
4946 #[allow(dead_code)]
4948 fn arg_list_f32(&self, args: &[Value], n: usize) -> Vec<f32> {
4949 match args.get(n) {
4950 Some(Value::List(v)) => v.iter().filter_map(|x| match x {
4951 Value::Number(n) => Some(*n as f32),
4952 _ => None,
4953 }).collect(),
4954 _ => Vec::new(),
4955 }
4956 }
4957
4958 #[cfg(not(target_arch = "wasm32"))]
4961 fn color_at(&self, args: &[Value], i: usize, default: u32) -> u32 {
4962 match (args.get(i), args.get(i + 1), args.get(i + 2)) {
4963 (Some(a), Some(b), Some(c)) => match (self.to_number(a), self.to_number(b), self.to_number(c)) {
4964 (Ok(r), Ok(g), Ok(bl)) =>
4965 ((r as u32 & 0xFF) << 16) | ((g as u32 & 0xFF) << 8) | (bl as u32 & 0xFF),
4966 _ => default,
4967 },
4968 _ => default,
4969 }
4970 }
4971
4972 #[cfg(not(target_arch = "wasm32"))]
4974 fn pitch_arg(&self, args: &[Value], i: usize, default: i32) -> i32 {
4975 match args.get(i) {
4976 Some(Value::Str(s)) => ling_music::note::parse_pitch(s).unwrap_or(default),
4977 Some(Value::Number(n)) => *n as i32,
4978 _ => default,
4979 }
4980 }
4981
4982 #[cfg(not(target_arch = "wasm32"))]
4984 fn mouse_now(&self) -> (f32, f32, bool) {
4985 let gfx = self.gfx.borrow();
4986 let (mx, my) = gfx.window.as_ref()
4987 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp)).unwrap_or((0.0, 0.0));
4988 let down = gfx.window.as_ref()
4989 .map(|w| w.get_mouse_down(minifb::MouseButton::Left)).unwrap_or(false);
4990 (mx, my, down)
4991 }
4992
4993 #[cfg(not(target_arch = "wasm32"))]
4997 fn draw_ui(&self, d: &ling_ui::widgets::Draw) {
4998 let mut gfx = self.gfx.borrow_mut();
4999 let (w, h, add) = (gfx.width, gfx.height, gfx.blend == 1);
5000 for (c, poly) in &d.fills {
5001 crate::gfx::raster::fill_contours_aa(&mut gfx.buffer, w, h, *c, add, std::slice::from_ref(poly));
5002 }
5003 for (c, pl) in &d.strokes {
5004 for s in pl.windows(2) {
5005 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]);
5006 }
5007 }
5008 }
5009
5010 fn tex_rect(&self, args: &[Value]) -> Result<(usize, usize, usize, usize), EvalErr> {
5012 let tx = self.arg_num(args, 0, 0.0)? as usize;
5013 let ty = self.arg_num(args, 1, 0.0)? as usize;
5014 let tw = self.arg_num(args, 2, 256.0)? as usize;
5015 let th = self.arg_num(args, 3, 256.0)? as usize;
5016 Ok((tx, ty, tw.max(1), th.max(1)))
5017 }
5018
5019 fn apply_binop(&self, op: &BinOp, l: Value, r: Value) -> EvalResult {
5020 match op {
5021 BinOp::Add => match (l, r) {
5022 (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
5023 (Value::Str(a), Value::Str(b)) => Ok(Value::Str(a + &b)),
5024 (Value::Str(a), b) => Ok(Value::Str(a + &b.to_string())),
5025 (a, Value::Str(b)) => Ok(Value::Str(a.to_string() + &b)),
5026 (a, b) => Err(EvalErr::from(format!("cannot add {:?} and {:?}", a, b))),
5027 },
5028 BinOp::Sub => Ok(Value::Number(self.to_number(&l)? - self.to_number(&r)?)),
5029 BinOp::Mul => Ok(Value::Number(self.to_number(&l)? * self.to_number(&r)?)),
5030 BinOp::Div => Ok(Value::Number(self.to_number(&l)? / self.to_number(&r)?)),
5031 BinOp::Rem => Ok(Value::Number(self.to_number(&l)? % self.to_number(&r)?)),
5032 BinOp::Eq => Ok(Value::Bool(values_equal(&l, &r))),
5033 BinOp::Ne => Ok(Value::Bool(!values_equal(&l, &r))),
5034 BinOp::Lt => Ok(Value::Bool(self.to_number(&l)? < self.to_number(&r)?)),
5035 BinOp::Gt => Ok(Value::Bool(self.to_number(&l)? > self.to_number(&r)?)),
5036 BinOp::Le => Ok(Value::Bool(self.to_number(&l)? <= self.to_number(&r)?)),
5037 BinOp::Ge => Ok(Value::Bool(self.to_number(&l)? >= self.to_number(&r)?)),
5038 BinOp::And => Ok(Value::Bool(self.is_truthy(&l) && self.is_truthy(&r))),
5039 BinOp::Or => Ok(Value::Bool(self.is_truthy(&l) || self.is_truthy(&r))),
5040 }
5041 }
5042
5043 fn builtin_format(&self, args: &[Value]) -> Result<String, EvalErr> {
5044 if args.is_empty() { return Ok(String::new()); }
5045 let fmt = match &args[0] {
5046 Value::Str(s) => s.clone(),
5047 other => return Ok(other.to_string()),
5048 };
5049
5050 let mut result = String::new();
5051 let mut arg_idx = 1usize;
5052 let mut chars = fmt.chars().peekable();
5053 while let Some(c) = chars.next() {
5054 if c == '{' {
5055 if chars.peek() == Some(&'}') {
5056 chars.next();
5057 if arg_idx < args.len() {
5058 result.push_str(&args[arg_idx].to_string());
5059 arg_idx += 1;
5060 }
5061 } else {
5062 let mut spec = String::new();
5063 for ch in chars.by_ref() {
5064 if ch == '}' { break; }
5065 spec.push(ch);
5066 }
5067 if arg_idx < args.len() {
5068 if spec.starts_with(":.") {
5069 if let Value::Number(n) = &args[arg_idx] {
5070 let prec: usize = spec[2..].trim_end_matches('f')
5071 .parse().unwrap_or(2);
5072 result.push_str(&format!("{:.prec$}", n));
5073 arg_idx += 1;
5074 continue;
5075 }
5076 }
5077 result.push_str(&args[arg_idx].to_string());
5078 arg_idx += 1;
5079 }
5080 }
5081 } else {
5082 result.push(c);
5083 }
5084 }
5085 Ok(result)
5086 }
5087}
5088
5089#[cfg(not(target_arch = "wasm32"))]
5090fn str_to_minifb_key(name: &str) -> Option<minifb::Key> {
5091 use minifb::Key;
5092 Some(match name {
5093 "numpad0" | "kp0" => Key::NumPad0,
5094 "numpad1" | "kp1" => Key::NumPad1,
5095 "numpad2" | "kp2" => Key::NumPad2,
5096 "numpad3" | "kp3" => Key::NumPad3,
5097 "numpad4" | "kp4" => Key::NumPad4,
5098 "numpad5" | "kp5" => Key::NumPad5,
5099 "numpad6" | "kp6" => Key::NumPad6,
5100 "numpad7" | "kp7" => Key::NumPad7,
5101 "numpad8" | "kp8" => Key::NumPad8,
5102 "numpad9" | "kp9" => Key::NumPad9,
5103 "numpad+" | "kp+" => Key::NumPadPlus,
5104 "numpad-" | "kp-" => Key::NumPadMinus,
5105 "numpad*" | "kp*" => Key::NumPadAsterisk,
5106 "numpad/" | "kp/" => Key::NumPadSlash,
5107 "left" => Key::Left,
5108 "right" => Key::Right,
5109 "up" => Key::Up,
5110 "down" => Key::Down,
5111 "space" => Key::Space,
5112 "enter" => Key::Enter,
5113 "escape" => Key::Escape,
5114 "pageup" => Key::PageUp,
5115 "pagedown" => Key::PageDown,
5116 "lshift" | "leftshift" => Key::LeftShift,
5117 "rshift" | "rightshift" => Key::RightShift,
5118 "lctrl" | "leftctrl" => Key::LeftCtrl,
5119 "rctrl" | "rightctrl" => Key::RightCtrl,
5120 "lalt" | "leftalt" => Key::LeftAlt,
5121 "ralt" | "rightalt" => Key::RightAlt,
5122 "tab" => Key::Tab,
5123 "backspace" => Key::Backspace,
5124 "delete" => Key::Delete,
5125 "insert" => Key::Insert,
5126 "home" => Key::Home,
5127 "end" => Key::End,
5128 "a" => Key::A, "b" => Key::B, "c" => Key::C, "d" => Key::D,
5129 "e" => Key::E, "f" => Key::F, "g" => Key::G, "h" => Key::H,
5130 "i" => Key::I, "j" => Key::J, "k" => Key::K, "l" => Key::L,
5131 "m" => Key::M, "n" => Key::N, "o" => Key::O, "p" => Key::P,
5132 "q" => Key::Q, "r" => Key::R, "s" => Key::S, "t" => Key::T,
5133 "u" => Key::U, "v" => Key::V, "w" => Key::W, "x" => Key::X,
5134 "y" => Key::Y, "z" => Key::Z,
5135 "0" => Key::Key0, "1" => Key::Key1, "2" => Key::Key2,
5136 "3" => Key::Key3, "4" => Key::Key4, "5" => Key::Key5,
5137 "6" => Key::Key6, "7" => Key::Key7, "8" => Key::Key8,
5138 "9" => Key::Key9,
5139 _ => return None,
5140 })
5141}
5142
5143fn values_equal(a: &Value, b: &Value) -> bool {
5144 match (a, b) {
5145 (Value::Number(x), Value::Number(y)) => (x - y).abs() < 1e-12,
5146 (Value::Str(x), Value::Str(y)) => x == y,
5147 (Value::Bool(x), Value::Bool(y)) => x == y,
5148 (Value::Unit, Value::Unit) => true,
5149 _ => false,
5150 }
5151}
5152
5153#[cfg(not(target_arch = "wasm32"))]
5160fn hide_console_window() {
5161 #[cfg(windows)]
5162 unsafe {
5163 extern "system" {
5164 fn GetConsoleWindow() -> isize;
5165 fn ShowWindow(hwnd: isize, nCmdShow: i32) -> i32;
5166 }
5167 let hwnd = GetConsoleWindow();
5168 if hwnd != 0 {
5169 ShowWindow(hwnd, 0); }
5171 }
5172}
5173
5174#[cfg(all(not(target_arch = "wasm32"), windows))]
5179fn make_borderless_fullscreen(hwnd: isize, screen_w: i32, screen_h: i32) {
5180 if hwnd == 0 {
5181 return;
5182 }
5183 unsafe {
5184 extern "system" {
5185 fn SetWindowLongPtrW(hwnd: isize, index: i32, new: isize) -> isize;
5186 fn SetWindowPos(hwnd: isize, insert_after: isize,
5187 x: i32, y: i32, cx: i32, cy: i32,
5188 flags: u32) -> i32;
5189 fn ShowWindow(hwnd: isize, cmd: i32) -> i32;
5190 }
5191 const GWL_STYLE: i32 = -16;
5192 const GWL_EXSTYLE: i32 = -20;
5193 SetWindowLongPtrW(hwnd, GWL_STYLE, 0x9000_0000isize);
5196 SetWindowLongPtrW(hwnd, GWL_EXSTYLE, 0);
5198 SetWindowPos(hwnd, -1isize, 0, 0, screen_w, screen_h, 0x0020 | 0x0040);
5200 ShowWindow(hwnd, 3); }
5202}
5203
5204#[cfg(all(not(target_arch = "wasm32"), windows))]
5207fn monitor_info() -> (i32, i32, i32) {
5208 unsafe {
5209 extern "system" {
5210 fn GetSystemMetrics(index: i32) -> i32;
5211 fn GetDC(hwnd: isize) -> isize;
5212 fn ReleaseDC(hwnd: isize, hdc: isize) -> i32;
5213 fn GetDeviceCaps(hdc: isize, index: i32) -> i32;
5214 }
5215 let w = GetSystemMetrics(0).max(1); let h = GetSystemMetrics(1).max(1); let hdc = GetDC(0);
5218 let mut hz = if hdc != 0 { GetDeviceCaps(hdc, 116) } else { 0 }; if hdc != 0 {
5220 ReleaseDC(0, hdc);
5221 }
5222 if hz <= 1 {
5223 hz = 60; }
5225 (w, h, hz)
5226 }
5227}
5228
5229#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
5231fn monitor_info() -> (i32, i32, i32) {
5232 let (w, h) = native_screen_size();
5233 (w as i32, h as i32, 60)
5234}
5235
5236#[cfg(target_arch = "wasm32")]
5238fn monitor_info() -> (i32, i32, i32) {
5239 let (w, h) = crate::gfx::webgl::canvas_size();
5240 (w as i32, h as i32, 60)
5241}
5242
5243#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
5246fn native_screen_size() -> (f64, f64) {
5247 (1920.0, 1080.0)
5250}