1#[cfg(not(target_arch = "wasm32"))]
3mod ai;
4#[cfg(not(target_arch = "wasm32"))]
5mod gamepad;
6#[cfg(target_arch = "wasm32")]
7mod input_web;
8#[cfg(not(target_arch = "wasm32"))]
9pub(crate) mod jit_abi;
10
11#[cfg(not(target_arch = "wasm32"))]
14pub fn init_aot_runtime() {
15 let interp = Interpreter::new();
16 jit_abi::init(interp);
17}
18
19pub fn now_secs() -> f64 {
22 #[cfg(target_arch = "wasm32")]
23 {
24 js_sys::Date::now() / 1000.0
25 }
26 #[cfg(not(target_arch = "wasm32"))]
27 {
28 std::time::SystemTime::now()
29 .duration_since(std::time::UNIX_EPOCH)
30 .map(|d| d.as_secs_f64())
31 .unwrap_or(0.0)
32 }
33}
34
35#[cfg(target_arch = "wasm32")]
38thread_local! {
39 static WASM_MODULES: std::cell::RefCell<std::collections::HashMap<String, String>> =
40 std::cell::RefCell::new(std::collections::HashMap::new());
41}
42
43#[cfg(target_arch = "wasm32")]
46pub fn register_wasm_module(path: &str, source: &str) {
47 WASM_MODULES.with(|m| m.borrow_mut().insert(path.to_string(), source.to_string()));
48}
49
50#[cfg(target_arch = "wasm32")]
52pub(crate) fn get_wasm_module(path: &str) -> Option<String> {
53 WASM_MODULES.with(|m| m.borrow().get(path).cloned())
54}
55
56#[cfg(target_arch = "wasm32")]
57#[inline]
58fn wasm_sleep_ms(ms: i32) {
59 if ms <= 0 {
60 return;
61 }
62
63 let global = js_sys::global();
66 let has_sab = js_sys::Reflect::has(&global, &wasm_bindgen::JsValue::from_str("SharedArrayBuffer"))
67 .unwrap_or(false);
68 let has_atomics =
69 js_sys::Reflect::has(&global, &wasm_bindgen::JsValue::from_str("Atomics")).unwrap_or(false);
70 if has_sab && has_atomics {
71 let sab = js_sys::SharedArrayBuffer::new(4);
72 let i32a = js_sys::Int32Array::new(&sab);
73 if js_sys::Atomics::wait_with_timeout(&i32a, 0, 0, ms as f64).is_ok() {
74 return;
75 }
76 }
77
78 let end = js_sys::Date::now() + ms as f64;
79 while js_sys::Date::now() < end {}
80}
81
82#[cfg(target_arch = "wasm32")]
83fn wasm_fetch_sync(path: &str, response_type: &str, return_expr: &str) -> Result<wasm_bindgen::JsValue, String> {
84 let quoted = js_sys::JSON::stringify(&wasm_bindgen::JsValue::from_str(path))
85 .ok()
86 .and_then(|s| s.as_string())
87 .unwrap_or_else(|| "\"\"".to_string());
88
89 let script = format!(
90 "(function(){{\n var xhr = new XMLHttpRequest();\n xhr.open('GET', {quoted}, false);\n xhr.responseType = '{response_type}';\n xhr.send(null);\n if ((xhr.status|0) !== 200 && (xhr.status|0) !== 0) {{ throw new Error('HTTP ' + xhr.status + ' for ' + {quoted}); }}\n return {return_expr};\n}})()"
91 );
92
93 js_sys::eval(&script)
94 .map_err(|e| e.as_string().unwrap_or_else(|| format!("JS eval failed: {:?}", e)))
95}
96
97#[cfg(target_arch = "wasm32")]
98fn wasm_fetch_bytes(path: &str) -> Result<Vec<u8>, String> {
99 let value = wasm_fetch_sync(path, "arraybuffer", "new Uint8Array(xhr.response || new ArrayBuffer(0))")?;
100 let arr = js_sys::Uint8Array::new(&value);
101 let mut out = vec![0u8; arr.length() as usize];
102 arr.copy_to(&mut out);
103 Ok(out)
104}
105
106#[cfg(target_arch = "wasm32")]
107fn wasm_fetch_text(path: &str) -> Result<String, String> {
108 let value = wasm_fetch_sync(path, "text", "String(xhr.responseText || '')")?;
109 Ok(value.as_string().unwrap_or_default())
110}
111
112#[cfg(not(target_arch = "wasm32"))]
113mod net;
114#[cfg(target_arch = "wasm32")]
115use js_sys;
116use crate::gfx::{GfxState, Light};
117use crate::parser::ast::*;
118use std::cell::RefCell;
119use std::collections::HashMap;
120use std::rc::Rc;
121use crate::gfx::raster::draw_line;
124#[cfg(not(target_arch = "wasm32"))]
125use crate::gfx::raster::fill_triangle;
126#[cfg(not(target_arch = "wasm32"))]
127use ling_audio::{AudioEngine, ToneParams, Wave};
128
129#[cfg(not(target_arch = "wasm32"))]
130use ling_audio::FftAnalyzer;
131
132#[cfg(not(target_arch = "wasm32"))]
133use ling_mic;
134
135#[derive(Debug, Clone)]
138pub enum Value {
139 Str(String),
140 Number(f64),
141 Bool(bool),
142 Unit,
143 List(Rc<Vec<Value>>),
144 Ok(Box<Value>),
145 Err(Box<Value>),
146 Fn(Vec<String>, Vec<Stmt>, Env),
147 Struct {
149 name: String,
150 fields: Vec<(String, Value)>,
151 },
152 Variant {
154 enum_name: String,
155 variant: String,
156 payload: Vec<Value>,
157 },
158}
159
160use rustc_hash::FxHashMap;
163type Env = FxHashMap<String, Value>;
164
165#[inline]
166fn new_env() -> Env {
167 FxHashMap::default()
168}
169
170impl std::fmt::Display for Value {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 match self {
173 Value::Str(s) => write!(f, "{s}"),
174 Value::Number(n) => {
175 if n.fract() == 0.0 && n.abs() < 1e15 {
176 write!(f, "{}", *n as i64)
177 } else {
178 write!(f, "{n}")
179 }
180 },
181 Value::Bool(b) => write!(f, "{b}"),
182 Value::Unit => write!(f, "()"),
183 Value::List(v) => {
184 write!(f, "[")?;
185 for (i, x) in v.iter().enumerate() {
186 if i > 0 {
187 write!(f, ", ")?;
188 }
189 write!(f, "{x}")?;
190 }
191 write!(f, "]")
192 },
193 Value::Ok(v) => write!(f, "Ok({v})"),
194 Value::Err(v) => write!(f, "Err({v})"),
195 Value::Fn(_, _, _) => write!(f, "<fn>"),
196 Value::Struct { name, fields } => {
197 write!(f, "{name} {{ ")?;
198 for (i, (k, v)) in fields.iter().enumerate() {
199 if i > 0 {
200 write!(f, ", ")?;
201 }
202 write!(f, "{k}: {v}")?;
203 }
204 write!(f, " }}")
205 },
206 Value::Variant { variant, payload, .. } => {
207 write!(f, "{variant}")?;
208 if !payload.is_empty() {
209 write!(f, "(")?;
210 for (i, v) in payload.iter().enumerate() {
211 if i > 0 {
212 write!(f, ", ")?;
213 }
214 write!(f, "{v}")?;
215 }
216 write!(f, ")")?;
217 }
218 Ok(())
219 },
220 }
221 }
222}
223
224#[cfg(target_arch = "wasm32")]
225fn wasm_unsupported_builtin(name: &str) -> Option<Value> {
226 Some(match name {
227 "audio_blip" | "提示音" | "ビープ音" | "효과음" | "เสียงบี๊บ"
229 | "ui_sound" | "界面音" | "UI音" | "인터페이스음" | "เสียงปุ่ม"
230 | "audio_stop_sfx" | "停止音效" | "効果音停止" | "효과음정지" | "หยุดเอฟเฟกต์ทั้งหมด" => {
231 Value::Unit
232 },
233
234 "music_load" | "载入音乐" | "音楽読込" | "음악로드" | "โหลดเพลง"
236 | "music_patch" | "乐器音色" | "音色読込" | "악기패치" | "แพตช์เครื่องดนตรี"
237 | "music_lrc" | "载入歌词" | "歌詞読込" | "가사로드" | "โหลดเนื้อเพลง"
238 | "music_midi_load" | "载入MIDI" | "MIDI読込" | "미디로드" | "โหลดมิดี" => {
239 Value::Number(-1.0)
240 },
241
242 "music_duration" | "音乐时长" | "音楽長さ" | "음악길이" | "ความยาวเพลง"
243 | "music_bpm" | "节拍速度" | "テンポ" | "템포" | "จังหวะต่อนาที"
244 | "music_pos" | "音乐位置" | "音楽位置" | "음악위치" | "ตำแหน่งเพลง"
245 | "music_mic_pitch" | "麦克风音高" | "マイク音程" | "마이크음정" | "ระดับเสียงไมค์"
246 | "music_hz" | "音符频率" | "音符周波数" | "음표주파수" | "ความถี่โน้ต"
247 | "music_pitch_score" | "音准评分" | "音程スコア" | "음정점수" | "คะแนนเสียง"
248 | "music_judge" | "判定" | "判定する" | "판정" | "ตัดสินจังหวะ"
249 | "music_midi_count" | "MIDI数量" | "MIDI数" | "미디수" | "จำนวนมิดี" => {
250 Value::Number(0.0)
251 },
252
253 "music_key" | "调性" | "調性" | "조성" | "คีย์เพลง"
254 | "music_lyric" | "当前歌词" | "現在歌詞" | "현재가사" | "เนื้อเพลงปัจจุบัน"
255 | "music_note_name" | "音名" | "音名称" | "음이름" | "ชื่อโน้ต"
256 | "music_grade_name" | "判定名" | "判定名称" | "판정이름" | "ชื่อการตัดสิน" => {
257 Value::Str(String::new())
258 },
259
260 "music_onsets" | "音符起点" | "オンセット" | "온셋" | "จุดเริ่มเสียง"
261 | "music_beat_grid" | "节拍网格" | "ビートグリッド" | "비트그리드" | "กริดจังหวะ"
262 | "music_midi_notes" | "MIDI音符" | "MIDIノート" | "미디음표" | "โน้ตมิดี"
263 | "music_midi_bars" | "MIDI音条" | "MIDIバー" | "미디바" | "แท่งมิดี"
264 | "music_fft" | "音乐频谱" | "音楽スペクトル" | "음악스펙트럼" | "สเปกตรัมเพลง" => {
265 Value::List(Vec::new())
266 },
267
268 "music_play" | "播放音乐" | "音楽再生" | "음악재생" | "เล่นเพลง"
269 | "music_pause" | "暂停音乐" | "音楽一時停止" | "음악일시정지" | "หยุดเพลงชั่วคราว"
270 | "music_stop" | "停止音乐" | "音楽停止" | "음악정지" | "หยุดเพลง"
271 | "music_seek" | "定位音乐" | "音楽シーク" | "음악탐색" | "ค้นหาเพลง"
272 | "music_volume" | "音乐音量" | "音楽音量" | "음악음량" | "ระดับเพลง"
273 | "music_note" | "弹音符" | "音符演奏" | "음표연주" | "เล่นโน้ต"
274 | "music_note_on" | "音符开始" | "音符オン" | "음표켜기" | "โน้ตเริ่ม"
275 | "music_note_off" | "音符结束" | "音符オフ" | "음표끄기" | "โน้ตจบ" => {
276 Value::Unit
277 },
278
279 "liquid_new" | "新建液体" | "液体新規" | "액체생성" | "สร้างของเหลว" => {
281 Value::Number(0.0)
282 },
283 "liquid_mix" | "液体混合" | "液体混合度" | "액체혼합" | "การผสมของเหลว" => {
284 Value::Number(0.0)
285 },
286 "liquid_set_colors" | "液体颜色" | "液体配色" | "액체색상" | "สีของเหลว"
287 | "liquid_splat" | "液体注入" | "液体追加" | "액체분사" | "หยดของเหลว"
288 | "liquid_gravity" | "液体重力" | "液体重力ベクトル" | "액체중력" | "แรงโน้มถ่วงเหลว"
289 | "liquid_step" | "液体步进" | "液体更新" | "액체스텝" | "ก้าวของเหลว"
290 | "liquid_step_all" | "液体全步进" | "液体全更新" | "전체액체스텝" | "ก้าวของเหลวทั้งหมด"
291 | "liquid_rainbow" | "液体彩虹" | "液体虹" | "액체무지개" | "ของเหลวสายรุ้ง"
292 | "liquid_draw" | "绘制液体" | "液体描画" | "액체그리기" | "วาดของเหลว"
293 | "liquid_draw_surface" | "液体贴面" | "液体曲面" | "액체곡면" | "ของเหลวบนพื้นผิว" => {
294 Value::Unit
295 },
296
297 "nn_new" | "建神经网" | "ニューラル作成" | "신경망생성" | "สร้างโครงข่าย"
299 | "nn_load" | "载入网" | "網読込" | "신경망불러오기" | "โหลดโครงข่าย" => {
300 Value::Number(-1.0)
301 },
302 "nn_forward" | "神经前向" | "順伝播" | "순전파" | "ส่งต่อโครงข่าย" => {
303 Value::List(Vec::new())
304 },
305 "nn_train" | "训练网" | "ニューラル学習" | "신경망학습" | "ฝึกโครงข่าย"
306 | "nn_dense" | "密集层" | "密層追加" | "밀집층" | "ชั้นหนาแน่น" => {
307 Value::Number(0.0)
308 },
309 "nn_save" | "保存网" | "網保存" | "신경망저장" | "บันทึกโครงข่าย" => {
310 Value::Bool(false)
311 },
312
313 "bt_build" | "建行为树" | "行動木構築" | "행동트리구성" | "สร้างต้นไม้พฤติกรรม" => {
315 Value::Number(-1.0)
316 },
317 "bt_tick" | "行为树滴答" | "行動木更新" | "행동트리틱" | "เดินต้นไม้พฤติกรรม" => {
318 Value::Str(String::new())
319 },
320 "bt_status" | "行为树状态" | "行動木状態" | "행동트리상태" | "สถานะต้นไม้พฤติกรรม" => {
321 Value::Number(0.0)
322 },
323 "bt_set" | "设事实" | "事実設定" | "사실설정" | "ตั้งข้อเท็จจริง" => Value::Unit,
324
325 "dialog_new" | "建对话模型" | "対話モデル作成" | "대화모델생성" | "สร้างโมเดลสนทนา"
327 | "dialog_load_model" | "对话载模" | "対話モデル読込" | "대화모델불러오기"
328 | "โหลดโมเดลสนทนา"
329 | "dialog_train" | "对话训练" | "対話訓練" | "대화훈련" | "ฝึกสนทนา"
330 | "dialog_load" | "对话载入" | "対話読込" | "대화불러오기" | "โหลดชุดสนทนา" => {
331 Value::Number(-1.0)
332 },
333 "dialog_say" | "对话生成" | "対話生成" | "대화생성" | "พูดสนทนา" => {
334 Value::Str(String::new())
335 },
336 "dialog_save" | "对话存模" | "対話モデル保存" | "대화모델저장" | "บันทึกโมเดลสนทนา" => {
337 Value::Bool(false)
338 },
339 "dialog_learn" | "对话学习" | "対話学習" | "대화학습" | "เรียนรู้สนทนา" => Value::Unit,
340
341 "net_connect" | "联网" | "ネット接続" | "네트연결" | "เชื่อมเน็ต"
343 | "net_listen" | "监听" | "待機" | "리슨" | "รอรับ"
344 | "net_send" | "发送" | "送信" | "전송" | "ส่ง" => Value::Number(-1.0),
345 "net_recv" | "接收" | "受信" | "수신" | "รับ"
346 | "net_status" | "连接状态" | "接続状態" | "연결상태" | "สถานะการเชื่อม" => {
347 Value::Str(String::new())
348 },
349 "net_discover" | "发现" | "探索" | "검색" | "ค้นหาเครือข่าย" => {
350 Value::List(Vec::new())
351 },
352 "net_close" | "断开" | "切断" | "연결종료" | "ตัดเชื่อม"
353 | "net_test" | "测连接" | "接続テスト" | "연결테스트" | "ทดสอบเน็ต" => {
354 Value::Number(0.0)
355 },
356
357 _ => Value::Unit,
359 })
360}
361
362#[derive(Debug)]
365pub(crate) enum EvalErr {
366 Runtime(String),
367 Return(Value),
368 #[allow(dead_code)] Break,
370}
371
372impl From<String> for EvalErr {
373 fn from(s: String) -> Self {
374 EvalErr::Runtime(s)
375 }
376}
377
378type EvalResult = Result<Value, EvalErr>;
379
380struct SvgWriter {
385 path: String,
386 width: f64,
387 height: f64,
388 elements: Vec<String>,
389}
390
391impl SvgWriter {
392 fn new(path: String, width: f64, height: f64) -> Self {
393 let bg = format!("<rect width=\"{width}\" height=\"{height}\" fill=\"#0a0a0a\"/>");
394 Self { path, width, height, elements: vec![bg] }
395 }
396
397 fn save(&self) -> std::io::Result<()> {
398 let w = self.width;
399 let h = self.height;
400 let mut out = format!(
401 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
402 <svg xmlns=\"http://www.w3.org/2000/svg\" \
403 width=\"{w}\" height=\"{h}\" viewBox=\"0 0 {w} {h}\">\n"
404 );
405 for elem in &self.elements {
406 out.push_str(" ");
407 out.push_str(elem);
408 out.push('\n');
409 }
410 out.push_str("</svg>\n");
411 if let Some(parent) = std::path::Path::new(&self.path).parent() {
413 if !parent.as_os_str().is_empty() {
414 let _ = std::fs::create_dir_all(parent);
415 }
416 }
417 std::fs::write(&self.path, out.as_bytes())
418 }
419}
420
421fn hsl_to_hex(h: f64, s: f64, l: f64) -> String {
422 let s = s / 100.0;
423 let l = l / 100.0;
424 let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
425 let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
426 let m = l - c / 2.0;
427 let (r1, g1, b1) = if h < 60.0 {
428 (c, x, 0.0)
429 } else if h < 120.0 {
430 (x, c, 0.0)
431 } else if h < 180.0 {
432 (0.0, c, x)
433 } else if h < 240.0 {
434 (0.0, x, c)
435 } else if h < 300.0 {
436 (x, 0.0, c)
437 } else {
438 (c, 0.0, x)
439 };
440 let r = ((r1 + m) * 255.0).round() as u8;
441 let g = ((g1 + m) * 255.0).round() as u8;
442 let b = ((b1 + m) * 255.0).round() as u8;
443 format!("#{r:02x}{g:02x}{b:02x}")
444}
445
446fn tex_hash(x: i32, y: i32, seed: u32) -> f32 {
449 let mut h = (x as u32)
450 .wrapping_add((y as u32).wrapping_mul(2654435769))
451 .wrapping_add(seed.wrapping_mul(1234567891));
452 h ^= h >> 16;
453 h = h.wrapping_mul(0x45d9f3b);
454 h ^= h >> 16;
455 h as f32 / u32::MAX as f32
456}
457
458fn tex_vnoise(x: f32, y: f32, seed: u32) -> f32 {
459 let xi = x.floor() as i32;
460 let yi = y.floor() as i32;
461 let sm = |t: f32| t * t * (3.0 - 2.0 * t);
462 let xf = sm(x - xi as f32);
463 let yf = sm(y - yi as f32);
464 let a = tex_hash(xi, yi, seed);
465 let b = tex_hash(xi + 1, yi, seed);
466 let c = tex_hash(xi, yi + 1, seed);
467 let d = tex_hash(xi + 1, yi + 1, seed);
468 a + (b - a) * xf + (c - a) * yf + (a - b - c + d) * xf * yf
469}
470
471fn tex_fbm(x: f32, y: f32, octaves: u32, seed: u32) -> f32 {
472 let mut v = 0.0f32;
473 let mut amp = 0.5f32;
474 let mut f = 1.0f32;
475 for i in 0..octaves {
476 v += tex_vnoise(x * f, y * f, seed.wrapping_add(i * 7919)) * amp;
477 amp *= 0.5;
478 f *= 2.0;
479 }
480 v
481}
482
483fn tex_palette(name: &str, t: f32) -> [f32; 3] {
484 let (a, b, c, d): ([f32; 3], [f32; 3], [f32; 3], [f32; 3]) = match name {
485 "fire" => (
486 [0.8, 0.4, 0.1],
487 [0.7, 0.3, 0.1],
488 [1.0, 0.5, 0.3],
489 [0.0, 0.5, 0.8],
490 ),
491 "ocean" => (
492 [0.1, 0.4, 0.7],
493 [0.3, 0.3, 0.4],
494 [0.8, 1.0, 0.5],
495 [0.3, 0.0, 0.6],
496 ),
497 "psychedelic" => (
498 [0.5, 0.5, 0.5],
499 [0.8, 0.8, 0.8],
500 [1.0, 1.3, 0.7],
501 [0.0, 0.15, 0.3],
502 ),
503 "neon" => (
504 [0.5, 0.5, 0.5],
505 [0.5, 0.5, 0.5],
506 [2.0, 1.0, 0.0],
507 [0.5, 0.2, 0.25],
508 ),
509 "forest" => (
510 [0.3, 0.5, 0.2],
511 [0.2, 0.3, 0.1],
512 [1.0, 0.5, 0.8],
513 [0.1, 0.3, 0.6],
514 ),
515 _ => (
516 [0.5, 0.5, 0.5],
517 [0.5, 0.5, 0.5],
518 [1.0, 1.0, 1.0],
519 [0.0, 0.333, 0.667],
520 ),
521 };
522 [0, 1, 2]
523 .map(|i| (a[i] + b[i] * (std::f32::consts::TAU * (c[i] * t + d[i])).cos()).clamp(0.0, 1.0))
524}
525
526#[cfg(not(target_arch = "wasm32"))]
528fn key_char(k: minifb::Key) -> Option<char> {
529 use minifb::Key::*;
530 Some(match k {
531 A => 'a',
532 B => 'b',
533 C => 'c',
534 D => 'd',
535 E => 'e',
536 F => 'f',
537 G => 'g',
538 H => 'h',
539 I => 'i',
540 J => 'j',
541 K => 'k',
542 L => 'l',
543 M => 'm',
544 N => 'n',
545 O => 'o',
546 P => 'p',
547 Q => 'q',
548 R => 'r',
549 S => 's',
550 T => 't',
551 U => 'u',
552 V => 'v',
553 W => 'w',
554 X => 'x',
555 Y => 'y',
556 Z => 'z',
557 Key0 => '0',
558 Key1 => '1',
559 Key2 => '2',
560 Key3 => '3',
561 Key4 => '4',
562 Key5 => '5',
563 Key6 => '6',
564 Key7 => '7',
565 Key8 => '8',
566 Key9 => '9',
567 Space => ' ',
568 Minus => '-',
569 Period => '.',
570 _ => return None,
571 })
572}
573
574fn hex_encode(bytes: &[u8]) -> String {
576 let mut s = String::with_capacity(bytes.len() * 2);
577 for b in bytes {
578 s.push_str(&format!("{b:02x}"));
579 }
580 s
581}
582
583#[cfg(not(target_arch = "wasm32"))]
585fn decode_blob(s: &str) -> Result<Vec<u8>, String> {
586 use base64::Engine as _;
587 use std::io::Read as _;
588 let comp = base64::engine::general_purpose::STANDARD
589 .decode(s.trim())
590 .map_err(|e| format!("base64: {e}"))?;
591 let mut out = Vec::new();
592 flate2::read::ZlibDecoder::new(&comp[..])
593 .read_to_end(&mut out)
594 .map_err(|e| format!("inflate: {e}"))?;
595 Ok(out)
596}
597
598#[cfg(not(target_arch = "wasm32"))]
600fn hex_decode(s: &str) -> Vec<u8> {
601 let s = s.trim();
602 (0..s.len() / 2)
603 .filter_map(|i| u8::from_str_radix(s.get(i * 2..i * 2 + 2)?, 16).ok())
604 .collect()
605}
606
607#[cfg(not(target_arch = "wasm32"))]
609fn hex_to_32(s: &str) -> [u8; 32] {
610 let v = hex_decode(s);
611 let mut out = [0u8; 32];
612 let n = v.len().min(32);
613 out[..n].copy_from_slice(&v[..n]);
614 out
615}
616
617fn tex_rgb(r: f32, g: f32, b: f32) -> u32 {
618 ((r * 255.0) as u32) << 16 | ((g * 255.0) as u32) << 8 | (b * 255.0) as u32
619}
620
621const PERM: [u8; 512] = [
624 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
625 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
626 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 35, 63, 189, 114, 56, 42, 123,
627 165, 38, 72, 93, 69, 139, 138, 78, 149, 159, 56, 89, 152, 78, 61, 140, 63, 26, 142, 76, 124,
628 132, 72, 11, 90, 44, 82, 59, 96, 41, 148, 126, 157, 13, 49, 27, 176, 33, 47, 14, 97, 78, 71,
629 40, 87, 183, 4, 122, 92, 7, 72, 3, 246, 17, 225, 87, 91, 106, 203, 190, 57, 74, 76, 88, 207,
630 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146,
631 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196,
632 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94,
633 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55,
634 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180,
635 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185,
636 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,
637 140, 161, 137, 13, 191, 230, 66, 104, 153, 199, 167, 147, 99, 179, 92,
638 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
640 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
641 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 35, 63, 189, 114, 56, 42, 123,
642 165, 38, 72, 93, 69, 139, 138, 78, 149, 159, 56, 89, 152, 78, 61, 140, 63, 26, 142, 76, 124,
643 132, 72, 11, 90, 44, 82, 59, 96, 41, 148, 126, 157, 13, 49, 27, 176, 33, 47, 14, 97, 78, 71,
644 40, 87, 183, 4, 122, 92, 7, 72, 3, 246, 17, 225, 87, 91, 106, 203, 190, 57, 74, 76, 88, 207,
645 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146,
646 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196,
647 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94,
648 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55,
649 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174,
650];
651
652fn fade(t: f32) -> f32 {
653 t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
654}
655
656fn grad(hash: u8, x: f32, y: f32, z: f32) -> f32 {
657 let h = hash & 15;
658 let u = if h < 8 { x } else { y };
659 let v = if h < 8 { y } else { z };
660 (if (h & 1) == 0 { u } else { -u }) + (if (h & 2) == 0 { v } else { -v })
661}
662
663fn perlin3(x: f32, y: f32, z: f32) -> f32 {
664 let xi = (x.floor() as i32) & 255;
665 let yi = (y.floor() as i32) & 255;
666 let zi = (z.floor() as i32) & 255;
667
668 let xf = x - x.floor();
669 let yf = y - y.floor();
670 let zf = z - z.floor();
671
672 let u = fade(xf);
673 let v = fade(yf);
674 let w = fade(zf);
675
676 let p0 = PERM[xi as usize] as usize;
677 let p1 = PERM[((xi + 1) & 255) as usize] as usize;
678 let pa = PERM[(p0 + yi as usize) & 255] as usize;
679 let pb = PERM[(p0 + ((yi + 1) & 255) as usize) & 255] as usize;
680 let pc = PERM[(p1 + yi as usize) & 255] as usize;
681 let pd = PERM[(p1 + ((yi + 1) & 255) as usize) & 255] as usize;
682
683 let g000 = grad(PERM[(pa + zi as usize) & 255], xf, yf, zf);
684 let g001 = grad(
685 PERM[(pa + ((zi + 1) & 255) as usize) & 255],
686 xf,
687 yf,
688 zf - 1.0,
689 );
690 let g010 = grad(PERM[(pb + zi as usize) & 255], xf, yf - 1.0, zf);
691 let g011 = grad(
692 PERM[(pb + ((zi + 1) & 255) as usize) & 255],
693 xf,
694 yf - 1.0,
695 zf - 1.0,
696 );
697 let g100 = grad(PERM[(pc + zi as usize) & 255], xf - 1.0, yf, zf);
698 let g101 = grad(
699 PERM[(pc + ((zi + 1) & 255) as usize) & 255],
700 xf - 1.0,
701 yf,
702 zf - 1.0,
703 );
704 let g110 = grad(PERM[(pd + zi as usize) & 255], xf - 1.0, yf - 1.0, zf);
705 let g111 = grad(
706 PERM[(pd + ((zi + 1) & 255) as usize) & 255],
707 xf - 1.0,
708 yf - 1.0,
709 zf - 1.0,
710 );
711
712 let l00 = g000 + u * (g100 - g000);
713 let l01 = g001 + u * (g101 - g001);
714 let l10 = g010 + u * (g110 - g010);
715 let l11 = g011 + u * (g111 - g011);
716
717 let l0 = l00 + v * (l10 - l00);
718 let l1 = l01 + v * (l11 - l01);
719
720 l0 + w * (l1 - l0)
721}
722
723fn fast_rand_f64(state: &mut u64) -> f64 {
724 *state = state
725 .wrapping_mul(6364136223846793005)
726 .wrapping_add(1442695040888963407);
727 ((*state >> 32) as u32) as f64 / 4294967296.0
728}
729
730#[inline]
734fn put_px(buf: &mut [u32], idx: usize, color: u32, blend: u8) {
735 if idx >= buf.len() {
736 return;
737 }
738 if blend == 0 {
739 buf[idx] = color;
740 } else {
741 let old = buf[idx];
742 let r = (((old >> 16) & 255) + ((color >> 16) & 255)).min(255);
743 let g = (((old >> 8) & 255) + ((color >> 8) & 255)).min(255);
744 let b = ((old & 255) + (color & 255)).min(255);
745 buf[idx] = (r << 16) | (g << 8) | b;
746 }
747}
748
749#[inline]
751fn rgb(r: f64, g: f64, b: f64) -> u32 {
752 let r = (r as i64).clamp(0, 255) as u32;
753 let g = (g as i64).clamp(0, 255) as u32;
754 let b = (b as i64).clamp(0, 255) as u32;
755 (r << 16) | (g << 8) | b
756}
757
758fn draw_circle_outline(
759 buf: &mut [u32],
760 w: i32,
761 h: i32,
762 cx: i32,
763 cy: i32,
764 r: i32,
765 color: u32,
766 blend: u8,
767) {
768 let r = r.clamp(0, 20000); if r == 0 {
770 return;
771 }
772 let mut x = 0;
773 let mut y = r;
774 let mut d = 3 - 2 * r;
775 while x <= y {
776 plot_circle_points(buf, w, h, cx, cy, x, y, color, blend);
777 if d < 0 {
778 d += 4 * x + 6;
779 } else {
780 d += 4 * (x - y) + 10;
781 y -= 1;
782 }
783 x += 1;
784 }
785}
786
787fn plot_circle_points(
788 buf: &mut [u32],
789 w: i32,
790 h: i32,
791 cx: i32,
792 cy: i32,
793 x: i32,
794 y: i32,
795 color: u32,
796 blend: u8,
797) {
798 let points = [
799 (cx + x, cy + y),
800 (cx - x, cy + y),
801 (cx + x, cy - y),
802 (cx - x, cy - y),
803 (cx + y, cy + x),
804 (cx - y, cy + x),
805 (cx + y, cy - x),
806 (cx - y, cy - x),
807 ];
808 for &(px, py) in &points {
809 if px >= 0 && px < w && py >= 0 && py < h {
810 put_px(buf, (py * w + px) as usize, color, blend);
811 }
812 }
813}
814
815fn draw_circle_filled(
816 buf: &mut [u32],
817 w: i32,
818 h: i32,
819 cx: i32,
820 cy: i32,
821 r: i32,
822 color: u32,
823 blend: u8,
824) {
825 if r <= 0 {
826 return;
827 }
828 for dy in -r..=r {
829 let dx_max = ((r * r - dy * dy) as f64).sqrt() as i32;
830 let py = cy + dy;
831 if py < 0 || py >= h {
832 continue;
833 }
834 for dx in -dx_max..=dx_max {
835 let px = cx + dx;
836 if px >= 0 && px < w {
837 put_px(buf, (py * w + px) as usize, color, blend);
838 }
839 }
840 }
841}
842
843#[cfg(test)]
844mod draw_tests {
845 use super::*;
846
847 #[test]
848 fn filled_circle_actually_writes_pixels() {
849 let mut buf = vec![0u32; 100 * 100];
850 draw_circle_filled(&mut buf, 100, 100, 50, 50, 10, 0xFF00FF, 0);
851 assert_eq!(buf[50 * 100 + 50], 0xFF00FF, "centre pixel must be filled");
852 assert_eq!(buf[0], 0, "far corner must stay clear");
853 let n = buf.iter().filter(|&&p| p != 0).count();
854 assert!(n > 200 && n < 500, "r=10 disc area ≈ 314, got {n}");
855 }
856
857 #[test]
858 fn circle_outline_writes_a_ring() {
859 let mut buf = vec![0u32; 100 * 100];
860 draw_circle_outline(&mut buf, 100, 100, 50, 50, 20, 0x00FF00, 0);
861 assert_eq!(buf[50 * 100 + 50], 0, "outline must NOT fill the centre");
862 assert!(
863 buf.iter().any(|&p| p == 0x00FF00),
864 "outline must draw a ring"
865 );
866 }
867
868 #[test]
869 fn additive_blend_accumulates_channels() {
870 let mut buf = vec![0x202020u32; 1];
871 put_px(&mut buf, 0, 0x404040, 1);
872 assert_eq!(buf[0], 0x606060);
873 }
874}
875
876#[derive(Clone, Copy)]
882pub struct UiTheme {
883 pub primary: u32,
884 pub accent: u32,
885 pub track: u32,
886 pub warn: u32,
887 pub text: u32,
888 pub bg: u32,
889}
890
891impl Default for UiTheme {
892 fn default() -> Self {
893 Self {
894 primary: 0x00D2FF, accent: 0x28FFB4, track: 0x2C3E64, warn: 0xFF5A5A, text: 0xBEEBFF, bg: 0x0A1018, }
901 }
902}
903
904#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
905pub struct Interpreter {
906 globals: HashMap<String, Expr>,
907 global_seed: Env,
910 functions: FxHashMap<String, Rc<FnDef>>,
911 pub(crate) structs: HashMap<String, Vec<String>>,
913 enum_variants: HashMap<String, (String, usize)>,
915 _modules: HashMap<String, Vec<FnDef>>,
916 gfx: RefCell<GfxState>,
917 svg: RefCell<Option<SvgWriter>>,
918 pub source_dir: Option<std::path::PathBuf>,
920 loaded_files: std::collections::HashSet<String>,
922 #[cfg(not(target_arch = "wasm32"))]
924 audio: Option<AudioEngine>,
925 #[cfg(not(target_arch = "wasm32"))]
926 fft: RefCell<FftAnalyzer>,
927 fft_bands_cache: RefCell<Vec<f32>>,
928 start_time_secs: f64,
931 frame_num: u64,
933 #[cfg(target_arch = "wasm32")]
935 wasm_target_fps: f64,
936 #[cfg(target_arch = "wasm32")]
938 wasm_next_present_ms: f64,
939 rand_state: u64,
941 #[cfg(not(target_arch = "wasm32"))]
943 mic: Option<ling_mic::MicInput>,
944 #[cfg(not(target_arch = "wasm32"))]
946 crypto_ids: Vec<ling_crypto::KnotIdentity>,
947 text_buffer: String,
949 record_n: u32,
951 #[cfg(not(target_arch = "wasm32"))]
953 mic_buffer: Vec<f32>,
954 #[cfg(not(target_arch = "wasm32"))]
956 fonts: Vec<ling_graphics::VectorFont>,
957 ui_theme: UiTheme,
959 mouse_was_down: bool,
961 #[cfg(not(target_arch = "wasm32"))]
963 music: Option<ling_music::MusicEngine>,
964 #[cfg(not(target_arch = "wasm32"))]
965 music_init: bool,
966 tracks: Vec<ling_music::DecodedAudio>,
968 lyrics: Vec<ling_music::Lyrics>,
970 midis: Vec<ling_music::MidiSong>,
972 soft_bodies: Vec<ling_physics::soft::SoftBody>,
974 rigid_world: ling_physics::rigid::PhysicsWorld,
976 liquids: Vec<ling_physics::liquid::LiquidGrid>,
978 meshes: Vec<crate::gfx::shapes::ColorMesh>,
979 dialog: Option<ling_game::dialog::Dialog>,
981 dialog_colors: [u32; 4],
983 frames: Vec<String>,
985 error_trace: Option<Vec<String>>,
988 #[cfg(not(target_arch = "wasm32"))]
992 input: RefCell<Option<InputState>>,
993}
994
995#[cfg(not(target_arch = "wasm32"))]
997struct InputState {
998 sensorium: ling_input::Sensorium,
999 backend: ling_input::backend::GilrsBackend,
1000}
1001
1002impl Interpreter {
1003 pub fn new() -> Self {
1004 #[cfg(not(target_arch = "wasm32"))]
1005 let audio = AudioEngine::new()
1006 .map_err(|e| eprintln!("audio init failed (no sound): {e}"))
1007 .ok();
1008 Self {
1009 globals: HashMap::new(),
1010 global_seed: new_env(),
1011 functions: FxHashMap::default(),
1012 structs: HashMap::new(),
1013 enum_variants: HashMap::new(),
1014 _modules: HashMap::new(),
1015 gfx: RefCell::new(GfxState::new()),
1016 svg: RefCell::new(None),
1017 source_dir: None,
1018 loaded_files: std::collections::HashSet::new(),
1019 #[cfg(not(target_arch = "wasm32"))]
1020 audio,
1021 #[cfg(not(target_arch = "wasm32"))]
1022 fft: RefCell::new(FftAnalyzer::new(2048, 44100)),
1023 fft_bands_cache: RefCell::new(vec![]),
1024 start_time_secs: crate::runtime::now_secs(),
1025 frame_num: 0,
1026 #[cfg(target_arch = "wasm32")]
1027 wasm_target_fps: 60.0,
1028 #[cfg(target_arch = "wasm32")]
1029 wasm_next_present_ms: 0.0,
1030 rand_state: 0x123456789ABCDEF,
1031 #[cfg(not(target_arch = "wasm32"))]
1032 mic: None,
1033 #[cfg(not(target_arch = "wasm32"))]
1034 crypto_ids: Vec::new(),
1035 text_buffer: String::new(),
1036 record_n: 0,
1037 #[cfg(not(target_arch = "wasm32"))]
1038 mic_buffer: Vec::new(),
1039 #[cfg(not(target_arch = "wasm32"))]
1040 fonts: Vec::new(),
1041 ui_theme: UiTheme::default(),
1042 mouse_was_down: false,
1043 #[cfg(not(target_arch = "wasm32"))]
1044 music: None,
1045 #[cfg(not(target_arch = "wasm32"))]
1046 music_init: false,
1047 tracks: Vec::new(),
1048 lyrics: Vec::new(),
1049 midis: Vec::new(),
1050 soft_bodies: Vec::new(),
1051 rigid_world: ling_physics::rigid::PhysicsWorld::new(),
1052 liquids: Vec::new(),
1053 meshes: Vec::new(),
1054 dialog: None,
1055 dialog_colors: [0xE6F2FF, 0xFFD24A, 0x4AD2FF, 0x6CFF8C], frames: Vec::new(),
1057 error_trace: None,
1058 #[cfg(not(target_arch = "wasm32"))]
1059 input: RefCell::new(None),
1060 }
1061 }
1062
1063 #[cfg(not(target_arch = "wasm32"))]
1067 fn pad_poll(&self) -> usize {
1068 let mut slot = self.input.borrow_mut();
1069 if slot.is_none() {
1070 match ling_input::backend::GilrsBackend::new() {
1071 Ok(backend) => {
1072 *slot = Some(InputState { sensorium: ling_input::Sensorium::new(4), backend });
1073 },
1074 Err(_) => return 0,
1075 }
1076 }
1077 let st = slot.as_mut().unwrap();
1078 st.sensorium.begin_frame();
1079 st.sensorium.pump(&mut st.backend);
1080 st.sensorium.update(1.0 / 60.0);
1081 st.sensorium.devices.count()
1082 }
1083
1084 #[cfg(not(target_arch = "wasm32"))]
1087 fn with_pad<T>(&self, slot: usize, default: T, f: impl FnOnce(&ling_input::Gamepad) -> T) -> T {
1088 let inp = self.input.borrow();
1089 match inp.as_ref().and_then(|s| s.sensorium.player(slot)) {
1090 Some(p) => f(p),
1091 None => default,
1092 }
1093 }
1094
1095 pub fn take_error_trace(&mut self) -> Vec<String> {
1098 self.error_trace.take().unwrap_or_default()
1099 }
1100
1101 #[cfg(target_arch = "wasm32")]
1102 fn wasm_pace_frame(&mut self) {
1103 let fps = self.wasm_target_fps.max(1.0);
1104 let frame_ms = 1000.0 / fps;
1105 let now = js_sys::Date::now();
1106 if self.wasm_next_present_ms <= 0.0 {
1107 self.wasm_next_present_ms = now + frame_ms;
1108 return;
1109 }
1110
1111 let wait_ms = (self.wasm_next_present_ms - now).floor() as i32;
1112 if wait_ms > 0 {
1113 wasm_sleep_ms(wait_ms);
1114 }
1115
1116 let after = js_sys::Date::now();
1117 if after > self.wasm_next_present_ms + frame_ms * 3.0 {
1118 self.wasm_next_present_ms = after + frame_ms;
1119 } else {
1120 self.wasm_next_present_ms += frame_ms;
1121 }
1122 }
1123
1124 fn framed<T, F>(&mut self, name: &str, body: F) -> Result<T, EvalErr>
1127 where
1128 F: FnOnce(&mut Self) -> Result<T, EvalErr>,
1129 {
1130 self.frames.push(name.to_string());
1131 let result = body(self);
1132 if matches!(result, Err(EvalErr::Runtime(_))) && self.error_trace.is_none() {
1133 self.error_trace = Some(self.frames.clone());
1134 }
1135 self.frames.pop();
1136 result
1137 }
1138
1139 #[cfg(not(target_arch = "wasm32"))]
1143 fn render_dialog(&mut self, x: f32, y: f32, w: f32, h: f32, font: i64, t: f32) {
1144 let (runs, typing) = match &self.dialog {
1145 Some(d) if !d.is_closed() => {
1146 let runs: Vec<(String, usize, bool)> = d
1147 .visible_runs()
1148 .into_iter()
1149 .map(|r| (r.text, r.role.index(), r.newline_before))
1150 .collect();
1151 (runs, d.is_typing())
1152 },
1153 _ => return,
1154 };
1155 let colors = self.dialog_colors;
1156 let b = 12.0;
1158 let corners: Vec<[f32; 2]> = vec![
1159 [x + b, y],
1160 [x + w - b, y],
1161 [x + w, y + b],
1162 [x + w, y + h - b],
1163 [x + w - b, y + h],
1164 [x + b, y + h],
1165 [x, y + h - b],
1166 [x, y + b],
1167 [x + b, y],
1168 ];
1169 {
1170 let mut gfx = self.gfx.borrow_mut();
1171 let (bw, bh) = (gfx.width, gfx.height);
1172 crate::gfx::raster::fill_contours_aa(
1173 &mut gfx.buffer,
1174 bw,
1175 bh,
1176 0x0A1018,
1177 false,
1178 std::slice::from_ref(&corners),
1179 );
1180 for seg in corners.windows(2) {
1181 crate::gfx::raster::draw_line_aa(
1182 &mut gfx.buffer,
1183 bw,
1184 bh,
1185 0x00D2FF,
1186 false,
1187 seg[0][0],
1188 seg[0][1],
1189 seg[1][0],
1190 seg[1][1],
1191 );
1192 }
1193 }
1194 let px = 22.0f32;
1196 let pad = 20.0f32;
1197 let line_h = px * 1.45;
1198 let mut cx = x + pad;
1199 let mut cy = y + pad;
1200 let use_font = font >= 0 && (font as usize) < self.fonts.len();
1201 for (text, role, nl) in &runs {
1202 if *nl {
1203 cx = x + pad;
1204 cy += line_h;
1205 }
1206 for word in text.split_inclusive(' ') {
1207 let wpx = if use_font {
1208 self.fonts[font as usize].measure(word, px)
1209 } else {
1210 ling_ui::holo::text_width(word, px * 0.6, px * 0.24)
1211 };
1212 if cx + wpx > x + w - pad && cx > x + pad + 1.0 {
1213 cx = x + pad;
1214 cy += line_h;
1215 }
1216 if cy + line_h > y + h {
1217 break;
1218 }
1219 let col = colors[(*role).min(3)];
1220 if use_font {
1221 let glyphs = self.font_layout_2d_glyphs(font as usize, cx, cy, px, word);
1222 let mut gfx = self.gfx.borrow_mut();
1223 let (bw, bh, add) = (gfx.width, gfx.height, gfx.blend == 1);
1224 for contours in &glyphs {
1225 crate::gfx::raster::fill_contours_aa(
1226 &mut gfx.buffer,
1227 bw,
1228 bh,
1229 col,
1230 add,
1231 contours,
1232 );
1233 }
1234 } else {
1235 let segs = ling_ui::holo::text_lines(word, cx, cy, px * 0.6, px, px * 0.24);
1236 let mut gfx = self.gfx.borrow_mut();
1237 let (bw, bh) = (gfx.width, gfx.height);
1238 for s in segs {
1239 draw_line(&mut gfx.buffer, bw, bh, col, s[0], s[1], s[2], s[3]);
1240 }
1241 }
1242 cx += wpx;
1243 }
1244 }
1245 if !typing && (t * 3.0).sin() > 0.0 {
1247 let ax = x + w - 26.0;
1248 let ay = y + h - 22.0;
1249 let mut gfx = self.gfx.borrow_mut();
1250 let (bw, bh) = (gfx.width, gfx.height);
1251 crate::gfx::raster::fill_contours_aa(
1252 &mut gfx.buffer,
1253 bw,
1254 bh,
1255 0x00D2FF,
1256 false,
1257 std::slice::from_ref(&vec![
1258 [ax - 7.0, ay],
1259 [ax + 7.0, ay],
1260 [ax, ay + 9.0],
1261 [ax - 7.0, ay],
1262 ]),
1263 );
1264 }
1265 }
1266
1267 #[cfg(not(target_arch = "wasm32"))]
1270 fn ensure_music(&mut self) -> bool {
1271 if self.music.is_some() {
1272 return true;
1273 }
1274 if self.music_init {
1275 return false;
1276 }
1277 self.music_init = true;
1278 match ling_music::MusicEngine::new() {
1279 Ok(m) => {
1280 self.music = Some(m);
1281 true
1282 },
1283 Err(e) => {
1284 eprintln!("music engine init failed (no music playback): {e}");
1285 false
1286 },
1287 }
1288 }
1289
1290 #[cfg(target_arch = "wasm32")]
1291 fn wasm_resolve_source_path(&self, path: &str) -> String {
1292 let p = path.trim();
1293 if p.is_empty() {
1294 return String::new();
1295 }
1296 if p.contains("://") || p.starts_with('/') || p.starts_with("./") || p.starts_with("../") {
1297 return p.to_string();
1298 }
1299 if let Some(d) = &self.source_dir {
1300 let base = d.to_string_lossy().replace('\\', "/");
1301 if !base.is_empty() {
1302 return format!(
1303 "{}/{}",
1304 base.trim_end_matches('/'),
1305 p.trim_start_matches("./")
1306 );
1307 }
1308 }
1309 p.to_string()
1310 }
1311
1312 #[cfg(target_arch = "wasm32")]
1313 fn wasm_music_builtin(&mut self, name: &str, args: &[Value]) -> Result<Option<Value>, EvalErr> {
1314 match name {
1315 "music_load" | "载入音乐" | "音楽読込" | "음악로드" | "โหลดเพลง" => {
1317 let path = self.arg_str(args, 0, "");
1318 let resolved = self.wasm_resolve_source_path(&path);
1319 match wasm_fetch_bytes(&resolved)
1320 .and_then(|bytes| ling_music::from_bytes(&bytes).map_err(|e| e.to_string()))
1321 {
1322 Ok(t) => {
1323 let id = self.tracks.len();
1324 self.tracks.push(t);
1325 return Ok(Some(Value::Number(id as f64)));
1326 },
1327 Err(e) => {
1328 eprintln!("music_load failed ({path}): {e}");
1329 return Ok(Some(Value::Number(-1.0)));
1330 },
1331 }
1332 },
1333 "music_duration" | "音乐时长" | "音楽長さ" | "음악길이" | "ความยาวเพลง" => {
1334 let id = self.arg_num(args, 0, 0.0)? as i64;
1335 let d = self
1336 .tracks
1337 .get(id as usize)
1338 .map(|t| t.duration)
1339 .unwrap_or(0.0);
1340 return Ok(Some(Value::Number(d as f64)));
1341 },
1342 "music_bpm" | "节拍速度" | "テンポ" | "템포" | "จังหวะต่อนาที" => {
1343 let id = self.arg_num(args, 0, 0.0)? as i64;
1344 let b = self
1345 .tracks
1346 .get(id as usize)
1347 .map(|t| ling_music::analysis::bpm(&t.mono, t.rate))
1348 .unwrap_or(0.0);
1349 return Ok(Some(Value::Number(b as f64)));
1350 },
1351 "music_key" | "调性" | "調性" | "조성" | "คีย์เพลง" => {
1352 let id = self.arg_num(args, 0, 0.0)? as i64;
1353 let k = self
1354 .tracks
1355 .get(id as usize)
1356 .map(|t| ling_music::analysis::key_name(&t.mono, t.rate))
1357 .unwrap_or_default();
1358 return Ok(Some(Value::Str(k)));
1359 },
1360 "music_onsets" | "音符起点" | "オンセット" | "온셋" | "จุดเริ่มเสียง" => {
1361 let id = self.arg_num(args, 0, 0.0)? as i64;
1362 let v = self
1363 .tracks
1364 .get(id as usize)
1365 .map(|t| ling_music::analysis::onsets(&t.mono, t.rate))
1366 .unwrap_or_default();
1367 return Ok(Some(Value::List(
1368 v.into_iter().map(|x| Value::Number(x as f64)).collect(),
1369 )));
1370 },
1371 "music_beat_grid" | "节拍网格" | "ビートグリッド" | "비트그리드" | "กริดจังหวะ" => {
1372 let id = self.arg_num(args, 0, 0.0)? as i64;
1373 let beats = self
1374 .tracks
1375 .get(id as usize)
1376 .map(|t| {
1377 let b = ling_music::analysis::bpm(&t.mono, t.rate);
1378 ling_music::analysis::beat_grid(&t.mono, t.rate, b)
1379 })
1380 .unwrap_or_default();
1381 return Ok(Some(Value::List(
1382 beats.into_iter().map(|x| Value::Number(x as f64)).collect(),
1383 )));
1384 },
1385 "music_lrc" | "载入歌词" | "歌詞読込" | "가사로드" | "โหลดเนื้อเพลง" => {
1386 let path = self.arg_str(args, 0, "");
1387 let resolved = self.wasm_resolve_source_path(&path);
1388 match wasm_fetch_text(&resolved) {
1389 Ok(text) => {
1390 let id = self.lyrics.len();
1391 self.lyrics.push(ling_music::Lyrics::parse(&text));
1392 return Ok(Some(Value::Number(id as f64)));
1393 },
1394 Err(e) => {
1395 eprintln!("music_lrc failed ({path}): {e}");
1396 return Ok(Some(Value::Number(-1.0)));
1397 },
1398 }
1399 },
1400 "music_lyric" | "当前歌词" | "現在歌詞" | "현재가사" | "เนื้อเพลงปัจจุบัน" => {
1401 let id = self.arg_num(args, 0, 0.0)? as i64;
1402 let t = self.arg_num(args, 1, 0.0)? as f32;
1403 let line = self
1404 .lyrics
1405 .get(id as usize)
1406 .map(|l| l.line_at(t).to_string())
1407 .unwrap_or_default();
1408 return Ok(Some(Value::Str(line)));
1409 },
1410 "music_midi_load" | "载入MIDI" | "MIDI読込" | "미디로드" | "โหลดมิดี" => {
1411 let path = self.arg_str(args, 0, "");
1412 let resolved = self.wasm_resolve_source_path(&path);
1413 match wasm_fetch_bytes(&resolved)
1414 .and_then(|bytes| ling_music::midi::from_bytes(&bytes).map_err(|e| e.to_string()))
1415 {
1416 Ok(m) => {
1417 let id = self.midis.len();
1418 self.midis.push(m);
1419 return Ok(Some(Value::Number(id as f64)));
1420 },
1421 Err(e) => {
1422 eprintln!("music_midi_load failed ({path}): {e}");
1423 return Ok(Some(Value::Number(-1.0)));
1424 },
1425 }
1426 },
1427 "music_midi_count" | "MIDI数量" | "MIDI数" | "미디수" | "จำนวนมิดี" => {
1428 let id = self.arg_num(args, 0, 0.0)? as i64;
1429 let n = self
1430 .midis
1431 .get(id as usize)
1432 .map(|m| m.notes.len())
1433 .unwrap_or(0);
1434 return Ok(Some(Value::Number(n as f64)));
1435 },
1436 "music_midi_notes" | "MIDI音符" | "MIDIノート" | "미디음표" | "โน้ตมิดี" => {
1437 let id = self.arg_num(args, 0, 0.0)? as i64;
1438 let mut out = Vec::new();
1439 if let Some(m) = self.midis.get(id as usize) {
1440 for n in &m.notes {
1441 out.push(Value::Number(n.time as f64));
1442 out.push(Value::Number(n.midi as f64));
1443 }
1444 }
1445 return Ok(Some(Value::List(out)));
1446 },
1447 "music_midi_bars" | "MIDI音条" | "MIDIバー" | "미디바" | "แท่งมิดี" => {
1448 let id = self.arg_num(args, 0, 0.0)? as i64;
1449 let mut out = Vec::new();
1450 if let Some(m) = self.midis.get(id as usize) {
1451 for n in &m.notes {
1452 out.push(Value::Number(n.time as f64));
1453 out.push(Value::Number(n.midi as f64));
1454 out.push(Value::Number(n.dur as f64));
1455 }
1456 }
1457 return Ok(Some(Value::List(out)));
1458 },
1459 "music_judge" | "判定" | "判定する" | "판정" | "ตัดสินจังหวะ" => {
1460 let delta_ms = self.arg_num(args, 0, 9999.0)? as f32;
1461 return Ok(Some(Value::Number(
1462 ling_music::Grade::judge(delta_ms).index() as f64,
1463 )));
1464 },
1465 "music_grade_name" | "判定名" | "判定名称" | "판정이름" | "ชื่อการตัดสิน" => {
1466 let idx = self.arg_num(args, 0, 4.0)? as i32;
1467 return Ok(Some(Value::Str(
1468 ling_music::Grade::from_index(idx).name().to_string(),
1469 )));
1470 },
1471 "music_note_name" | "音名" | "音名称" | "음이름" | "ชื่อโน้ต" => {
1472 let hz = self.arg_num(args, 0, 0.0)? as f32;
1473 return Ok(Some(Value::Str(ling_music::note::hz_to_name(hz))));
1474 },
1475 "music_hz" | "音符频率" | "音符周波数" | "음표주파수" | "ความถี่โน้ต" => {
1476 let midi = match args.get(0) {
1477 Some(Value::Str(s)) => ling_music::note::parse_pitch(s).unwrap_or(69),
1478 Some(Value::Number(n)) => *n as i32,
1479 _ => 69,
1480 };
1481 return Ok(Some(Value::Number(
1482 ling_music::note::midi_to_hz(midi as f32) as f64,
1483 )));
1484 },
1485 "music_pitch_score" | "音准评分" | "音程スコア" | "음정점수" | "คะแนนเสียง" => {
1486 let hz = self.arg_num(args, 0, 0.0)? as f32;
1487 let target = self.arg_num(args, 1, 0.0)? as f32;
1488 return Ok(Some(Value::Number(
1489 ling_music::karaoke::pitch_score(hz, target) as f64,
1490 )));
1491 },
1492
1493 "music_play" | "播放音乐" | "音楽再生" | "음악재생" | "เล่นเพลง" => {
1495 let id = self.arg_num(args, 0, 0.0)? as usize;
1496 if let Some(t) = self.tracks.get(id) {
1497 crate::gfx::audio_web::play_music(id, &t.stereo, t.channels, t.rate, 1.0);
1498 }
1499 return Ok(Some(Value::Unit));
1500 },
1501 "music_pause" | "暂停音乐" | "音楽一時停止" | "음악일시정지" | "หยุดเพลงชั่วคราว"
1502 | "music_stop" | "停止音乐" | "音楽停止" | "음악정지" | "หยุดเพลง" => {
1503 let id = self.arg_num(args, 0, 0.0)? as usize;
1504 crate::gfx::audio_web::stop_music(id);
1505 return Ok(Some(Value::Unit));
1506 },
1507 "music_seek" | "定位音乐" | "音楽シーク" | "음악탐색" | "ค้นหาเพลง" => {
1508 return Ok(Some(Value::Unit));
1510 },
1511 "music_pos" | "音乐位置" | "音楽位置" | "음악위치" | "ตำแหน่งเพลง" => {
1512 return Ok(Some(Value::Number(
1513 crate::gfx::audio_web::current_music_position(),
1514 )));
1515 },
1516 "music_volume" | "音乐音量" | "音楽音量" | "음악음량" | "ระดับเพลง" => {
1517 let vol = self.arg_num(args, 0, 0.8)? as f32;
1518 crate::gfx::audio_web::set_music_volume(0, vol);
1520 return Ok(Some(Value::Unit));
1521 },
1522
1523 "music_fft" | "音乐频谱" | "音楽スペクトル" | "음악스펙트럼" | "สเปกตรัมเพลง" => {
1525 let id = self.arg_num(args, 0, 0.0)? as usize;
1526 let nbands = self.arg_num(args, 1, 16.0)? as usize;
1527 let pos = crate::gfx::audio_web::current_music_position() as f32;
1528 let bands = if let Some(t) = self.tracks.get(id) {
1529 ling_music::analysis::fft_bands_at_pos(&t.mono, t.rate, pos, nbands)
1530 } else {
1531 vec![0.0f32; nbands]
1532 };
1533 return Ok(Some(Value::List(
1534 bands.into_iter().map(|x| Value::Number(x as f64)).collect(),
1535 )));
1536 },
1537
1538 _ => {},
1539 }
1540 Ok(None)
1541 }
1542
1543 #[cfg(not(target_arch = "wasm32"))]
1547 fn font_layout_2d(
1548 &mut self,
1549 id: usize,
1550 x: f32,
1551 y: f32,
1552 px: f32,
1553 text: &str,
1554 ) -> Vec<Vec<[f32; 2]>> {
1555 let mut out = Vec::new();
1556 for g in self.font_layout_2d_glyphs(id, x, y, px, text) {
1557 out.extend(g);
1558 }
1559 out
1560 }
1561
1562 #[cfg(not(target_arch = "wasm32"))]
1565 fn font_layout_2d_glyphs(
1566 &mut self,
1567 id: usize,
1568 x: f32,
1569 y: f32,
1570 px: f32,
1571 text: &str,
1572 ) -> Vec<Vec<Vec<[f32; 2]>>> {
1573 let font = &mut self.fonts[id];
1574 let asc = font.ascent();
1575 let tol = 0.3 / px;
1576 let mut pen = 0.0f32;
1577 let mut glyphs = Vec::new();
1578 for ch in text.chars() {
1579 let go = font.glyph_outline(ch, tol);
1580 let mut contours = Vec::with_capacity(go.polylines.len());
1581 for pl in &go.polylines {
1582 let mapped: Vec<[f32; 2]> = pl
1583 .iter()
1584 .map(|p| [x + (pen + p[0]) * px, y + (asc - p[1]) * px])
1585 .collect();
1586 contours.push(mapped);
1587 }
1588 glyphs.push(contours);
1589 pen += go.advance;
1590 }
1591 glyphs
1592 }
1593
1594 pub fn register_program(&mut self, program: &Program) -> Result<(), String> {
1599 for item in &program.items {
1600 self.register_item("", item)?;
1601 }
1602 let mut env = new_env();
1603 let non_do: Vec<_> = self
1604 .globals
1605 .iter()
1606 .filter(|(_, e)| !matches!(e, Expr::Do(_)))
1607 .map(|(k, e)| (k.clone(), e.clone()))
1608 .collect();
1609 let mut pending: Vec<(String, Expr)> = Vec::new();
1610 for (k, expr) in &non_do {
1611 let mut tmp = new_env();
1612 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
1613 env.insert(k.clone(), v);
1614 } else {
1615 pending.push((k.clone(), expr.clone()));
1616 }
1617 }
1618 for (k, expr) in &pending {
1619 let mut tmp = env.clone();
1620 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
1621 env.insert(k.clone(), v);
1622 }
1623 }
1624 self.global_seed = env;
1625 Ok(())
1626 }
1627
1628 pub fn run_program(&mut self, program: &Program) -> Result<(), String> {
1629 self.register_program(program)?;
1630 let entry = self
1631 .find_entry()
1632 .ok_or("no entry point — need `bind start = do {...}` or `ผูก เริ่ม = ทำ {...}`")?;
1633 let mut env = self.global_seed.clone();
1634 self.framed("start", |me| me.eval_expr(&entry, &mut env))
1635 .map(|_| ())
1636 .map_err(|e| match e {
1637 EvalErr::Runtime(s) => s,
1638 EvalErr::Return(_) => "unexpected top-level return".to_string(),
1639 EvalErr::Break => "unexpected break at top level".to_string(),
1640 })
1641 }
1642
1643 fn register_item(&mut self, ns: &str, item: &Item) -> Result<(), String> {
1644 match item {
1645 Item::Bind(name, expr) => {
1646 let key = if ns.is_empty() {
1647 name.clone()
1648 } else {
1649 format!("{ns}::{name}")
1650 };
1651 self.globals.insert(key, expr.clone());
1652 },
1653 Item::Fn(def) => {
1654 let key = if ns.is_empty() {
1655 def.name.clone()
1656 } else {
1657 format!("{ns}::{}", def.name)
1658 };
1659 self.functions.insert(key, Rc::new(def.clone()));
1660 },
1661 Item::Mod(name, body) => {
1662 let child_ns = if ns.is_empty() {
1663 name.clone()
1664 } else {
1665 format!("{ns}::{name}")
1666 };
1667 for child in body {
1668 self.register_item(&child_ns, child)?;
1669 }
1670 },
1671 Item::TypeAlias(_, _) => {},
1672 Item::Struct(name, fields) => {
1673 self.structs.insert(name.clone(), fields.clone());
1674 if !ns.is_empty() {
1675 self.structs.insert(format!("{ns}::{name}"), fields.clone());
1676 }
1677 },
1678 Item::Enum(name, variants) => {
1679 for v in variants {
1680 self.enum_variants
1681 .insert(v.name.clone(), (name.clone(), v.arity));
1682 self.enum_variants
1683 .insert(format!("{name}::{}", v.name), (name.clone(), v.arity));
1684 if !ns.is_empty() {
1685 self.enum_variants
1686 .insert(format!("{ns}::{name}::{}", v.name), (name.clone(), v.arity));
1687 }
1688 }
1689 },
1690 Item::Use { path, alias } => {
1691 self.load_module(path, alias.as_deref(), ns)?;
1692 },
1693 }
1694 Ok(())
1695 }
1696
1697 fn load_module(
1702 &mut self,
1703 path: &str,
1704 alias: Option<&str>,
1705 parent_ns: &str,
1706 ) -> Result<(), String> {
1707 #[cfg(target_arch = "wasm32")]
1709 let (source, sub_dir) = {
1710 if self.loaded_files.contains(path) {
1712 return Ok(());
1713 }
1714 self.loaded_files.insert(path.to_string());
1715
1716 let src = crate::runtime::get_wasm_module(path)
1717 .or_else(|| crate::runtime::get_wasm_module(&format!("{}.ling", path)))
1718 .ok_or_else(|| format!("use: cannot find module '{path}'"))?;
1719 (src, None::<std::path::PathBuf>)
1720 };
1721
1722 #[cfg(not(target_arch = "wasm32"))]
1724 let (source, sub_dir) = {
1725 let base_dir = self
1726 .source_dir
1727 .clone()
1728 .unwrap_or_else(|| std::path::PathBuf::from("."));
1729 let raw = std::path::Path::new(path);
1730 let candidates: Vec<std::path::PathBuf> = vec![
1731 base_dir.join(format!("{}.ling", path)),
1732 base_dir.join(format!("{}.灵", path)),
1733 base_dir.join(format!("{}.령", path)),
1734 base_dir.join(format!("{}.霊", path)),
1735 base_dir.join(format!("{}.ลิง", path)),
1736 base_dir.join(raw),
1737 std::path::PathBuf::from(format!("{}.ling", path)),
1738 std::path::PathBuf::from(path),
1739 ];
1740
1741 let resolved = candidates
1742 .into_iter()
1743 .find(|p| p.exists())
1744 .ok_or_else(|| format!("use: cannot find module '{path}'"))?;
1745
1746 let canonical = resolved
1747 .canonicalize()
1748 .unwrap_or_else(|_| resolved.clone())
1749 .to_string_lossy()
1750 .to_string();
1751
1752 if self.loaded_files.contains(&canonical) {
1754 return Ok(());
1755 }
1756 self.loaded_files.insert(canonical.clone());
1757
1758 let src = std::fs::read_to_string(&resolved)
1759 .map_err(|e| format!("use: failed to read '{path}': {e}"))?;
1760 let dir = resolved.parent().map(|p| p.to_path_buf());
1761 (src, dir)
1762 };
1763
1764 let program = crate::parser::parse(&source)
1765 .map_err(|e| format!("use: parse error in '{path}': {e}"))?;
1766
1767 let target_ns = match (parent_ns.is_empty(), alias) {
1769 (_, Some(a)) if !parent_ns.is_empty() => format!("{parent_ns}::{a}"),
1770 (_, Some(a)) => a.to_string(),
1771 (false, None) => parent_ns.to_string(),
1772 (true, None) => String::new(),
1773 };
1774
1775 let prev_dir = self.source_dir.clone();
1777 self.source_dir = sub_dir;
1778
1779 for item in &program.items {
1780 self.register_item(&target_ns, item)?;
1781 }
1782
1783 self.source_dir = prev_dir;
1784 Ok(())
1785 }
1786
1787 fn find_entry(&self) -> Option<Expr> {
1788 for key in crate::entry::ENTRY_NAMES {
1790 if let Some(e) = self.globals.get(*key) {
1791 return Some(e.clone());
1792 }
1793 }
1794 self.globals
1795 .values()
1796 .find(|e| matches!(e, Expr::Do(_)))
1797 .cloned()
1798 }
1799
1800 fn eval_expr(&mut self, expr: &Expr, env: &mut Env) -> EvalResult {
1803 match expr {
1804 Expr::Str(s) => Ok(Value::Str(s.clone())),
1805 Expr::Number(n) => Ok(Value::Number(*n)),
1806 Expr::Bool(b) => Ok(Value::Bool(*b)),
1807 Expr::Unit => Ok(Value::Unit),
1808 Expr::Array(elems) => {
1809 let vs: Vec<_> = elems
1810 .iter()
1811 .map(|e| self.eval_expr(e, env))
1812 .collect::<Result<_, _>>()?;
1813 Ok(Value::List(Rc::new(vs)))
1814 },
1815
1816 Expr::Ident(name) => self.lookup(name, env),
1817
1818 Expr::Path(segs) => {
1819 if segs.len() == 1 {
1820 return self.lookup(&segs[0], env);
1821 }
1822 Ok(Value::Str(segs.join("::")))
1823 },
1824
1825 Expr::Ref(inner) => self.eval_expr(inner, env),
1826 Expr::Await(inner) => self.eval_expr(inner, env),
1827
1828 Expr::Do(stmts) => {
1829 let mut local = env.clone();
1830 Ok(self.exec_block(stmts, &mut local)?.unwrap_or(Value::Unit))
1831 },
1832
1833 Expr::BinOp(op, lhs, rhs) => {
1834 let l = self.eval_expr(lhs, env)?;
1835 let r = self.eval_expr(rhs, env)?;
1836 self.apply_binop(op, l, r)
1837 },
1838
1839 Expr::If { cond, then, elseifs, else_body } => {
1840 let cond_val = self.eval_expr(cond, env)?;
1841 if self.is_truthy(&cond_val) {
1842 return Ok(self.exec_block(then, env)?.unwrap_or(Value::Unit));
1843 }
1844 for (ei_cond, ei_body) in elseifs {
1845 let ei_cond_val = self.eval_expr(ei_cond, env)?;
1846 if self.is_truthy(&ei_cond_val) {
1847 return Ok(self.exec_block(ei_body, env)?.unwrap_or(Value::Unit));
1848 }
1849 }
1850 if let Some(eb) = else_body {
1851 return Ok(self.exec_block(eb, env)?.unwrap_or(Value::Unit));
1852 }
1853 Ok(Value::Unit)
1854 },
1855
1856 Expr::While { cond, body } => {
1857 loop {
1861 let cv = self.eval_expr(cond, env)?;
1862 if !self.is_truthy(&cv) {
1863 break;
1864 }
1865 match self.exec_block(body, env) {
1866 Ok(_) => {},
1867 Err(EvalErr::Break) => break,
1868 Err(e) => return Err(e),
1869 }
1870 }
1871 Ok(Value::Unit)
1872 },
1873
1874 Expr::For { var, iter, body } => {
1875 let iter_val = self.eval_expr(iter, env)?;
1876 let items = self.value_to_iter(iter_val)?;
1877 for item in items {
1878 let mut local = env.clone();
1879 local.insert(var.clone(), item);
1880 match self.exec_block(body, &mut local) {
1881 Ok(_) => {},
1882 Err(EvalErr::Break) => break,
1883 Err(e) => return Err(e),
1884 }
1885 }
1886 Ok(Value::Unit)
1887 },
1888
1889 Expr::Match(subject, arms) => {
1890 let subj = self.eval_expr(subject, env)?;
1891 for arm in arms {
1892 if let Some(bindings) = self.match_pattern(&arm.pattern, &subj) {
1893 let mut local = env.clone();
1894 local.extend(bindings);
1895 return self.eval_expr(&arm.body, &mut local);
1896 }
1897 }
1898 Ok(Value::Unit)
1899 },
1900
1901 Expr::Range(lo, hi) => {
1902 let lo_v = self.eval_expr(lo, env)?;
1903 let hi_v = self.eval_expr(hi, env)?;
1904 let lo_n = self.to_number(&lo_v)? as i64;
1905 let hi_n = self.to_number(&hi_v)? as i64;
1906 Ok(Value::List(Rc::new(
1907 (lo_n..hi_n).map(|i| Value::Number(i as f64)).collect(),
1908 )))
1909 },
1910
1911 Expr::Index(base, idx) => {
1912 let b = self.eval_expr(base, env)?;
1913 let i = self.eval_expr(idx, env)?;
1914 let n = self.to_number(&i)? as usize;
1915 match b {
1916 Value::List(v) => v
1917 .get(n)
1918 .cloned()
1919 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
1920 Value::Str(s) => s
1921 .chars()
1922 .nth(n)
1923 .map(|c| Value::Str(c.to_string()))
1924 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
1925 other => Err(EvalErr::from(format!("cannot index {:?}", other))),
1926 }
1927 },
1928
1929 Expr::Call(callee, args) => {
1930 let arg_vals: Vec<Value> = args
1931 .iter()
1932 .map(|a| self.eval_expr(a, env))
1933 .collect::<Result<_, _>>()?;
1934 match callee.as_ref() {
1935 Expr::Ident(name) => self.call_named(name, arg_vals, env),
1936 Expr::Path(segs) => self.call_named(&segs.join("::"), arg_vals, env),
1937 _ => {
1938 let v = self.eval_expr(callee, env)?;
1939 self.call_value(v, arg_vals)
1940 },
1941 }
1942 },
1943
1944 Expr::MethodCall { receiver, method, args } => {
1945 let recv = self.eval_expr(receiver, env)?;
1946 let arg_vals: Vec<Value> = args
1947 .iter()
1948 .map(|a| self.eval_expr(a, env))
1949 .collect::<Result<_, _>>()?;
1950 self.call_method(recv, method, arg_vals)
1951 },
1952
1953 Expr::Closure(params, body) => Ok(Value::Fn(
1954 params.clone(),
1955 vec![Stmt::Expr(*body.clone())],
1956 env.clone(),
1957 )),
1958 }
1959 }
1960
1961 fn exec_block(&mut self, stmts: &[Stmt], env: &mut Env) -> Result<Option<Value>, EvalErr> {
1964 let mut last: Option<Value> = None;
1965 for stmt in stmts {
1966 match stmt {
1967 Stmt::Bind(name, expr) => {
1968 match self.try_inplace_list_update(name, expr, env)? {
1969 Some(v) => env.insert(name.clone(), v),
1970 None => {
1971 let v = self.eval_expr(expr, env)?;
1972 env.insert(name.clone(), v)
1973 },
1974 };
1975 last = None;
1976 },
1977 Stmt::Return(expr) => {
1978 let v = self.eval_expr(expr, env)?;
1979 return Err(EvalErr::Return(v));
1980 },
1981 Stmt::Expr(expr) => {
1982 last = Some(self.eval_expr(expr, env)?);
1983 },
1984 }
1985 }
1986 Ok(last)
1987 }
1988
1989 fn try_inplace_list_update(
1996 &mut self,
1997 name: &str,
1998 expr: &Expr,
1999 env: &mut Env,
2000 ) -> Result<Option<Value>, EvalErr> {
2001 let Expr::Call(callee, args) = expr else { return Ok(None) };
2002 let Expr::Ident(fname) = callee.as_ref() else { return Ok(None) };
2003 let is_push = matches!(
2004 fname.as_str(),
2005 "list_push" | "เพิ่มรายการ" | "列表添加" | "リスト追加" | "목록추가"
2006 );
2007 let is_set = matches!(
2008 fname.as_str(),
2009 "list_set" | "ตั้งรายการ" | "设元素" | "要素設定" | "요소설정"
2010 );
2011 if !is_push && !is_set {
2012 return Ok(None);
2013 }
2014 match args.first() {
2017 Some(Expr::Ident(a0)) if a0 == name => {},
2018 _ => return Ok(None),
2019 }
2020 if self.functions.contains_key(fname.as_str()) {
2021 return Ok(None);
2022 }
2023 if is_push {
2024 if args.len() != 2 {
2025 return Ok(None);
2026 }
2027 let val = self.eval_expr(&args[1], env)?;
2028 match env.remove(name) {
2029 Some(Value::List(mut v)) => {
2030 Rc::make_mut(&mut v).push(val);
2031 Ok(Some(Value::List(v)))
2032 },
2033 other => {
2034 if let Some(o) = other {
2035 env.insert(name.to_string(), o);
2036 }
2037 Ok(None)
2038 },
2039 }
2040 } else {
2041 if args.len() != 3 {
2042 return Ok(None);
2043 }
2044 let idx_v = self.eval_expr(&args[1], env)?;
2045 let idx = self.to_number(&idx_v).unwrap_or(0.0) as usize;
2046 let val = self.eval_expr(&args[2], env)?;
2047 match env.remove(name) {
2048 Some(Value::List(mut v)) => {
2049 if idx < v.len() {
2050 Rc::make_mut(&mut v)[idx] = val;
2051 }
2052 Ok(Some(Value::List(v)))
2053 },
2054 other => {
2055 if let Some(o) = other {
2056 env.insert(name.to_string(), o);
2057 }
2058 Ok(None)
2059 },
2060 }
2061 }
2062 }
2063
2064 fn lookup(&self, name: &str, env: &Env) -> EvalResult {
2067 if let Some(v) = env.get(name) {
2068 return Ok(v.clone());
2069 }
2070 if let Some(v) = self.global_seed.get(name) {
2073 return Ok(v.clone());
2074 }
2075 if self.functions.contains_key(name) {
2076 let def = &self.functions[name];
2077 return Ok(Value::Fn(def.params.clone(), def.body.clone(), new_env()));
2078 }
2079 if let Some((enum_name, 0)) = self.enum_variants.get(name).cloned() {
2081 let variant = name.rsplit("::").next().unwrap_or(name).to_string();
2082 return Ok(Value::Variant { enum_name, variant, payload: Vec::new() });
2083 }
2084 match name {
2086 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" => {
2087 return Ok(Value::Number(std::f64::consts::PI))
2088 },
2089 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" => {
2090 return Ok(Value::Number(std::f64::consts::TAU))
2091 },
2092 _ => {},
2093 }
2094 Err(EvalErr::from(format!("undefined: '{name}'")))
2095 }
2096
2097 pub(crate) fn call_named(&mut self, name: &str, args: Vec<Value>, env: &Env) -> EvalResult {
2106 if !ling_profile_enabled() {
2107 return self.call_named_inner(name, args, env);
2108 }
2109 let t0 = crate::runtime::now_secs();
2110 let r = self.call_named_inner(name, args, env);
2111 ling_profile_record(name, ((crate::runtime::now_secs() - t0) * 1_000_000_000.0) as u128);
2112 r
2113 }
2114
2115 fn call_named_inner(&mut self, name: &str, args: Vec<Value>, env: &Env) -> EvalResult {
2116 if let Some(def) = self.functions.get(name).cloned() {
2119 let mut call_env = FxHashMap::with_capacity_and_hasher(def.params.len(), Default::default());
2120 let _ = env; for (param, arg) in def.params.iter().zip(args) {
2122 call_env.insert(param.clone(), arg);
2123 }
2124 return match self.framed(name, |me| me.exec_block(&def.body, &mut call_env)) {
2125 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
2126 Err(EvalErr::Return(v)) => Ok(v),
2127 Err(e) => Err(e),
2128 };
2129 }
2130
2131 #[cfg(target_arch = "wasm32")]
2132 if let Some(v) = self.wasm_music_builtin(name, &args)? {
2133 return Ok(v);
2134 }
2135
2136 match name {
2137 "__ling_global" => {
2140 if let Some(Value::Str(g)) = args.first() {
2141 if let Some(v) = self.global_seed.get(g.as_str()) {
2142 return Ok(v.clone());
2143 }
2144 }
2145 return Ok(Value::Unit);
2146 },
2147 "print" | "println" | "印" | "打印" | "印刷" | "พิมพ์" | "출력" | "вывести"
2149 | "imprimir" | "afficher" => {
2150 let s = args
2151 .iter()
2152 .map(|v| v.to_string())
2153 .collect::<Vec<_>>()
2154 .join("");
2155 println!("{s}");
2156 return Ok(Value::Unit);
2157 },
2158 "print_color" | "พิมพ์สี" => {
2161 #[cfg(windows)]
2162 {
2163 use std::sync::Once;
2164 static VT: Once = Once::new();
2165 VT.call_once(|| {
2166 extern "system" {
2167 fn GetStdHandle(n: u32) -> *mut std::ffi::c_void;
2168 fn GetConsoleMode(h: *mut std::ffi::c_void, m: *mut u32) -> i32;
2169 fn SetConsoleMode(h: *mut std::ffi::c_void, m: u32) -> i32;
2170 }
2171 unsafe {
2172 let h = GetStdHandle(0xFFFF_FFF5u32); let mut mode = 0u32;
2174 if GetConsoleMode(h, &mut mode) != 0 {
2175 SetConsoleMode(h, mode | 0x0004); }
2177 }
2178 });
2179 }
2180 let col = self.arg_num(&args, 0, 7.0)? as i64;
2181 let s = args
2182 .iter()
2183 .skip(1)
2184 .map(|v| v.to_string())
2185 .collect::<Vec<_>>()
2186 .join("");
2187 let code = 90 + col.clamp(0, 7);
2188 println!("\x1b[1;{code}m{s}\x1b[0m");
2189 return Ok(Value::Unit);
2190 },
2191 "format"
2193 | "格式"
2194 | "フォーマット"
2195 | "서식"
2196 | "รูปแบบ"
2197 | "форматировать"
2198 | "formatear"
2199 | "formater" => {
2200 return Ok(Value::Str(self.builtin_format(&args)?));
2201 },
2202 "格式::拼接" | "format::join" => match args.first() {
2204 Some(Value::List(items)) => {
2205 return Ok(Value::Str(items.iter().map(|v| v.to_string()).collect()));
2206 },
2207 _ => return Ok(Value::Str(self.builtin_format(&args)?)),
2208 },
2209 "ok" | "好" | "良し" | "좋아" | "โอเค" => {
2211 let val = args.into_iter().next().unwrap_or(Value::Unit);
2212 return Ok(Value::Ok(Box::new(val)));
2213 },
2214 "bad" | "坏" | "err" | "悪い" | "나쁨" | "ผิด" => {
2215 let val = args.into_iter().next().unwrap_or(Value::Unit);
2216 return Ok(Value::Err(Box::new(val)));
2217 },
2218 "向量::从" | "Vec::from" => {
2220 if let Some(Value::List(v)) = args.first() {
2221 return Ok(Value::List(v.clone()));
2222 }
2223 return Ok(Value::List(Rc::new(args)));
2224 },
2225 "向量::有容量" | "Vec::with_capacity" => return Ok(Value::List(Rc::new(Vec::new()))),
2226 "计时::获取当前小时" | "Timer::hour" => return Ok(Value::Number(14.0)),
2228 "计时::现在" | "Timer::now" => return Ok(Value::Number(1000.0)),
2229 "sleep" | "หยุด" | "นอน" | "sleep_ms" | "睡眠" | "眠る" | "スリープ" | "잠자기"
2231 | "잠" | "流水::睡眠" | "Flow::sleep" => {
2232 if let Some(ms_val) = args.first() {
2233 if let Ok(ms) = self.to_number(ms_val) {
2234 #[cfg(target_arch = "wasm32")]
2235 wasm_sleep_ms(ms.max(0.0) as i32);
2236 #[cfg(not(target_arch = "wasm32"))]
2237 std::thread::sleep(std::time::Duration::from_millis(ms as u64));
2238 }
2239 }
2240 return Ok(Value::Unit);
2241 },
2242 "流水::并行" | "Flow::parallel" => {
2244 if let Some(Value::Fn(params, body, mut cap)) = args.first().cloned() {
2245 let _ = params;
2246 match self.exec_block(&body, &mut cap) {
2247 Ok(Some(v)) => return Ok(v),
2248 Ok(None) => return Ok(Value::Unit),
2249 Err(EvalErr::Return(v)) => return Ok(v),
2250 Err(e) => return Err(e),
2251 }
2252 }
2253 return Ok(Value::Unit);
2254 },
2255
2256 "sin" | "ไซน์" | "正弦" | "サイン" | "사인" => {
2265 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sin()));
2266 },
2267 "cos" | "โคไซน์" | "余弦" | "コサイン" | "코사인" => {
2268 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cos()));
2269 },
2270
2271 "tanh" | "tanhf" | "双曲正切" | "双曲線正接" | "쌍곡탄젠트" => {
2274 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tanh()));
2275 },
2276
2277 "tan" | "แทนเจนต์" | "正切" | "タンジェント" | "탄젠트" => {
2278 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tan()));
2279 },
2280 "asin" | "arcsin" | "反正弦" | "アークサイン" | "아크사인" | "อาร์กไซน์" =>
2281 {
2282 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.asin()));
2283 },
2284 "acos" | "arccos" | "反余弦" | "アークコサイン" | "아크코사인" | "อาร์กโคไซน์" =>
2285 {
2286 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.acos()));
2287 },
2288 "atan" | "arctan" | "反正切" | "アークタンジェント" | "아크탄젠트" | "อาร์กแทนเจนต์" =>
2289 {
2290 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.atan()));
2291 },
2292 "atan2" | "arctan2" | "反正切2" | "アークタンジェント2" | "아크탄젠트2" =>
2293 {
2294 let y = self.arg_num(&args, 0, 0.0)?;
2295 let x = self.arg_num(&args, 1, 1.0)?;
2296 return Ok(Value::Number(y.atan2(x)));
2297 },
2298
2299 "sqrt" | "รากที่สอง" | "平方根" | "根" | "제곱근" => {
2301 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sqrt()));
2302 },
2303 "cbrt" | "立方根" | "세제곱근" | "รากที่สาม" => {
2304 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cbrt()));
2305 },
2306 "pow" | "ยกกำลัง" | "幂" | "べき乗" | "거듭제곱" => {
2307 let base = self.arg_num(&args, 0, 0.0)?;
2308 let exp = self.arg_num(&args, 1, 1.0)?;
2309 return Ok(Value::Number(base.powf(exp)));
2310 },
2311 "exp" | "指数" | "指数関数" | "지수" => {
2312 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.exp()));
2313 },
2314 "hypot" | "斜边" | "斜辺" | "빗변" => {
2315 let x = self.arg_num(&args, 0, 0.0)?;
2316 let y = self.arg_num(&args, 1, 0.0)?;
2317 return Ok(Value::Number(x.hypot(y)));
2318 },
2319
2320 "ln" | "log" | "ลอการิทึม" | "对数" | "対数" | "로그" => {
2322 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.ln()));
2323 },
2324 "log2" | "对数2" | "対数2" | "로그2" => {
2325 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log2()));
2326 },
2327 "log10" | "对数10" | "対数10" | "로그10" => {
2328 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log10()));
2329 },
2330
2331 "abs" | "ค่าสัมบูรณ์" | "绝对值" | "绝对" | "絶対値" | "절댓값" | "절대값" =>
2333 {
2334 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.abs()));
2335 },
2336 "floor" | "ปัดลง" | "向下取整" | "下整" | "床関数" | "내림" => {
2337 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.floor()));
2338 },
2339 "ceil" | "ปัดขึ้น" | "向上取整" | "上整" | "天井関数" | "올림" =>
2340 {
2341 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.ceil()));
2342 },
2343 "round" | "ปัดเศษ" | "四舍五入" | "四舍" | "四捨五入" | "반올림" =>
2344 {
2345 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.round()));
2346 },
2347 "trunc"
2348 | "int"
2349 | "ตัดทศนิยม"
2350 | "取整"
2351 | "整数化"
2352 | "整数"
2353 | "截整"
2354 | "정수화"
2355 | "정수"
2356 | "切り捨て"
2357 | "버림" => {
2358 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.trunc()));
2359 },
2360 "fract" | "小数部分" | "小数部" | "소수부" => {
2361 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.fract()));
2362 },
2363
2364 "min" | "ต่ำสุด" | "最小" | "최솟값" => {
2366 let a = self.arg_num(&args, 0, 0.0)?;
2367 let b = self.arg_num(&args, 1, 0.0)?;
2368 return Ok(Value::Number(a.min(b)));
2369 },
2370 "max" | "สูงสุด" | "最大" | "최댓값" => {
2371 let a = self.arg_num(&args, 0, 0.0)?;
2372 let b = self.arg_num(&args, 1, 0.0)?;
2373 return Ok(Value::Number(a.max(b)));
2374 },
2375 "clamp" | "จำกัด" | "截取" | "範囲制限" | "범위제한" => {
2376 let x = self.arg_num(&args, 0, 0.0)?;
2377 let lo = self.arg_num(&args, 1, 0.0)?;
2378 let hi = self.arg_num(&args, 2, 1.0)?;
2379 return Ok(Value::Number(x.clamp(lo, hi)));
2380 },
2381
2382 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" => {
2384 return Ok(Value::Number(std::f64::consts::PI))
2385 },
2386 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" => {
2387 return Ok(Value::Number(std::f64::consts::TAU))
2388 },
2389
2390 "vnoise" | "noise2" | "นอยส์2ดี" | "柏林噪声2D" | "バリューノイズ2D" | "값노이즈2D" =>
2396 {
2397 let x = self.arg_num(&args, 0, 0.0)? as f32;
2398 let y = self.arg_num(&args, 1, 0.0)? as f32;
2399 let seed = self.arg_num(&args, 2, 0.0)? as u32;
2400 return Ok(Value::Number(tex_vnoise(x, y, seed) as f64));
2401 },
2402
2403 "fbm" | "นอยส์ออร์แกนิก" | "分形噪声" | "フラクタルノイズ" | "프랙탈노이즈" =>
2404 {
2405 let x = self.arg_num(&args, 0, 0.0)? as f32;
2406 let y = self.arg_num(&args, 1, 0.0)? as f32;
2407 let octaves = self.arg_num(&args, 2, 4.0)? as u32;
2408 let seed = self.arg_num(&args, 3, 0.0)? as u32;
2409 return Ok(Value::Number(tex_fbm(x, y, octaves, seed) as f64));
2410 },
2411
2412 "perlin"
2413 | "perlin3"
2414 | "เพอร์ลิน3ดี"
2415 | "柏林噪声3D"
2416 | "パーリンノイズ3D"
2417 | "펄린노이즈3D" => {
2418 let x = self.arg_num(&args, 0, 0.0)? as f32;
2419 let y = self.arg_num(&args, 1, 0.0)? as f32;
2420 let z = self.arg_num(&args, 2, 0.0)? as f32;
2421 return Ok(Value::Number(perlin3(x, y, z) as f64));
2422 },
2423
2424 "lerp" | "ค่าระหว่าง" | "线性插值" | "線形補間" | "선형보간" =>
2426 {
2427 let a = self.arg_num(&args, 0, 0.0)?;
2428 let b = self.arg_num(&args, 1, 1.0)?;
2429 let t = self.arg_num(&args, 2, 0.0)?;
2430 return Ok(Value::Number(a + (b - a) * t));
2431 },
2432
2433 "smoothstep" | "เปลี่ยนแบบนุ่ม" | "平滑步进" | "スムーズステップ" | "스무스스텝" =>
2434 {
2435 let lo = self.arg_num(&args, 0, 0.0)?;
2436 let hi = self.arg_num(&args, 1, 1.0)?;
2437 let x = self.arg_num(&args, 2, 0.5)?;
2438 let t = ((x - lo) / (hi - lo)).clamp(0.0, 1.0);
2439 return Ok(Value::Number(t * t * (3.0 - 2.0 * t)));
2440 },
2441
2442 "rand" | "สุ่ม" | "随机" | "乱数" | "난수" => {
2443 let val = fast_rand_f64(&mut self.rand_state);
2444 return Ok(Value::Number(val));
2445 },
2446
2447 "sign" | "เครื่องหมาย" | "符号" | "符号関数" | "부호" => {
2448 let x = self.arg_num(&args, 0, 0.0)?;
2449 return Ok(Value::Number(x.signum()));
2450 },
2451
2452 "hsv_to_rgb" | "เอชเอสวีเป็นRGB" | "HSV转RGB" | "HSV変換RGB" | "HSV변환RGB" =>
2453 {
2454 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;
2458 let x = c * (1.0 - (((h / 60.0) % 2.0) - 1.0).abs());
2459 let m = v - c;
2460 let (r1, g1, b1) = if h < 60.0 {
2461 (c, x, 0.0)
2462 } else if h < 120.0 {
2463 (x, c, 0.0)
2464 } else if h < 180.0 {
2465 (0.0, c, x)
2466 } else if h < 240.0 {
2467 (0.0, x, c)
2468 } else if h < 300.0 {
2469 (x, 0.0, c)
2470 } else {
2471 (c, 0.0, x)
2472 };
2473 let r = ((r1 + m) * 255.0).round();
2474 let g = ((g1 + m) * 255.0).round();
2475 let b = ((b1 + m) * 255.0).round();
2476 return Ok(Value::List(Rc::new(vec![
2477 Value::Number(r),
2478 Value::Number(g),
2479 Value::Number(b),
2480 ])));
2481 },
2482
2483 "lerp_color" | "ไล่สี" | "颜色插值" | "色補間" | "색보간" => {
2484 let r1 = self.arg_num(&args, 0, 0.0)?;
2485 let g1 = self.arg_num(&args, 1, 0.0)?;
2486 let b1 = self.arg_num(&args, 2, 0.0)?;
2487 let r2 = self.arg_num(&args, 3, 255.0)?;
2488 let g2 = self.arg_num(&args, 4, 255.0)?;
2489 let b2 = self.arg_num(&args, 5, 255.0)?;
2490 let t = self.arg_num(&args, 6, 0.0)?;
2491 let r = r1 + (r2 - r1) * t;
2492 let g = g1 + (g2 - g1) * t;
2493 let b = b1 + (b2 - b1) * t;
2494 let c = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
2495 self.gfx.borrow_mut().color = c;
2496 return Ok(Value::Unit);
2497 },
2498
2499 "time_now" | "เวลาปัจจุบัน" | "当前时间" | "経過時間" | "현재시간" =>
2501 {
2502 return Ok(Value::Number(crate::runtime::now_secs() - self.start_time_secs));
2503 },
2504
2505 "epoch_now" | "เวลาโลก" | "datetime" | "现在时刻" | "現在時刻" | "현재시각" =>
2509 {
2510 return Ok(Value::Number(crate::runtime::now_secs()));
2511 },
2512
2513 "frame_count" | "เฟรม" | "帧数" | "フレーム数" | "프레임수" => {
2514 return Ok(Value::Number(self.frame_num as f64));
2515 },
2516
2517 "mic_open" | "เปิดไมค์" | "开麦克风" | "マイク開く" | "마이크열기" =>
2519 {
2520 #[cfg(not(target_arch = "wasm32"))]
2521 {
2522 match ling_mic::MicInput::open(Default::default()) {
2523 Ok(mic) => {
2524 let _ = mic.start(|_samples: &[f32]| {}); self.mic = Some(mic);
2526 return Ok(Value::Number(1.0)); },
2528 Err(_e) => {
2531 self.mic = None;
2532 return Ok(Value::Number(0.0));
2533 },
2534 }
2535 }
2536 #[cfg(target_arch = "wasm32")]
2537 return Ok(Value::Unit);
2538 },
2539
2540 "mic_rms" | "เสียงRMS" | "麦克风音量" | "マイクRMS" | "마이크RMS" =>
2541 {
2542 #[cfg(not(target_arch = "wasm32"))]
2543 {
2544 let rms = self
2545 .mic
2546 .as_ref()
2547 .map(|m: &ling_mic::MicInput| m.rms())
2548 .unwrap_or(0.0);
2549 return Ok(Value::Number(rms as f64));
2550 }
2551 #[cfg(target_arch = "wasm32")]
2552 return Ok(Value::Number(0.0));
2553 },
2554
2555 "mic_peak" | "เสียงพีค" | "麦克风峰值" | "マイクピーク" | "마이크피크" =>
2556 {
2557 #[cfg(not(target_arch = "wasm32"))]
2558 {
2559 let peak = self
2560 .mic
2561 .as_ref()
2562 .map(|m: &ling_mic::MicInput| m.peak())
2563 .unwrap_or(0.0);
2564 return Ok(Value::Number(peak as f64));
2565 }
2566 #[cfg(target_arch = "wasm32")]
2567 return Ok(Value::Number(0.0));
2568 },
2569
2570 "mic_fft" | "วิเคราะห์เสียงสด" | "实时频谱" | "リアルタイムFFT" | "실시간FFT" =>
2571 {
2572 #[cfg(not(target_arch = "wasm32"))]
2573 {
2574 let n = self.arg_num(&args, 0, 8.0)? as usize;
2575 if let Some(mic) = self.mic.as_ref() {
2576 let samples = mic.latest_samples();
2577 self.fft.borrow_mut().push_samples(&samples);
2578 }
2579 let bands = self.fft.borrow().freq_bands(n);
2580 let result: Vec<Value> = bands.iter().map(|&v| Value::Number(v as f64)).collect();
2581 return Ok(Value::List(Rc::new(result)));
2582 }
2583 #[cfg(target_arch = "wasm32")]
2584 return Ok(Value::List(vec![]));
2585 },
2586
2587 "set_blend" | "โหมดผสม" | "混合模式" | "ブレンドモード" | "블렌드모드" =>
2589 {
2590 let mode = self.arg_num(&args, 0, 0.0)? as u8;
2591 let mut gfx = self.gfx.borrow_mut();
2592 gfx.blend = mode;
2593 let a = gfx.alpha;
2594 gfx.depth_queue.set_state(mode, a); return Ok(Value::Unit);
2596 },
2597
2598 "draw_circle" | "วาดวงกลม" | "画圆" | "円描画" | "원그리기" =>
2600 {
2601 let cx = self.arg_num(&args, 0, 0.0)? as i32;
2602 let cy = self.arg_num(&args, 1, 0.0)? as i32;
2603 let r = self.arg_num(&args, 2, 10.0)? as i32;
2604 let mut gfx = self.gfx.borrow_mut();
2605 let (w, h, color, blend) =
2606 (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
2607 draw_circle_outline(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
2608 return Ok(Value::Unit);
2609 },
2610
2611 "draw_filled_circle"
2612 | "draw_disc"
2613 | "วาดวงกลมทึบ"
2614 | "画实心圆"
2615 | "塗りつぶし円"
2616 | "원채우기" => {
2617 let cx = self.arg_num(&args, 0, 0.0)? as i32;
2618 let cy = self.arg_num(&args, 1, 0.0)? as i32;
2619 let r = self.arg_num(&args, 2, 10.0)? as i32;
2620 let mut gfx = self.gfx.borrow_mut();
2621 let (w, h, color, blend) =
2622 (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
2623 draw_circle_filled(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
2624 return Ok(Value::Unit);
2625 },
2626
2627 "set_alpha" | "ตั้งความโปร่งใส" | "设透明" | "アルファ設定" | "투명도설정" =>
2633 {
2634 let a = self.arg_num(&args, 0, 1.0)? as f32;
2635 let mut gfx = self.gfx.borrow_mut();
2636 gfx.alpha = a.clamp(0.0, 1.0);
2637 let (m, al) = (gfx.blend, gfx.alpha);
2638 gfx.depth_queue.set_state(m, al); return Ok(Value::Unit);
2640 },
2641
2642 "set_color_space" | "ปริภูมิสี" | "色彩空间" | "色空間" | "색공간" =>
2646 {
2647 let m = self.arg_num(&args, 0, 0.0)? as i64;
2648 self.gfx.borrow_mut().linear_blend = m != 0;
2649 return Ok(Value::Unit);
2650 },
2651
2652 "set_gradient_space" | "ปริภูมิไล่สี" | "渐变空间" | "グラデ空間" | "그라데이션공간" =>
2655 {
2656 let m = self.arg_num(&args, 0, 1.0)? as i64;
2657 self.gfx.borrow_mut().grad_oklab = m != 0;
2658 return Ok(Value::Unit);
2659 },
2660
2661 "mix_color" | "ผสมสี" | "混合颜色" | "色混合" | "색혼합" => {
2665 let c0 = rgb(
2666 self.arg_num(&args, 0, 0.0)?,
2667 self.arg_num(&args, 1, 0.0)?,
2668 self.arg_num(&args, 2, 0.0)?,
2669 );
2670 let c1 = rgb(
2671 self.arg_num(&args, 3, 255.0)?,
2672 self.arg_num(&args, 4, 255.0)?,
2673 self.arg_num(&args, 5, 255.0)?,
2674 );
2675 let t = self.arg_num(&args, 6, 0.5)? as f32;
2676 self.gfx.borrow_mut().color = crate::gfx::color::mix_oklab(c0, c1, t);
2677 return Ok(Value::Unit);
2678 },
2679
2680 "set_depth_test" | "ทดสอบความลึก" | "深度测试" | "深度テスト" | "깊이테스트" =>
2684 {
2685 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
2686 self.gfx.borrow_mut().depth_test = on;
2687 return Ok(Value::Unit);
2688 },
2689
2690 "set_flat_shade" | "ตั้งแฟลตเชด" | "平面着色" | "フラット着色" | "평면음영" =>
2693 {
2694 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
2695 self.gfx.borrow_mut().flat_shade = on;
2696 return Ok(Value::Unit);
2697 },
2698
2699 "clear_depth" | "ล้างความลึก" | "清深度" | "深度クリア" | "깊이지우기" =>
2703 {
2704 self.gfx.borrow_mut().zbuf_needs_clear = true;
2705 return Ok(Value::Unit);
2706 },
2707
2708 "depth_blur" | "เบลอความลึก" | "dof" | "depth_of_field" | "景深" =>
2714 {
2715 let focus = self.arg_num(&args, 0, 30.0)? as f32;
2716 let range = self.arg_num(&args, 1, 60.0)? as f32;
2717 let radius = self.arg_num(&args, 2, 3.0)?.max(0.0) as usize;
2718 let mut gfx = self.gfx.borrow_mut();
2719 let w = gfx.width;
2720 let h = gfx.height;
2721 if gfx.depth_buf.len() == w * h {
2722 let g = &mut *gfx;
2723 crate::gfx::raster::depth_of_field(
2724 &mut g.buffer,
2725 &g.depth_buf,
2726 w,
2727 h,
2728 focus,
2729 range,
2730 radius,
2731 );
2732 }
2733 return Ok(Value::Unit);
2734 },
2735
2736 "grad_triangle" | "สามเหลี่ยมไล่สี" | "渐变三角" | "グラデ三角" | "그라데삼각" =>
2740 {
2741 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
2742 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
2743 let c0 = rgb(
2744 self.arg_num(&args, 2, 255.0)?,
2745 self.arg_num(&args, 3, 255.0)?,
2746 self.arg_num(&args, 4, 255.0)?,
2747 );
2748 let x1 = self.arg_num(&args, 5, 0.0)? as f32;
2749 let y1 = self.arg_num(&args, 6, 0.0)? as f32;
2750 let c1 = rgb(
2751 self.arg_num(&args, 7, 255.0)?,
2752 self.arg_num(&args, 8, 255.0)?,
2753 self.arg_num(&args, 9, 255.0)?,
2754 );
2755 let x2 = self.arg_num(&args, 10, 0.0)? as f32;
2756 let y2 = self.arg_num(&args, 11, 0.0)? as f32;
2757 let c2 = rgb(
2758 self.arg_num(&args, 12, 255.0)?,
2759 self.arg_num(&args, 13, 255.0)?,
2760 self.arg_num(&args, 14, 255.0)?,
2761 );
2762 let mut gfx = self.gfx.borrow_mut();
2763 let (w, h, alpha, mode, lin, ok) = (
2764 gfx.width,
2765 gfx.height,
2766 gfx.alpha,
2767 gfx.blend,
2768 gfx.linear_blend,
2769 gfx.grad_oklab,
2770 );
2771 crate::gfx::raster::fill_triangle_grad(
2772 &mut gfx.buffer,
2773 w,
2774 h,
2775 alpha,
2776 mode,
2777 lin,
2778 ok,
2779 x0,
2780 y0,
2781 c0,
2782 x1,
2783 y1,
2784 c1,
2785 x2,
2786 y2,
2787 c2,
2788 );
2789 return Ok(Value::Unit);
2790 },
2791
2792 "grad_rect" | "สี่เหลี่ยมไล่สี" | "渐变矩形" | "グラデ矩形" | "그라데사각" =>
2795 {
2796 let x = self.arg_num(&args, 0, 0.0)? as f32;
2797 let y = self.arg_num(&args, 1, 0.0)? as f32;
2798 let rw = self.arg_num(&args, 2, 0.0)? as f32;
2799 let rh = self.arg_num(&args, 3, 0.0)? as f32;
2800 let c0 = rgb(
2801 self.arg_num(&args, 4, 255.0)?,
2802 self.arg_num(&args, 5, 255.0)?,
2803 self.arg_num(&args, 6, 255.0)?,
2804 );
2805 let c1 = rgb(
2806 self.arg_num(&args, 7, 0.0)?,
2807 self.arg_num(&args, 8, 0.0)?,
2808 self.arg_num(&args, 9, 0.0)?,
2809 );
2810 let dir = self.arg_num(&args, 10, 1.0)? as u8;
2811 let mut gfx = self.gfx.borrow_mut();
2812 let (w, h, alpha, mode, lin, ok) = (
2813 gfx.width,
2814 gfx.height,
2815 gfx.alpha,
2816 gfx.blend,
2817 gfx.linear_blend,
2818 gfx.grad_oklab,
2819 );
2820 crate::gfx::raster::fill_rect_grad(
2821 &mut gfx.buffer,
2822 w,
2823 h,
2824 alpha,
2825 mode,
2826 lin,
2827 ok,
2828 x,
2829 y,
2830 rw,
2831 rh,
2832 c0,
2833 c1,
2834 dir,
2835 );
2836 return Ok(Value::Unit);
2837 },
2838
2839 "shadow_blob" | "เงาวงรี" | "阴影斑" | "影ブロブ" | "그림자블롭" =>
2843 {
2844 let cx = self.arg_num(&args, 0, 0.0)? as f32;
2845 let cy = self.arg_num(&args, 1, 0.0)? as f32;
2846 let rx = self.arg_num(&args, 2, 16.0)? as f32;
2847 let ry = self.arg_num(&args, 3, 8.0)? as f32;
2848 let a = self.arg_num(&args, 4, 0.5)? as f32;
2849 let mut gfx = self.gfx.borrow_mut();
2850 let (w, h, color, soft, mode, lin) = (
2851 gfx.width,
2852 gfx.height,
2853 gfx.color,
2854 gfx.shadow.soft,
2855 gfx.blend,
2856 gfx.linear_blend,
2857 );
2858 crate::gfx::raster::fill_disc_soft(
2859 &mut gfx.buffer,
2860 w,
2861 h,
2862 cx,
2863 cy,
2864 rx,
2865 ry,
2866 color,
2867 a,
2868 soft,
2869 mode,
2870 lin,
2871 );
2872 return Ok(Value::Unit);
2873 },
2874
2875 "cast_shadow" | "ทอดเงา" | "投射阴影" | "影を落とす" | "그림자드리우기" =>
2880 {
2881 let cx = self.arg_num(&args, 0, 0.0)? as f32;
2882 let cy = self.arg_num(&args, 1, 0.0)? as f32;
2883 let height = (self.arg_num(&args, 2, 0.0)? as f32).max(0.0);
2884 let mut gfx = self.gfx.borrow_mut();
2885 let sp = gfx.shadow;
2886 let radius = (sp.base + sp.grow * height).max(0.5);
2887 let alpha = (sp.alpha - sp.fade * height).clamp(0.04, 1.0);
2888 let soft = (sp.soft + height * 0.004).clamp(0.0, 0.95);
2889 let (w, h, color, mode, lin) = (
2890 gfx.width,
2891 gfx.height,
2892 gfx.color,
2893 gfx.blend,
2894 gfx.linear_blend,
2895 );
2896 crate::gfx::raster::fill_disc_soft(
2897 &mut gfx.buffer,
2898 w,
2899 h,
2900 cx,
2901 cy,
2902 radius,
2903 radius * 0.62,
2904 color,
2905 alpha,
2906 soft,
2907 mode,
2908 lin,
2909 );
2910 return Ok(Value::Unit);
2911 },
2912
2913 "shadow_params" | "ตั้งค่าเงา" | "阴影参数" | "影設定" | "그림자설정" =>
2916 {
2917 let cur = self.gfx.borrow().shadow;
2918 let base = self.arg_num(&args, 0, cur.base as f64)? as f32;
2919 let grow = self.arg_num(&args, 1, cur.grow as f64)? as f32;
2920 let alpha = self.arg_num(&args, 2, cur.alpha as f64)? as f32;
2921 let fade = self.arg_num(&args, 3, cur.fade as f64)? as f32;
2922 let soft = self.arg_num(&args, 4, cur.soft as f64)? as f32;
2923 self.gfx.borrow_mut().shadow =
2924 crate::gfx::ShadowParams { base, grow, alpha, fade, soft };
2925 return Ok(Value::Unit);
2926 },
2927
2928 "depth_triangle" | "สามเหลี่ยมเรียงลึก" | "深度三角" | "深度三角形" | "깊이삼각" =>
2933 {
2934 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
2935 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
2936 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
2937 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
2938 let x2 = self.arg_num(&args, 4, 0.0)? as f32;
2939 let y2 = self.arg_num(&args, 5, 0.0)? as f32;
2940 let z = self.arg_num(&args, 6, 0.0)? as f32;
2941 let mut gfx = self.gfx.borrow_mut();
2942 let color = gfx.color;
2943 gfx.depth_queue
2944 .push_triangle(z, color, x0, y0, x1, y1, x2, y2);
2945 return Ok(Value::Unit);
2946 },
2947
2948 "depth_line" | "เส้นเรียงลึก" | "深度线" | "深度線" | "깊이선" =>
2951 {
2952 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
2953 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
2954 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
2955 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
2956 let z = self.arg_num(&args, 4, 0.0)? as f32;
2957 let mut gfx = self.gfx.borrow_mut();
2958 let color = gfx.color;
2959 gfx.depth_queue.push_line(z, color, x0, y0, x1, y1);
2960 return Ok(Value::Unit);
2961 },
2962
2963 "เปิดหน้าต่าง" | "open_window" | "gfx_window" | "开窗" | "ウィンドウ開く" | "창열기" =>
2970 {
2971 let w = self.arg_num(&args, 0, 800.0)? as usize;
2972 let h = self.arg_num(&args, 1, 600.0)? as usize;
2973 #[cfg(not(target_arch = "wasm32"))]
2974 {
2975 let title = args
2976 .get(2)
2977 .map(|v| v.to_string())
2978 .unwrap_or_else(|| "Ling".into());
2979 let mut gfx = self.gfx.borrow_mut();
2980 let mut win = minifb::Window::new(
2981 &title,
2982 w,
2983 h,
2984 minifb::WindowOptions {
2985 resize: false,
2986 scale: minifb::Scale::X1,
2987 ..Default::default()
2988 },
2989 )
2990 .map_err(|e| EvalErr::from(format!("cannot open window: {e}")))?;
2991 #[allow(deprecated)]
2992 win.limit_update_rate(Some(std::time::Duration::from_millis(8)));
2993 gfx.buffer = vec![0u32; w * h];
2994 gfx.width = w;
2995 gfx.height = h;
2996 gfx.window = Some(win);
2997 gfx.sync_projection();
2998 hide_console_window();
2999 }
3000 #[cfg(target_arch = "wasm32")]
3001 {
3002 let mut gfx = self.gfx.borrow_mut();
3003 gfx.width = w;
3004 gfx.height = h;
3005 gfx.buffer.resize(w * h, 0); gfx.sync_projection();
3007 crate::gfx::webgl::resize(w as u32, h as u32);
3008 }
3009 return Ok(Value::Unit);
3010 },
3011
3012 "เติม" | "fill" | "gfx_fill" | "clear" | "填" | "塗り潰し" | "채우기" | "清"
3014 | "消去" | "지우기" => {
3015 let r = self.arg_num(&args, 0, 0.0)? as u32;
3016 let g = self.arg_num(&args, 1, 0.0)? as u32;
3017 let b = self.arg_num(&args, 2, 0.0)? as u32;
3018 #[cfg(not(target_arch = "wasm32"))]
3019 {
3020 let c = (r << 16) | (g << 8) | b;
3021 let mut gfx = self.gfx.borrow_mut();
3022 gfx.buffer.fill(c);
3023 gfx.zbuf_needs_clear = true; gfx.edge_set.clear(); }
3026 #[cfg(target_arch = "wasm32")]
3027 {
3028 let mut gfx = self.gfx.borrow_mut();
3029 gfx.fill_r = r as f32 / 255.0;
3030 gfx.fill_g = g as f32 / 255.0;
3031 gfx.fill_b = b as f32 / 255.0;
3032 let c = (r << 16) | (g << 8) | b;
3033 gfx.buffer.fill(c);
3034 gfx.zbuf_needs_clear = true;
3035 gfx.edge_set.clear();
3036 }
3037 return Ok(Value::Unit);
3038 },
3039
3040 "set_color_hsl" | "颜色HSL" | "色相" | "HSL色" | "HSL색설정" | "สีHSLวาด" =>
3043 {
3044 let h = self.arg_num(&args, 0, 0.0)?;
3045 let s = self.arg_num(&args, 1, 70.0)?;
3046 let l = self.arg_num(&args, 2, 50.0)?;
3047 let hex = hsl_to_hex(h, s, l);
3048 let r = u32::from_str_radix(&hex[1..3], 16).unwrap_or(255);
3049 let g = u32::from_str_radix(&hex[3..5], 16).unwrap_or(255);
3050 let b = u32::from_str_radix(&hex[5..7], 16).unwrap_or(255);
3051 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
3052 return Ok(Value::Unit);
3053 },
3054
3055 "สีดินสอ" | "set_color" | "gfx_color" | "color" | "设色" | "色設定" | "색설정" =>
3057 {
3058 let r = self.arg_num(&args, 0, 255.0)? as u32;
3059 let g = self.arg_num(&args, 1, 255.0)? as u32;
3060 let b = self.arg_num(&args, 2, 255.0)? as u32;
3061 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
3062 return Ok(Value::Unit);
3063 },
3064
3065 "วาดสามเหลี่ยม"
3067 | "draw_triangle"
3068 | "gfx_triangle"
3069 | "triangle"
3070 | "画三角"
3071 | "三角形描画"
3072 | "삼각형그리기" => {
3073 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
3074 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
3075 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
3076 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
3077 let x2 = self.arg_num(&args, 4, 0.0)? as f32;
3078 let y2 = self.arg_num(&args, 5, 0.0)? as f32;
3079 let mut gfx = self.gfx.borrow_mut();
3080 let color = gfx.color;
3081 #[cfg(not(target_arch = "wasm32"))]
3082 {
3083 let w = gfx.width;
3084 let h = gfx.height;
3085 fill_triangle(&mut gfx.buffer, w, h, color, x0, y0, x1, y1, x2, y2);
3086 }
3087 #[cfg(target_arch = "wasm32")]
3088 gfx.depth_queue
3089 .push_triangle(0.0, color, x0, y0, x1, y1, x2, y2);
3090 return Ok(Value::Unit);
3091 },
3092
3093 "วาดเส้น" | "draw_line" | "gfx_line" | "line" | "画线" | "線描く" | "선그리기" =>
3095 {
3096 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
3097 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
3098 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
3099 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
3100 let mut gfx = self.gfx.borrow_mut();
3101 let color = gfx.color;
3102 #[cfg(not(target_arch = "wasm32"))]
3103 {
3104 let w = gfx.width;
3105 let h = gfx.height;
3106 draw_line(&mut gfx.buffer, w, h, color, x0, y0, x1, y1);
3107 }
3108 #[cfg(target_arch = "wasm32")]
3109 gfx.depth_queue.push_line(0.0, color, x0, y0, x1, y1);
3110 return Ok(Value::Unit);
3111 },
3112
3113 "วาดจุด" | "draw_pixel" | "gfx_pixel" | "pixel" | "画点" | "点描く" | "점그리기" =>
3115 {
3116 let px = self.arg_num(&args, 0, 0.0)? as i32;
3117 let py = self.arg_num(&args, 1, 0.0)? as i32;
3118 #[cfg(not(target_arch = "wasm32"))]
3119 {
3120 let mut gfx = self.gfx.borrow_mut();
3121 let color = gfx.color;
3122 let w = gfx.width;
3123 let h = gfx.height;
3124 if px >= 0 && py >= 0 && (px as usize) < w && (py as usize) < h {
3125 gfx.buffer[py as usize * w + px as usize] = color;
3126 }
3127 }
3128 #[cfg(target_arch = "wasm32")]
3129 {
3130 let mut gfx = self.gfx.borrow_mut();
3132 let color = gfx.color;
3133 let x = px as f32;
3134 let y = py as f32;
3135 gfx.depth_queue
3136 .push_triangle(0.0, color, x, y, x + 1.0, y, x + 1.0, y + 1.0);
3137 gfx.depth_queue
3138 .push_triangle(0.0, color, x, y, x + 1.0, y + 1.0, x, y + 1.0);
3139 }
3140 return Ok(Value::Unit);
3141 },
3142
3143 "แสดงผล" | "present" | "gfx_present" | "show" | "显" | "呈现" | "表示" | "표시" =>
3145 {
3146 #[cfg(not(target_arch = "wasm32"))]
3147 {
3148 ling_fps_tick();
3149 ling_phase_frame();
3150 {
3152 let mut gfx = self.gfx.borrow_mut();
3153 if !gfx.depth_queue.is_empty() {
3154 let w = gfx.width;
3155 let h = gfx.height;
3156 let dt = gfx.depth_test;
3157 let reset_z = gfx.zbuf_needs_clear;
3158 let (bm, ba) = (gfx.blend, gfx.alpha);
3159 let queue = std::mem::take(&mut gfx.depth_queue);
3160 {
3161 let g = &mut *gfx;
3162 let z = if dt { Some(&mut g.depth_buf) } else { None };
3163 queue.flush(&mut g.buffer, z, reset_z, w, h);
3164 }
3165 gfx.depth_queue.set_state(bm, ba);
3166 gfx.zbuf_needs_clear = false;
3167 }
3168 let _t = std::time::Instant::now();
3169 gfx.toon_post_process();
3170 ling_phase_add(phase::TOON, _t.elapsed().as_nanos());
3171 let w = gfx.width;
3172 let h = gfx.height;
3173 let g = &mut *gfx;
3174 if let Some(win) = g.window.as_mut() {
3175 let _b = std::time::Instant::now();
3176 win.update_with_buffer(&g.buffer, w, h)
3177 .map_err(|e| EvalErr::from(format!("present error: {e}")))?;
3178 ling_phase_add(phase::BLIT, _b.elapsed().as_nanos());
3179 }
3180 }
3181 let mouse_pos = {
3183 let gfx = self.gfx.borrow();
3184 gfx.window
3185 .as_ref()
3186 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
3187 };
3188 let mut gfx = self.gfx.borrow_mut();
3189 if gfx.mouse_captured {
3190 let w = gfx.width as f32;
3191 let h = gfx.height as f32;
3192 if let Some((mx, my)) = mouse_pos {
3193 if gfx.last_mx.is_nan() {
3194 gfx.mouse_dx = 0.0;
3195 gfx.mouse_dy = 0.0;
3196 gfx.last_mx = mx;
3197 gfx.last_my = my;
3198 } else {
3199 gfx.mouse_dx = mx - gfx.last_mx;
3200 gfx.mouse_dy = my - gfx.last_my;
3201 let margin = 6.0;
3204 let (mut nx, mut ny, mut warp) = (mx, my, false);
3205 if mx < margin {
3206 nx = w - margin - 2.0;
3207 warp = true;
3208 } else if mx > w - margin {
3209 nx = margin + 2.0;
3210 warp = true;
3211 }
3212 if my < margin {
3213 ny = h - margin - 2.0;
3214 warp = true;
3215 } else if my > h - margin {
3216 ny = margin + 2.0;
3217 warp = true;
3218 }
3219 if warp {
3220 #[cfg(windows)]
3221 unsafe {
3222 #[repr(C)]
3223 struct RECT {
3224 left: i32,
3225 top: i32,
3226 right: i32,
3227 bottom: i32,
3228 }
3229 extern "system" {
3230 fn GetForegroundWindow() -> isize;
3231 fn GetWindowRect(hwnd: isize, lpRect: *mut RECT)
3232 -> i32;
3233 fn SetCursorPos(x: i32, y: i32) -> i32;
3234 }
3235 let hwnd = GetForegroundWindow();
3236 let mut rect =
3237 RECT { left: 0, top: 0, right: 0, bottom: 0 };
3238 if GetWindowRect(hwnd, &mut rect) != 0 {
3239 SetCursorPos(
3240 rect.left + nx as i32,
3241 rect.top + ny as i32,
3242 );
3243 }
3244 }
3245 gfx.last_mx = nx;
3246 gfx.last_my = ny;
3247 } else {
3248 gfx.last_mx = mx;
3249 gfx.last_my = my;
3250 }
3251 }
3252 } else {
3253 gfx.mouse_dx = 0.0;
3254 gfx.mouse_dy = 0.0;
3255 }
3256 } else if let Some((mx, my)) = mouse_pos {
3257 if gfx.last_mx.is_nan() {
3258 gfx.mouse_dx = 0.0;
3259 gfx.mouse_dy = 0.0;
3260 } else {
3261 gfx.mouse_dx = mx - gfx.last_mx;
3262 gfx.mouse_dy = my - gfx.last_my;
3263 }
3264 gfx.last_mx = mx;
3265 gfx.last_my = my;
3266 } else {
3267 gfx.mouse_dx = 0.0;
3268 gfx.mouse_dy = 0.0;
3269 }
3270 }
3271 #[cfg(target_arch = "wasm32")]
3272 {
3273 {
3274 let mut gfx = self.gfx.borrow_mut();
3278 let w = gfx.width;
3279 let h = gfx.height;
3280 if gfx.buffer.len() != w * h {
3281 gfx.buffer.resize(w * h, 0);
3282 }
3283 if !gfx.depth_queue.is_empty() {
3284 let dt = gfx.depth_test;
3285 let reset_z = gfx.zbuf_needs_clear;
3286 let queue = std::mem::take(&mut gfx.depth_queue);
3287 {
3288 let g = &mut *gfx;
3289 let z = if dt { Some(&mut g.depth_buf) } else { None };
3290 queue.flush(&mut g.buffer, z, reset_z, w, h);
3291 }
3292 gfx.zbuf_needs_clear = false;
3293 }
3294 gfx.toon_post_process();
3295 crate::gfx::webgl::blit_rgb(&gfx.buffer, w, h);
3296 }
3297 self.wasm_pace_frame();
3298 }
3299 #[cfg(not(target_arch = "wasm32"))]
3301 {
3302 let (_, _, down) = self.mouse_now();
3303 self.mouse_was_down = down;
3304 }
3305 self.frame_num += 1;
3307 return Ok(Value::Unit);
3308 },
3309
3310 "เปิดหน้าต่างเต็มจอ"
3312 | "open_fullscreen"
3313 | "fullscreen"
3314 | "全屏"
3315 | "全画面"
3316 | "전체화면" => {
3317 #[cfg(target_arch = "wasm32")]
3320 let (default_w, default_h) = {
3321 let (cw, ch) = crate::gfx::webgl::canvas_size();
3322 (cw as f64, ch as f64)
3323 };
3324 #[cfg(all(not(target_arch = "wasm32"), windows))]
3326 let (default_w, default_h) = unsafe {
3327 extern "system" {
3328 fn GetSystemMetrics(nIndex: i32) -> i32;
3329 }
3330 (GetSystemMetrics(0) as f64, GetSystemMetrics(1) as f64)
3331 };
3332 #[cfg(all(not(target_arch = "wasm32"), not(windows)))]
3333 let (default_w, default_h) = native_screen_size();
3334
3335 let w = args
3336 .get(1)
3337 .map(|v| self.to_number(v).unwrap_or(default_w) as usize)
3338 .unwrap_or(default_w as usize);
3339 let h = args
3340 .get(2)
3341 .map(|v| self.to_number(v).unwrap_or(default_h) as usize)
3342 .unwrap_or(default_h as usize);
3343 #[cfg(not(target_arch = "wasm32"))]
3344 {
3345 let title = args
3346 .get(0)
3347 .map(|v| v.to_string())
3348 .unwrap_or_else(|| "Ling".into());
3349 let mut gfx = self.gfx.borrow_mut();
3350 let mut win = minifb::Window::new(
3351 &title,
3352 w,
3353 h,
3354 minifb::WindowOptions {
3355 borderless: true,
3356 title: false,
3357 resize: false,
3358 topmost: true,
3359 scale: minifb::Scale::X1,
3360 ..Default::default()
3361 },
3362 )
3363 .map_err(|e| EvalErr::from(format!("cannot open fullscreen: {e}")))?;
3364 match std::env::var("LING_FPS_CAP").ok().and_then(|v| v.parse::<usize>().ok()) {
3369 Some(0) => win.set_target_fps(100_000),
3370 Some(cap) => win.set_target_fps(cap),
3371 None => win.set_target_fps(monitor_info().2.max(30) as usize),
3372 }
3373 #[cfg(windows)]
3375 let hwnd = win.get_window_handle() as isize;
3376 gfx.buffer = vec![0u32; w * h];
3377 gfx.width = w;
3378 gfx.height = h;
3379 gfx.window = Some(win);
3380 gfx.sync_projection();
3381 #[cfg(windows)]
3383 make_borderless_fullscreen(hwnd, w as i32, h as i32);
3384 hide_console_window();
3385 }
3386 #[cfg(target_arch = "wasm32")]
3387 {
3388 let mut gfx = self.gfx.borrow_mut();
3389 gfx.width = w;
3390 gfx.height = h;
3391 gfx.buffer.resize(w * h, 0); gfx.sync_projection();
3393 crate::gfx::webgl::resize(w as u32, h as u32);
3394 }
3395 return Ok(Value::Unit);
3396 },
3397
3398 "get_width" | "ความกว้าง" | "宽" | "幅取得" | "너비" => {
3400 return Ok(Value::Number(self.gfx.borrow().width as f64));
3401 },
3402 "get_height" | "ความสูง" | "高" | "高取得" | "높이" => {
3403 return Ok(Value::Number(self.gfx.borrow().height as f64));
3404 },
3405
3406 "monitor_width" | "screen_width" | "屏宽" | "画面幅" | "화면너비" | "ความกว้างจอ" =>
3409 {
3410 return Ok(Value::Number(monitor_info().0 as f64));
3411 },
3412 "monitor_height" | "screen_height" | "屏高" | "画面高" | "화면높이" | "ความสูงจอ" =>
3414 {
3415 return Ok(Value::Number(monitor_info().1 as f64));
3416 },
3417 "monitor_refresh"
3419 | "monitor_hz"
3420 | "monitor_fps"
3421 | "refresh_rate"
3422 | "刷新率"
3423 | "リフレッシュレート"
3424 | "주사율"
3425 | "อัตรารีเฟรช" => {
3426 return Ok(Value::Number(monitor_info().2 as f64));
3427 },
3428 "monitor_info" | "screen_info" | "屏幕信息" | "画面情報" | "화면정보" | "ข้อมูลจอ" =>
3430 {
3431 let (w, h, hz) = monitor_info();
3432 return Ok(Value::List(Rc::new(vec![
3433 Value::Number(w as f64),
3434 Value::Number(h as f64),
3435 Value::Number(hz as f64),
3436 ])));
3437 },
3438 "set_fps"
3440 | "set_target_fps"
3441 | "target_fps"
3442 | "设帧率"
3443 | "フレームレート設定"
3444 | "프레임설정"
3445 | "ตั้งเฟรมเรต" => {
3446 #[cfg(not(target_arch = "wasm32"))]
3447 {
3448 let fps = self.arg_num(&args, 0, 60.0)?.max(1.0) as usize;
3449 let mut gfx = self.gfx.borrow_mut();
3450 if let Some(win) = gfx.window.as_mut() {
3451 win.set_target_fps(fps);
3452 }
3453 }
3454 #[cfg(target_arch = "wasm32")]
3455 {
3456 self.wasm_target_fps = self.arg_num(&args, 0, 60.0)?.max(1.0);
3457 self.wasm_next_present_ms = 0.0;
3458 }
3459 return Ok(Value::Unit);
3460 },
3461
3462 "หน้าต่างเปิดอยู่"
3464 | "window_is_open"
3465 | "gfx_is_open"
3466 | "is_open"
3467 | "窗开"
3468 | "開いている"
3469 | "창열림" => {
3470 #[cfg(not(target_arch = "wasm32"))]
3471 {
3472 let gfx = self.gfx.borrow();
3473 let open = gfx
3474 .window
3475 .as_ref()
3476 .map(|w| w.is_open() && !w.is_key_down(minifb::Key::Escape))
3477 .unwrap_or(false);
3478 return Ok(Value::Bool(open));
3479 }
3480 #[cfg(target_arch = "wasm32")]
3481 return Ok(Value::Bool(true));
3482 },
3483
3484 "key_down" | "กดค้าง" | "按键" | "キー押す" | "키누름" => {
3486 #[cfg(not(target_arch = "wasm32"))]
3487 {
3488 let name = self.arg_str(&args, 0, "");
3489 let gfx = self.gfx.borrow();
3490 let down = gfx
3491 .window
3492 .as_ref()
3493 .and_then(|w| str_to_minifb_key(&name).map(|k| w.is_key_down(k)))
3494 .unwrap_or(false);
3495 return Ok(Value::Bool(down));
3496 }
3497 #[cfg(target_arch = "wasm32")]
3498 return Ok(Value::Bool(false));
3499 },
3500
3501 "key_pressed" | "กดปุ่ม" | "键按" | "キー押した" | "키눌림" => {
3503 #[cfg(not(target_arch = "wasm32"))]
3504 {
3505 let name = self.arg_str(&args, 0, "");
3506 let pressed = {
3507 let gfx = self.gfx.borrow();
3508 gfx.window
3509 .as_ref()
3510 .and_then(|w| {
3511 str_to_minifb_key(&name)
3512 .map(|k| w.is_key_pressed(k, minifb::KeyRepeat::No))
3513 })
3514 .unwrap_or(false)
3515 };
3516 let pressed =
3518 pressed || ((name == "enter" || name == "return") && gamepad::start_edge());
3519 return Ok(Value::Bool(pressed));
3520 }
3521 #[cfg(target_arch = "wasm32")]
3522 {
3523 let name = self.arg_str(&args, 0, "");
3524 let pressed = crate::gfx::wasm_is_key_pressed(&name);
3525 return Ok(Value::Bool(pressed));
3526 }
3527 },
3528
3529 "mouse_dx" | "เมาส์X" | "鼠ΔX" | "マウスΔX" | "마우스ΔX" => {
3531 #[cfg(not(target_arch = "wasm32"))]
3532 return Ok(Value::Number(self.gfx.borrow().mouse_dx as f64));
3533 #[cfg(target_arch = "wasm32")]
3534 return Ok(Value::Number(0.0));
3535 },
3536 #[cfg(not(target_arch = "wasm32"))]
3538 "mouse_scroll" | "ล้อเมาส์" | "滚轮" | "ホイール" | "스크롤" =>
3539 {
3540 let gfx = self.gfx.borrow();
3541 let s = gfx
3542 .window
3543 .as_ref()
3544 .and_then(|w| w.get_scroll_wheel())
3545 .map(|(_, y)| y as f64)
3546 .unwrap_or(0.0);
3547 return Ok(Value::Number(s));
3548 },
3549 #[cfg(target_arch = "wasm32")]
3550 "mouse_scroll" | "ล้อเมาส์" | "滚轮" | "ホイール" | "스크롤" =>
3551 {
3552 return Ok(Value::Number(0.0));
3553 },
3554 "mouse_dy" | "เมาส์Y" | "鼠ΔY" | "マウスΔY" | "마우스ΔY" => {
3555 #[cfg(not(target_arch = "wasm32"))]
3556 return Ok(Value::Number(self.gfx.borrow().mouse_dy as f64));
3557 #[cfg(target_arch = "wasm32")]
3558 return Ok(Value::Number(0.0));
3559 },
3560
3561 "pad_poll" | "手柄轮询" | "パッド更新" | "패드폴링" | "อัปเดตแพด" =>
3564 {
3565 #[cfg(not(target_arch = "wasm32"))]
3566 return Ok(Value::Number(self.pad_poll() as f64));
3567 #[cfg(target_arch = "wasm32")]
3568 return Ok(Value::Number(input_web::poll() as f64));
3569 },
3570 "pad_count" | "手柄数" | "パッド数" | "패드수" | "จำนวนแพด" =>
3572 {
3573 #[cfg(not(target_arch = "wasm32"))]
3574 {
3575 let inp = self.input.borrow();
3576 let n = inp.as_ref().map_or(0, |s| s.sensorium.devices.count());
3577 return Ok(Value::Number(n as f64));
3578 }
3579 #[cfg(target_arch = "wasm32")]
3580 return Ok(Value::Number(input_web::count() as f64));
3581 },
3582 "pad_connected" | "手柄连接" | "パッド接続" | "패드연결" | "แพดเชื่อม" =>
3584 {
3585 #[cfg(not(target_arch = "wasm32"))]
3586 {
3587 let i = self.arg_num(&args, 0, 0.0)? as usize;
3588 let inp = self.input.borrow();
3589 let c = inp
3590 .as_ref()
3591 .is_some_and(|s| s.sensorium.devices.for_player(i as u8).is_some());
3592 return Ok(Value::Bool(c));
3593 }
3594 #[cfg(target_arch = "wasm32")]
3595 {
3596 let i = self.arg_num(&args, 0, 0.0)? as usize;
3597 return Ok(Value::Bool(input_web::is_connected(i)));
3598 }
3599 },
3600 "pad_button" | "手柄按键" | "パッドボタン" | "패드버튼" | "ปุ่มแพด" =>
3602 {
3603 #[cfg(not(target_arch = "wasm32"))]
3604 {
3605 let i = self.arg_num(&args, 0, 0.0)? as usize;
3606 let name = self.arg_str(&args, 1, "");
3607 let down = parse_pad_button(&name)
3608 .is_some_and(|b| self.with_pad(i, false, |p| p.is_down(b)));
3609 return Ok(Value::Bool(down));
3610 }
3611 #[cfg(target_arch = "wasm32")]
3612 {
3613 let i = self.arg_num(&args, 0, 0.0)? as usize;
3614 let name = self.arg_str(&args, 1, "");
3615 return Ok(Value::Bool(input_web::button_down(i, &name)));
3616 }
3617 },
3618 "pad_pressed" | "手柄按下" | "パッド押下" | "패드눌림" | "แพดกด" =>
3621 {
3622 #[cfg(not(target_arch = "wasm32"))]
3623 {
3624 let i = self.arg_num(&args, 0, 0.0)? as usize;
3625 let name = self.arg_str(&args, 1, "");
3626 let p = parse_pad_button(&name)
3627 .is_some_and(|b| self.with_pad(i, false, |g| g.just_pressed(b)));
3628 return Ok(Value::Bool(p));
3629 }
3630 #[cfg(target_arch = "wasm32")]
3631 {
3632 let i = self.arg_num(&args, 0, 0.0)? as usize;
3633 let name = self.arg_str(&args, 1, "");
3634 return Ok(Value::Bool(input_web::button_down(i, &name)));
3635 }
3636 },
3637 "pad_lx" | "手柄左X" | "パッド左X" | "패드왼X" | "แพดซ้ายX" => {
3639 #[cfg(not(target_arch = "wasm32"))]
3640 {
3641 let i = self.arg_num(&args, 0, 0.0)? as usize;
3642 return Ok(Value::Number(
3643 self.with_pad(i, 0.0, |p| p.left_stick.x as f64),
3644 ));
3645 }
3646 #[cfg(target_arch = "wasm32")]
3647 {
3648 let i = self.arg_num(&args, 0, 0.0)? as usize;
3649 return Ok(Value::Number(input_web::axis_lx(i) as f64));
3650 }
3651 },
3652 "pad_ly" | "手柄左Y" | "パッド左Y" | "패드왼Y" | "แพดซ้ายY" => {
3653 #[cfg(not(target_arch = "wasm32"))]
3654 {
3655 let i = self.arg_num(&args, 0, 0.0)? as usize;
3656 return Ok(Value::Number(
3657 self.with_pad(i, 0.0, |p| p.left_stick.y as f64),
3658 ));
3659 }
3660 #[cfg(target_arch = "wasm32")]
3661 {
3662 let i = self.arg_num(&args, 0, 0.0)? as usize;
3663 return Ok(Value::Number(input_web::axis_ly(i) as f64));
3664 }
3665 },
3666 "pad_rx" | "手柄右X" | "パッド右X" | "패드오X" | "แพดขวาX" => {
3667 #[cfg(not(target_arch = "wasm32"))]
3668 {
3669 let i = self.arg_num(&args, 0, 0.0)? as usize;
3670 return Ok(Value::Number(
3671 self.with_pad(i, 0.0, |p| p.right_stick.x as f64),
3672 ));
3673 }
3674 #[cfg(target_arch = "wasm32")]
3675 {
3676 let i = self.arg_num(&args, 0, 0.0)? as usize;
3677 return Ok(Value::Number(input_web::axis_rx(i) as f64));
3678 }
3679 },
3680 "pad_ry" | "手柄右Y" | "パッド右Y" | "패드오Y" | "แพดขวาY" => {
3681 #[cfg(not(target_arch = "wasm32"))]
3682 {
3683 let i = self.arg_num(&args, 0, 0.0)? as usize;
3684 return Ok(Value::Number(
3685 self.with_pad(i, 0.0, |p| p.right_stick.y as f64),
3686 ));
3687 }
3688 #[cfg(target_arch = "wasm32")]
3689 {
3690 let i = self.arg_num(&args, 0, 0.0)? as usize;
3691 return Ok(Value::Number(input_web::axis_ry(i) as f64));
3692 }
3693 },
3694 "pad_lt" | "手柄左扳机" | "パッド左トリガー" | "패드왼트리거" | "ไกแพดซ้าย" =>
3696 {
3697 #[cfg(not(target_arch = "wasm32"))]
3698 {
3699 let i = self.arg_num(&args, 0, 0.0)? as usize;
3700 return Ok(Value::Number(
3701 self.with_pad(i, 0.0, |p| p.left_trigger as f64),
3702 ));
3703 }
3704 #[cfg(target_arch = "wasm32")]
3705 {
3706 let i = self.arg_num(&args, 0, 0.0)? as usize;
3707 return Ok(Value::Number(input_web::trigger_lt(i) as f64));
3708 }
3709 },
3710 "pad_rt" | "手柄右扳机" | "パッド右トリガー" | "패드오트리거" | "ไกแพดขวา" =>
3711 {
3712 #[cfg(not(target_arch = "wasm32"))]
3713 {
3714 let i = self.arg_num(&args, 0, 0.0)? as usize;
3715 return Ok(Value::Number(
3716 self.with_pad(i, 0.0, |p| p.right_trigger as f64),
3717 ));
3718 }
3719 #[cfg(target_arch = "wasm32")]
3720 {
3721 let i = self.arg_num(&args, 0, 0.0)? as usize;
3722 return Ok(Value::Number(input_web::trigger_rt(i) as f64));
3723 }
3724 },
3725 "pad_rumble" | "手柄震动" | "パッド振動" | "패드진동" | "แพดสั่น" =>
3727 {
3728 #[cfg(not(target_arch = "wasm32"))]
3729 {
3730 use ling_input::backend::InputBackend;
3731 let i = self.arg_num(&args, 0, 0.0)? as usize;
3732 let lo = self.arg_num(&args, 1, 0.0)? as f32;
3733 let hi = self.arg_num(&args, 2, lo as f64)? as f32;
3734 let mut inp = self.input.borrow_mut();
3735 if let Some(s) = inp.as_mut() {
3736 if let Some(dev) = s.sensorium.devices.for_player(i as u8).map(|d| d.id) {
3737 s.backend.set_rumble(
3738 dev,
3739 ling_input::Rumble { low: lo, high: hi, ..Default::default() },
3740 );
3741 }
3742 }
3743 return Ok(Value::Unit);
3744 }
3745 #[cfg(target_arch = "wasm32")]
3746 return Ok(Value::Unit);
3747 },
3748
3749 "set_camera_pos" | "ตั้งตำแหน่งกล้อง" | "镜坐标" | "カメラ座標" | "카메라좌표" =>
3751 {
3752 let x = self.arg_num(&args, 0, 0.0)? as f32;
3753 let y = self.arg_num(&args, 1, 0.0)? as f32;
3754 let z = self.arg_num(&args, 2, 0.0)? as f32;
3755 {
3756 let mut gfx = self.gfx.borrow_mut();
3757 gfx.camera.tx = x;
3758 gfx.camera.ty = y;
3759 gfx.camera.tz = z;
3760 }
3761 #[cfg(not(target_arch = "wasm32"))]
3762 if let Some(audio) = &self.audio {
3763 audio.set_listener_pos(x, y, z);
3764 }
3765 return Ok(Value::Unit);
3766 },
3767
3768 "move_camera" => {
3770 let dx = self.arg_num(&args, 0, 0.0)? as f32;
3771 let dy = self.arg_num(&args, 1, 0.0)? as f32;
3772 let dz = self.arg_num(&args, 2, 0.0)? as f32;
3773 let mut gfx = self.gfx.borrow_mut();
3774 gfx.camera.tx += dx;
3775 gfx.camera.ty += dy;
3776 gfx.camera.tz += dz;
3777 return Ok(Value::Unit);
3778 },
3779
3780 "set_zdist" | "ตั้งระยะห่าง" | "镜距" | "Z距離設定" | "Z거리설정" =>
3782 {
3783 let d = self.arg_num(&args, 0, 5.0)? as f32;
3784 self.gfx.borrow_mut().camera.zdist = d;
3785 return Ok(Value::Unit);
3786 },
3787
3788 "capture_mouse" | "จับเมาส์" | "捕鼠" | "マウス捕捉" | "마우스잡기" =>
3790 {
3791 #[cfg(not(target_arch = "wasm32"))]
3792 {
3793 let mut gfx = self.gfx.borrow_mut();
3794 gfx.mouse_captured = true;
3795 gfx.last_mx = f32::NAN;
3796 if let Some(win) = gfx.window.as_mut() {
3797 win.set_cursor_visibility(false);
3798 }
3799 }
3800 return Ok(Value::Unit);
3801 },
3802
3803 "release_mouse" => {
3805 #[cfg(not(target_arch = "wasm32"))]
3806 {
3807 let mut gfx = self.gfx.borrow_mut();
3808 gfx.mouse_captured = false;
3809 gfx.last_mx = f32::NAN;
3810 if let Some(win) = gfx.window.as_mut() {
3811 win.set_cursor_visibility(true);
3812 }
3813 #[cfg(windows)]
3814 unsafe {
3815 extern "system" {
3817 fn ClipCursor(lpRect: *const std::ffi::c_void) -> i32;
3818 }
3819 ClipCursor(std::ptr::null());
3820 }
3821 }
3822 return Ok(Value::Unit);
3823 },
3824
3825 "set_camera" | "ตั้งกล้อง" | "设镜" | "设置摄像机" | "カメラ設定" | "카메라설정" =>
3832 {
3833 let cry = self.arg_num(&args, 0, 1.0)? as f32;
3834 let sry = self.arg_num(&args, 1, 0.0)? as f32;
3835 let crx = self.arg_num(&args, 2, 1.0)? as f32;
3836 let srx = self.arg_num(&args, 3, 0.0)? as f32;
3837 let mut gfx = self.gfx.borrow_mut();
3838 gfx.camera.cry = cry;
3839 gfx.camera.sry = sry;
3840 gfx.camera.crx = crx;
3841 gfx.camera.srx = srx;
3842 return Ok(Value::Unit);
3843 },
3844
3845 "set_projection" | "ตั้งโปรเจกชัน" | "投影" | "投影設定" | "투영설정" =>
3848 {
3849 let cx = self.arg_num(&args, 0, 960.0)? as f32;
3850 let cy = self.arg_num(&args, 1, 540.0)? as f32;
3851 let focal = self.arg_num(&args, 2, 1080.0)? as f32;
3852 let zdist = self.arg_num(&args, 3, 5.0)? as f32;
3853 let mut gfx = self.gfx.borrow_mut();
3854 gfx.camera.cx = cx;
3855 gfx.camera.cy = cy;
3856 gfx.camera.focal = focal;
3857 gfx.camera.zdist = zdist;
3858 return Ok(Value::Unit);
3859 },
3860
3861 "draw_mesh" | "วาดเมช" => {
3868 let pos = match args.first() {
3869 Some(Value::List(v)) => v,
3870 _ => return Ok(Value::Unit),
3871 };
3872 let idx = match args.get(1) {
3873 Some(Value::List(v)) => v,
3874 _ => return Ok(Value::Unit),
3875 };
3876 let ox = self.arg_num(&args, 2, 0.0)? as f32;
3877 let oy = self.arg_num(&args, 3, 0.0)? as f32;
3878 let oz = self.arg_num(&args, 4, 0.0)? as f32;
3879 let scale = self.arg_num(&args, 5, 1.0)? as f32;
3880 let mode = self.arg_num(&args, 6, 0.0)? as i64;
3881 let nv = pos.len() / 3;
3882 if nv == 0 {
3883 return Ok(Value::Unit);
3884 }
3885 let mut world = vec![0.0f32; nv * 3];
3886 for i in 0..nv {
3887 world[i * 3] = ox + self.to_number(&pos[i * 3]).unwrap_or(0.0) as f32 * scale;
3888 world[i * 3 + 1] =
3889 oy + self.to_number(&pos[i * 3 + 1]).unwrap_or(0.0) as f32 * scale;
3890 world[i * 3 + 2] =
3891 oz + self.to_number(&pos[i * 3 + 2]).unwrap_or(0.0) as f32 * scale;
3892 }
3893 let mut gfx = self.gfx.borrow_mut();
3894 let cp = {
3895 let c = &gfx.camera;
3896 ling_gpu::CameraParams {
3897 cry: c.cry,
3898 sry: c.sry,
3899 crx: c.crx,
3900 srx: c.srx,
3901 cx: c.cx,
3902 cy: c.cy,
3903 focal: c.focal,
3904 zdist: c.zdist,
3905 tx: c.tx,
3906 ty: c.ty,
3907 tz: c.tz,
3908 }
3909 };
3910 let near = -gfx.camera.zdist + 0.02;
3911 let base = gfx.color;
3912 let ambient = gfx.ambient;
3913 let mut proj = vec![0.0f32; nv * 3]; ling_gpu::backend().project_points(&world, &cp, &mut proj);
3915 let nt = idx.len() / 3;
3916 for t in 0..nt {
3917 let ia = self.to_number(&idx[t * 3]).unwrap_or(0.0) as usize;
3918 let ib = self.to_number(&idx[t * 3 + 1]).unwrap_or(0.0) as usize;
3919 let ic = self.to_number(&idx[t * 3 + 2]).unwrap_or(0.0) as usize;
3920 if ia >= nv || ib >= nv || ic >= nv {
3921 continue;
3922 }
3923 let (da, db, dc) = (proj[ia * 3 + 2], proj[ib * 3 + 2], proj[ic * 3 + 2]);
3924 if (da + db + dc) / 3.0 <= near {
3925 continue;
3926 } let col = if mode == 1 {
3928 let h = t as f32 * 0.6;
3929 let r = ((h.sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
3930 let g = (((h + 2.094).sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
3931 let b = (((h + 4.189).sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
3932 (r << 16) | (g << 8) | b
3933 } else {
3934 let (ax, ay, az) = (world[ia * 3], world[ia * 3 + 1], world[ia * 3 + 2]);
3935 let (bx, by, bz) = (world[ib * 3], world[ib * 3 + 1], world[ib * 3 + 2]);
3936 let (px, py, pz) = (world[ic * 3], world[ic * 3 + 1], world[ic * 3 + 2]);
3937 let (ux, uy, uz) = (bx - ax, by - ay, bz - az);
3938 let (vx, vy, vz) = (px - ax, py - ay, pz - az);
3939 let normal = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];
3940 let centroid = [
3941 (ax + bx + px) / 3.0,
3942 (ay + by + py) / 3.0,
3943 (az + bz + pz) / 3.0,
3944 ];
3945 if gfx.flat_shade {
3946 base
3947 } else {
3948 crate::gfx::light::compute_lit_color(
3949 base,
3950 normal,
3951 centroid,
3952 &gfx.lights,
3953 ambient,
3954 )
3955 }
3956 };
3957 let depth = (da + db + dc) / 3.0;
3958 let col = gfx.fog_apply(col, depth);
3959 gfx.depth_queue.push_triangle_zv(
3963 col,
3964 proj[ia * 3],
3965 proj[ia * 3 + 1],
3966 da,
3967 proj[ib * 3],
3968 proj[ib * 3 + 1],
3969 db,
3970 proj[ic * 3],
3971 proj[ic * 3 + 1],
3972 dc,
3973 );
3974 }
3975 return Ok(Value::Unit);
3976 },
3977
3978 "add_light" | "เพิ่มแสง" | "加灯" | "ライト追加" | "조명추가" =>
3982 {
3983 let x = self.arg_num(&args, 0, 0.0)? as f32;
3984 let y = self.arg_num(&args, 1, -3.0)? as f32;
3985 let z = self.arg_num(&args, 2, 3.0)? as f32;
3986 let mut r = self.arg_num(&args, 3, 1.0)? as f32;
3987 let mut g = self.arg_num(&args, 4, 1.0)? as f32;
3988 let mut b = self.arg_num(&args, 5, 1.0)? as f32;
3989 if r > 1.5 || g > 1.5 || b > 1.5 {
3992 r /= 255.0;
3993 g /= 255.0;
3994 b /= 255.0;
3995 }
3996 let intensity = self.arg_num(&args, 6, 1.0)? as f32;
3997 let radius = self.arg_num(&args, 7, 0.0)? as f32;
3998 self.gfx
3999 .borrow_mut()
4000 .lights
4001 .push(Light { x, y, z, r, g, b, intensity, radius });
4002 return Ok(Value::Unit);
4003 },
4004
4005 "clear_lights" | "ล้างแสง" | "清灯" | "ライト消去" | "조명초기화" =>
4007 {
4008 self.gfx.borrow_mut().lights.clear();
4009 return Ok(Value::Unit);
4010 },
4011
4012 "set_material" | "ตั้งวัสดุ" | "设置材质" | "マテリアル設定" | "재질설정" =>
4022 {
4023 let key = self.arg_str(&args, 0, "");
4024 let val = self.arg_num(&args, 1, 0.0)?;
4025 let mut gfx = self.gfx.borrow_mut();
4026 let mat = gfx.material.get_or_insert_with(crate::gfx::LingMaterial::default);
4027 match key.as_str() {
4028 "albedo" => mat.albedo = val as u32,
4029 "roughness" => mat.roughness = val as f32,
4030 "metallic" => mat.metallic = val as f32,
4031 "emission" => mat.emission = val as u32,
4032 "emission_strength" => mat.emission_strength = val as f32,
4033 "specular" => mat.specular = val as f32,
4034 "specular_tint" => mat.specular_tint = val as f32,
4035 "subsurface" => mat.subsurface = val as f32,
4036 "subsurface_color" => mat.subsurface_color = val as u32,
4037 "clearcoat" => mat.clearcoat = val as f32,
4038 "clearcoat_roughness"=> mat.clearcoat_roughness= val as f32,
4039 "transmission" => mat.transmission = val as f32,
4040 "ior" => mat.ior = val as f32,
4041 "iridescence" => mat.iridescence = val as f32,
4042 "sheen" => mat.sheen = val as f32,
4043 "anisotropy" => mat.anisotropy = val as f32,
4044 "anisotropy_angle" => mat.anisotropy_angle = val as f32,
4045 "toon_bands" => mat.toon_bands = val as u32,
4046 "shadow_softness" => mat.shadow_softness = val as f32,
4047 "outline_px" => mat.outline_px = val as f32,
4048 "outline_color" => mat.outline_color = val as u32,
4049 "highlight_color" => mat.highlight_color = val as u32,
4050 _ => {}
4051 }
4052 return Ok(Value::Unit);
4053 },
4054
4055 "reset_material" | "รีเซ็ตวัสดุ" | "重置材质" | "マテリアルリセット" | "재질초기화" =>
4058 {
4059 self.gfx.borrow_mut().material = None;
4060 return Ok(Value::Unit);
4061 },
4062
4063 "toon_outlines" | "ตั้งเส้นขอบการ์ตูน" | "卡通轮廓" | "トゥーンアウトライン" | "툰아웃라인" =>
4069 {
4070 let px = self.arg_num(&args, 0, 0.0)? as f32;
4071 let color = self.arg_num(&args, 1, 0.0)? as u32;
4072 let thresh= self.arg_num(&args, 2, 0.05)? as f32;
4073 let mut gfx = self.gfx.borrow_mut();
4074 gfx.toon.outline_px = px;
4075 gfx.toon.outline_color = color;
4076 gfx.toon.outline_thresh = thresh;
4077 return Ok(Value::Unit);
4078 },
4079
4080 "tone_stop" | "ตั้งจุดโทน" | "色调停止" | "トーンストップ" | "톤스톱" =>
4086 {
4087 let t = self.arg_num(&args, 0, 0.0)? as f32;
4088 let val = self.arg_num(&args, 1, 1.0)? as f32;
4089 let mut gfx = self.gfx.borrow_mut();
4090 gfx.toon.ramp.stops.push(crate::gfx::toon::ToneStop {
4091 t: t.clamp(0.0, 1.0),
4092 value: val.clamp(0.0, 1.0),
4093 });
4094 gfx.toon.ramp.stops.sort_by(|a, b|
4095 a.t.partial_cmp(&b.t).unwrap_or(std::cmp::Ordering::Equal));
4096 return Ok(Value::Unit);
4097 },
4098
4099 "tone_smooth" | "ตั้งโทนนุ่ม" | "色调平滑" | "トーンスムーズ" | "톤스무스" =>
4102 {
4103 let v = self.arg_num(&args, 0, 0.0)? as f32;
4104 self.gfx.borrow_mut().toon.ramp.smooth = v > 0.5;
4105 return Ok(Value::Unit);
4106 },
4107
4108 "tone_bezier" | "ตั้งโทนเบซิเยร์" | "色调贝塞尔" | "トーンベジェ" | "톤베지어" =>
4116 {
4117 let y1 = self.arg_num(&args, 0, 1.0/3.0)? as f32;
4118 let y2 = self.arg_num(&args, 1, 2.0/3.0)? as f32;
4119 self.gfx.borrow_mut().toon.ramp.bezier = Some([y1, y2]);
4120 return Ok(Value::Unit);
4121 },
4122
4123 "tone_bezier_off" | "ปิดโทนเบซิเยร์" | "关闭色调贝塞尔" | "トーンベジェオフ" | "톤베지어끄기" =>
4125 {
4126 self.gfx.borrow_mut().toon.ramp.bezier = None;
4127 return Ok(Value::Unit);
4128 },
4129
4130 "tone_ramp_reset" | "รีเซ็ตการไล่โทน" | "重置色调渐变" | "トーンランプリセット" | "톤램프리셋" =>
4132 {
4133 self.gfx.borrow_mut().toon.ramp = crate::gfx::toon::ToneRamp::default();
4134 return Ok(Value::Unit);
4135 },
4136
4137 "tone_ramp_clear" | "ล้างการไล่โทน" | "清除色调渐变" | "トーンランプクリア" | "톤램프클리어" =>
4139 {
4140 self.gfx.borrow_mut().toon.ramp.stops.clear();
4141 return Ok(Value::Unit);
4142 },
4143
4144 "shadow_smooth" | "ตั้งเงานุ่ม" | "柔化阴影" | "影ソフト" | "그림자부드럽게" =>
4147 {
4148 let s = self.arg_num(&args, 0, 0.0)? as f32;
4149 let mut gfx = self.gfx.borrow_mut();
4150 gfx.toon.ramp.smooth = s > 0.05;
4151 if s > 0.05 {
4152 let y1 = (0.333 + s * 0.2).clamp(0.0, 1.0);
4153 let y2 = (0.667 - s * 0.2).clamp(0.0, 1.0);
4154 gfx.toon.ramp.bezier = Some([y1, y2]);
4155 } else {
4156 gfx.toon.ramp.bezier = None;
4157 }
4158 return Ok(Value::Unit);
4159 },
4160
4161 "toon_highlight" | "ตั้งไฮไลท์การ์ตูน" | "卡通高光" | "トゥーンハイライト" | "툰하이라이트" =>
4163 {
4164 let _strength = self.arg_num(&args, 0, 0.0)? as f32;
4166 let _thresh = self.arg_num(&args, 2, 0.78)? as f32;
4167 return Ok(Value::Unit);
4169 },
4170
4171 "set_ambient" | "ตั้งแสงรอบข้าง" | "环境光" | "環境光設定" | "환경광설정" =>
4173 {
4174 let v = self.arg_num(&args, 0, 0.15)? as f32;
4175 self.gfx.borrow_mut().ambient = v;
4176 return Ok(Value::Unit);
4177 },
4178
4179 "set_fog" | "ตั้งหมอก" | "雾" | "霧設定" | "안개설정" => {
4182 let r = self.arg_num(&args, 0, 0.0)?.clamp(0.0, 255.0) as u32;
4183 let g = self.arg_num(&args, 1, 0.0)?.clamp(0.0, 255.0) as u32;
4184 let b = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 255.0) as u32;
4185 let start = self.arg_num(&args, 3, 0.0)? as f32;
4186 let end = self.arg_num(&args, 4, 0.0)? as f32;
4187 let mut gfx = self.gfx.borrow_mut();
4188 gfx.fog_color = (r << 16) | (g << 8) | b;
4189 gfx.fog_start = start;
4190 gfx.fog_end = end;
4191 return Ok(Value::Unit);
4192 },
4193
4194 "วาดสามเหลี่ยม3มิติ" | "draw_triangle_3d" | "triangle3d" =>
4198 {
4199 let ax = self.arg_num(&args, 0, 0.0)? as f32;
4200 let ay = self.arg_num(&args, 1, 0.0)? as f32;
4201 let az = self.arg_num(&args, 2, 0.0)? as f32;
4202 let bx = self.arg_num(&args, 3, 0.0)? as f32;
4203 let by = self.arg_num(&args, 4, 0.0)? as f32;
4204 let bz = self.arg_num(&args, 5, 0.0)? as f32;
4205 let cx = self.arg_num(&args, 6, 0.0)? as f32;
4206 let cy = self.arg_num(&args, 7, 0.0)? as f32;
4207 let cz = self.arg_num(&args, 8, 0.0)? as f32;
4208
4209 let mut gfx = self.gfx.borrow_mut();
4210
4211 if gfx.mesh_capture.is_some() {
4213 let col = gfx.color;
4214 gfx.mesh_capture
4215 .as_mut()
4216 .unwrap()
4217 .push(([ax, ay, az, bx, by, bz, cx, cy, cz], col));
4218 return Ok(Value::Unit);
4219 }
4220
4221 gfx.submit_triangle(ax, ay, az, bx, by, bz, cx, cy, cz);
4222 return Ok(Value::Unit);
4223 },
4224
4225 "เริ่มอบเมช" | "mesh_bake_begin" =>
4227 {
4228 self.gfx.borrow_mut().mesh_capture = Some(Vec::new());
4229 return Ok(Value::Unit);
4230 },
4231
4232 "เมชแคชรับ" | "mesh_cache_get" =>
4234 {
4235 let key = self.arg_num(&args, 0, 0.0)? as i64;
4236 let h = self.gfx.borrow().mesh_cache.get(&key).copied();
4237 return Ok(Value::Number(h.map(|x| x as f64).unwrap_or(-1.0)));
4238 },
4239
4240 "เมชแคชตั้ง" | "mesh_cache_put" =>
4242 {
4243 let key = self.arg_num(&args, 0, 0.0)? as i64;
4244 let h = self.arg_num(&args, 1, 0.0)? as usize;
4245 let mut gfx = self.gfx.borrow_mut();
4246 const CAP: usize = 256;
4247 if gfx.mesh_cache.len() >= CAP {
4248 let evict: Vec<usize> = gfx.mesh_cache.values().copied().collect();
4249 gfx.mesh_cache.clear();
4250 for id in evict {
4251 if id < gfx.meshes.len() {
4252 gfx.meshes[id].clear();
4253 gfx.mesh_free.push(id);
4254 }
4255 }
4256 }
4257 gfx.mesh_cache.insert(key, h);
4258 return Ok(Value::Unit);
4259 },
4260
4261 "เมชแคชล้าง" | "mesh_cache_clear" =>
4263 {
4264 let mut gfx = self.gfx.borrow_mut();
4265 let evict: Vec<usize> = gfx.mesh_cache.values().copied().collect();
4266 gfx.mesh_cache.clear();
4267 for id in evict {
4268 if id < gfx.meshes.len() {
4269 gfx.meshes[id].clear();
4270 gfx.mesh_free.push(id);
4271 }
4272 }
4273 return Ok(Value::Unit);
4274 },
4275
4276 "จบอบเมช" | "mesh_bake_end" =>
4278 {
4279 let mut gfx = self.gfx.borrow_mut();
4280 let tris = gfx.mesh_capture.take().unwrap_or_default();
4281 let id = gfx.mesh_register(tris);
4282 return Ok(Value::Number(id as f64));
4283 },
4284
4285 "วาดอบเมช" | "mesh_bake_draw" | "วาดอบเมชสี" | "mesh_bake_draw_col" =>
4289 {
4290 let baked_col = matches!(name, "วาดอบเมชสี" | "mesh_bake_draw_col");
4291 let id = self.arg_num(&args, 0, 0.0)? as usize;
4292 let ox = self.arg_num(&args, 1, 0.0)? as f32;
4293 let oy = self.arg_num(&args, 2, 0.0)? as f32;
4294 let oz = self.arg_num(&args, 3, 0.0)? as f32;
4295 let rx = self.arg_num(&args, 4, 1.0)? as f32;
4296 let ry = self.arg_num(&args, 5, 0.0)? as f32;
4297 let rz = self.arg_num(&args, 6, 0.0)? as f32;
4298 let ux = self.arg_num(&args, 7, 0.0)? as f32;
4299 let uy = self.arg_num(&args, 8, 1.0)? as f32;
4300 let uz = self.arg_num(&args, 9, 0.0)? as f32;
4301 let s = self.arg_num(&args, 10, 1.0)? as f32;
4302 self.gfx.borrow_mut().mesh_draw(id, ox, oy, oz, rx, ry, rz, ux, uy, uz, s, baked_col);
4303 return Ok(Value::Unit);
4304 },
4305
4306 "draw_quad_3d" | "quad3d" | "วาดสี่เหลี่ยม3มิติ"
4310 | "draw_pent_3d" | "pent3d" | "วาดห้าเหลี่ยม3มิติ"
4311 | "draw_hex_3d" | "hex3d" | "วาดหกเหลี่ยม3มิติ"
4312 | "draw_polygon_3d" | "polygon3d" | "วาดรูปหลายเหลี่ยม3มิติ" =>
4313 {
4314 let mut wxs: [f32; 8] = [0.0; 8];
4316 let mut wys: [f32; 8] = [0.0; 8];
4317 let mut wzs: [f32; 8] = [0.0; 8];
4318 let n_verts;
4319
4320 if args.len() == 1 {
4321 let list = match &args[0] {
4323 Value::List(l) => l.clone(),
4324 _ => return Err(EvalErr::from("draw_polygon_3d: expected list".to_string())),
4325 };
4326 let coords: Vec<f32> = list.iter()
4327 .map(|v| match v { Value::Number(n) => *n as f32, _ => 0.0 })
4328 .collect();
4329 n_verts = (coords.len() / 3).min(8);
4330 for i in 0..n_verts {
4331 wxs[i] = coords[i * 3];
4332 wys[i] = coords[i * 3 + 1];
4333 wzs[i] = coords[i * 3 + 2];
4334 }
4335 } else {
4336 n_verts = (args.len() / 3).min(8);
4338 for i in 0..n_verts {
4339 wxs[i] = self.arg_num(&args, i * 3, 0.0)? as f32;
4340 wys[i] = self.arg_num(&args, i * 3 + 1, 0.0)? as f32;
4341 wzs[i] = self.arg_num(&args, i * 3 + 2, 0.0)? as f32;
4342 }
4343 }
4344 if n_verts < 3 { return Ok(Value::Unit); }
4345
4346 let mut gfx = self.gfx.borrow_mut();
4347
4348 let normal = crate::gfx::poly::face_normal(
4350 wxs[0], wys[0], wzs[0],
4351 wxs[1], wys[1], wzs[1],
4352 wxs[2], wys[2], wzs[2],
4353 );
4354
4355 let mut wcs: [u32; 8] = [0; 8];
4357 if gfx.flat_shade {
4358 let c = gfx.color;
4359 for i in 0..n_verts { wcs[i] = c; }
4360 } else if let Some(ref mat) = gfx.material.clone() {
4361 let cam = [gfx.camera.cx, gfx.camera.cy, gfx.camera.zdist];
4362 let lights: Vec<_> = gfx.lights.clone();
4363 let ambient = gfx.ambient;
4364 for i in 0..n_verts {
4365 let v = [wxs[i], wys[i], wzs[i]];
4366 let vd = [cam[0]-v[0], cam[1]-v[1], cam[2]-v[2]];
4367 wcs[i] = crate::gfx::material::shade(mat, normal, vd, v, &lights, ambient);
4368 }
4369 } else {
4370 let base = gfx.color;
4371 let lights: Vec<_> = gfx.lights.clone();
4372 let ambient = gfx.ambient;
4373 for i in 0..n_verts {
4374 wcs[i] = crate::gfx::light::compute_lit_color_linear(
4375 base, normal, [wxs[i], wys[i], wzs[i]], &lights, ambient,
4376 );
4377 }
4378 }
4379
4380 let near = -gfx.camera.zdist + 0.05;
4382 let mut clip_in: [(f32,f32,f32,f32,u32); crate::gfx::poly::MAX_CLIP_VERTS]
4383 = [(0.0,0.0,0.0,0.0,0); crate::gfx::poly::MAX_CLIP_VERTS];
4384 for i in 0..n_verts {
4385 let d = gfx.camera.depth(wxs[i], wys[i], wzs[i]);
4386 clip_in[i] = (wxs[i], wys[i], wzs[i], d, wcs[i]);
4387 }
4388 let mut clip_out: [(f32,f32,f32,f32,u32); crate::gfx::poly::MAX_CLIP_VERTS]
4389 = [(0.0,0.0,0.0,0.0,0); crate::gfx::poly::MAX_CLIP_VERTS];
4390 let pn = crate::gfx::poly::clip_near(&clip_in, n_verts, near, &mut clip_out);
4391 if pn < 3 { return Ok(Value::Unit); }
4392
4393 let mut proj: [(f32,f32,f32,u32); crate::gfx::poly::MAX_CLIP_VERTS]
4395 = [(0.0,0.0,0.0,0); crate::gfx::poly::MAX_CLIP_VERTS];
4396 for i in 0..pn {
4397 let (sx, sy, sz) = gfx.camera.project(clip_out[i].0, clip_out[i].1, clip_out[i].2);
4398 let fc = gfx.fog_apply(clip_out[i].4, sz);
4399 proj[i] = (sx, sy, sz, fc);
4400 }
4401
4402 crate::gfx::poly::fan_emit_proj(&proj, pn, |x0,y0,z0,c0, x1,y1,z1,c1, x2,y2,z2,c2| {
4404 gfx.depth_queue.push_triangle_g_zv(
4405 x0,y0,z0,c0, x1,y1,z1,c1, x2,y2,z2,c2, 3,
4406 );
4407 });
4408 return Ok(Value::Unit);
4409 },
4410
4411 "วาดเส้น3มิติ" | "draw_line_3d" | "line3d" | "画3D线" | "3D線描く" | "3D선그리기" =>
4415 {
4416 let ax = self.arg_num(&args, 0, 0.0)? as f32;
4417 let ay = self.arg_num(&args, 1, 0.0)? as f32;
4418 let az = self.arg_num(&args, 2, 0.0)? as f32;
4419 let bx = self.arg_num(&args, 3, 0.0)? as f32;
4420 let by = self.arg_num(&args, 4, 0.0)? as f32;
4421 let bz = self.arg_num(&args, 5, 0.0)? as f32;
4422
4423 let mut gfx = self.gfx.borrow_mut();
4424 let color = gfx.color;
4425 let near = -gfx.camera.zdist + 0.05;
4427 let mut lax = ax;
4428 let mut lay = ay;
4429 let mut laz = az;
4430 let mut lbx = bx;
4431 let mut lby = by;
4432 let mut lbz = bz;
4433 let da_raw = gfx.camera.depth(lax, lay, laz);
4434 let db_raw = gfx.camera.depth(lbx, lby, lbz);
4435 if da_raw <= near && db_raw <= near {
4436 return Ok(Value::Unit);
4437 }
4438 if da_raw <= near {
4439 let t = (near - da_raw) / (db_raw - da_raw);
4440 lax += t * (lbx - lax);
4441 lay += t * (lby - lay);
4442 laz += t * (lbz - laz);
4443 } else if db_raw <= near {
4444 let t = (near - da_raw) / (db_raw - da_raw);
4445 lbx = lax + t * (lbx - lax);
4446 lby = lay + t * (lby - lay);
4447 lbz = laz + t * (lbz - laz);
4448 }
4449 if !gfx.edge_set.try_insert(lax, lay, laz, lbx, lby, lbz) {
4451 return Ok(Value::Unit);
4452 }
4453 let (sax, say, da) = gfx.camera.project(lax, lay, laz);
4454 let (sbx, sby, db) = gfx.camera.project(lbx, lby, lbz);
4455 let depth = (da + db) / 2.0;
4456 let color = gfx.fog_apply(color, depth);
4457 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
4458 return Ok(Value::Unit);
4459 },
4460
4461 #[cfg(not(target_arch = "wasm32"))]
4471 "orb_shell" | "球壳" | "オーブ殻" | "오브껍질" | "เปลือกทรงกลม" =>
4472 {
4473 let cx = self.arg_num(&args, 0, 0.)? as f32;
4474 let cy = self.arg_num(&args, 1, 0.)? as f32;
4475 let cz = self.arg_num(&args, 2, 0.)? as f32;
4476 let radius = self.arg_num(&args, 3, 1.0)? as f32;
4477 let ry = self.arg_num(&args, 4, 0.)? as f32;
4478 let rx = self.arg_num(&args, 5, 0.)? as f32;
4479 let density = (self.arg_num(&args, 6, 10.)? as i32).clamp(1, 48);
4480 let tr = (self.arg_num(&args, 7, 230.)? as f32).clamp(0., 255.);
4481 let tg = (self.arg_num(&args, 8, 230.)? as f32).clamp(0., 255.);
4482 let tb = (self.arg_num(&args, 9, 235.)? as f32).clamp(0., 255.);
4483 let (cyr, syr) = (ry.cos(), ry.sin());
4484 let (cxr, sxr) = (rx.cos(), rx.sin());
4485 let tau = std::f32::consts::TAU;
4486 let pi = std::f32::consts::PI;
4487 let turns = 6.0_f32; let nseg = 96; let inv_r = if radius.abs() > 1e-5 {
4490 1.0 / radius
4491 } else {
4492 0.0
4493 };
4494 let pt = |u: f32, theta0: f32, dir: f32| -> ([f32; 3], f32) {
4497 let phi = pi * u; let th = dir * turns * tau * u + theta0;
4499 let (mut x, y, mut z) = (
4500 phi.sin() * th.cos() * radius,
4501 phi.cos() * radius,
4502 phi.sin() * th.sin() * radius,
4503 );
4504 let x1 = x * cyr + z * syr; let z1 = -x * syr + z * cyr;
4506 x = x1;
4507 z = z1;
4508 let y2 = y * cxr - z * sxr; let z2 = y * sxr + z * cxr;
4510 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.0, 1.0);
4512 ([cx + x, cy + y2, cz + z2], facing)
4513 };
4514 let mut gfx = self.gfx.borrow_mut();
4515 let near = -gfx.camera.zdist + 0.05;
4516 let seg = |gfx: &mut crate::gfx::GfxState, a: [f32; 3], b: [f32; 3], lum: f32| {
4518 let (mut lax, mut lay, mut laz) = (a[0], a[1], a[2]);
4519 let (mut lbx, mut lby, mut lbz) = (b[0], b[1], b[2]);
4520 let da = gfx.camera.depth(lax, lay, laz);
4521 let db = gfx.camera.depth(lbx, lby, lbz);
4522 if da <= near && db <= near {
4523 return;
4524 }
4525 if da <= near {
4526 let t = (near - da) / (db - da);
4527 lax += t * (lbx - lax);
4528 lay += t * (lby - lay);
4529 laz += t * (lbz - laz);
4530 } else if db <= near {
4531 let t = (near - da) / (db - da);
4532 lbx = lax + t * (lbx - lax);
4533 lby = lay + t * (lby - lay);
4534 lbz = laz + t * (lbz - laz);
4535 }
4536 let (sax, say, da2) = gfx.camera.project(lax, lay, laz);
4537 let (sbx, sby, db2) = gfx.camera.project(lbx, lby, lbz);
4538 let l = (0.12 + 0.88 * lum).clamp(0.0, 1.0);
4540 let cr = (tr * l) as u32;
4541 let cg = (tg * l) as u32;
4542 let cb = (tb * l) as u32;
4543 let color = (cr << 16) | (cg << 8) | cb;
4544 gfx.depth_queue
4545 .push_line((da2 + db2) * 0.5, color, sax, say, sbx, sby);
4546 };
4547 for &dir in &[1.0_f32, -1.0_f32] {
4549 for s in 0..density {
4550 let theta0 = s as f32 * tau / density as f32;
4551 let mut prev = pt(0.0, theta0, dir);
4552 for k in 1..=nseg {
4553 let cur = pt(k as f32 / nseg as f32, theta0, dir);
4554 seg(&mut gfx, prev.0, cur.0, (prev.1 + cur.1) * 0.5);
4555 prev = cur;
4556 }
4557 }
4558 }
4559 return Ok(Value::Unit);
4560 },
4561
4562 #[cfg(not(target_arch = "wasm32"))]
4570 "orb_particles" | "球内粒子" | "オーブ粒子" | "오브입자" | "อนุภาคทรงกลม" =>
4571 {
4572 let cx = self.arg_num(&args, 0, 0.)? as f32;
4573 let cy = self.arg_num(&args, 1, 0.)? as f32;
4574 let cz = self.arg_num(&args, 2, 0.)? as f32;
4575 let radius = self.arg_num(&args, 3, 1.0)? as f32;
4576 let count = (self.arg_num(&args, 4, 160.)? as i32).clamp(1, 4000);
4577 let t = self.arg_num(&args, 5, 0.)? as f32;
4578 let tr = (self.arg_num(&args, 6, 255.)? as f32).clamp(0., 255.);
4579 let tg = (self.arg_num(&args, 7, 255.)? as f32).clamp(0., 255.);
4580 let tb = (self.arg_num(&args, 8, 255.)? as f32).clamp(0., 255.);
4581 let inv_r = if radius.abs() > 1e-5 {
4582 1.0 / radius
4583 } else {
4584 0.0
4585 };
4586 let h = |mut x: u32| -> f32 {
4588 x = x.wrapping_mul(747796405).wrapping_add(2891336453);
4589 x = ((x >> ((x >> 28).wrapping_add(4))) ^ x).wrapping_mul(277803737);
4590 (((x >> 22) ^ x) & 0xFFFFFF) as f32 / 16_777_216.0
4591 };
4592 let tau = std::f32::consts::TAU;
4593 let (cyr, syr) = ((t * 0.5).cos(), (t * 0.5).sin());
4595 let (cxr, sxr) = ((t * 0.23).cos(), (t * 0.23).sin());
4596 let mut gfx = self.gfx.borrow_mut();
4597 let near = -gfx.camera.zdist + 0.05;
4598 let (sw, sh) = (gfx.width as i32, gfx.height as i32);
4599 for i in 0..count {
4600 let i = i as u32;
4601 let u = h(i.wrapping_mul(3) + 1);
4603 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) = (
4607 rr * ph.sin() * th.cos(),
4608 rr * ph.cos(),
4609 rr * ph.sin() * th.sin(),
4610 );
4611 let x1 = x * cyr + z * syr;
4613 let z1 = -x * syr + z * cyr;
4614 x = x1;
4615 z = z1;
4616 let y2 = y * cxr - z * sxr;
4617 let z2 = y * sxr + z * cxr;
4618 let (wx, wy, wz) = (cx + x, cy + y2, cz + z2);
4619 if gfx.camera.depth(wx, wy, wz) <= near {
4620 continue;
4621 }
4622 let (sx, sy, dep) = gfx.camera.project(wx, wy, wz);
4623 let sxi = sx as i32;
4624 let syi = sy as i32;
4625 if sxi < 0 || syi < 0 || sxi >= sw || syi >= sh {
4626 continue;
4627 }
4628 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.15, 1.0);
4630 let l = facing;
4631 let cr = (tr * l) as u32;
4632 let cg = (tg * l) as u32;
4633 let cb = (tb * l) as u32;
4634 let color = (cr << 16) | (cg << 8) | cb;
4635 let len = if facing > 0.7 { 1.0 } else { 0.0 };
4637 gfx.depth_queue.push_line(dep, color, sx, sy, sx + len, sy);
4638 }
4639 return Ok(Value::Unit);
4640 },
4641
4642 "project_3d" | "投影3D" | "3D投影" | "3D투영" | "ฉาย3มิติ" => {
4646 let x = self.arg_num(&args, 0, 0.0)? as f32;
4647 let y = self.arg_num(&args, 1, 0.0)? as f32;
4648 let z = self.arg_num(&args, 2, 0.0)? as f32;
4649 let gfx = self.gfx.borrow();
4650 let near = -gfx.camera.zdist + 0.05;
4651 let d = gfx.camera.depth(x, y, z);
4652 if d <= near {
4653 return Ok(Value::List(Rc::new(vec![
4654 Value::Number(-99999.0),
4655 Value::Number(-99999.0),
4656 Value::Number(d as f64),
4657 ])));
4658 }
4659 let (sx, sy, depth) = gfx.camera.project(x, y, z);
4660 return Ok(Value::List(Rc::new(vec![
4661 Value::Number(sx as f64),
4662 Value::Number(sy as f64),
4663 Value::Number(depth as f64),
4664 ])));
4665 },
4666 #[cfg(not(target_arch = "wasm32"))]
4669 "draw_poly" | "填充多边形" | "ポリゴン塗り" | "다각형채우기" | "เติมรูปหลายเหลี่ยม" =>
4670 {
4671 let mut pts: Vec<[f32; 2]> = Vec::new();
4672 if let Some(Value::List(v)) = args.first() {
4673 let mut i = 0;
4674 while i + 1 < v.len() {
4675 let x = self.to_number(&v[i]).unwrap_or(0.0) as f32;
4676 let y = self.to_number(&v[i + 1]).unwrap_or(0.0) as f32;
4677 pts.push([x, y]);
4678 i += 2;
4679 }
4680 }
4681 if pts.len() >= 3 {
4682 if pts[0] != pts[pts.len() - 1] {
4683 let p0 = pts[0];
4684 pts.push(p0);
4685 } let mut gfx = self.gfx.borrow_mut();
4687 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
4688 crate::gfx::raster::fill_contours_aa(
4689 &mut gfx.buffer,
4690 w,
4691 h,
4692 color,
4693 add,
4694 std::slice::from_ref(&pts),
4695 );
4696 }
4697 return Ok(Value::Unit);
4698 },
4699
4700 "vtex_grid" | "ลายตาราง" | "纹格" | "格子模様" | "격자무늬" =>
4709 {
4710 let cx = self.arg_num(&args, 0, 0.)? as f32;
4711 let cy = self.arg_num(&args, 1, 0.)? as f32;
4712 let cz = self.arg_num(&args, 2, 0.)? as f32;
4713 let ux = self.arg_num(&args, 3, 1.)? as f32;
4714 let uy = self.arg_num(&args, 4, 0.)? as f32;
4715 let uz = self.arg_num(&args, 5, 0.)? as f32;
4716 let vx = self.arg_num(&args, 6, 0.)? as f32;
4717 let vy = self.arg_num(&args, 7, 0.)? as f32;
4718 let vz = self.arg_num(&args, 8, 1.)? as f32;
4719 let cols = self.arg_num(&args, 9, 10.)? as usize;
4720 let rows = self.arg_num(&args, 10, 10.)? as usize;
4721 let cw = self.arg_num(&args, 11, 1.)? as f32;
4722 let ch = self.arg_num(&args, 12, 1.)? as f32;
4723 let fr = self.arg_num(&args, 13, 0.)? as f32;
4724 let hue = self.arg_num(&args, 14, 0.)? as f32;
4725 let mut gfx = self.gfx.borrow_mut();
4726 let cam = gfx.camera.clone();
4727 crate::gfx::vtex::draw_grid(
4728 &mut gfx.depth_queue,
4729 &cam,
4730 cx,
4731 cy,
4732 cz,
4733 ux,
4734 uy,
4735 uz,
4736 vx,
4737 vy,
4738 vz,
4739 cols,
4740 rows,
4741 cw,
4742 ch,
4743 fr,
4744 hue,
4745 );
4746 return Ok(Value::Unit);
4747 },
4748
4749 "vtex_rings" | "ลายวงซ้อน" | "纹环" | "同心円" | "동심원" => {
4751 let cx = self.arg_num(&args, 0, 0.)? as f32;
4752 let cy = self.arg_num(&args, 1, 0.)? as f32;
4753 let cz = self.arg_num(&args, 2, 0.)? as f32;
4754 let ux = self.arg_num(&args, 3, 1.)? as f32;
4755 let uy = self.arg_num(&args, 4, 0.)? as f32;
4756 let uz = self.arg_num(&args, 5, 0.)? as f32;
4757 let vx = self.arg_num(&args, 6, 0.)? as f32;
4758 let vy = self.arg_num(&args, 7, 0.)? as f32;
4759 let vz = self.arg_num(&args, 8, 1.)? as f32;
4760 let nr = self.arg_num(&args, 9, 6.)? as usize;
4761 let ns = self.arg_num(&args, 10, 6.)? as usize;
4762 let mr = self.arg_num(&args, 11, 3.)? as f32;
4763 let tw = self.arg_num(&args, 12, 0.)? as f32;
4764 let fr = self.arg_num(&args, 13, 0.)? as f32;
4765 let hue = self.arg_num(&args, 14, 0.)? as f32;
4766 let mut gfx = self.gfx.borrow_mut();
4767 let cam = gfx.camera.clone();
4768 crate::gfx::vtex::draw_rings(
4769 &mut gfx.depth_queue,
4770 &cam,
4771 cx,
4772 cy,
4773 cz,
4774 ux,
4775 uy,
4776 uz,
4777 vx,
4778 vy,
4779 vz,
4780 nr,
4781 ns,
4782 mr,
4783 tw,
4784 fr,
4785 hue,
4786 );
4787 return Ok(Value::Unit);
4788 },
4789
4790 "vtex_star" | "ลายดาว" | "纹星" | "星模様" | "별무늬" => {
4792 let cx = self.arg_num(&args, 0, 0.)? as f32;
4793 let cy = self.arg_num(&args, 1, 0.)? as f32;
4794 let cz = self.arg_num(&args, 2, 0.)? as f32;
4795 let ux = self.arg_num(&args, 3, 1.)? as f32;
4796 let uy = self.arg_num(&args, 4, 0.)? as f32;
4797 let uz = self.arg_num(&args, 5, 0.)? as f32;
4798 let vx = self.arg_num(&args, 6, 0.)? as f32;
4799 let vy = self.arg_num(&args, 7, 0.)? as f32;
4800 let vz = self.arg_num(&args, 8, 1.)? as f32;
4801 let np = self.arg_num(&args, 9, 6.)? as usize;
4802 let ro = self.arg_num(&args, 10, 2.)? as f32;
4803 let ri = self.arg_num(&args, 11, 1.)? as f32;
4804 let rs = self.arg_num(&args, 12, 0.01)? as f32;
4805 let fr = self.arg_num(&args, 13, 0.)? as f32;
4806 let hue = self.arg_num(&args, 14, 0.)? as f32;
4807 let mut gfx = self.gfx.borrow_mut();
4808 let cam = gfx.camera.clone();
4809 crate::gfx::vtex::draw_star(
4810 &mut gfx.depth_queue,
4811 &cam,
4812 cx,
4813 cy,
4814 cz,
4815 ux,
4816 uy,
4817 uz,
4818 vx,
4819 vy,
4820 vz,
4821 np,
4822 ro,
4823 ri,
4824 rs,
4825 fr,
4826 hue,
4827 );
4828 return Ok(Value::Unit);
4829 },
4830
4831 "vtex_spiral" | "ลายเกลียว" | "纹螺" | "螺旋" | "나선" => {
4833 let cx = self.arg_num(&args, 0, 0.)? as f32;
4834 let cy = self.arg_num(&args, 1, 0.)? as f32;
4835 let cz = self.arg_num(&args, 2, 0.)? as f32;
4836 let ux = self.arg_num(&args, 3, 1.)? as f32;
4837 let uy = self.arg_num(&args, 4, 0.)? as f32;
4838 let uz = self.arg_num(&args, 5, 0.)? as f32;
4839 let vx = self.arg_num(&args, 6, 0.)? as f32;
4840 let vy = self.arg_num(&args, 7, 0.)? as f32;
4841 let vz = self.arg_num(&args, 8, 1.)? as f32;
4842 let nt = self.arg_num(&args, 9, 3.)? as f32;
4843 let mr = self.arg_num(&args, 10, 3.)? as f32;
4844 let st = self.arg_num(&args, 11, 120.)? as usize;
4845 let fr = self.arg_num(&args, 12, 0.)? as f32;
4846 let hue = self.arg_num(&args, 13, 0.)? as f32;
4847 let mut gfx = self.gfx.borrow_mut();
4848 let cam = gfx.camera.clone();
4849 crate::gfx::vtex::draw_spiral(
4850 &mut gfx.depth_queue,
4851 &cam,
4852 cx,
4853 cy,
4854 cz,
4855 ux,
4856 uy,
4857 uz,
4858 vx,
4859 vy,
4860 vz,
4861 nt,
4862 mr,
4863 st,
4864 fr,
4865 hue,
4866 );
4867 return Ok(Value::Unit);
4868 },
4869
4870 "vtex_flower" | "ลายดอก" | "纹花" | "花模様" | "꽃무늬" => {
4872 let cx = self.arg_num(&args, 0, 0.)? as f32;
4873 let cy = self.arg_num(&args, 1, 0.)? as f32;
4874 let cz = self.arg_num(&args, 2, 0.)? as f32;
4875 let ux = self.arg_num(&args, 3, 1.)? as f32;
4876 let uy = self.arg_num(&args, 4, 0.)? as f32;
4877 let uz = self.arg_num(&args, 5, 0.)? as f32;
4878 let vx = self.arg_num(&args, 6, 0.)? as f32;
4879 let vy = self.arg_num(&args, 7, 0.)? as f32;
4880 let vz = self.arg_num(&args, 8, 1.)? as f32;
4881 let r = self.arg_num(&args, 9, 1.)? as f32;
4882 let ns = self.arg_num(&args, 10, 24.)? as usize;
4883 let fr = self.arg_num(&args, 11, 0.)? as f32;
4884 let hue = self.arg_num(&args, 12, 0.)? as f32;
4885 let mut gfx = self.gfx.borrow_mut();
4886 let cam = gfx.camera.clone();
4887 crate::gfx::vtex::draw_flower(
4888 &mut gfx.depth_queue,
4889 &cam,
4890 cx,
4891 cy,
4892 cz,
4893 ux,
4894 uy,
4895 uz,
4896 vx,
4897 vy,
4898 vz,
4899 r,
4900 ns,
4901 fr,
4902 hue,
4903 );
4904 return Ok(Value::Unit);
4905 },
4906
4907 "vtex_letter_rain" | "ลายอักษรไหล" | "纹字雨" | "文字雨" | "글자비" =>
4909 {
4910 let cx = self.arg_num(&args, 0, 0.)? as f32;
4911 let cy = self.arg_num(&args, 1, 0.)? as f32;
4912 let cz = self.arg_num(&args, 2, 0.)? as f32;
4913 let ux = self.arg_num(&args, 3, 1.)? as f32;
4914 let uy = self.arg_num(&args, 4, 0.)? as f32;
4915 let uz = self.arg_num(&args, 5, 0.)? as f32;
4916 let vx = self.arg_num(&args, 6, 0.)? as f32;
4917 let vy = self.arg_num(&args, 7, 0.)? as f32;
4918 let vz = self.arg_num(&args, 8, 1.)? as f32;
4919 let nc = self.arg_num(&args, 9, 16.)? as usize;
4920 let nv = self.arg_num(&args, 10, 14.)? as usize;
4921 let cw = self.arg_num(&args, 11, 0.65)? as f32;
4922 let rh = self.arg_num(&args, 12, 0.60)? as f32;
4923 let sp = self.arg_num(&args, 13, 0.025)? as f32;
4924 let fr = self.arg_num(&args, 14, 0.)? as f32;
4925 let hue = self.arg_num(&args, 15, 0.)? as f32;
4926 let mut gfx = self.gfx.borrow_mut();
4927 let cam = gfx.camera.clone();
4928 crate::gfx::vtex::draw_letter_rain(
4929 &mut gfx.depth_queue,
4930 &cam,
4931 cx,
4932 cy,
4933 cz,
4934 ux,
4935 uy,
4936 uz,
4937 vx,
4938 vy,
4939 vz,
4940 nc,
4941 nv,
4942 cw,
4943 rh,
4944 sp,
4945 fr,
4946 hue,
4947 );
4948 return Ok(Value::Unit);
4949 },
4950
4951 "vtex_hyperbolic_uv" | "ลายไฮเพอร์โบลิก" | "纹曲面" | "双曲線" | "쌍곡선" =>
4953 {
4954 let cx = self.arg_num(&args, 0, 0.)? as f32;
4955 let cy = self.arg_num(&args, 1, 0.)? as f32;
4956 let cz = self.arg_num(&args, 2, 0.)? as f32;
4957 let ux = self.arg_num(&args, 3, 1.)? as f32;
4958 let uy = self.arg_num(&args, 4, 0.)? as f32;
4959 let uz = self.arg_num(&args, 5, 0.)? as f32;
4960 let vx = self.arg_num(&args, 6, 0.)? as f32;
4961 let vy = self.arg_num(&args, 7, 0.)? as f32;
4962 let vz = self.arg_num(&args, 8, 1.)? as f32;
4963 let mr = self.arg_num(&args, 9, 5.)? as f32;
4964 let nc = self.arg_num(&args, 10, 12.)? as usize;
4965 let nr = self.arg_num(&args, 11, 18.)? as usize;
4966 let fr = self.arg_num(&args, 12, 0.)? as f32;
4967 let hue = self.arg_num(&args, 13, 0.)? as f32;
4968 let mut gfx = self.gfx.borrow_mut();
4969 let cam = gfx.camera.clone();
4970 crate::gfx::vtex::draw_hyperbolic_uv(
4971 &mut gfx.depth_queue,
4972 &cam,
4973 cx,
4974 cy,
4975 cz,
4976 ux,
4977 uy,
4978 uz,
4979 vx,
4980 vy,
4981 vz,
4982 mr,
4983 nc,
4984 nr,
4985 fr,
4986 hue,
4987 );
4988 return Ok(Value::Unit);
4989 },
4990
4991 "vtex_halftone" | "ลายจุด" | "纹半调" | "網点模様" | "망점" => {
4993 let cx = self.arg_num(&args, 0, 0.)? as f32;
4994 let cy = self.arg_num(&args, 1, 0.)? as f32;
4995 let cz = self.arg_num(&args, 2, 0.)? as f32;
4996 let ux = self.arg_num(&args, 3, 1.)? as f32;
4997 let uy = self.arg_num(&args, 4, 0.)? as f32;
4998 let uz = self.arg_num(&args, 5, 0.)? as f32;
4999 let vx = self.arg_num(&args, 6, 0.)? as f32;
5000 let vy = self.arg_num(&args, 7, 0.)? as f32;
5001 let vz = self.arg_num(&args, 8, 1.)? as f32;
5002 let cols = self.arg_num(&args, 9, 16.)? as usize;
5003 let rows = self.arg_num(&args, 10, 12.)? as usize;
5004 let cw = self.arg_num(&args, 11, 0.5)? as f32;
5005 let ch = self.arg_num(&args, 12, 0.5)? as f32;
5006 let dens = self.arg_num(&args, 13, 0.4)? as f32;
5007 let fr = self.arg_num(&args, 14, 0.)? as f32;
5008 let hue = self.arg_num(&args, 15, 0.)? as f32;
5009 let mut gfx = self.gfx.borrow_mut();
5010 let cam = gfx.camera.clone();
5011 crate::gfx::vtex::draw_halftone(
5012 &mut gfx.depth_queue,
5013 &cam,
5014 cx,
5015 cy,
5016 cz,
5017 ux,
5018 uy,
5019 uz,
5020 vx,
5021 vy,
5022 vz,
5023 cols,
5024 rows,
5025 cw,
5026 ch,
5027 dens,
5028 fr,
5029 hue,
5030 );
5031 return Ok(Value::Unit);
5032 },
5033
5034 "vtex_tessellated" | "ลายตาข่าย" | "纹镶嵌" | "網目模様" | "격자망" =>
5036 {
5037 let cx = self.arg_num(&args, 0, 0.)? as f32;
5038 let cy = self.arg_num(&args, 1, 0.)? as f32;
5039 let cz = self.arg_num(&args, 2, 0.)? as f32;
5040 let ux = self.arg_num(&args, 3, 1.)? as f32;
5041 let uy = self.arg_num(&args, 4, 0.)? as f32;
5042 let uz = self.arg_num(&args, 5, 0.)? as f32;
5043 let vx = self.arg_num(&args, 6, 0.)? as f32;
5044 let vy = self.arg_num(&args, 7, 0.)? as f32;
5045 let vz = self.arg_num(&args, 8, 1.)? as f32;
5046 let cols = self.arg_num(&args, 9, 14.)? as usize;
5047 let rows = self.arg_num(&args, 10, 10.)? as usize;
5048 let cell = self.arg_num(&args, 11, 0.6)? as f32;
5049 let amp = self.arg_num(&args, 12, 0.25)? as f32;
5050 let freq = self.arg_num(&args, 13, 4.)? as f32;
5051 let fr = self.arg_num(&args, 14, 0.)? as f32;
5052 let hue = self.arg_num(&args, 15, 0.)? as f32;
5053 let mut gfx = self.gfx.borrow_mut();
5054 let cam = gfx.camera.clone();
5055 crate::gfx::vtex::draw_tessellated(
5056 &mut gfx.depth_queue,
5057 &cam,
5058 cx,
5059 cy,
5060 cz,
5061 ux,
5062 uy,
5063 uz,
5064 vx,
5065 vy,
5066 vz,
5067 cols,
5068 rows,
5069 cell,
5070 amp,
5071 freq,
5072 fr,
5073 hue,
5074 );
5075 return Ok(Value::Unit);
5076 },
5077
5078 "vtex_lotus" | "ลายดอกบัว" | "纹莲" | "蓮模様" | "연꽃무늬" =>
5080 {
5081 let cx = self.arg_num(&args, 0, 0.)? as f32;
5082 let cy = self.arg_num(&args, 1, 0.)? as f32;
5083 let cz = self.arg_num(&args, 2, 0.)? as f32;
5084 let ux = self.arg_num(&args, 3, 1.)? as f32;
5085 let uy = self.arg_num(&args, 4, 0.)? as f32;
5086 let uz = self.arg_num(&args, 5, 0.)? as f32;
5087 let vx = self.arg_num(&args, 6, 0.)? as f32;
5088 let vy = self.arg_num(&args, 7, 0.)? as f32;
5089 let vz = self.arg_num(&args, 8, 1.)? as f32;
5090 let ri = self.arg_num(&args, 9, 1.)? as f32;
5091 let ro = self.arg_num(&args, 10, 2.)? as f32;
5092 let np = self.arg_num(&args, 11, 12.)? as usize;
5093 let fr = self.arg_num(&args, 12, 0.)? as f32;
5094 let hue = self.arg_num(&args, 13, 0.)? as f32;
5095 let mut gfx = self.gfx.borrow_mut();
5096 let cam = gfx.camera.clone();
5097 crate::gfx::vtex::draw_lotus(
5098 &mut gfx.depth_queue,
5099 &cam,
5100 cx,
5101 cy,
5102 cz,
5103 ux,
5104 uy,
5105 uz,
5106 vx,
5107 vy,
5108 vz,
5109 ri,
5110 ro,
5111 np,
5112 fr,
5113 hue,
5114 );
5115 return Ok(Value::Unit);
5116 },
5117
5118 "vtex_chakra" | "ลายจักร" | "纹轮" | "輪模様" | "바퀴무늬" => {
5120 let cx = self.arg_num(&args, 0, 0.)? as f32;
5121 let cy = self.arg_num(&args, 1, 0.)? as f32;
5122 let cz = self.arg_num(&args, 2, 0.)? as f32;
5123 let ux = self.arg_num(&args, 3, 1.)? as f32;
5124 let uy = self.arg_num(&args, 4, 0.)? as f32;
5125 let uz = self.arg_num(&args, 5, 0.)? as f32;
5126 let vx = self.arg_num(&args, 6, 0.)? as f32;
5127 let vy = self.arg_num(&args, 7, 0.)? as f32;
5128 let vz = self.arg_num(&args, 8, 1.)? as f32;
5129 let r = self.arg_num(&args, 9, 2.)? as f32;
5130 let ns = self.arg_num(&args, 10, 8.)? as usize;
5131 let fr = self.arg_num(&args, 11, 0.)? as f32;
5132 let hue = self.arg_num(&args, 12, 0.)? as f32;
5133 let mut gfx = self.gfx.borrow_mut();
5134 let cam = gfx.camera.clone();
5135 crate::gfx::vtex::draw_chakra(
5136 &mut gfx.depth_queue,
5137 &cam,
5138 cx,
5139 cy,
5140 cz,
5141 ux,
5142 uy,
5143 uz,
5144 vx,
5145 vy,
5146 vz,
5147 r,
5148 ns,
5149 fr,
5150 hue,
5151 );
5152 return Ok(Value::Unit);
5153 },
5154
5155 "vtex_yantra" | "ลายยันต์" | "纹咒" | "護符模様" | "부적무늬" =>
5157 {
5158 let cx = self.arg_num(&args, 0, 0.)? as f32;
5159 let cy = self.arg_num(&args, 1, 0.)? as f32;
5160 let cz = self.arg_num(&args, 2, 0.)? as f32;
5161 let ux = self.arg_num(&args, 3, 1.)? as f32;
5162 let uy = self.arg_num(&args, 4, 0.)? as f32;
5163 let uz = self.arg_num(&args, 5, 0.)? as f32;
5164 let vx = self.arg_num(&args, 6, 0.)? as f32;
5165 let vy = self.arg_num(&args, 7, 0.)? as f32;
5166 let vz = self.arg_num(&args, 8, 1.)? as f32;
5167 let nl = self.arg_num(&args, 9, 4.)? as usize;
5168 let mr = self.arg_num(&args, 10, 3.)? as f32;
5169 let fr = self.arg_num(&args, 11, 0.)? as f32;
5170 let hue = self.arg_num(&args, 12, 0.)? as f32;
5171 let mut gfx = self.gfx.borrow_mut();
5172 let cam = gfx.camera.clone();
5173 crate::gfx::vtex::draw_yantra(
5174 &mut gfx.depth_queue,
5175 &cam,
5176 cx,
5177 cy,
5178 cz,
5179 ux,
5180 uy,
5181 uz,
5182 vx,
5183 vy,
5184 vz,
5185 nl,
5186 mr,
5187 fr,
5188 hue,
5189 );
5190 return Ok(Value::Unit);
5191 },
5192
5193 "vtex_spiked_cog" | "ฟันเฟืองหนาม" | "纹棘轮" | "歯車模様" | "톱니바퀴" =>
5195 {
5196 let cx = self.arg_num(&args, 0, 0.)? as f32;
5197 let cy = self.arg_num(&args, 1, 0.)? as f32;
5198 let cz = self.arg_num(&args, 2, 0.)? as f32;
5199 let ux = self.arg_num(&args, 3, 1.)? as f32;
5200 let uy = self.arg_num(&args, 4, 0.)? as f32;
5201 let uz = self.arg_num(&args, 5, 0.)? as f32;
5202 let vx = self.arg_num(&args, 6, 0.)? as f32;
5203 let vy = self.arg_num(&args, 7, 0.)? as f32;
5204 let vz = self.arg_num(&args, 8, 1.)? as f32;
5205 let nt = self.arg_num(&args, 9, 12.)? as usize;
5206 let rb = self.arg_num(&args, 10, 1.)? as f32;
5207 let rs = self.arg_num(&args, 11, 1.3)? as f32;
5208 let rh = self.arg_num(&args, 12, 0.2)? as f32;
5209 let ns = self.arg_num(&args, 13, 6.)? as usize;
5210 let fr = self.arg_num(&args, 14, 0.)? as f32;
5211 let hue = self.arg_num(&args, 15, 0.)? as f32;
5212 let mut gfx = self.gfx.borrow_mut();
5213 let cam = gfx.camera.clone();
5214 crate::gfx::vtex::draw_spiked_cog(
5215 &mut gfx.depth_queue,
5216 &cam,
5217 cx,
5218 cy,
5219 cz,
5220 ux,
5221 uy,
5222 uz,
5223 vx,
5224 vy,
5225 vz,
5226 nt,
5227 rb,
5228 rs,
5229 rh,
5230 ns,
5231 fr,
5232 hue,
5233 );
5234 return Ok(Value::Unit);
5235 },
5236
5237 "vtex_torii" | "ประตูโทริอิ" | "纹鸟居" | "鳥居" | "도리이" =>
5239 {
5240 let cx = self.arg_num(&args, 0, 0.)? as f32;
5241 let cy = self.arg_num(&args, 1, 0.)? as f32;
5242 let cz = self.arg_num(&args, 2, 0.)? as f32;
5243 let ux = self.arg_num(&args, 3, 1.)? as f32;
5244 let uy = self.arg_num(&args, 4, 0.)? as f32;
5245 let uz = self.arg_num(&args, 5, 0.)? as f32;
5246 let vx = self.arg_num(&args, 6, 0.)? as f32;
5247 let vy = self.arg_num(&args, 7, 0.)? as f32;
5248 let vz = self.arg_num(&args, 8, 1.)? as f32;
5249 let w = self.arg_num(&args, 9, 4.)? as f32;
5250 let h = self.arg_num(&args, 10, 5.)? as f32;
5251 let fr = self.arg_num(&args, 11, 0.)? as f32;
5252 let hue = self.arg_num(&args, 12, 0.)? as f32;
5253 let mut gfx = self.gfx.borrow_mut();
5254 let cam = gfx.camera.clone();
5255 crate::gfx::vtex::draw_torii(
5256 &mut gfx.depth_queue,
5257 &cam,
5258 cx,
5259 cy,
5260 cz,
5261 ux,
5262 uy,
5263 uz,
5264 vx,
5265 vy,
5266 vz,
5267 w,
5268 h,
5269 fr,
5270 hue,
5271 );
5272 return Ok(Value::Unit);
5273 },
5274
5275 "vtex_pagoda" | "เจดีย์" | "纹塔" | "塔" | "탑" => {
5277 let cx = self.arg_num(&args, 0, 0.)? as f32;
5278 let cy = self.arg_num(&args, 1, 0.)? as f32;
5279 let cz = self.arg_num(&args, 2, 0.)? as f32;
5280 let ux = self.arg_num(&args, 3, 1.)? as f32;
5281 let uy = self.arg_num(&args, 4, 0.)? as f32;
5282 let uz = self.arg_num(&args, 5, 0.)? as f32;
5283 let vx = self.arg_num(&args, 6, 0.)? as f32;
5284 let vy = self.arg_num(&args, 7, 0.)? as f32;
5285 let vz = self.arg_num(&args, 8, 1.)? as f32;
5286 let nt = self.arg_num(&args, 9, 5.)? as usize;
5287 let bw = self.arg_num(&args, 10, 2.)? as f32;
5288 let th = self.arg_num(&args, 11, 1.)? as f32;
5289 let tp = self.arg_num(&args, 12, 0.72)? as f32;
5290 let eo = self.arg_num(&args, 13, 0.28)? as f32;
5291 let fr = self.arg_num(&args, 14, 0.)? as f32;
5292 let hue = self.arg_num(&args, 15, 0.)? as f32;
5293 let mut gfx = self.gfx.borrow_mut();
5294 let cam = gfx.camera.clone();
5295 crate::gfx::vtex::draw_pagoda(
5296 &mut gfx.depth_queue,
5297 &cam,
5298 cx,
5299 cy,
5300 cz,
5301 ux,
5302 uy,
5303 uz,
5304 vx,
5305 vy,
5306 vz,
5307 nt,
5308 bw,
5309 th,
5310 tp,
5311 eo,
5312 fr,
5313 hue,
5314 );
5315 return Ok(Value::Unit);
5316 },
5317
5318 #[cfg(not(target_arch = "wasm32"))]
5324 "audio_tone"
5325 | "เสียงโทน"
5326 | "音调"
5327 | "音調"
5328 | "음조"
5329 | "空间音"
5330 | "空間音"
5331 | "공간음" => {
5332 let idx = self.arg_num(&args, 0, 0.0)? as usize;
5333 let x = self.arg_num(&args, 1, 0.0)? as f32;
5334 let y = self.arg_num(&args, 2, 0.0)? as f32;
5335 let z = self.arg_num(&args, 3, 0.0)? as f32;
5336 let w = self.arg_num(&args, 4, 1.0)? as f32;
5337 let freq = self.arg_num(&args, 5, 220.0)? as f32;
5338 let amp = self.arg_num(&args, 6, 0.15)? as f32;
5339 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
5340 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
5341 if let Some(audio) = &self.audio {
5342 audio.set_tone(
5343 idx,
5344 ToneParams { x, y, z, w, freq, amp, lfo_rate, lfo_depth },
5345 );
5346 }
5347 return Ok(Value::Unit);
5348 },
5349
5350 #[cfg(not(target_arch = "wasm32"))]
5351 "audio_listener" | "ผู้ฟัง" | "音频监听" | "音声リスナー" | "오디오리스너" =>
5352 {
5353 let cry = self.arg_num(&args, 0, 1.0)? as f32;
5354 let sry = self.arg_num(&args, 1, 0.0)? as f32;
5355 let crx = self.arg_num(&args, 2, 1.0)? as f32;
5356 let srx = self.arg_num(&args, 3, 0.0)? as f32;
5357 if let Some(audio) = &self.audio {
5358 audio.set_listener(cry, sry, crx, srx);
5359 }
5360 return Ok(Value::Unit);
5361 },
5362
5363 #[cfg(not(target_arch = "wasm32"))]
5364 "audio_bgm" | "เพลงพื้นหลัง" | "เพลงประกอบ" | "背景乐" | "BGM" | "배경음악" =>
5365 {
5366 let path = match args.first() {
5367 Some(Value::Str(s)) => s.clone(),
5368 _ => return Ok(Value::Unit),
5369 };
5370 let vol = self.arg_num(&args, 1, 0.5)? as f32;
5371 if let Some(audio) = &self.audio {
5372 audio.load_bgm(&path, vol);
5373 }
5374 return Ok(Value::Unit);
5375 },
5376
5377 #[cfg(not(target_arch = "wasm32"))]
5378 "audio_bgm_volume"
5379 | "ระดับเสียงพื้นหลัง"
5380 | "ระดับเพลงประกอบ"
5381 | "背景乐音量"
5382 | "BGM音量"
5383 | "배경음악음량" => {
5384 let vol = self.arg_num(&args, 0, 0.5)? as f32;
5385 if let Some(audio) = &self.audio {
5386 audio.set_bgm_volume(vol);
5387 }
5388 return Ok(Value::Unit);
5389 },
5390
5391 #[cfg(not(target_arch = "wasm32"))]
5392 "audio_volume" | "ระดับเสียง" | "音量" | "음량" => {
5393 let vol = self.arg_num(&args, 0, 0.7)? as f32;
5394 if let Some(audio) = &self.audio {
5395 audio.set_master_volume(vol);
5396 }
5397 return Ok(Value::Unit);
5398 },
5399
5400 #[cfg(target_arch = "wasm32")]
5402 "audio_tone"
5403 | "เสียงโทน"
5404 | "音调"
5405 | "音調"
5406 | "음조"
5407 | "空间音"
5408 | "空間音"
5409 | "공간음" => {
5410 let idx = self.arg_num(&args, 0, 0.0)? as usize;
5411 let x = self.arg_num(&args, 1, 0.0)? as f32;
5412 let y = self.arg_num(&args, 2, 0.0)? as f32;
5413 let z = self.arg_num(&args, 3, 0.0)? as f32;
5414 let w = self.arg_num(&args, 4, 1.0)? as f32;
5415 let freq = self.arg_num(&args, 5, 220.0)? as f32;
5416 let amp = self.arg_num(&args, 6, 0.15)? as f32;
5417 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
5418 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
5419 crate::gfx::audio_web::set_tone(idx, x, y, z, w, freq, amp, lfo_rate, lfo_depth);
5420 return Ok(Value::Unit);
5421 },
5422
5423 #[cfg(target_arch = "wasm32")]
5424 "audio_listener" | "ผู้ฟัง" | "音频监听" | "音声リスナー" | "오디오리스너" =>
5425 {
5426 let cry = self.arg_num(&args, 0, 1.0)? as f32;
5427 let sry = self.arg_num(&args, 1, 0.0)? as f32;
5428 let crx = self.arg_num(&args, 2, 1.0)? as f32;
5429 let srx = self.arg_num(&args, 3, 0.0)? as f32;
5430 crate::gfx::audio_web::set_listener(cry, sry, crx, srx);
5431 return Ok(Value::Unit);
5432 },
5433
5434 #[cfg(target_arch = "wasm32")]
5435 "audio_bgm" | "เพลงพื้นหลัง" | "เพลงประกอบ" | "背景乐" | "BGM" | "배경음악" =>
5436 {
5437 let path = self.arg_str(&args, 0, "");
5438 let vol = self.arg_num(&args, 1, 0.5)? as f32;
5439 crate::gfx::audio_web::load_bgm(&path, vol);
5440 return Ok(Value::Unit);
5441 },
5442
5443 #[cfg(target_arch = "wasm32")]
5444 "audio_bgm_volume"
5445 | "ระดับเสียงพื้นหลัง"
5446 | "ระดับเพลงประกอบ"
5447 | "背景乐音量"
5448 | "BGM音量"
5449 | "배경음악음량" => {
5450 let vol = self.arg_num(&args, 0, 0.5)? as f32;
5451 crate::gfx::audio_web::set_bgm_volume(vol);
5452 return Ok(Value::Unit);
5453 },
5454
5455 #[cfg(target_arch = "wasm32")]
5456 "audio_volume" | "ระดับเสียง" | "音量" | "음량" => {
5457 let vol = self.arg_num(&args, 0, 0.7)? as f32;
5458 crate::gfx::audio_web::set_master_volume(vol);
5459 return Ok(Value::Unit);
5460 },
5461
5462 #[cfg(target_arch = "wasm32")]
5464 "audio_sample_load" | "载入采样" | "サンプル読込" | "샘플로드" | "โหลดตัวอย่างเสียง" =>
5465 {
5466 let path = self.arg_str(&args, 0, "");
5467 let resolved = self.wasm_resolve_source_path(&path);
5468 match wasm_fetch_bytes(&resolved)
5469 .and_then(|bytes| ling_music::from_bytes(&bytes).map_err(|e| e.to_string()))
5470 {
5471 Ok(t) => {
5472 let id = crate::gfx::audio_web::add_sample(&t.stereo, t.channels, t.rate);
5473 return Ok(Value::Number(id as f64));
5474 },
5475 Err(e) => {
5476 eprintln!("audio_sample_load failed ({path}): {e}");
5477 return Ok(Value::Number(-1.0));
5478 },
5479 }
5480 },
5481 #[cfg(target_arch = "wasm32")]
5482 "audio_sample_play" | "播放采样" | "サンプル再生" | "샘플재생" | "เล่นตัวอย่างเสียง" =>
5483 {
5484 let id = self.arg_num(&args, 0, 0.0)? as usize;
5485 let x = self.arg_num(&args, 1, 0.0)? as f32;
5486 let y = self.arg_num(&args, 2, 0.0)? as f32;
5487 let z = self.arg_num(&args, 3, 0.0)? as f32;
5488 let vol = self.arg_num(&args, 5, 1.0)? as f32;
5490 let looping = self.arg_num(&args, 6, 0.0)? > 0.5;
5491 crate::gfx::audio_web::play_sample(id, x, y, z, vol, looping);
5492 return Ok(Value::Number(0.0));
5493 },
5494 #[cfg(target_arch = "wasm32")]
5495 "audio_sample_stop" | "停止采样" | "サンプル停止" | "샘플정지" | "หยุดตัวอย่างเสียง"
5496 | "audio_fx_reverb" | "混响" | "リバーブ" | "리버브" | "เสียงก้อง"
5497 | "audio_fx_delay" | "回声" | "ディレイ効果" | "딜레이" | "เสียงสะท้อน"
5498 | "audio_fx_lowpass" | "低通滤波" | "ローパス" | "저역통과" | "กรองความถี่ต่ำ" => {
5499 return Ok(Value::Unit);
5500 },
5501
5502 "รอหน้าต่าง" | "wait_window" | "gfx_wait" => {
5504 #[cfg(not(target_arch = "wasm32"))]
5505 loop {
5506 let still_open = {
5507 let gfx = self.gfx.borrow();
5508 gfx.window
5509 .as_ref()
5510 .map(|w| w.is_open() && !w.is_key_down(minifb::Key::Escape))
5511 .unwrap_or(false)
5512 };
5513 if !still_open {
5514 break;
5515 }
5516 let (buf, w, h) = {
5517 let gfx = self.gfx.borrow();
5518 (gfx.buffer.clone(), gfx.width, gfx.height)
5519 };
5520 let mut gfx = self.gfx.borrow_mut();
5521 if let Some(win) = gfx.window.as_mut() {
5522 if win.update_with_buffer(&buf, w, h).is_err() {
5523 break;
5524 }
5525 }
5526 }
5527 return Ok(Value::Unit);
5528 },
5529
5530 "read_file" | "อ่านไฟล์" => {
5532 #[cfg(target_arch = "wasm32")]
5533 return Ok(Value::Str(String::new()));
5534 #[cfg(not(target_arch = "wasm32"))]
5535 {
5536 let path = self.arg_str(&args, 0, "");
5537 return std::fs::read_to_string(&path)
5538 .map(Value::Str)
5539 .map_err(|e| EvalErr::from(format!("read_file '{path}': {e}")));
5540 }
5541 },
5542 #[cfg(not(target_arch = "wasm32"))]
5544 "net_host" | "เน็ตโฮสต์" => {
5545 let port = self.arg_num(&args, 0, 7777.0)? as u16;
5546 net::host(port);
5547 return Ok(Value::Unit);
5548 },
5549 #[cfg(not(target_arch = "wasm32"))]
5550 "net_join" | "เน็ตจอย" => {
5551 let ip = self.arg_str(&args, 0, "127.0.0.1");
5552 let port = self.arg_num(&args, 1, 7777.0)? as u16;
5553 net::join(&ip, port);
5554 return Ok(Value::Unit);
5555 },
5556 #[cfg(not(target_arch = "wasm32"))]
5557 "net_send" | "เน็ตส่ง" => {
5558 let s = self.arg_str(&args, 0, "");
5559 net::send(&s);
5560 return Ok(Value::Unit);
5561 },
5562 #[cfg(not(target_arch = "wasm32"))]
5563 "net_recv" | "เน็ตรับ" => {
5564 return Ok(Value::Str(net::recv()));
5565 },
5566 #[cfg(not(target_arch = "wasm32"))]
5567 "net_status" | "เน็ตสถานะ" => {
5568 return Ok(Value::Number(net::status() as f64));
5569 },
5570 #[cfg(not(target_arch = "wasm32"))]
5572 "net_announce" | "เน็ตประกาศ" => {
5573 let port = self.arg_num(&args, 0, 7778.0)? as u16;
5574 let info = self.arg_str(&args, 1, "");
5575 net::announce(port, &info);
5576 return Ok(Value::Unit);
5577 },
5578 #[cfg(not(target_arch = "wasm32"))]
5579 "net_announce_stop" | "เน็ตหยุดประกาศ" => {
5580 net::announce_stop();
5581 return Ok(Value::Unit);
5582 },
5583 #[cfg(not(target_arch = "wasm32"))]
5584 "net_discover" | "เน็ตค้นหา" => {
5585 let port = self.arg_num(&args, 0, 7778.0)? as u16;
5586 return Ok(Value::Str(net::discover(port)));
5587 },
5588 #[cfg(not(target_arch = "wasm32"))]
5589 "net_test" | "เน็ตทดสอบ" => {
5590 let port = self.arg_num(&args, 0, 7777.0)? as u16;
5591 return Ok(Value::Str(net::test_bind(port)));
5592 },
5593 #[cfg(not(target_arch = "wasm32"))]
5595 "gamepad_poll" | "จอยโพล" => {
5596 gamepad::poll();
5597 return Ok(Value::Unit);
5598 },
5599 #[cfg(not(target_arch = "wasm32"))]
5600 "gamepad_button" | "จอยปุ่ม" => {
5601 let name = self.arg_str(&args, 0, "");
5602 return Ok(Value::Number(if gamepad::button(&name) {
5603 1.0
5604 } else {
5605 0.0
5606 }));
5607 },
5608 #[cfg(not(target_arch = "wasm32"))]
5609 "gamepad_axis" | "จอยแกน" => {
5610 let name = self.arg_str(&args, 0, "");
5611 return Ok(Value::Number(gamepad::axis(&name) as f64));
5612 },
5613 #[cfg(not(target_arch = "wasm32"))]
5614 "gamepad_rumble" | "จอยสั่น" => {
5615 let low = self.arg_num(&args, 0, 0.0)? as f32;
5616 let high = self.arg_num(&args, 1, 0.0)? as f32;
5617 let ms = self.arg_num(&args, 2, 200.0)? as u32;
5618 gamepad::rumble(low, high, ms);
5619 return Ok(Value::Unit);
5620 },
5621 #[cfg(not(target_arch = "wasm32"))]
5622 "gamepad_list" | "จอยรายการ" => {
5623 return Ok(Value::Str(gamepad::list()));
5624 },
5625 #[cfg(not(target_arch = "wasm32"))]
5626 "gamepad_any" | "จอยใดๆ" => {
5627 return Ok(Value::Number(if gamepad::any_button() { 1.0 } else { 0.0 }));
5628 },
5629 #[cfg(target_arch = "wasm32")]
5631 "gamepad_poll" | "จอยโพล"
5632 | "gamepad_rumble" | "จอยสั่น" => {
5633 return Ok(Value::Unit);
5634 },
5635 #[cfg(target_arch = "wasm32")]
5636 "gamepad_button" | "จอยปุ่ม"
5637 | "gamepad_axis" | "จอยแกน"
5638 | "gamepad_any" | "จอยใดๆ" => {
5639 return Ok(Value::Number(0.0));
5640 },
5641 #[cfg(target_arch = "wasm32")]
5642 "gamepad_list" | "จอยรายการ" => {
5643 return Ok(Value::Str(String::new()));
5644 },
5645
5646 #[cfg(not(target_arch = "wasm32"))]
5649 "nn_new" | "建神经网" | "ニューラル作成" | "신경망생성" | "สร้างโครงข่าย" =>
5650 {
5651 let n_in = self.arg_num(&args, 0, 1.0)?.max(0.0) as usize;
5652 let seed = self.arg_num(&args, 1, 1.0)? as u64;
5653 return Ok(Value::Number(ai::nn_new(n_in, seed) as f64));
5654 },
5655 #[cfg(not(target_arch = "wasm32"))]
5657 "nn_dense" | "密集层" | "密層追加" | "밀집층" | "ชั้นหนาแน่น" =>
5658 {
5659 let id = self.arg_num(&args, 0, -1.0)? as i64;
5660 let units = self.arg_num(&args, 1, 1.0)?.max(1.0) as usize;
5661 let act = self.arg_str(&args, 2, "relu");
5662 ai::nn_dense(id, units, &act);
5663 return Ok(Value::Unit);
5664 },
5665 #[cfg(not(target_arch = "wasm32"))]
5667 "nn_forward" | "神经前向" | "順伝播" | "순전파" | "ส่งต่อโครงข่าย" =>
5668 {
5669 let id = self.arg_num(&args, 0, -1.0)? as i64;
5670 let input = self.arg_list_f32(&args, 1);
5671 let out = ai::nn_forward(id, &input);
5672 return Ok(Value::List(Rc::new(
5673 out.into_iter().map(|v| Value::Number(v as f64)).collect(),
5674 )));
5675 },
5676 #[cfg(not(target_arch = "wasm32"))]
5678 "nn_train" | "训练网" | "ニューラル学習" | "신경망학습" | "ฝึกโครงข่าย" =>
5679 {
5680 let id = self.arg_num(&args, 0, -1.0)? as i64;
5681 let input = self.arg_list_f32(&args, 1);
5682 let target = self.arg_list_f32(&args, 2);
5683 let lr = self.arg_num(&args, 3, 0.01)? as f32;
5684 return Ok(Value::Number(ai::nn_train(id, &input, &target, lr) as f64));
5685 },
5686 #[cfg(not(target_arch = "wasm32"))]
5688 "nn_save" | "保存网" | "網保存" | "신경망저장" | "บันทึกโครงข่าย" =>
5689 {
5690 let id = self.arg_num(&args, 0, -1.0)? as i64;
5691 let path = self.arg_str(&args, 1, "model.lnn");
5692 return Ok(Value::Bool(ai::nn_save(id, &path)));
5693 },
5694 #[cfg(not(target_arch = "wasm32"))]
5696 "nn_load" | "载入网" | "網読込" | "신경망불러오기" | "โหลดโครงข่าย" =>
5697 {
5698 let path = self.arg_str(&args, 0, "model.lnn");
5699 return Ok(Value::Number(ai::nn_load(&path) as f64));
5700 },
5701
5702 #[cfg(not(target_arch = "wasm32"))]
5705 "bt_build" | "建行为树" | "行動木構築" | "행동트리구성" | "สร้างต้นไม้พฤติกรรม" =>
5706 {
5707 let spec = self.arg_str(&args, 0, "");
5708 return Ok(Value::Number(ai::bt_build(&spec) as f64));
5709 },
5710 #[cfg(not(target_arch = "wasm32"))]
5712 "bt_set" | "设事实" | "事実設定" | "사실설정" | "ตั้งข้อเท็จจริง" =>
5713 {
5714 let id = self.arg_num(&args, 0, -1.0)? as i64;
5715 let key = self.arg_str(&args, 1, "");
5716 let val = self.arg_num(&args, 2, 0.0)? as f32;
5717 ai::bt_set(id, &key, val);
5718 return Ok(Value::Unit);
5719 },
5720 #[cfg(not(target_arch = "wasm32"))]
5722 "bt_tick" | "行为树滴答" | "行動木更新" | "행동트리틱" | "เดินต้นไม้พฤติกรรม" =>
5723 {
5724 let id = self.arg_num(&args, 0, -1.0)? as i64;
5725 return Ok(Value::Str(ai::bt_tick(id)));
5726 },
5727 #[cfg(not(target_arch = "wasm32"))]
5729 "bt_status" | "行为树状态" | "行動木状態" | "행동트리상태" | "สถานะต้นไม้พฤติกรรม" =>
5730 {
5731 let id = self.arg_num(&args, 0, -1.0)? as i64;
5732 return Ok(Value::Number(ai::bt_status(id) as f64));
5733 },
5734
5735 #[cfg(not(target_arch = "wasm32"))]
5738 "dialog_new" | "建对话模型" | "対話モデル作成" | "대화모델생성" | "สร้างโมเดลสนทนา" =>
5739 {
5740 let ctx = self.arg_num(&args, 0, 3.0)?.max(1.0) as usize;
5741 let embed = self.arg_num(&args, 1, 32.0)?.max(1.0) as usize;
5742 let hidden = self.arg_num(&args, 2, 64.0)?.max(1.0) as usize;
5743 let seed = self.arg_num(&args, 3, 1.0)? as u64;
5744 return Ok(Value::Number(
5745 ai::dialog_new(ctx, embed, hidden, seed) as f64
5746 ));
5747 },
5748 #[cfg(not(target_arch = "wasm32"))]
5750 "dialog_learn" | "对话学习" | "対話学習" | "대화학습" | "เรียนรู้สนทนา" =>
5751 {
5752 let id = self.arg_num(&args, 0, -1.0)? as i64;
5753 let text = self.arg_str(&args, 1, "");
5754 ai::dialog_learn(id, &text);
5755 return Ok(Value::Unit);
5756 },
5757 #[cfg(not(target_arch = "wasm32"))]
5759 "dialog_load" | "对话载入" | "対話読込" | "대화불러오기" | "โหลดชุดสนทนา" =>
5760 {
5761 let id = self.arg_num(&args, 0, -1.0)? as i64;
5762 let path = self.arg_str(&args, 1, "");
5763 return Ok(Value::Number(ai::dialog_load(id, &path) as f64));
5764 },
5765 #[cfg(not(target_arch = "wasm32"))]
5767 "dialog_train" | "对话训练" | "対話訓練" | "대화훈련" | "ฝึกสนทนา" =>
5768 {
5769 let id = self.arg_num(&args, 0, -1.0)? as i64;
5770 let epochs = self.arg_num(&args, 1, 20.0)?.max(1.0) as usize;
5771 let lr = self.arg_num(&args, 2, 0.1)? as f32;
5772 return Ok(Value::Number(ai::dialog_train(id, epochs, lr) as f64));
5773 },
5774 #[cfg(not(target_arch = "wasm32"))]
5776 "dialog_say" | "对话生成" | "対話生成" | "대화생성" | "พูดสนทนา" =>
5777 {
5778 let id = self.arg_num(&args, 0, -1.0)? as i64;
5779 let prompt = self.arg_str(&args, 1, "");
5780 let max = self.arg_num(&args, 2, 24.0)?.max(1.0) as usize;
5781 let temp = self.arg_num(&args, 3, 0.8)? as f32;
5782 return Ok(Value::Str(ai::dialog_say(id, &prompt, max, temp)));
5783 },
5784 #[cfg(not(target_arch = "wasm32"))]
5786 "dialog_save" | "对话存模" | "対話モデル保存" | "대화모델저장" | "บันทึกโมเดลสนทนา" =>
5787 {
5788 let id = self.arg_num(&args, 0, -1.0)? as i64;
5789 let path = self.arg_str(&args, 1, "model.llm");
5790 return Ok(Value::Bool(ai::dialog_save(id, &path)));
5791 },
5792 #[cfg(not(target_arch = "wasm32"))]
5794 "dialog_load_model"
5795 | "对话载模"
5796 | "対話モデル読込"
5797 | "대화모델불러오기"
5798 | "โหลดโมเดลสนทนา" => {
5799 let path = self.arg_str(&args, 0, "model.llm");
5800 return Ok(Value::Number(ai::dialog_load_model(&path) as f64));
5801 },
5802
5803 "write_file" | "เขียนไฟล์" => {
5804 #[cfg(target_arch = "wasm32")]
5805 return Ok(Value::Unit);
5806 #[cfg(not(target_arch = "wasm32"))]
5807 {
5808 let path = self.arg_str(&args, 0, "");
5809 let content = self.arg_str(&args, 1, "");
5810 std::fs::write(&path, content.as_bytes())
5811 .map_err(|e| EvalErr::from(format!("write_file '{path}': {e}")))?;
5812 return Ok(Value::Unit);
5813 }
5814 },
5815 "print_file" | "พิมพ์ไฟล์" => {
5816 let content = self.arg_str(&args, 0, "");
5817 print!("{content}");
5818 return Ok(Value::Unit);
5819 },
5820
5821 "get_args" | "รับอาร์กิวเมนต์" => {
5823 let v: Vec<Value> = std::env::args().map(Value::Str).collect();
5824 return Ok(Value::List(Rc::new(v)));
5825 },
5826
5827 "split" | "str_split" | "แยก" => {
5829 let s = self.arg_str(&args, 0, "");
5830 let sep = self.arg_str(&args, 1, "\n");
5831 let sep = if sep.is_empty() { "\n".into() } else { sep };
5832 let parts: Vec<Value> = s
5833 .split(sep.as_str())
5834 .map(|p| Value::Str(p.to_string()))
5835 .collect();
5836 return Ok(Value::List(Rc::new(parts)));
5837 },
5838 "trim" | "str_trim" | "ตัดช่องว่าง" => {
5839 let s = self.arg_str(&args, 0, "");
5840 return Ok(Value::Str(s.trim().to_string()));
5841 },
5842 "starts_with" | "str_starts_with" | "เริ่มด้วย" => {
5843 let s = self.arg_str(&args, 0, "");
5844 let prefix = self.arg_str(&args, 1, "");
5845 return Ok(Value::Bool(s.starts_with(prefix.as_str())));
5846 },
5847 "ends_with" | "str_ends_with" | "ลงท้ายด้วย" => {
5848 let s = self.arg_str(&args, 0, "");
5849 let suffix = self.arg_str(&args, 1, "");
5850 return Ok(Value::Bool(s.ends_with(suffix.as_str())));
5851 },
5852 "str_replace" | "แทนสตริง" => {
5853 let s = self.arg_str(&args, 0, "");
5854 let from = self.arg_str(&args, 1, "");
5855 let to = self.arg_str(&args, 2, "");
5856 return Ok(Value::Str(s.replace(from.as_str(), to.as_str())));
5857 },
5858 "str_find" | "หาในสตริง" => {
5859 let s = self.arg_str(&args, 0, "");
5860 let needle = self.arg_str(&args, 1, "");
5861 let pos = s
5863 .find(needle.as_str())
5864 .map(|byte_i| s[..byte_i].chars().count() as f64)
5865 .unwrap_or(-1.0);
5866 return Ok(Value::Number(pos));
5867 },
5868 "substr" | "str_slice" | "ส่วนสตริง" => {
5869 let s = self.arg_str(&args, 0, "");
5870 let start = self.arg_num(&args, 1, 0.0)? as usize;
5871 let len = args
5872 .get(2)
5873 .map(|v| self.to_number(v).unwrap_or(999999.0) as usize)
5874 .unwrap_or_else(|| s.chars().count().saturating_sub(start));
5875 let chars: Vec<char> = s.chars().collect();
5876 let end = (start + len).min(chars.len());
5877 let slice: String = chars.get(start..end).unwrap_or(&[]).iter().collect();
5878 return Ok(Value::Str(slice));
5879 },
5880 "to_str" | "str" | "num_str" | "แปลงสตริง" => {
5881 let v = args.into_iter().next().unwrap_or(Value::Unit);
5882 return Ok(Value::Str(v.to_string()));
5883 },
5884 "str_repeat" | "ทำซ้ำสตริง" => {
5885 let s = self.arg_str(&args, 0, "");
5886 let n = self.arg_num(&args, 1, 1.0)? as usize;
5887 return Ok(Value::Str(s.repeat(n)));
5888 },
5889 "str_upper" => {
5890 let s = self.arg_str(&args, 0, "");
5891 return Ok(Value::Str(s.to_uppercase()));
5892 },
5893 "str_lower" => {
5894 let s = self.arg_str(&args, 0, "");
5895 return Ok(Value::Str(s.to_lowercase()));
5896 },
5897 "str_len" | "len" | "ความยาว" | "长度" | "長さ" | "길이" => {
5898 match args.first() {
5899 Some(Value::Str(s)) => return Ok(Value::Number(s.chars().count() as f64)),
5900 Some(Value::List(v)) => return Ok(Value::Number(v.len() as f64)),
5901 _ => return Ok(Value::Number(0.0)),
5902 }
5903 },
5904
5905 "hash_str" | "แฮช" => {
5907 let s = self.arg_str(&args, 0, "");
5908 let mut h: u64 = 14695981039346656037_u64;
5909 for b in s.bytes() {
5910 h ^= b as u64;
5911 h = h.wrapping_mul(1099511628211);
5912 }
5913 return Ok(Value::Number((h & 0xFFFFFF) as f64 / 16777215.0));
5914 },
5915 "hash_int" | "แฮชจำนวน" => {
5916 let s = self.arg_str(&args, 0, "");
5917 let n = self.arg_num(&args, 1, 100.0)? as u64;
5918 let mut h: u64 = 14695981039346656037_u64;
5919 for b in s.bytes() {
5920 h ^= b as u64;
5921 h = h.wrapping_mul(1099511628211);
5922 }
5923 return Ok(Value::Number((h % n.max(1)) as f64));
5924 },
5925
5926 "list_new" | "รายการใหม่" | "新建列表" | "新規リスト" | "새목록" =>
5928 {
5929 return Ok(Value::List(Rc::new(Vec::new())));
5930 },
5931 "list_push" | "เพิ่มรายการ" | "列表添加" | "リスト追加" | "목록추가" =>
5932 {
5933 let lst = args.first().cloned().unwrap_or(Value::List(Rc::new(vec![])));
5934 let val = args.get(1).cloned().unwrap_or(Value::Unit);
5935 if let Value::List(mut v) = lst {
5936 Rc::make_mut(&mut v).push(val);
5937 return Ok(Value::List(v));
5938 }
5939 return Ok(Value::List(Rc::new(vec![val])));
5940 },
5941 "list_get" | "รับรายการ" | "取元素" | "要素取得" | "요소가져오기" =>
5942 {
5943 let i = self.arg_num(&args, 1, 0.0)? as usize;
5945 if let Some(Value::List(v)) = args.first() {
5946 return Ok(v.get(i).cloned().unwrap_or(Value::Str(String::new())));
5947 }
5948 return Ok(Value::Str(String::new()));
5949 },
5950 "list_set" | "ตั้งรายการ" | "设元素" | "要素設定" | "요소설정" =>
5954 {
5955 let idx = self.arg_num(&args, 1, 0.0)? as usize;
5956 let mut ai = args.into_iter();
5957 let lst = ai.next().unwrap_or(Value::List(Rc::new(vec![])));
5958 ai.next(); let val = ai.next().unwrap_or(Value::Unit);
5960 if let Value::List(mut v) = lst {
5961 if idx < v.len() {
5962 Rc::make_mut(&mut v)[idx] = val;
5963 }
5964 return Ok(Value::List(v));
5965 }
5966 return Ok(Value::List(Rc::new(vec![])));
5967 },
5968 "list_join" | "join" | "รวมรายการ" | "连接" | "連結" | "연결" =>
5969 {
5970 let lst = args.first().cloned().unwrap_or(Value::List(Rc::new(vec![])));
5971 let sep = args.get(1).map(|v| v.to_string()).unwrap_or_default();
5972 if let Value::List(v) = lst {
5973 return Ok(Value::Str(
5974 v.iter()
5975 .map(|x| x.to_string())
5976 .collect::<Vec<_>>()
5977 .join(&sep),
5978 ));
5979 }
5980 return Ok(Value::Str(String::new()));
5981 },
5982 #[cfg(not(target_arch = "wasm32"))]
5986 "blob_f32" | "blob_i32" => {
5987 let s = self.arg_str(&args, 0, "");
5988 let is_i32 = name == "blob_i32";
5989 match decode_blob(&s) {
5990 Ok(bytes) => {
5991 let mut out = Vec::with_capacity(bytes.len() / 4);
5992 for ch in bytes.chunks_exact(4) {
5993 let arr = [ch[0], ch[1], ch[2], ch[3]];
5994 let n = if is_i32 {
5995 i32::from_le_bytes(arr) as f64
5996 } else {
5997 f32::from_le_bytes(arr) as f64
5998 };
5999 out.push(Value::Number(n));
6000 }
6001 return Ok(Value::List(Rc::new(out)));
6002 },
6003 Err(e) => {
6004 eprintln!("blob decode failed: {e}");
6005 return Ok(Value::List(Rc::new(vec![])));
6006 },
6007 }
6008 },
6009
6010 "svg_begin" | "开始SVG" | "เริ่มSVG" => {
6017 let path = self.arg_str(&args, 0, "output.svg");
6018 let width = self.arg_num(&args, 1, 800.0)?;
6019 let height = self.arg_num(&args, 2, 600.0)?;
6020 *self.svg.borrow_mut() = Some(SvgWriter::new(path, width, height));
6021 return Ok(Value::Unit);
6022 },
6023
6024 "svg_rect" | "SVG矩形" | "SVGสี่เหลี่ยม" => {
6025 let x = self.arg_num(&args, 0, 0.0)?;
6026 let y = self.arg_num(&args, 1, 0.0)?;
6027 let w = self.arg_num(&args, 2, 10.0)?;
6028 let h = self.arg_num(&args, 3, 10.0)?;
6029 let fill = self.arg_str(&args, 4, "#ffffff");
6030 if let Some(svg) = self.svg.borrow_mut().as_mut() {
6031 svg.elements.push(format!(
6032 "<rect x=\"{x:.1}\" y=\"{y:.1}\" width=\"{w:.1}\" \
6033 height=\"{h:.1}\" fill=\"{fill}\"/>"
6034 ));
6035 }
6036 return Ok(Value::Unit);
6037 },
6038
6039 "svg_circle" | "SVG圆形" | "SVGวงกลม" => {
6040 let cx = self.arg_num(&args, 0, 0.0)?;
6041 let cy = self.arg_num(&args, 1, 0.0)?;
6042 let r = self.arg_num(&args, 2, 5.0)?;
6043 let fill = self.arg_str(&args, 3, "#ffffff");
6044 if let Some(svg) = self.svg.borrow_mut().as_mut() {
6045 svg.elements.push(format!(
6046 "<circle cx=\"{cx:.1}\" cy=\"{cy:.1}\" r=\"{r:.1}\" fill=\"{fill}\"/>"
6047 ));
6048 }
6049 return Ok(Value::Unit);
6050 },
6051
6052 "svg_line" | "SVG线段" | "SVGเส้น" => {
6053 let x1 = self.arg_num(&args, 0, 0.0)?;
6054 let y1 = self.arg_num(&args, 1, 0.0)?;
6055 let x2 = self.arg_num(&args, 2, 0.0)?;
6056 let y2 = self.arg_num(&args, 3, 0.0)?;
6057 let stroke = self.arg_str(&args, 4, "#ffffff");
6058 let sw = self.arg_num(&args, 5, 1.0)?;
6059 if let Some(svg) = self.svg.borrow_mut().as_mut() {
6060 svg.elements.push(format!(
6061 "<line x1=\"{x1:.1}\" y1=\"{y1:.1}\" x2=\"{x2:.1}\" y2=\"{y2:.1}\" \
6062 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"
6063 ));
6064 }
6065 return Ok(Value::Unit);
6066 },
6067
6068 "svg_polyline" | "SVG折线" | "SVGเส้นหัก" => {
6069 let pts = self.arg_str(&args, 0, "");
6070 let stroke = self.arg_str(&args, 1, "#ffffff");
6071 let sw = self.arg_num(&args, 2, 1.0)?;
6072 if let Some(svg) = self.svg.borrow_mut().as_mut() {
6073 svg.elements.push(format!(
6074 "<polyline points=\"{pts}\" fill=\"none\" \
6075 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"
6076 ));
6077 }
6078 return Ok(Value::Unit);
6079 },
6080
6081 "svg_text" | "SVG文本" | "SVGข้อความ" => {
6082 let x = self.arg_num(&args, 0, 0.0)?;
6083 let y = self.arg_num(&args, 1, 0.0)?;
6084 let text = self.arg_str(&args, 2, "");
6085 let fill = self.arg_str(&args, 3, "#ffffff");
6086 let size = self.arg_num(&args, 4, 12.0)?;
6087 if let Some(svg) = self.svg.borrow_mut().as_mut() {
6088 let safe = text
6089 .replace('&', "&")
6090 .replace('<', "<")
6091 .replace('>', ">");
6092 svg.elements.push(format!(
6093 "<text x=\"{x:.1}\" y=\"{y:.1}\" fill=\"{fill}\" \
6094 font-family=\"monospace\" font-size=\"{size:.0}\">{safe}</text>"
6095 ));
6096 }
6097 return Ok(Value::Unit);
6098 },
6099
6100 "svg_end" | "结束SVG" | "จบSVG" => {
6101 {
6102 let borrow = self.svg.borrow();
6103 if let Some(svg) = borrow.as_ref() {
6104 svg.save()
6105 .map_err(|e| EvalErr::from(format!("svg_end: {e}")))?;
6106 }
6107 }
6108 *self.svg.borrow_mut() = None;
6109 return Ok(Value::Unit);
6110 },
6111
6112 "hsl_color" | "HSL颜色" | "สีHSL" => {
6113 let h = self.arg_num(&args, 0, 0.0)?;
6114 let s = self.arg_num(&args, 1, 70.0)?;
6115 let l = self.arg_num(&args, 2, 50.0)?;
6116 return Ok(Value::Str(hsl_to_hex(h, s, l)));
6117 },
6118
6119 #[cfg(not(target_arch = "wasm32"))]
6125 "fft_push" | "วิเคราะห์เสียง" | "频谱输入" | "FFT入力" | "FFT입력" =>
6126 {
6127 if let Some(Value::List(v)) = args.first() {
6128 let samples: Vec<f32> = v
6129 .iter()
6130 .filter_map(|x| {
6131 if let Value::Number(n) = x {
6132 Some(*n as f32)
6133 } else {
6134 None
6135 }
6136 })
6137 .collect();
6138 self.fft.borrow_mut().push_samples(&samples);
6139 }
6140 return Ok(Value::Unit);
6141 },
6142
6143 #[cfg(not(target_arch = "wasm32"))]
6145 "fft_bands" | "แถบความถี่" | "频段" | "周波数帯" | "주파수대" =>
6146 {
6147 let n = self.arg_num(&args, 0, 32.0)? as usize;
6148 let bands = self.fft.borrow().freq_bands(n);
6149 *self.fft_bands_cache.borrow_mut() = bands.clone();
6150 return Ok(Value::List(Rc::new(
6151 bands.into_iter().map(|v| Value::Number(v as f64)).collect(),
6152 )));
6153 },
6154
6155 #[cfg(not(target_arch = "wasm32"))]
6157 "fft_beat" | "จังหวะเสียง" | "节拍检测" | "ビート検出" | "비트" =>
6158 {
6159 return Ok(Value::Bool(self.fft.borrow().is_beat()));
6160 },
6161
6162 #[cfg(not(target_arch = "wasm32"))]
6164 "fft_beat_ratio" | "อัตราจังหวะ" | "节拍比" | "ビート比" | "비트비율" =>
6165 {
6166 return Ok(Value::Number(self.fft.borrow().beat_ratio() as f64));
6167 },
6168
6169 #[cfg(not(target_arch = "wasm32"))]
6171 "fft_rms" | "ระดับRMS" | "均方根" | "二乗平均" | "RMS레벨" => {
6172 return Ok(Value::Number(self.fft.borrow().rms() as f64));
6173 },
6174
6175 #[cfg(not(target_arch = "wasm32"))]
6177 "fft_dominant_freq" | "ความถี่หลัก" | "主频" | "主要周波数" | "주파수" =>
6178 {
6179 return Ok(Value::Number(self.fft.borrow().dominant_freq() as f64));
6180 },
6181
6182 #[cfg(target_arch = "wasm32")]
6184 "fft_push" | "วิเคราะห์เสียง" | "频谱输入" | "FFT入力" | "FFT입력" =>
6185 {
6186 return Ok(Value::Unit);
6187 },
6188 #[cfg(target_arch = "wasm32")]
6189 "fft_bands" | "แถบความถี่" | "频段" | "周波数帯" | "주파수대" =>
6190 {
6191 let n = self.arg_num(&args, 0, 32.0)? as usize;
6192 return Ok(Value::List(vec![Value::Number(0.0); n]));
6193 },
6194 #[cfg(target_arch = "wasm32")]
6195 "fft_beat" | "จังหวะเสียง" | "节拍检测" | "ビート検出" | "비트" =>
6196 {
6197 return Ok(Value::Bool(false));
6198 },
6199 #[cfg(target_arch = "wasm32")]
6200 "fft_beat_ratio" | "อัตราจังหวะ" | "节拍比" | "ビート比" | "비트비율" =>
6201 {
6202 return Ok(Value::Number(1.0));
6203 },
6204 #[cfg(target_arch = "wasm32")]
6205 "fft_rms" | "ระดับRMS" | "均方根" | "二乗平均" | "RMS레벨" => {
6206 return Ok(Value::Number(0.0));
6207 },
6208 #[cfg(target_arch = "wasm32")]
6209 "fft_dominant_freq" | "ความถี่หลัก" | "主频" | "主要周波数" | "주파수" =>
6210 {
6211 return Ok(Value::Number(0.0));
6212 },
6213
6214 "tex_checkerboard" | "ลายตารางหมากรุก" => {
6222 let (tx, ty, tw, th) = self.tex_rect(&args)?;
6223 let tiles = self.arg_num(&args, 4, 8.0)? as u32;
6224 let (r1, g1, b1) = (
6225 self.arg_num(&args, 5, 255.)? as u32,
6226 self.arg_num(&args, 6, 255.)? as u32,
6227 self.arg_num(&args, 7, 255.)? as u32,
6228 );
6229 let (r2, g2, b2) = (
6230 self.arg_num(&args, 8, 0.)? as u32,
6231 self.arg_num(&args, 9, 0.)? as u32,
6232 self.arg_num(&args, 10, 0.)? as u32,
6233 );
6234 let c1 = (r1 << 16) | (g1 << 8) | b1;
6235 let c2 = (r2 << 16) | (g2 << 8) | b2;
6236 let mut gfx = self.gfx.borrow_mut();
6237 let (bw, bh) = (gfx.width, gfx.height);
6238 for row in 0..th {
6239 for col in 0..tw {
6240 let cx = col as u32 * tiles / tw as u32;
6241 let cy = row as u32 * tiles / th as u32;
6242 let (dx, dy) = (tx + col, ty + row);
6243 if dx < bw && dy < bh {
6244 gfx.buffer[dy * bw + dx] = if (cx + cy) % 2 == 0 { c1 } else { c2 };
6245 }
6246 }
6247 }
6248 return Ok(Value::Unit);
6249 },
6250
6251 "tex_gradient" | "ลายไล่สี" => {
6253 let (tx, ty, tw, th) = self.tex_rect(&args)?;
6254 let angle = self.arg_num(&args, 4, 0.0)? as f32;
6255 let (r1, g1, b1) = (
6256 self.arg_num(&args, 5, 0.)? as f32 / 255.,
6257 self.arg_num(&args, 6, 0.)? as f32 / 255.,
6258 self.arg_num(&args, 7, 0.)? as f32 / 255.,
6259 );
6260 let (r2, g2, b2) = (
6261 self.arg_num(&args, 8, 255.)? as f32 / 255.,
6262 self.arg_num(&args, 9, 255.)? as f32 / 255.,
6263 self.arg_num(&args, 10, 255.)? as f32 / 255.,
6264 );
6265 let (ca, sa) = (angle.to_radians().cos(), angle.to_radians().sin());
6266 let mut gfx = self.gfx.borrow_mut();
6267 let (bw, bh) = (gfx.width, gfx.height);
6268 for row in 0..th {
6269 for col in 0..tw {
6270 let nx = col as f32 / tw as f32 - 0.5;
6271 let ny = row as f32 / th as f32 - 0.5;
6272 let t = ((nx * ca + ny * sa + 0.707) / 1.414).clamp(0., 1.);
6273 let (dx, dy) = (tx + col, ty + row);
6274 if dx < bw && dy < bh {
6275 gfx.buffer[dy * bw + dx] =
6276 tex_rgb(r1 + (r2 - r1) * t, g1 + (g2 - g1) * t, b1 + (b2 - b1) * t);
6277 }
6278 }
6279 }
6280 return Ok(Value::Unit);
6281 },
6282
6283 "tex_noise" | "ลายนอยส์" => {
6285 let (tx, ty, tw, th) = self.tex_rect(&args)?;
6286 let scale = self.arg_num(&args, 4, 4.0)? as f32;
6287 let octaves = self.arg_num(&args, 5, 4.0)? as u32;
6288 let seed = self.arg_num(&args, 6, 0.0)? as u32;
6289 let palette = self.arg_str(&args, 7, "rainbow");
6290 let mut gfx = self.gfx.borrow_mut();
6291 let (bw, bh) = (gfx.width, gfx.height);
6292 for row in 0..th {
6293 for col in 0..tw {
6294 let v = tex_fbm(
6295 col as f32 * scale / tw as f32,
6296 row as f32 * scale / th as f32,
6297 octaves,
6298 seed,
6299 );
6300 let [r, g, b] = tex_palette(&palette, v);
6301 let (dx, dy) = (tx + col, ty + row);
6302 if dx < bw && dy < bh {
6303 gfx.buffer[dy * bw + dx] = tex_rgb(r, g, b);
6304 }
6305 }
6306 }
6307 return Ok(Value::Unit);
6308 },
6309
6310 "tex_freq_map" | "ลายความถี่" => {
6313 let (tx, ty, tw, th) = self.tex_rect(&args)?;
6314 let time = self.arg_num(&args, 4, 0.0)? as f32;
6315 let speed = self.arg_num(&args, 5, 0.3)? as f32;
6316 let palette = self.arg_str(&args, 6, "rainbow");
6317 let bands: Vec<f32> = {
6318 let c = self.fft_bands_cache.borrow();
6319 if c.is_empty() {
6320 vec![0.0; 32]
6321 } else {
6322 c.clone()
6323 }
6324 };
6325 let n = bands.len().max(1);
6326 let mut gfx = self.gfx.borrow_mut();
6327 let (bw, bh) = (gfx.width, gfx.height);
6328 for row in 0..th {
6329 for col in 0..tw {
6330 let band_idx = (col * n / tw.max(1)).min(n - 1);
6331 let mag = bands[band_idx].clamp(0., 1.);
6332 let fill_y = (mag * th as f32) as usize;
6333 if row >= th.saturating_sub(fill_y) {
6334 let t = (col as f32 / tw as f32 + time * speed) % 1.0;
6335 let [r, g, b] = tex_palette(&palette, t);
6336 let bright = mag * (1.0 - row as f32 / th as f32 * 0.5);
6337 let (dx, dy) = (tx + col, ty + row);
6338 if dx < bw && dy < bh {
6339 gfx.buffer[dy * bw + dx] =
6340 tex_rgb(r * bright, g * bright, b * bright);
6341 }
6342 }
6343 }
6344 }
6345 return Ok(Value::Unit);
6346 },
6347
6348 "tex_spiral" | "ลายเกลียวหมุน" => {
6350 let (tx, ty, tw, th) = self.tex_rect(&args)?;
6351 let freq = self.arg_num(&args, 4, 5.0)? as f32;
6352 let n_bands = self.arg_num(&args, 5, 8.0)? as f32;
6353 let time = self.arg_num(&args, 6, 0.0)? as f32;
6354 let palette = self.arg_str(&args, 7, "rainbow");
6355 let mut gfx = self.gfx.borrow_mut();
6356 let (bw, bh) = (gfx.width, gfx.height);
6357 for row in 0..th {
6358 for col in 0..tw {
6359 let nx = col as f32 / tw as f32 - 0.5;
6360 let ny = row as f32 / th as f32 - 0.5;
6361 let r = (nx * nx + ny * ny).sqrt();
6362 let theta = ny.atan2(nx);
6363 let t = ((r * freq - theta / std::f32::consts::TAU + time * 0.5) * n_bands
6364 % 1.0)
6365 .abs();
6366 let [cr, cg, cb] = tex_palette(&palette, t);
6367 let (dx, dy) = (tx + col, ty + row);
6368 if dx < bw && dy < bh {
6369 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
6370 }
6371 }
6372 }
6373 return Ok(Value::Unit);
6374 },
6375
6376 "tex_ripple" | "ลายระลอก" => {
6378 let (tx, ty, tw, th) = self.tex_rect(&args)?;
6379 let freq = self.arg_num(&args, 4, 10.0)? as f32;
6380 let rcx = self.arg_num(&args, 5, 0.5)? as f32;
6381 let rcy = self.arg_num(&args, 6, 0.5)? as f32;
6382 let time = self.arg_num(&args, 7, 0.0)? as f32;
6383 let palette = self.arg_str(&args, 8, "ocean");
6384 let mut gfx = self.gfx.borrow_mut();
6385 let (bw, bh) = (gfx.width, gfx.height);
6386 for row in 0..th {
6387 for col in 0..tw {
6388 let nx = col as f32 / tw as f32 - rcx;
6389 let ny = row as f32 / th as f32 - rcy;
6390 let r = (nx * nx + ny * ny).sqrt();
6391 let t = ((r * freq - time) % 1.0).abs();
6392 let [cr, cg, cb] = tex_palette(&palette, t);
6393 let (dx, dy) = (tx + col, ty + row);
6394 if dx < bw && dy < bh {
6395 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
6396 }
6397 }
6398 }
6399 return Ok(Value::Unit);
6400 },
6401
6402 "tex_mandelbrot" | "ลายแมนเดลบรอต" => {
6404 let (tx, ty, tw, th) = self.tex_rect(&args)?;
6405 let zoom = self.arg_num(&args, 4, 1.0)?;
6406 let mcx = self.arg_num(&args, 5, -0.5)?;
6407 let mcy = self.arg_num(&args, 6, 0.0)?;
6408 let max_iter = self.arg_num(&args, 7, 64.0)? as u32;
6409 let palette = self.arg_str(&args, 8, "psychedelic");
6410 let mut gfx = self.gfx.borrow_mut();
6411 let (bw, bh) = (gfx.width, gfx.height);
6412 for row in 0..th {
6413 for col in 0..tw {
6414 let zx0 = (col as f64 / tw as f64 - 0.5) / zoom + mcx;
6415 let zy0 = (row as f64 / th as f64 - 0.5) / zoom + mcy;
6416 let mut x = 0.0f64;
6417 let mut y = 0.0f64;
6418 let mut i = 0u32;
6419 while i < max_iter && x * x + y * y < 4.0 {
6420 let t = x * x - y * y + zx0;
6421 y = 2.0 * x * y + zy0;
6422 x = t;
6423 i += 1;
6424 }
6425 let t = if i == max_iter {
6426 0.0f32
6427 } else {
6428 (i as f32
6429 - (x as f32 * x as f32 + y as f32 * y as f32).ln().ln()
6430 / 2.0f32.ln())
6431 / max_iter as f32
6432 };
6433 let [cr, cg, cb] = tex_palette(&palette, t.clamp(0., 1.));
6434 let (dx, dy) = (tx + col, ty + row);
6435 if dx < bw && dy < bh {
6436 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
6437 }
6438 }
6439 }
6440 return Ok(Value::Unit);
6441 },
6442
6443 "tex_julia" | "ลายจูเลีย" => {
6445 let (tx, ty, tw, th) = self.tex_rect(&args)?;
6446 let c_re = self.arg_num(&args, 4, -0.7)?;
6447 let c_im = self.arg_num(&args, 5, 0.27)?;
6448 let max_iter = self.arg_num(&args, 6, 64.0)? as u32;
6449 let palette = self.arg_str(&args, 7, "neon");
6450 let mut gfx = self.gfx.borrow_mut();
6451 let (bw, bh) = (gfx.width, gfx.height);
6452 for row in 0..th {
6453 for col in 0..tw {
6454 let mut zx = (col as f64 / tw as f64 - 0.5) * 3.5;
6455 let mut zy = (row as f64 / th as f64 - 0.5) * 3.5;
6456 let mut i = 0u32;
6457 while i < max_iter && zx * zx + zy * zy < 4.0 {
6458 let t = zx * zx - zy * zy + c_re;
6459 zy = 2.0 * zx * zy + c_im;
6460 zx = t;
6461 i += 1;
6462 }
6463 let t = i as f32 / max_iter as f32;
6464 let [cr, cg, cb] = tex_palette(&palette, t);
6465 let (dx, dy) = (tx + col, ty + row);
6466 if dx < bw && dy < bh {
6467 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
6468 }
6469 }
6470 }
6471 return Ok(Value::Unit);
6472 },
6473
6474 "tex_voronoi" | "ลายโวโรนอย" => {
6476 let (tx, ty, tw, th) = self.tex_rect(&args)?;
6477 let cells = self.arg_num(&args, 4, 16.0)? as u32;
6478 let seed = self.arg_num(&args, 5, 42.0)? as u32;
6479 let palette = self.arg_str(&args, 6, "rainbow");
6480 let pts: Vec<[f32; 2]> = (0..cells)
6481 .map(|i| {
6482 [
6483 tex_hash(i as i32, 0, seed),
6484 tex_hash(i as i32, 1, seed + 999),
6485 ]
6486 })
6487 .collect();
6488 let mut gfx = self.gfx.borrow_mut();
6489 let (bw, bh) = (gfx.width, gfx.height);
6490 for row in 0..th {
6491 for col in 0..tw {
6492 let (fx, fy) = (col as f32 / tw as f32, row as f32 / th as f32);
6493 let (min_d, nearest) = pts.iter().enumerate().fold(
6494 (f32::MAX, 0usize),
6495 |(d, idx), (i, &[cx, cy])| {
6496 let dd = (fx - cx).powi(2) + (fy - cy).powi(2);
6497 if dd < d {
6498 (dd, i)
6499 } else {
6500 (d, idx)
6501 }
6502 },
6503 );
6504 let t = (nearest as f32 / cells as f32 + min_d * 4.0) % 1.0;
6505 let [cr, cg, cb] = tex_palette(&palette, t);
6506 let (dx, dy) = (tx + col, ty + row);
6507 if dx < bw && dy < bh {
6508 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
6509 }
6510 }
6511 }
6512 return Ok(Value::Unit);
6513 },
6514
6515 "tex_halftone" | "ลายฮาล์ฟโทน" => {
6517 let (tx, ty, tw, th) = self.tex_rect(&args)?;
6518 let dot_size = self.arg_num(&args, 4, 0.05)? as f32;
6519 let time = self.arg_num(&args, 5, 0.0)? as f32;
6520 let palette = self.arg_str(&args, 6, "rainbow");
6521 let mut gfx = self.gfx.borrow_mut();
6522 let (bw, bh) = (gfx.width, gfx.height);
6523 for row in 0..th {
6524 for col in 0..tw {
6525 let (fx, fy) = (col as f32 / tw as f32, row as f32 / th as f32);
6526 let gx = (fx / dot_size).floor();
6527 let gy = (fy / dot_size).floor();
6528 let lx = (fx / dot_size - gx - 0.5) * 2.0;
6529 let ly = (fy / dot_size - gy - 0.5) * 2.0;
6530 let r = (lx * lx + ly * ly).sqrt();
6531 let t = (gx / (1.0 / dot_size) + time * 0.1) % 1.0;
6532 let a = if r < 0.7 {
6533 ((0.7 - r) / 0.7).clamp(0., 1.)
6534 } else {
6535 0.0
6536 };
6537 if a > 0.0 {
6538 let [cr, cg, cb] = tex_palette(&palette, t);
6539 let (dx, dy) = (tx + col, ty + row);
6540 if dx < bw && dy < bh {
6541 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
6542 }
6543 }
6544 }
6545 }
6546 return Ok(Value::Unit);
6547 },
6548
6549 "set_shade_mode" | "设置着色" | "シェード設定" | "셰이드모드" | "ตั้งการแรเงา" =>
6554 {
6555 let m = self.arg_num(&args, 0, 2.0)? as u8;
6556 self.gfx.borrow_mut().shade_mode = m;
6557 return Ok(Value::Unit);
6558 },
6559 "set_cel_bands" | "设置色阶" | "セル段数" | "셀밴드" | "ตั้งระดับสี" =>
6561 {
6562 let n = (self.arg_num(&args, 0, 4.0)? as u32).max(2);
6563 self.gfx.borrow_mut().shade.bands = n;
6564 return Ok(Value::Unit);
6565 },
6566 "set_shadow_color" | "设置阴影色" | "影の色" | "그림자색" | "ตั้งสีเงา" =>
6568 {
6569 let r = self.arg_num(&args, 0, 26.)? as f32 / 255.0;
6570 let g = self.arg_num(&args, 1, 33.)? as f32 / 255.0;
6571 let b = self.arg_num(&args, 2, 77.)? as f32 / 255.0;
6572 self.gfx.borrow_mut().shade.shadow = [r, g, b];
6573 return Ok(Value::Unit);
6574 },
6575 #[cfg(not(target_arch = "wasm32"))]
6581 "crypto_hash" | "แฮชเข้ารหัส" | "几何哈希" | "幾何ハッシュ" | "기하해시" =>
6582 {
6583 let s = self.arg_str(&args, 0, "");
6584 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(
6585 s.as_bytes(),
6586 ))));
6587 },
6588 #[cfg(target_arch = "wasm32")]
6589 "crypto_hash" | "แฮชเข้ารหัส" | "几何哈希" | "幾何ハッシュ" | "기하해시" =>
6590 {
6591 let s = self.arg_str(&args, 0, "");
6592 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(
6593 s.as_bytes(),
6594 ))));
6595 },
6596 #[cfg(not(target_arch = "wasm32"))]
6598 "knot_points" | "จุดปม" | "结点坐标" | "結び目点" | "매듭점" => {
6599 let s = self.arg_str(&args, 0, "");
6600 let shape = ling_crypto::geo::KnotShape::from_bytes(s.as_bytes());
6601 let mut out = Vec::with_capacity(shape.points.len() * 3);
6602 for p in &shape.points {
6603 out.push(Value::Number(p[0] as f64));
6604 out.push(Value::Number(p[1] as f64));
6605 out.push(Value::Number(p[2] as f64));
6606 }
6607 return Ok(Value::List(Rc::new(out)));
6608 },
6609 #[cfg(target_arch = "wasm32")]
6610 "knot_points" | "จุดปม" | "结点坐标" | "結び目点" | "매듭점" => {
6611 let s = self.arg_str(&args, 0, "");
6612 let shape = ling_crypto::geo::KnotShape::from_bytes(s.as_bytes());
6613 let mut out = Vec::with_capacity(shape.points.len() * 3);
6614 for p in &shape.points {
6615 out.push(Value::Number(p[0] as f64));
6616 out.push(Value::Number(p[1] as f64));
6617 out.push(Value::Number(p[2] as f64));
6618 }
6619 return Ok(Value::List(out));
6620 },
6621 #[cfg(not(target_arch = "wasm32"))]
6622 "knot_label" | "ป้ายปม" | "结点标签" | "結び目ラベル" | "매듭라벨" =>
6623 {
6624 let s = self.arg_str(&args, 0, "");
6625 return Ok(Value::Str(
6626 ling_crypto::geo::KnotShape::from_bytes(s.as_bytes()).label(),
6627 ));
6628 },
6629 #[cfg(target_arch = "wasm32")]
6630 "knot_label" | "ป้ายปม" | "结点标签" | "結び目ラベル" | "매듭라벨" =>
6631 {
6632 let s = self.arg_str(&args, 0, "");
6633 return Ok(Value::Str(
6634 ling_crypto::geo::KnotShape::from_bytes(s.as_bytes()).label(),
6635 ));
6636 },
6637 #[cfg(not(target_arch = "wasm32"))]
6639 "knot_keygen" | "hybrid_keygen" | "สร้างกุญแจปม" | "生成密钥" | "鍵生成" | "키생성" =>
6640 {
6641 self.crypto_ids.push(ling_crypto::KnotIdentity::generate());
6642 return Ok(Value::Number((self.crypto_ids.len() - 1) as f64));
6643 },
6644 #[cfg(not(target_arch = "wasm32"))]
6645 "knot_public" | "hybrid_public" | "กุญแจสาธารณะปม" | "公钥" | "公開鍵" | "공개키" =>
6646 {
6647 let h = self.arg_num(&args, 0, 0.0)? as usize;
6648 let pk = self
6649 .crypto_ids
6650 .get(h)
6651 .map(|id| hex_encode(id.public_key()))
6652 .unwrap_or_default();
6653 return Ok(Value::Str(pk));
6654 },
6655 #[cfg(not(target_arch = "wasm32"))]
6657 "knot_encapsulate"
6658 | "hybrid_encapsulate"
6659 | "ห่อกุญแจปม"
6660 | "封装密钥"
6661 | "カプセル化"
6662 | "캡슐화" => {
6663 let pk = hex_decode(&self.arg_str(&args, 0, ""));
6664 match ling_crypto::geo::knot_encapsulate(&pk) {
6665 Ok((ct, ss)) => {
6666 return Ok(Value::List(Rc::new(vec![
6667 Value::Str(hex_encode(&ct)),
6668 Value::Str(hex_encode(&ss)),
6669 ])))
6670 },
6671 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
6672 }
6673 },
6674 #[cfg(not(target_arch = "wasm32"))]
6676 "knot_decapsulate"
6677 | "hybrid_decapsulate"
6678 | "แกะกุญแจปม"
6679 | "解封装密钥"
6680 | "カプセル解除"
6681 | "캡슐해제" => {
6682 let h = self.arg_num(&args, 0, 0.0)? as usize;
6683 let ct = hex_decode(&self.arg_str(&args, 1, ""));
6684 let ss = self
6685 .crypto_ids
6686 .get(h)
6687 .and_then(|id| id.decapsulate(&ct).ok())
6688 .map(|s| hex_encode(&s))
6689 .unwrap_or_default();
6690 return Ok(Value::Str(ss));
6691 },
6692 #[cfg(not(target_arch = "wasm32"))]
6694 "crypto_seal" | "ผนึก" | "封印" | "封印する" | "봉인" => {
6695 let key = hex_to_32(&self.arg_str(&args, 0, ""));
6696 let pt = self.arg_str(&args, 1, "");
6697 match ling_crypto::geo::holo_seal(key, pt.as_bytes()) {
6698 Ok(ct) => return Ok(Value::Str(hex_encode(&ct))),
6699 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
6700 }
6701 },
6702 #[cfg(not(target_arch = "wasm32"))]
6703 "crypto_open" | "เปิดผนึก" | "解封" | "封印解除" | "봉인해제" =>
6704 {
6705 let key = hex_to_32(&self.arg_str(&args, 0, ""));
6706 let ct = hex_decode(&self.arg_str(&args, 1, ""));
6707 match ling_crypto::geo::holo_open(key, &ct) {
6708 Ok(pt) => return Ok(Value::Str(String::from_utf8_lossy(&pt).into_owned())),
6709 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
6710 }
6711 },
6712 #[cfg(not(target_arch = "wasm32"))]
6714 "holo_points" | "จุดโฮโลแกรม" | "全息点" | "ホログラム点" | "홀로그램점" =>
6715 {
6716 let s = self.arg_str(&args, 0, "");
6717 let frags = ling_crypto::geo::scatter(s.as_bytes());
6718 let mut out = Vec::with_capacity(frags.len() * 4);
6719 for f in &frags {
6720 for c in f.coord {
6721 out.push(Value::Number(c as f64));
6722 }
6723 }
6724 return Ok(Value::List(Rc::new(out)));
6725 },
6726 #[cfg(not(target_arch = "wasm32"))]
6727 "holo_fragment_count"
6728 | "จำนวนชิ้นโฮโลแกรม"
6729 | "全息碎片数"
6730 | "ホログラム断片数"
6731 | "홀로그램조각수" => {
6732 let s = self.arg_str(&args, 0, "");
6733 return Ok(Value::Number(
6734 ling_crypto::geo::scatter(s.as_bytes()).len() as f64
6735 ));
6736 },
6737
6738 "ease" => {
6742 let name = self.arg_str(&args, 0, "ease");
6743 let t = self.arg_num(&args, 1, 0.0)? as f32;
6744 return Ok(Value::Number(
6745 ling_ui::Easing::from_name(&name).apply(t) as f64
6746 ));
6747 },
6748
6749 "tween" | "补间" | "補間" | "트윈" | "แทรกค่า" => {
6754 let a = self.arg_num(&args, 0, 0.0)?;
6755 let b = self.arg_num(&args, 1, 0.0)?;
6756 let t = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 1.0);
6757 return Ok(Value::Number(a + (b - a) * t));
6758 },
6759 "tween_ease" | "缓动补间" | "緩和補間" | "이징트윈" | "แทรกนุ่ม" =>
6760 {
6761 let a = self.arg_num(&args, 0, 0.0)? as f32;
6762 let b = self.arg_num(&args, 1, 0.0)? as f32;
6763 let t = self.arg_num(&args, 2, 0.0)? as f32;
6764 let kind = self.arg_str(&args, 3, "linear");
6765 let e = ling_animation::EaseFunction::from_name(&kind);
6766 return Ok(Value::Number(
6767 ling_animation::ease::tween_ease(&a, &b, t, e) as f64,
6768 ));
6769 },
6770 "breathe" | "呼吸" | "호흡" | "หายใจ" => {
6772 let t = self.arg_num(&args, 0, 0.0)? as f32;
6773 let rate = self.arg_num(&args, 1, 1.0)? as f32;
6774 let depth = self.arg_num(&args, 2, 0.1)? as f32;
6775 return Ok(Value::Number(
6776 ling_animation::scalar::breathe(t, rate, depth) as f64,
6777 ));
6778 },
6779 "wobble" | "摆动" | "揺れ" | "흔들림" | "โยก" => {
6780 let t = self.arg_num(&args, 0, 0.0)? as f32;
6781 let freq = self.arg_num(&args, 1, 1.0)? as f32;
6782 let amp = self.arg_num(&args, 2, 1.0)? as f32;
6783 let phase = self.arg_num(&args, 3, 0.0)? as f32;
6784 return Ok(Value::Number(
6785 ling_animation::scalar::wobble(t, freq, amp, phase) as f64,
6786 ));
6787 },
6788 "gait_phase" | "步相" | "歩相" | "걸음위상" | "เฟสก้าว" => {
6789 let t = self.arg_num(&args, 0, 0.0)? as f32;
6790 let speed = self.arg_num(&args, 1, 1.0)? as f32;
6791 return Ok(Value::Number(
6792 ling_animation::scalar::gait_phase(t, speed) as f64
6793 ));
6794 },
6795 "gait_swing" | "步摆" | "歩振り" | "걸음흔들" | "ก้าวแกว่ง" =>
6796 {
6797 let t = self.arg_num(&args, 0, 0.0)? as f32;
6798 let speed = self.arg_num(&args, 1, 1.0)? as f32;
6799 let stride = self.arg_num(&args, 2, 1.0)? as f32;
6800 return Ok(Value::Number(
6801 ling_animation::scalar::gait_swing(t, speed, stride) as f64,
6802 ));
6803 },
6804 "gait_lift" | "抬脚" | "足上げ" | "발들기" | "ยกเท้า" => {
6805 let t = self.arg_num(&args, 0, 0.0)? as f32;
6806 let speed = self.arg_num(&args, 1, 1.0)? as f32;
6807 let height = self.arg_num(&args, 2, 1.0)? as f32;
6808 return Ok(Value::Number(
6809 ling_animation::scalar::gait_lift(t, speed, height) as f64,
6810 ));
6811 },
6812 "spring_to" | "弹向" | "バネ寄せ" | "스프링이동" | "สปริงไป" =>
6813 {
6814 let pos = self.arg_num(&args, 0, 0.0)? as f32;
6815 let vel = self.arg_num(&args, 1, 0.0)? as f32;
6816 let target = self.arg_num(&args, 2, 0.0)? as f32;
6817 let stiffness = self.arg_num(&args, 3, 120.0)? as f32;
6818 let damping = self.arg_num(&args, 4, 14.0)? as f32;
6819 let dt = self.arg_num(&args, 5, 1.0 / 60.0)? as f32;
6820 let (np, nv) =
6821 ling_animation::scalar::spring_step(pos, vel, target, stiffness, damping, dt);
6822 return Ok(Value::List(Rc::new(vec![
6823 Value::Number(np as f64),
6824 Value::Number(nv as f64),
6825 ])));
6826 },
6827 "ik2" | "反解" | "逆運動" | "역운동" | "ไอเค2" => {
6828 let l1 = self.arg_num(&args, 0, 1.0)? as f32;
6829 let l2 = self.arg_num(&args, 1, 1.0)? as f32;
6830 let tx = self.arg_num(&args, 2, 0.0)? as f32;
6831 let ty = self.arg_num(&args, 3, 0.0)? as f32;
6832 let (sh, el) = ling_animation::scalar::two_bone_ik(l1, l2, tx, ty);
6833 return Ok(Value::List(Rc::new(vec![
6834 Value::Number(sh as f64),
6835 Value::Number(el as f64),
6836 ])));
6837 },
6838 "gear_couple" | "齿轮联动" | "歯車連動" | "기어연동" | "เฟืองทด" =>
6840 {
6841 let angle = self.arg_num(&args, 0, 0.0)? as f32;
6842 let ti = self.arg_num(&args, 1, 1.0)? as f32;
6843 let to = self.arg_num(&args, 2, 1.0)? as f32;
6844 return Ok(Value::Number(
6845 ling_animation::scalar::gear(angle, ti, to) as f64
6846 ));
6847 },
6848 "gear_train" | "齿轮组" | "歯車列" | "기어열" | "ชุดเฟือง" => {
6849 let angle = self.arg_num(&args, 0, 0.0)? as f32;
6850 let teeth: Vec<f32> = match args.get(1) {
6851 Some(Value::List(items)) => items
6852 .iter()
6853 .filter_map(|v| {
6854 if let Value::Number(n) = v {
6855 Some(*n as f32)
6856 } else {
6857 None
6858 }
6859 })
6860 .collect(),
6861 _ => Vec::new(),
6862 };
6863 let out = ling_animation::mechanism::gear_train(angle, &teeth);
6864 return Ok(Value::List(Rc::new(
6865 out.into_iter().map(|a| Value::Number(a as f64)).collect(),
6866 )));
6867 },
6868 "cam_lift" | "凸轮升程" | "カム揚程" | "캠리프트" | "ยกลูกเบี้ยว" =>
6869 {
6870 let angle = self.arg_num(&args, 0, 0.0)? as f32;
6871 let lift = self.arg_num(&args, 1, 1.0)? as f32;
6872 return Ok(Value::Number(
6873 ling_animation::scalar::cam_lift(angle, lift) as f64
6874 ));
6875 },
6876 "piston" | "活塞" | "ピストン" | "피스톤" | "ลูกสูบ" => {
6877 let angle = self.arg_num(&args, 0, 0.0)? as f32;
6878 let crank = self.arg_num(&args, 1, 1.0)? as f32;
6879 let rod = self.arg_num(&args, 2, 2.0)? as f32;
6880 return Ok(Value::Number(
6881 ling_animation::scalar::piston(angle, crank, rod) as f64,
6882 ));
6883 },
6884 "rack" | "齿条" | "ラック" | "랙" | "แร็ค" => {
6885 let angle = self.arg_num(&args, 0, 0.0)? as f32;
6886 let radius = self.arg_num(&args, 1, 1.0)? as f32;
6887 return Ok(Value::Number(
6888 ling_animation::scalar::rack(angle, radius) as f64
6889 ));
6890 },
6891 #[cfg(not(target_arch = "wasm32"))]
6892 "mouse_x" => {
6893 let gfx = self.gfx.borrow();
6894 let v = gfx
6895 .window
6896 .as_ref()
6897 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
6898 .map(|p| p.0 as f64)
6899 .unwrap_or(0.0);
6900 return Ok(Value::Number(v));
6901 },
6902 #[cfg(target_arch = "wasm32")]
6903 "mouse_x" => {
6904 return Ok(Value::Number(0.0));
6905 },
6906 #[cfg(not(target_arch = "wasm32"))]
6907 "mouse_y" => {
6908 let gfx = self.gfx.borrow();
6909 let v = gfx
6910 .window
6911 .as_ref()
6912 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
6913 .map(|p| p.1 as f64)
6914 .unwrap_or(0.0);
6915 return Ok(Value::Number(v));
6916 },
6917 #[cfg(target_arch = "wasm32")]
6918 "mouse_y" => {
6919 return Ok(Value::Number(0.0));
6920 },
6921 #[cfg(not(target_arch = "wasm32"))]
6922 "mouse_down" => {
6923 let gfx = self.gfx.borrow();
6924 let d = gfx
6925 .window
6926 .as_ref()
6927 .map(|w| w.get_mouse_down(minifb::MouseButton::Left))
6928 .unwrap_or(false);
6929 return Ok(Value::Bool(d));
6930 },
6931 #[cfg(target_arch = "wasm32")]
6932 "mouse_down" => {
6933 return Ok(Value::Bool(false));
6934 },
6935 #[cfg(not(target_arch = "wasm32"))]
6936 "mouse_down_right" | "เมาส์ขวา" => {
6937 let gfx = self.gfx.borrow();
6938 let d = gfx
6939 .window
6940 .as_ref()
6941 .map(|w| w.get_mouse_down(minifb::MouseButton::Right))
6942 .unwrap_or(false);
6943 return Ok(Value::Bool(d));
6944 },
6945 #[cfg(target_arch = "wasm32")]
6946 "mouse_down_right" | "เมาส์ขวา" => {
6947 return Ok(Value::Bool(false));
6948 },
6949 #[cfg(not(target_arch = "wasm32"))]
6950 "mouse_down_middle" | "เมาส์กลาง" => {
6951 let gfx = self.gfx.borrow();
6952 let d = gfx
6953 .window
6954 .as_ref()
6955 .map(|w| w.get_mouse_down(minifb::MouseButton::Middle))
6956 .unwrap_or(false);
6957 return Ok(Value::Bool(d));
6958 },
6959 #[cfg(target_arch = "wasm32")]
6960 "mouse_down_middle" | "เมาส์กลาง" => {
6961 return Ok(Value::Bool(false));
6962 },
6963 #[cfg(not(target_arch = "wasm32"))]
6964 "ui_hot" | "热区" | "ホットエリア" | "핫존" | "พื้นที่สัมผัส" =>
6965 {
6966 let x = self.arg_num(&args, 0, 0.0)? as f32;
6967 let y = self.arg_num(&args, 1, 0.0)? as f32;
6968 let w = self.arg_num(&args, 2, 0.0)? as f32;
6969 let h = self.arg_num(&args, 3, 0.0)? as f32;
6970 let gfx = self.gfx.borrow();
6971 let (mx, my) = gfx
6972 .window
6973 .as_ref()
6974 .and_then(|win| win.get_mouse_pos(minifb::MouseMode::Clamp))
6975 .unwrap_or((0.0, 0.0));
6976 return Ok(Value::Bool(ling_ui::holo::hit_rect(mx, my, x, y, w, h)));
6977 },
6978 #[cfg(target_arch = "wasm32")]
6979 "ui_hot" | "热区" | "ホットエリア" | "핫존" | "พื้นที่สัมผัส" =>
6980 {
6981 return Ok(Value::Bool(false));
6982 },
6983 "ui_text" | "界面文字" | "UI文字" | "UI텍스트" | "ข้อความหน้าจอ" =>
6985 {
6986 let x = self.arg_num(&args, 0, 0.0)? as f32;
6987 let y = self.arg_num(&args, 1, 0.0)? as f32;
6988 let scale = self.arg_num(&args, 2, 16.0)? as f32;
6989 let s = self.arg_str(&args, 3, "");
6990 let segs = ling_ui::holo::text_lines(&s, x, y, scale * 0.62, scale, scale * 0.24);
6991 let mut gfx = self.gfx.borrow_mut();
6992 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
6993 for sg in segs {
6994 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
6995 }
6996 return Ok(Value::Unit);
6997 },
6998 #[cfg(not(target_arch = "wasm32"))]
7001 "font_load" | "โหลดฟอนต์" | "加载字体" | "フォント読込" | "글꼴로드" =>
7002 {
7003 let path = self.arg_str(&args, 0, "");
7004 let weight = match self.arg_num(&args, 1, 0.0)? {
7006 w if w > 0.0 => Some(w as f32),
7007 _ => None,
7008 };
7009 let mut loaded = ling_graphics::VectorFont::from_path_weight(&path, weight);
7011 if loaded.is_err() {
7012 if let Some(dir) = &self.source_dir {
7013 let joined = dir.join(&path);
7014 loaded = ling_graphics::VectorFont::from_path_weight(
7015 &joined.to_string_lossy(),
7016 weight,
7017 );
7018 }
7019 }
7020 match loaded {
7021 Ok(f) => {
7022 let id = self.fonts.len();
7023 self.fonts.push(f);
7024 return Ok(Value::Number(id as f64));
7025 },
7026 Err(e) => {
7027 eprintln!("font_load failed ({path}): {e}");
7028 return Ok(Value::Number(-1.0));
7029 },
7030 }
7031 },
7032 #[cfg(target_arch = "wasm32")]
7033 "font_load" | "โหลดฟอนต์" | "加载字体" | "フォント読込" | "글꼴로드" =>
7034 {
7035 return Ok(Value::Number(-1.0));
7038 },
7039 #[cfg(not(target_arch = "wasm32"))]
7042 "font_text" | "ข้อความฟอนต์" | "字体文本" | "フォント文字" | "글꼴텍스트" =>
7043 {
7044 let id = self.arg_num(&args, 0, 0.0)? as i64;
7045 let x = self.arg_num(&args, 1, 0.0)? as f32;
7046 let y = self.arg_num(&args, 2, 0.0)? as f32;
7047 let px = self.arg_num(&args, 3, 16.0)? as f32;
7048 let s = self.arg_str(&args, 4, "");
7049 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
7050 let strokes = self.font_layout_2d(id as usize, x, y, px, &s);
7051 let mut gfx = self.gfx.borrow_mut();
7052 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
7053 for pl in &strokes {
7054 for seg in pl.windows(2) {
7055 crate::gfx::raster::draw_line_aa(
7056 &mut gfx.buffer,
7057 w,
7058 h,
7059 color,
7060 add,
7061 seg[0][0],
7062 seg[0][1],
7063 seg[1][0],
7064 seg[1][1],
7065 );
7066 }
7067 }
7068 }
7069 return Ok(Value::Unit);
7070 },
7071 #[cfg(target_arch = "wasm32")]
7072 "font_text" | "ข้อความฟอนต์" | "字体文本" | "フォント文字" | "글꼴텍스트" =>
7073 {
7074 return Ok(Value::Unit);
7075 },
7076 #[cfg(not(target_arch = "wasm32"))]
7078 "font_text_fill" | "เติมฟอนต์" | "填充字体" | "フォント塗り" | "글꼴채움" =>
7079 {
7080 let id = self.arg_num(&args, 0, 0.0)? as i64;
7081 let x = self.arg_num(&args, 1, 0.0)? as f32;
7082 let y = self.arg_num(&args, 2, 0.0)? as f32;
7083 let px = self.arg_num(&args, 3, 16.0)? as f32;
7084 let s = self.arg_str(&args, 4, "");
7085 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
7086 let glyphs = self.font_layout_2d_glyphs(id as usize, x, y, px, &s);
7088 let mut gfx = self.gfx.borrow_mut();
7089 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
7090 for contours in &glyphs {
7091 crate::gfx::raster::fill_contours_aa(
7092 &mut gfx.buffer,
7093 w,
7094 h,
7095 color,
7096 add,
7097 contours,
7098 );
7099 }
7100 }
7101 return Ok(Value::Unit);
7102 },
7103 #[cfg(target_arch = "wasm32")]
7104 "font_text_fill" | "เติมฟอนต์" | "填充字体" | "フォント塗り" | "글꼴채움" =>
7105 {
7106 return Ok(Value::Unit);
7107 },
7108 #[cfg(not(target_arch = "wasm32"))]
7112 "font_text_3d" | "ข้อความฟอนต์3มิติ" | "字体3D" | "フォント3D" | "글꼴3D" =>
7113 {
7114 let id = self.arg_num(&args, 0, 0.0)? as i64;
7115 let cx = self.arg_num(&args, 1, 0.0)? as f32;
7116 let cy = self.arg_num(&args, 2, 0.0)? as f32;
7117 let cz = self.arg_num(&args, 3, 0.0)? as f32;
7118 let ux = self.arg_num(&args, 4, 1.0)? as f32;
7119 let uy = self.arg_num(&args, 5, 0.0)? as f32;
7120 let uz = self.arg_num(&args, 6, 0.0)? as f32;
7121 let vx = self.arg_num(&args, 7, 0.0)? as f32;
7122 let vy = self.arg_num(&args, 8, 1.0)? as f32;
7123 let vz = self.arg_num(&args, 9, 0.0)? as f32;
7124 let size = self.arg_num(&args, 10, 1.0)? as f32;
7125 let s = self.arg_str(&args, 11, "");
7126 if id >= 0 && (id as usize) < self.fonts.len() && size > 0.0 {
7127 let font = &mut self.fonts[id as usize];
7129 let asc = font.ascent();
7130 let mut pen = 0.0f32;
7131 let mut lines: Vec<[f32; 6]> = Vec::new();
7132 for ch in s.chars() {
7133 let go = font.glyph_outline(ch, 0.01);
7134 for pl in &go.polylines {
7135 for seg in pl.windows(2) {
7136 let map = |p: [f32; 2]| {
7137 let a = pen + p[0];
7138 let b = p[1] - asc; [
7140 cx + a * size * ux + b * size * vx,
7141 cy + a * size * uy + b * size * vy,
7142 cz + a * size * uz + b * size * vz,
7143 ]
7144 };
7145 let p0 = map(seg[0]);
7146 let p1 = map(seg[1]);
7147 lines.push([p0[0], p0[1], p0[2], p1[0], p1[1], p1[2]]);
7148 }
7149 }
7150 pen += go.advance;
7151 }
7152 let mut gfx = self.gfx.borrow_mut();
7153 let color = gfx.color;
7154 let near = -gfx.camera.zdist + 0.05;
7155 for l in &lines {
7156 let (mut ax, mut ay, mut az) = (l[0], l[1], l[2]);
7157 let (mut bx, mut by, mut bz) = (l[3], l[4], l[5]);
7158 let da = gfx.camera.depth(ax, ay, az);
7159 let db = gfx.camera.depth(bx, by, bz);
7160 if da <= near && db <= near {
7161 continue;
7162 }
7163 if da <= near {
7164 let t = (near - da) / (db - da);
7165 ax += t * (bx - ax);
7166 ay += t * (by - ay);
7167 az += t * (bz - az);
7168 } else if db <= near {
7169 let t = (near - da) / (db - da);
7170 bx = ax + t * (bx - ax);
7171 by = ay + t * (by - ay);
7172 bz = az + t * (bz - az);
7173 }
7174 let (sax, say, da2) = gfx.camera.project(ax, ay, az);
7175 let (sbx, sby, db2) = gfx.camera.project(bx, by, bz);
7176 let depth = (da2 + db2) / 2.0;
7177 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
7178 }
7179 }
7180 return Ok(Value::Unit);
7181 },
7182 #[cfg(target_arch = "wasm32")]
7183 "font_text_3d" | "ข้อความฟอนต์3มิติ" | "字体3D" | "フォント3D" | "글꼴3D" =>
7184 {
7185 return Ok(Value::Unit);
7186 },
7187 #[cfg(not(target_arch = "wasm32"))]
7189 "font_width" | "ความกว้างฟอนต์" | "字体宽度" | "フォント幅" | "글꼴너비" =>
7190 {
7191 let id = self.arg_num(&args, 0, 0.0)? as i64;
7192 let px = self.arg_num(&args, 1, 16.0)? as f32;
7193 let s = self.arg_str(&args, 2, "");
7194 if id >= 0 && (id as usize) < self.fonts.len() {
7195 return Ok(Value::Number(self.fonts[id as usize].measure(&s, px) as f64));
7196 }
7197 return Ok(Value::Number(0.0));
7198 },
7199 #[cfg(target_arch = "wasm32")]
7200 "font_width" | "ความกว้างฟอนต์" | "字体宽度" | "フォント幅" | "글꼴너비" =>
7201 {
7202 return Ok(Value::Number(0.0));
7203 },
7204 "ui_frame" | "边框" | "フレーム枠" | "프레임틀" | "กรอบ" => {
7206 let x = self.arg_num(&args, 0, 0.0)? as f32;
7207 let y = self.arg_num(&args, 1, 0.0)? as f32;
7208 let w0 = self.arg_num(&args, 2, 0.0)? as f32;
7209 let h0 = self.arg_num(&args, 3, 0.0)? as f32;
7210 let l = self.arg_num(&args, 4, 14.0)? as f32;
7211 let segs = ling_ui::holo::corner_brackets(x, y, w0, h0, l);
7212 let mut gfx = self.gfx.borrow_mut();
7213 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
7214 for sg in segs {
7215 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
7216 }
7217 return Ok(Value::Unit);
7218 },
7219 "ui_bevel" | "斜角框" | "ベベル枠" | "베벨틀" | "กรอบเฉียง" =>
7221 {
7222 let x = self.arg_num(&args, 0, 0.0)? as f32;
7223 let y = self.arg_num(&args, 1, 0.0)? as f32;
7224 let w0 = self.arg_num(&args, 2, 0.0)? as f32;
7225 let h0 = self.arg_num(&args, 3, 0.0)? as f32;
7226 let bv = self.arg_num(&args, 4, 10.0)? as f32;
7227 let segs = ling_ui::holo::beveled_rect(x, y, w0, h0, bv);
7228 let mut gfx = self.gfx.borrow_mut();
7229 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
7230 for sg in segs {
7231 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
7232 }
7233 return Ok(Value::Unit);
7234 },
7235
7236 #[cfg(not(target_arch = "wasm32"))]
7242 "ui_theme" | "界面主题" | "UIテーマ" | "인터페이스테마" | "ธีมส่วนติดต่อ" =>
7243 {
7244 let cur = self.ui_theme;
7245 let primary = self.color_at(&args, 0, cur.primary);
7246 let accent = self.color_at(&args, 3, cur.accent);
7247 let track = self.color_at(&args, 6, cur.track);
7248 let warn = self.color_at(&args, 9, cur.warn);
7249 let text = self.color_at(&args, 12, cur.text);
7250 let bg = self.color_at(&args, 15, cur.bg);
7251 self.ui_theme = UiTheme { primary, accent, track, warn, text, bg };
7252 return Ok(Value::Unit);
7253 },
7254
7255 #[cfg(not(target_arch = "wasm32"))]
7257 "ui_radar" | "雷达" | "レーダー" | "레이더" | "เรดาร์" => {
7258 let cx = self.arg_num(&args, 0, 0.)? as f32;
7259 let cy = self.arg_num(&args, 1, 0.)? as f32;
7260 let r = self.arg_num(&args, 2, 60.)? as f32;
7261 let sweep = self.arg_num(&args, 3, 0.)? as f32;
7262 let th = self.ui_theme;
7263 let prim = self.color_at(&args, 4, th.primary);
7264 self.draw_ui(&ling_ui::widgets::radar(
7265 cx, cy, r, sweep, prim, th.accent, th.track,
7266 ));
7267 return Ok(Value::Unit);
7268 },
7269 #[cfg(not(target_arch = "wasm32"))]
7270 "ui_compass" | "罗盘" | "コンパス" | "나침반" | "เข็มทิศ" => {
7271 let x = self.arg_num(&args, 0, 0.)? as f32;
7272 let y = self.arg_num(&args, 1, 0.)? as f32;
7273 let w0 = self.arg_num(&args, 2, 300.)? as f32;
7274 let h0 = self.arg_num(&args, 3, 24.)? as f32;
7275 let head = self.arg_num(&args, 4, 0.)? as f32;
7276 let th = self.ui_theme;
7277 let prim = self.color_at(&args, 5, th.primary);
7278 self.draw_ui(&ling_ui::widgets::compass(
7279 x, y, w0, h0, head, prim, th.track,
7280 ));
7281 return Ok(Value::Unit);
7282 },
7283 #[cfg(not(target_arch = "wasm32"))]
7284 "ui_reticle" | "准星" | "照準" | "조준선" | "เป้าเล็ง" => {
7285 let cx = self.arg_num(&args, 0, 0.)? as f32;
7286 let cy = self.arg_num(&args, 1, 0.)? as f32;
7287 let r = self.arg_num(&args, 2, 30.)? as f32;
7288 let spread = self.arg_num(&args, 3, 0.)? as f32;
7289 let th = self.ui_theme;
7290 let prim = self.color_at(&args, 4, th.primary);
7291 self.draw_ui(&ling_ui::widgets::reticle(cx, cy, r, spread, prim));
7292 return Ok(Value::Unit);
7293 },
7294 #[cfg(not(target_arch = "wasm32"))]
7295 "ui_target" | "锁定框" | "ターゲット" | "표적" | "กรอบเป้า" =>
7296 {
7297 let x = self.arg_num(&args, 0, 0.)? as f32;
7298 let y = self.arg_num(&args, 1, 0.)? as f32;
7299 let w0 = self.arg_num(&args, 2, 80.)? as f32;
7300 let h0 = self.arg_num(&args, 3, 80.)? as f32;
7301 let lock = self.arg_num(&args, 4, 0.)? as f32;
7302 let th = self.ui_theme;
7303 let prim = self.color_at(&args, 5, th.primary);
7304 self.draw_ui(&ling_ui::widgets::target(
7305 x, y, w0, h0, lock, prim, th.accent,
7306 ));
7307 return Ok(Value::Unit);
7308 },
7309 #[cfg(not(target_arch = "wasm32"))]
7310 "ui_panel" | "面板" | "パネル" | "패널" | "แผง" => {
7311 let x = self.arg_num(&args, 0, 0.)? as f32;
7312 let y = self.arg_num(&args, 1, 0.)? as f32;
7313 let w0 = self.arg_num(&args, 2, 200.)? as f32;
7314 let h0 = self.arg_num(&args, 3, 120.)? as f32;
7315 let bv = self.arg_num(&args, 4, 12.)? as f32;
7316 let th = self.ui_theme;
7317 let prim = self.color_at(&args, 5, th.primary);
7318 self.draw_ui(&ling_ui::widgets::panel(x, y, w0, h0, bv, prim, th.bg));
7319 return Ok(Value::Unit);
7320 },
7321 #[cfg(not(target_arch = "wasm32"))]
7322 "ui_scanlines" | "扫描线" | "走査線" | "스캔라인" | "เส้นสแกน" =>
7323 {
7324 let x = self.arg_num(&args, 0, 0.)? as f32;
7325 let y = self.arg_num(&args, 1, 0.)? as f32;
7326 let w0 = self.arg_num(&args, 2, 200.)? as f32;
7327 let h0 = self.arg_num(&args, 3, 120.)? as f32;
7328 let dens = self.arg_num(&args, 4, 24.)? as usize;
7329 let th = self.ui_theme;
7330 let line = self.color_at(&args, 5, th.track);
7331 self.draw_ui(&ling_ui::widgets::scanlines(x, y, w0, h0, dens, line));
7332 return Ok(Value::Unit);
7333 },
7334
7335 #[cfg(not(target_arch = "wasm32"))]
7337 "ui_bar" | "进度条" | "バー" | "막대" | "แถบ" => {
7338 let x = self.arg_num(&args, 0, 0.)? as f32;
7339 let y = self.arg_num(&args, 1, 0.)? as f32;
7340 let w0 = self.arg_num(&args, 2, 160.)? as f32;
7341 let h0 = self.arg_num(&args, 3, 16.)? as f32;
7342 let val = self.arg_num(&args, 4, 0.)? as f32;
7343 let max = self.arg_num(&args, 5, 1.)? as f32;
7344 let th = self.ui_theme;
7345 let fill = self.color_at(&args, 6, th.primary);
7346 self.draw_ui(&ling_ui::widgets::bar(
7347 x,
7348 y,
7349 w0,
7350 h0,
7351 val / max.max(1e-6),
7352 fill,
7353 th.track,
7354 ));
7355 return Ok(Value::Unit);
7356 },
7357 #[cfg(not(target_arch = "wasm32"))]
7358 "ui_segbar" | "分段条" | "分割バー" | "분할막대" | "แถบแบ่ง" =>
7359 {
7360 let x = self.arg_num(&args, 0, 0.)? as f32;
7361 let y = self.arg_num(&args, 1, 0.)? as f32;
7362 let w0 = self.arg_num(&args, 2, 160.)? as f32;
7363 let h0 = self.arg_num(&args, 3, 16.)? as f32;
7364 let val = self.arg_num(&args, 4, 0.)? as f32;
7365 let max = self.arg_num(&args, 5, 1.)? as f32;
7366 let segs = self.arg_num(&args, 6, 10.)? as usize;
7367 let th = self.ui_theme;
7368 let fill = self.color_at(&args, 7, th.primary);
7369 self.draw_ui(&ling_ui::widgets::segbar(
7370 x,
7371 y,
7372 w0,
7373 h0,
7374 val / max.max(1e-6),
7375 segs,
7376 fill,
7377 th.track,
7378 ));
7379 return Ok(Value::Unit);
7380 },
7381 #[cfg(not(target_arch = "wasm32"))]
7382 "ui_gauge" | "仪表" | "ゲージ" | "게이지" | "มาตรวัด" => {
7383 let cx = self.arg_num(&args, 0, 0.)? as f32;
7384 let cy = self.arg_num(&args, 1, 0.)? as f32;
7385 let r = self.arg_num(&args, 2, 50.)? as f32;
7386 let val = self.arg_num(&args, 3, 0.)? as f32;
7387 let max = self.arg_num(&args, 4, 1.)? as f32;
7388 let th = self.ui_theme;
7389 let needle = self.color_at(&args, 5, th.warn);
7390 self.draw_ui(&ling_ui::widgets::gauge(
7391 cx,
7392 cy,
7393 r,
7394 val / max.max(1e-6),
7395 needle,
7396 th.accent,
7397 th.track,
7398 ));
7399 return Ok(Value::Unit);
7400 },
7401 #[cfg(not(target_arch = "wasm32"))]
7402 "ui_ring" | "环表" | "リングメーター" | "링미터" | "วงแหวนวัด" =>
7403 {
7404 let cx = self.arg_num(&args, 0, 0.)? as f32;
7405 let cy = self.arg_num(&args, 1, 0.)? as f32;
7406 let r = self.arg_num(&args, 2, 40.)? as f32;
7407 let val = self.arg_num(&args, 3, 0.)? as f32;
7408 let max = self.arg_num(&args, 4, 1.)? as f32;
7409 let th = self.ui_theme;
7410 let fill = self.color_at(&args, 5, th.primary);
7411 self.draw_ui(&ling_ui::widgets::ring(
7412 cx,
7413 cy,
7414 r,
7415 val / max.max(1e-6),
7416 fill,
7417 th.track,
7418 ));
7419 return Ok(Value::Unit);
7420 },
7421 #[cfg(not(target_arch = "wasm32"))]
7422 "ui_vu" | "音量条" | "VUメーター" | "음량막대" | "มาตรเสียง" =>
7423 {
7424 let x = self.arg_num(&args, 0, 0.)? as f32;
7425 let y = self.arg_num(&args, 1, 0.)? as f32;
7426 let w0 = self.arg_num(&args, 2, 160.)? as f32;
7427 let h0 = self.arg_num(&args, 3, 60.)? as f32;
7428 let levels = self.arg_list_f32(&args, 4);
7429 let th = self.ui_theme;
7430 let fill = self.color_at(&args, 5, th.primary);
7431 self.draw_ui(&ling_ui::widgets::vu(x, y, w0, h0, &levels, fill, th.warn));
7432 return Ok(Value::Unit);
7433 },
7434 #[cfg(not(target_arch = "wasm32"))]
7435 "ui_spark" | "迷你图" | "スパークライン" | "스파크라인" | "กราฟจิ๋ว" =>
7436 {
7437 let x = self.arg_num(&args, 0, 0.)? as f32;
7438 let y = self.arg_num(&args, 1, 0.)? as f32;
7439 let w0 = self.arg_num(&args, 2, 160.)? as f32;
7440 let h0 = self.arg_num(&args, 3, 40.)? as f32;
7441 let vals = self.arg_list_f32(&args, 4);
7442 let th = self.ui_theme;
7443 let line = self.color_at(&args, 5, th.accent);
7444 self.draw_ui(&ling_ui::widgets::spark(x, y, w0, h0, &vals, line));
7445 return Ok(Value::Unit);
7446 },
7447 #[cfg(not(target_arch = "wasm32"))]
7448 "ui_battery" | "电池" | "バッテリー" | "배터리" | "แบตเตอรี่" =>
7449 {
7450 let x = self.arg_num(&args, 0, 0.)? as f32;
7451 let y = self.arg_num(&args, 1, 0.)? as f32;
7452 let w0 = self.arg_num(&args, 2, 50.)? as f32;
7453 let h0 = self.arg_num(&args, 3, 22.)? as f32;
7454 let val = self.arg_num(&args, 4, 1.)? as f32;
7455 let max = self.arg_num(&args, 5, 1.)? as f32;
7456 let th = self.ui_theme;
7457 let fill = self.color_at(&args, 6, th.accent);
7458 self.draw_ui(&ling_ui::widgets::battery(
7459 x,
7460 y,
7461 w0,
7462 h0,
7463 val / max.max(1e-6),
7464 fill,
7465 th.track,
7466 th.warn,
7467 ));
7468 return Ok(Value::Unit);
7469 },
7470
7471 #[cfg(not(target_arch = "wasm32"))]
7473 "ui_button" | "按钮" | "ボタン" | "버튼" | "ปุ่ม" => {
7474 let x = self.arg_num(&args, 0, 0.)? as f32;
7475 let y = self.arg_num(&args, 1, 0.)? as f32;
7476 let w0 = self.arg_num(&args, 2, 120.)? as f32;
7477 let h0 = self.arg_num(&args, 3, 40.)? as f32;
7478 let (mx, my, down) = self.mouse_now();
7479 let hover = ling_ui::holo::hit_rect(mx, my, x, y, w0, h0);
7480 let clicked = hover && down && !self.mouse_was_down;
7481 let th = self.ui_theme;
7482 let prim = self.color_at(&args, 4, th.primary);
7483 self.draw_ui(&ling_ui::widgets::button(
7484 x,
7485 y,
7486 w0,
7487 h0,
7488 hover,
7489 down && hover,
7490 prim,
7491 th.bg,
7492 ));
7493 return Ok(Value::Number(if clicked { 1.0 } else { 0.0 }));
7494 },
7495 #[cfg(not(target_arch = "wasm32"))]
7496 "ui_toggle" | "开关" | "トグル" | "토글" | "สวิตช์" => {
7497 let x = self.arg_num(&args, 0, 0.)? as f32;
7498 let y = self.arg_num(&args, 1, 0.)? as f32;
7499 let w0 = self.arg_num(&args, 2, 52.)? as f32;
7500 let h0 = self.arg_num(&args, 3, 24.)? as f32;
7501 let mut state = self.arg_num(&args, 4, 0.)? > 0.5;
7502 let (mx, my, down) = self.mouse_now();
7503 let hover = ling_ui::holo::hit_rect(mx, my, x, y, w0, h0);
7504 if hover && down && !self.mouse_was_down {
7505 state = !state;
7506 }
7507 let th = self.ui_theme;
7508 let on = self.color_at(&args, 5, th.accent);
7509 self.draw_ui(&ling_ui::widgets::toggle(x, y, w0, h0, state, on, th.track));
7510 return Ok(Value::Number(if state { 1.0 } else { 0.0 }));
7511 },
7512 #[cfg(not(target_arch = "wasm32"))]
7513 "ui_slider" | "滑块" | "スライダー" | "슬라이더" | "แถบเลื่อน" =>
7514 {
7515 let x = self.arg_num(&args, 0, 0.)? as f32;
7516 let y = self.arg_num(&args, 1, 0.)? as f32;
7517 let w0 = self.arg_num(&args, 2, 160.)? as f32;
7518 let mut val = self.arg_num(&args, 3, 0.)? as f32;
7519 let mn = self.arg_num(&args, 4, 0.)? as f32;
7520 let mx_ = self.arg_num(&args, 5, 1.)? as f32;
7521 let (mx, my, down) = self.mouse_now();
7522 let hover = ling_ui::holo::hit_rect(mx, my, x - 8.0, y - 10.0, w0 + 16.0, 20.0);
7523 if hover && down {
7524 let frac = ((mx - x) / w0).max(0.0).min(1.0);
7525 val = mn + (mx_ - mn) * frac;
7526 }
7527 let frac = ((val - mn) / (mx_ - mn).abs().max(1e-6)).max(0.0).min(1.0);
7528 let th = self.ui_theme;
7529 let fill = self.color_at(&args, 6, th.primary);
7530 self.draw_ui(&ling_ui::widgets::slider(
7531 x, y, w0, frac, hover, fill, th.track,
7532 ));
7533 return Ok(Value::Number(val as f64));
7534 },
7535 #[cfg(not(target_arch = "wasm32"))]
7536 "ui_checkbox" | "复选框" | "チェックボックス" | "체크박스" | "ช่องเลือก" =>
7537 {
7538 let x = self.arg_num(&args, 0, 0.)? as f32;
7539 let y = self.arg_num(&args, 1, 0.)? as f32;
7540 let s = self.arg_num(&args, 2, 20.)? as f32;
7541 let mut checked = self.arg_num(&args, 3, 0.)? > 0.5;
7542 let (mx, my, down) = self.mouse_now();
7543 let hover = ling_ui::holo::hit_rect(mx, my, x, y, s, s);
7544 if hover && down && !self.mouse_was_down {
7545 checked = !checked;
7546 }
7547 let th = self.ui_theme;
7548 let prim = self.color_at(&args, 4, th.primary);
7549 self.draw_ui(&ling_ui::widgets::checkbox(
7550 x, y, s, checked, hover, prim, th.track,
7551 ));
7552 return Ok(Value::Number(if checked { 1.0 } else { 0.0 }));
7553 },
7554 #[cfg(not(target_arch = "wasm32"))]
7555 "ui_tabs" | "标签页" | "タブ" | "탭" | "แท็บ" => {
7556 let x = self.arg_num(&args, 0, 0.)? as f32;
7557 let y = self.arg_num(&args, 1, 0.)? as f32;
7558 let w0 = self.arg_num(&args, 2, 240.)? as f32;
7559 let h0 = self.arg_num(&args, 3, 28.)? as f32;
7560 let count = self.arg_num(&args, 4, 3.)? as usize;
7561 let mut active = self.arg_num(&args, 5, 0.)? as i32;
7562 let (mx, my, down) = self.mouse_now();
7563 let mut hover = -1;
7564 if my >= y && my <= y + h0 && mx >= x && mx <= x + w0 && count > 0 {
7565 hover = (((mx - x) / (w0 / count as f32)) as i32)
7566 .max(0)
7567 .min(count as i32 - 1);
7568 if down && !self.mouse_was_down {
7569 active = hover;
7570 }
7571 }
7572 let th = self.ui_theme;
7573 let prim = self.color_at(&args, 6, th.primary);
7574 self.draw_ui(&ling_ui::widgets::tabs(
7575 x,
7576 y,
7577 w0,
7578 h0,
7579 count,
7580 active as usize,
7581 hover,
7582 prim,
7583 th.track,
7584 ));
7585 return Ok(Value::Number(active as f64));
7586 },
7587 #[cfg(not(target_arch = "wasm32"))]
7588 "ui_progress" | "进度" | "プログレス" | "진행바" | "ความคืบหน้า" =>
7589 {
7590 let x = self.arg_num(&args, 0, 0.)? as f32;
7591 let y = self.arg_num(&args, 1, 0.)? as f32;
7592 let w0 = self.arg_num(&args, 2, 200.)? as f32;
7593 let h0 = self.arg_num(&args, 3, 12.)? as f32;
7594 let frac = self.arg_num(&args, 4, 0.)? as f32;
7595 let th = self.ui_theme;
7596 let fill = self.color_at(&args, 5, th.accent);
7597 self.draw_ui(&ling_ui::widgets::progress(
7598 x, y, w0, h0, frac, fill, th.track,
7599 ));
7600 return Ok(Value::Unit);
7601 },
7602 #[cfg(not(target_arch = "wasm32"))]
7603 "ui_tooltip" | "提示框" | "ツールチップ" | "툴팁" | "คำแนะนำ" =>
7604 {
7605 let x = self.arg_num(&args, 0, 0.)? as f32;
7606 let y = self.arg_num(&args, 1, 0.)? as f32;
7607 let w0 = self.arg_num(&args, 2, 120.)? as f32;
7608 let h0 = self.arg_num(&args, 3, 28.)? as f32;
7609 let th = self.ui_theme;
7610 let prim = self.color_at(&args, 4, th.primary);
7611 self.draw_ui(&ling_ui::widgets::tooltip(x, y, w0, h0, prim, th.bg));
7612 return Ok(Value::Unit);
7613 },
7614 #[cfg(not(target_arch = "wasm32"))]
7615 "ui_stepper" | "步进器" | "ステッパー" | "스테퍼" | "ตัวปรับค่า" =>
7616 {
7617 let x = self.arg_num(&args, 0, 0.)? as f32;
7618 let y = self.arg_num(&args, 1, 0.)? as f32;
7619 let w0 = self.arg_num(&args, 2, 120.)? as f32;
7620 let h0 = self.arg_num(&args, 3, 28.)? as f32;
7621 let mut val = self.arg_num(&args, 4, 0.)? as f32;
7622 let step = self.arg_num(&args, 5, 1.)? as f32;
7623 let (mx, my, down) = self.mouse_now();
7624 let hm = ling_ui::holo::hit_rect(mx, my, x, y, h0, h0);
7625 let hp = ling_ui::holo::hit_rect(mx, my, x + w0 - h0, y, h0, h0);
7626 if down && !self.mouse_was_down {
7627 if hm {
7628 val -= step;
7629 }
7630 if hp {
7631 val += step;
7632 }
7633 }
7634 let th = self.ui_theme;
7635 let prim = self.color_at(&args, 6, th.primary);
7636 self.draw_ui(&ling_ui::widgets::stepper(
7637 x, y, w0, h0, hm, hp, prim, th.track,
7638 ));
7639 return Ok(Value::Number(val as f64));
7640 },
7641
7642 #[cfg(not(target_arch = "wasm32"))]
7644 "ui_healthbar" | "血条" | "体力バー" | "체력바" | "แถบพลังชีวิต" =>
7645 {
7646 let x = self.arg_num(&args, 0, 0.)? as f32;
7647 let y = self.arg_num(&args, 1, 0.)? as f32;
7648 let w0 = self.arg_num(&args, 2, 180.)? as f32;
7649 let h0 = self.arg_num(&args, 3, 16.)? as f32;
7650 let val = self.arg_num(&args, 4, 1.)? as f32;
7651 let max = self.arg_num(&args, 5, 1.)? as f32;
7652 let pulse = self.arg_num(&args, 6, 0.)? as f32;
7653 let th = self.ui_theme;
7654 let full = self.color_at(&args, 7, th.accent);
7655 self.draw_ui(&ling_ui::widgets::healthbar(
7656 x,
7657 y,
7658 w0,
7659 h0,
7660 val / max.max(1e-6),
7661 pulse,
7662 full,
7663 th.warn,
7664 th.track,
7665 ));
7666 return Ok(Value::Unit);
7667 },
7668 #[cfg(not(target_arch = "wasm32"))]
7669 "ui_cooldown" | "冷却" | "クールダウン" | "쿨다운" | "คูลดาวน์" =>
7670 {
7671 let cx = self.arg_num(&args, 0, 0.)? as f32;
7672 let cy = self.arg_num(&args, 1, 0.)? as f32;
7673 let r = self.arg_num(&args, 2, 28.)? as f32;
7674 let frac = self.arg_num(&args, 3, 0.)? as f32;
7675 let th = self.ui_theme;
7676 let fill = self.color_at(&args, 4, th.primary);
7677 self.draw_ui(&ling_ui::widgets::cooldown(cx, cy, r, frac, fill, th.track));
7678 return Ok(Value::Unit);
7679 },
7680 #[cfg(not(target_arch = "wasm32"))]
7681 "ui_counter" | "计数器" | "カウンター" | "카운터" | "ตัวนับ" => {
7682 let x = self.arg_num(&args, 0, 0.)? as f32;
7683 let y = self.arg_num(&args, 1, 0.)? as f32;
7684 let dw = self.arg_num(&args, 2, 14.)? as f32;
7685 let dh = self.arg_num(&args, 3, 24.)? as f32;
7686 let val = self.arg_num(&args, 4, 0.)? as i64;
7687 let digits = self.arg_num(&args, 5, 4.)? as usize;
7688 let th = self.ui_theme;
7689 let on = self.color_at(&args, 6, th.primary);
7690 let off = ling_ui::widgets::shade(th.track, 0.5);
7691 self.draw_ui(&ling_ui::widgets::counter(
7692 x, y, dw, dh, val, digits, on, off,
7693 ));
7694 return Ok(Value::Unit);
7695 },
7696 #[cfg(not(target_arch = "wasm32"))]
7697 "ui_minimap" | "小地图" | "ミニマップ" | "미니맵" | "แผนที่ย่อ" =>
7698 {
7699 let x = self.arg_num(&args, 0, 0.)? as f32;
7700 let y = self.arg_num(&args, 1, 0.)? as f32;
7701 let w0 = self.arg_num(&args, 2, 140.)? as f32;
7702 let h0 = self.arg_num(&args, 3, 140.)? as f32;
7703 let th = self.ui_theme;
7704 let prim = self.color_at(&args, 4, th.primary);
7705 self.draw_ui(&ling_ui::widgets::minimap(x, y, w0, h0, prim, th.bg));
7706 return Ok(Value::Unit);
7707 },
7708 #[cfg(not(target_arch = "wasm32"))]
7709 "ui_dpad" | "方向键" | "方向パッド" | "방향패드" | "ปุ่มทิศทาง" =>
7710 {
7711 let cx = self.arg_num(&args, 0, 0.)? as f32;
7712 let cy = self.arg_num(&args, 1, 0.)? as f32;
7713 let r = self.arg_num(&args, 2, 50.)? as f32;
7714 let (mx, my, down) = self.mouse_now();
7715 let mut dir = 0;
7716 if down {
7717 let (dx, dy) = (mx - cx, my - cy);
7718 if dx * dx + dy * dy <= r * r {
7719 if dx.abs() > dy.abs() {
7720 dir = if dx > 0.0 { 2 } else { 4 };
7721 } else {
7722 dir = if dy > 0.0 { 3 } else { 1 };
7723 }
7724 }
7725 }
7726 let th = self.ui_theme;
7727 let prim = self.color_at(&args, 3, th.primary);
7728 self.draw_ui(&ling_ui::widgets::dpad(cx, cy, r, dir, prim, th.track));
7729 return Ok(Value::Number(dir as f64));
7730 },
7731 #[cfg(not(target_arch = "wasm32"))]
7732 "ui_slotgrid" | "物品格" | "スロットグリッド" | "슬롯격자" | "ช่องไอเทม" =>
7733 {
7734 let x = self.arg_num(&args, 0, 0.)? as f32;
7735 let y = self.arg_num(&args, 1, 0.)? as f32;
7736 let cols = self.arg_num(&args, 2, 4.)? as usize;
7737 let rows = self.arg_num(&args, 3, 1.)? as usize;
7738 let cell = self.arg_num(&args, 4, 36.)? as f32;
7739 let sel = self.arg_num(&args, 5, -1.)? as i32;
7740 let th = self.ui_theme;
7741 let prim = self.color_at(&args, 6, th.primary);
7742 self.draw_ui(&ling_ui::widgets::slotgrid(
7743 x, y, cols, rows, cell, sel, prim, th.track,
7744 ));
7745 return Ok(Value::Unit);
7746 },
7747 #[cfg(not(target_arch = "wasm32"))]
7748 "ui_vignette" | "暗角" | "ビネット" | "비네트" | "ขอบมืด" => {
7749 let intensity = self.arg_num(&args, 0, 0.5)? as f32;
7750 let (w, h) = {
7751 let g = self.gfx.borrow();
7752 (g.width as f32, g.height as f32)
7753 };
7754 let th = self.ui_theme;
7755 let col = self.color_at(&args, 1, th.warn);
7756 self.draw_ui(&ling_ui::widgets::vignette(w, h, intensity, col));
7757 return Ok(Value::Unit);
7758 },
7759
7760 #[cfg(not(target_arch = "wasm32"))]
7762 "ui_gauge3d" | "立体仪表" | "立体ゲージ" | "입체게이지" | "มาตรวัด3มิติ" =>
7763 {
7764 let cx = self.arg_num(&args, 0, 0.)? as f32;
7765 let cy = self.arg_num(&args, 1, 0.)? as f32;
7766 let r = self.arg_num(&args, 2, 50.)? as f32;
7767 let val = self.arg_num(&args, 3, 0.)? as f32;
7768 let max = self.arg_num(&args, 4, 1.)? as f32;
7769 let spin = self.arg_num(&args, 5, 0.)? as f32;
7770 let th = self.ui_theme;
7771 let fill = self.color_at(&args, 6, th.primary);
7772 self.draw_ui(&ling_ui::widgets::gauge3d(
7773 cx,
7774 cy,
7775 r,
7776 val / max.max(1e-6),
7777 spin,
7778 fill,
7779 th.track,
7780 ));
7781 return Ok(Value::Unit);
7782 },
7783 #[cfg(not(target_arch = "wasm32"))]
7784 "ui_panel3d" | "立体面板" | "立体パネル" | "입체패널" | "แผง3มิติ" =>
7785 {
7786 let x = self.arg_num(&args, 0, 0.)? as f32;
7787 let y = self.arg_num(&args, 1, 0.)? as f32;
7788 let w0 = self.arg_num(&args, 2, 200.)? as f32;
7789 let h0 = self.arg_num(&args, 3, 120.)? as f32;
7790 let depth = self.arg_num(&args, 4, 14.)? as f32;
7791 let th = self.ui_theme;
7792 let prim = self.color_at(&args, 5, th.primary);
7793 self.draw_ui(&ling_ui::widgets::panel3d(x, y, w0, h0, depth, prim, th.bg));
7794 return Ok(Value::Unit);
7795 },
7796 #[cfg(not(target_arch = "wasm32"))]
7797 "ui_radar3d" | "立体雷达" | "立体レーダー" | "입체레이더" | "เรดาร์3มิติ" =>
7798 {
7799 let cx = self.arg_num(&args, 0, 0.)? as f32;
7800 let cy = self.arg_num(&args, 1, 0.)? as f32;
7801 let r = self.arg_num(&args, 2, 60.)? as f32;
7802 let tilt = self.arg_num(&args, 3, 0.9)? as f32;
7803 let sweep = self.arg_num(&args, 4, 0.)? as f32;
7804 let th = self.ui_theme;
7805 let prim = self.color_at(&args, 5, th.primary);
7806 self.draw_ui(&ling_ui::widgets::radar3d(
7807 cx, cy, r, tilt, sweep, prim, th.track,
7808 ));
7809 return Ok(Value::Unit);
7810 },
7811
7812 #[cfg(not(target_arch = "wasm32"))]
7814 "audio_blip" | "提示音" | "ビープ音" | "효과음" | "เสียงบี๊บ" =>
7815 {
7816 let freq = self.arg_num(&args, 0, 660.)? as f32;
7817 let dur = self.arg_num(&args, 1, 0.08)? as f32;
7818 let wave = Wave::from_name(&self.arg_str(&args, 2, "sine"));
7819 let amp = self.arg_num(&args, 3, 0.25)? as f32;
7820 if let Some(audio) = &self.audio {
7821 audio.blip(freq, amp, dur, wave);
7822 }
7823 return Ok(Value::Unit);
7824 },
7825 #[cfg(not(target_arch = "wasm32"))]
7826 "ui_sound" | "界面音" | "UI音" | "인터페이스음" | "เสียงปุ่ม" =>
7827 {
7828 let name = self.arg_str(&args, 0, "click");
7829 if let Some(audio) = &self.audio {
7830 match name.as_str() {
7831 "hover" => audio.blip(880.0, 0.10, 0.04, Wave::Sine),
7832 "confirm" => {
7833 audio.blip(660.0, 0.22, 0.07, Wave::Square);
7834 audio.blip(990.0, 0.18, 0.10, Wave::Square);
7835 },
7836 "error" => {
7837 audio.blip(180.0, 0.30, 0.16, Wave::Saw);
7838 audio.blip(140.0, 0.30, 0.18, Wave::Saw);
7839 },
7840 "toggle" => audio.blip(520.0, 0.22, 0.05, Wave::Triangle),
7841 "tick" => audio.blip(1500.0, 0.12, 0.02, Wave::Square),
7842 _ => audio.blip(720.0, 0.26, 0.05, Wave::Square), }
7844 }
7845 return Ok(Value::Unit);
7846 },
7847
7848 #[cfg(not(target_arch = "wasm32"))]
7856 "music_load" | "载入音乐" | "音楽読込" | "음악로드" | "โหลดเพลง" =>
7857 {
7858 let path = self.arg_str(&args, 0, "");
7859 let resolved = if std::path::Path::new(&path).exists() {
7860 path.clone()
7861 } else if let Some(d) = &self.source_dir {
7862 d.join(&path).to_string_lossy().into_owned()
7863 } else {
7864 path.clone()
7865 };
7866 match ling_music::load(&resolved) {
7867 Ok(t) => {
7868 let id = self.tracks.len();
7869 self.tracks.push(t);
7870 return Ok(Value::Number(id as f64));
7871 },
7872 Err(e) => {
7873 eprintln!("music_load failed ({path}): {e}");
7874 return Ok(Value::Number(-1.0));
7875 },
7876 }
7877 },
7878 #[cfg(not(target_arch = "wasm32"))]
7879 "music_duration" | "音乐时长" | "音楽長さ" | "음악길이" | "ความยาวเพลง" =>
7880 {
7881 let id = self.arg_num(&args, 0, 0.0)? as i64;
7882 let d = self
7883 .tracks
7884 .get(id as usize)
7885 .map(|t| t.duration)
7886 .unwrap_or(0.0);
7887 return Ok(Value::Number(d as f64));
7888 },
7889 #[cfg(not(target_arch = "wasm32"))]
7890 "music_bpm" | "节拍速度" | "テンポ" | "템포" | "จังหวะต่อนาที" =>
7891 {
7892 let id = self.arg_num(&args, 0, 0.0)? as i64;
7893 let b = self
7894 .tracks
7895 .get(id as usize)
7896 .map(|t| ling_music::analysis::bpm(&t.mono, t.rate))
7897 .unwrap_or(0.0);
7898 return Ok(Value::Number(b as f64));
7899 },
7900 #[cfg(not(target_arch = "wasm32"))]
7901 "music_key" | "调性" | "調性" | "조성" | "คีย์เพลง" => {
7902 let id = self.arg_num(&args, 0, 0.0)? as i64;
7903 let k = self
7904 .tracks
7905 .get(id as usize)
7906 .map(|t| ling_music::analysis::key_name(&t.mono, t.rate))
7907 .unwrap_or_default();
7908 return Ok(Value::Str(k));
7909 },
7910 #[cfg(not(target_arch = "wasm32"))]
7911 "music_onsets" | "音符起点" | "オンセット" | "온셋" | "จุดเริ่มเสียง" =>
7912 {
7913 let id = self.arg_num(&args, 0, 0.0)? as i64;
7914 let v = self
7915 .tracks
7916 .get(id as usize)
7917 .map(|t| ling_music::analysis::onsets(&t.mono, t.rate))
7918 .unwrap_or_default();
7919 return Ok(Value::List(Rc::new(
7920 v.into_iter().map(|x| Value::Number(x as f64)).collect(),
7921 )));
7922 },
7923 #[cfg(not(target_arch = "wasm32"))]
7924 "music_beat_grid" | "节拍网格" | "ビートグリッド" | "비트그리드" | "กริดจังหวะ" =>
7925 {
7926 let id = self.arg_num(&args, 0, 0.0)? as i64;
7927 let beats = self
7928 .tracks
7929 .get(id as usize)
7930 .map(|t| {
7931 let b = ling_music::analysis::bpm(&t.mono, t.rate);
7932 ling_music::analysis::beat_grid(&t.mono, t.rate, b)
7933 })
7934 .unwrap_or_default();
7935 return Ok(Value::List(Rc::new(
7936 beats.into_iter().map(|x| Value::Number(x as f64)).collect(),
7937 )));
7938 },
7939
7940 #[cfg(not(target_arch = "wasm32"))]
7942 "music_play" | "播放音乐" | "音楽再生" | "음악재생" | "เล่นเพลง" =>
7943 {
7944 let id = self.arg_num(&args, 0, 0.0)? as i64;
7945 if self.ensure_music() {
7946 let track = self
7947 .tracks
7948 .get(id as usize)
7949 .map(|t| (t.stereo.clone(), t.rate));
7950 if let (Some((st, rate)), Some(m)) = (track, &self.music) {
7951 m.set_track(st, rate);
7952 m.play();
7953 } else if let Some(m) = &self.music {
7954 m.play();
7955 }
7956 }
7957 return Ok(Value::Unit);
7958 },
7959 #[cfg(not(target_arch = "wasm32"))]
7960 "music_pause" | "暂停音乐" | "音楽一時停止" | "음악일시정지" | "หยุดเพลงชั่วคราว" =>
7961 {
7962 if let Some(m) = &self.music {
7963 m.pause();
7964 }
7965 return Ok(Value::Unit);
7966 },
7967 #[cfg(not(target_arch = "wasm32"))]
7968 "music_stop" | "停止音乐" | "音楽停止" | "음악정지" | "หยุดเพลง" =>
7969 {
7970 if let Some(m) = &self.music {
7971 m.stop();
7972 }
7973 return Ok(Value::Unit);
7974 },
7975 #[cfg(not(target_arch = "wasm32"))]
7976 "music_seek" | "定位音乐" | "音楽シーク" | "음악탐색" | "ค้นหาเพลง" =>
7977 {
7978 let sec = self.arg_num(&args, 0, 0.0)? as f32;
7979 if let Some(m) = &self.music {
7980 m.seek(sec);
7981 }
7982 return Ok(Value::Unit);
7983 },
7984 #[cfg(not(target_arch = "wasm32"))]
7985 "music_pos" | "音乐位置" | "音楽位置" | "음악위치" | "ตำแหน่งเพลง" =>
7986 {
7987 let p = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
7988 return Ok(Value::Number(p as f64));
7989 },
7990 #[cfg(not(target_arch = "wasm32"))]
7991 "music_volume" | "音乐音量" | "音楽音量" | "음악음량" | "ระดับเพลง" =>
7992 {
7993 let v = self.arg_num(&args, 0, 0.8)? as f32;
7994 if self.ensure_music() {
7995 if let Some(m) = &self.music {
7996 m.set_volume(v);
7997 }
7998 }
7999 return Ok(Value::Unit);
8000 },
8001
8002 #[cfg(not(target_arch = "wasm32"))]
8004 "music_patch" | "乐器音色" | "音色読込" | "악기패치" | "แพตช์เครื่องดนตรี" =>
8005 {
8006 let path = self.arg_str(&args, 0, "");
8007 let resolved = if std::path::Path::new(&path).exists() {
8008 path.clone()
8009 } else if let Some(d) = &self.source_dir {
8010 d.join(&path).to_string_lossy().into_owned()
8011 } else {
8012 path.clone()
8013 };
8014 if !self.ensure_music() {
8015 return Ok(Value::Number(-1.0));
8016 }
8017 match ling_music::patch::from_path(&resolved) {
8018 Ok(p) => {
8019 let id = self.music.as_ref().unwrap().add_patch(p);
8020 return Ok(Value::Number(id as f64));
8021 },
8022 Err(e) => {
8023 eprintln!("music_patch failed ({path}): {e}");
8024 return Ok(Value::Number(-1.0));
8025 },
8026 }
8027 },
8028 #[cfg(not(target_arch = "wasm32"))]
8029 "music_note" | "弹音符" | "音符演奏" | "음표연주" | "เล่นโน้ต" =>
8030 {
8031 let inst = self.arg_num(&args, 0, 0.0)? as usize;
8032 let midi = self.pitch_arg(&args, 1, 60);
8033 let dur = self.arg_num(&args, 2, 0.5)? as f32;
8034 let vel = self.arg_num(&args, 3, 0.9)? as f32;
8035 if self.ensure_music() {
8036 if let Some(m) = &self.music {
8037 m.note(inst, midi, vel, dur);
8038 }
8039 }
8040 return Ok(Value::Unit);
8041 },
8042 #[cfg(not(target_arch = "wasm32"))]
8043 "music_note_on" | "音符开始" | "音符オン" | "음표켜기" | "โน้ตเริ่ม" =>
8044 {
8045 let inst = self.arg_num(&args, 0, 0.0)? as usize;
8046 let midi = self.pitch_arg(&args, 1, 60);
8047 let vel = self.arg_num(&args, 2, 0.9)? as f32;
8048 if self.ensure_music() {
8049 if let Some(m) = &self.music {
8050 m.note_on(inst, midi, vel);
8051 }
8052 }
8053 return Ok(Value::Unit);
8054 },
8055 #[cfg(not(target_arch = "wasm32"))]
8056 "music_note_off" | "音符结束" | "音符オフ" | "음표끄기" | "โน้ตจบ" =>
8057 {
8058 let inst = self.arg_num(&args, 0, 0.0)? as usize;
8059 let midi = self.pitch_arg(&args, 1, 60);
8060 if let Some(m) = &self.music {
8061 m.note_off(inst, midi);
8062 }
8063 return Ok(Value::Unit);
8064 },
8065
8066 #[cfg(not(target_arch = "wasm32"))]
8068 "music_judge" | "判定" | "判定する" | "판정" | "ตัดสินจังหวะ" =>
8069 {
8070 let delta_ms = self.arg_num(&args, 0, 9999.0)? as f32;
8071 return Ok(Value::Number(
8072 ling_music::Grade::judge(delta_ms).index() as f64
8073 ));
8074 },
8075 #[cfg(not(target_arch = "wasm32"))]
8076 "music_grade_name" | "判定名" | "判定名称" | "판정이름" | "ชื่อการตัดสิน" =>
8077 {
8078 let idx = self.arg_num(&args, 0, 4.0)? as i32;
8079 return Ok(Value::Str(
8080 ling_music::Grade::from_index(idx).name().to_string(),
8081 ));
8082 },
8083
8084 #[cfg(not(target_arch = "wasm32"))]
8086 "music_lrc" | "载入歌词" | "歌詞読込" | "가사로드" | "โหลดเนื้อเพลง" =>
8087 {
8088 let path = self.arg_str(&args, 0, "");
8089 let resolved = if std::path::Path::new(&path).exists() {
8090 path.clone()
8091 } else if let Some(d) = &self.source_dir {
8092 d.join(&path).to_string_lossy().into_owned()
8093 } else {
8094 path.clone()
8095 };
8096 match std::fs::read_to_string(&resolved) {
8097 Ok(text) => {
8098 let id = self.lyrics.len();
8099 self.lyrics.push(ling_music::Lyrics::parse(&text));
8100 return Ok(Value::Number(id as f64));
8101 },
8102 Err(e) => {
8103 eprintln!("music_lrc failed ({path}): {e}");
8104 return Ok(Value::Number(-1.0));
8105 },
8106 }
8107 },
8108 #[cfg(not(target_arch = "wasm32"))]
8109 "music_lyric" | "当前歌词" | "現在歌詞" | "현재가사" | "เนื้อเพลงปัจจุบัน" =>
8110 {
8111 let id = self.arg_num(&args, 0, 0.0)? as i64;
8112 let t = self.arg_num(&args, 1, 0.0)? as f32;
8113 let line = self
8114 .lyrics
8115 .get(id as usize)
8116 .map(|l| l.line_at(t).to_string())
8117 .unwrap_or_default();
8118 return Ok(Value::Str(line));
8119 },
8120 #[cfg(not(target_arch = "wasm32"))]
8121 "music_mic_pitch" | "麦克风音高" | "マイク音程" | "마이크음정" | "ระดับเสียงไมค์" =>
8122 {
8123 let hz = if let Some(mic) = self.mic.as_ref() {
8124 let s = mic.latest_samples();
8125 let rate = mic.sample_rate();
8126 ling_music::pitch::detect(&s, rate).unwrap_or(0.0)
8127 } else {
8128 0.0
8129 };
8130 return Ok(Value::Number(hz as f64));
8131 },
8132 #[cfg(not(target_arch = "wasm32"))]
8133 "music_note_name" | "音名" | "音名称" | "음이름" | "ชื่อโน้ต" =>
8134 {
8135 let hz = self.arg_num(&args, 0, 0.0)? as f32;
8136 return Ok(Value::Str(ling_music::note::hz_to_name(hz)));
8137 },
8138 #[cfg(not(target_arch = "wasm32"))]
8139 "music_hz" | "音符频率" | "音符周波数" | "음표주파수" | "ความถี่โน้ต" =>
8140 {
8141 let midi = self.pitch_arg(&args, 0, 69);
8142 return Ok(Value::Number(
8143 ling_music::note::midi_to_hz(midi as f32) as f64
8144 ));
8145 },
8146 #[cfg(not(target_arch = "wasm32"))]
8147 "music_pitch_score" | "音准评分" | "音程スコア" | "음정점수" | "คะแนนเสียง" =>
8148 {
8149 let hz = self.arg_num(&args, 0, 0.0)? as f32;
8150 let target = self.arg_num(&args, 1, 0.0)? as f32;
8151 return Ok(Value::Number(
8152 ling_music::karaoke::pitch_score(hz, target) as f64
8153 ));
8154 },
8155
8156 #[cfg(not(target_arch = "wasm32"))]
8158 "music_midi_load" | "载入MIDI" | "MIDI読込" | "미디로드" | "โหลดมิดี" =>
8159 {
8160 let path = self.arg_str(&args, 0, "");
8161 let resolved = if std::path::Path::new(&path).exists() {
8162 path.clone()
8163 } else if let Some(d) = &self.source_dir {
8164 d.join(&path).to_string_lossy().into_owned()
8165 } else {
8166 path.clone()
8167 };
8168 match ling_music::midi::load(&resolved) {
8169 Ok(m) => {
8170 let id = self.midis.len();
8171 self.midis.push(m);
8172 return Ok(Value::Number(id as f64));
8173 },
8174 Err(e) => {
8175 eprintln!("music_midi_load failed ({path}): {e}");
8176 return Ok(Value::Number(-1.0));
8177 },
8178 }
8179 },
8180 #[cfg(not(target_arch = "wasm32"))]
8181 "music_midi_count" | "MIDI数量" | "MIDI数" | "미디수" | "จำนวนมิดี" =>
8182 {
8183 let id = self.arg_num(&args, 0, 0.0)? as i64;
8184 let n = self
8185 .midis
8186 .get(id as usize)
8187 .map(|m| m.notes.len())
8188 .unwrap_or(0);
8189 return Ok(Value::Number(n as f64));
8190 },
8191 #[cfg(not(target_arch = "wasm32"))]
8193 "music_midi_notes" | "MIDI音符" | "MIDIノート" | "미디음표" | "โน้ตมิดี" =>
8194 {
8195 let id = self.arg_num(&args, 0, 0.0)? as i64;
8196 let mut out = Vec::new();
8197 if let Some(m) = self.midis.get(id as usize) {
8198 for n in &m.notes {
8199 out.push(Value::Number(n.time as f64));
8200 out.push(Value::Number(n.midi as f64));
8201 }
8202 }
8203 return Ok(Value::List(Rc::new(out)));
8204 },
8205 #[cfg(not(target_arch = "wasm32"))]
8207 "music_midi_bars" | "MIDI音条" | "MIDIバー" | "미디바" | "แท่งมิดี" =>
8208 {
8209 let id = self.arg_num(&args, 0, 0.0)? as i64;
8210 let mut out = Vec::new();
8211 if let Some(m) = self.midis.get(id as usize) {
8212 for n in &m.notes {
8213 out.push(Value::Number(n.time as f64));
8214 out.push(Value::Number(n.midi as f64));
8215 out.push(Value::Number(n.dur as f64));
8216 }
8217 }
8218 return Ok(Value::List(Rc::new(out)));
8219 },
8220
8221 #[cfg(not(target_arch = "wasm32"))]
8223 "music_fft" | "音乐频谱" | "音楽スペクトル" | "음악스펙트럼" | "สเปกตรัมเพลง" =>
8224 {
8225 let id = self.arg_num(&args, 0, 0.0)? as i64;
8226 let nbands = self.arg_num(&args, 1, 16.0)? as usize;
8227 let pos = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
8228 if let Some(t) = self.tracks.get(id as usize) {
8229 let idx = (pos * t.rate as f32) as usize;
8230 let end = (idx + 2048).min(t.mono.len());
8231 if end > idx + 64 {
8232 self.fft.borrow_mut().push_samples(&t.mono[idx..end]);
8233 }
8234 }
8235 let bands = self.fft.borrow().freq_bands(nbands);
8236 return Ok(Value::List(Rc::new(
8237 bands.into_iter().map(|x| Value::Number(x as f64)).collect(),
8238 )));
8239 },
8240
8241 #[cfg(not(target_arch = "wasm32"))]
8243 "audio_stop_sfx" | "停止音效" | "効果音停止" | "효과음정지" | "หยุดเอฟเฟกต์ทั้งหมด" =>
8244 {
8245 if let Some(a) = &self.audio {
8246 a.stop_all_sfx();
8247 }
8248 return Ok(Value::Unit);
8249 },
8250 #[cfg(not(target_arch = "wasm32"))]
8252 "audio_sfx" | "音效" | "空間効果音" | "공간효과음" | "เสียงเอฟเฟกต์" =>
8253 {
8254 let x = self.arg_num(&args, 0, 0.0)? as f32;
8255 let y = self.arg_num(&args, 1, 0.0)? as f32;
8256 let z = self.arg_num(&args, 2, 0.0)? as f32;
8257 let w = self.arg_num(&args, 3, 1.0)? as f32;
8258 let freq = self.arg_num(&args, 4, 440.0)? as f32;
8259 let amp = self.arg_num(&args, 5, 0.3)? as f32;
8260 let dur = self.arg_num(&args, 6, 0.15)? as f32;
8261 let wave = Wave::from_name(&self.arg_str(&args, 7, "sine"));
8262 if let Some(a) = &self.audio {
8263 a.sfx(x, y, z, w, freq, amp, dur, wave);
8264 }
8265 return Ok(Value::Unit);
8266 },
8267 #[cfg(not(target_arch = "wasm32"))]
8272 "morph_note" | "โน้ตมอร์ฟ" | "变形音" | "モーフ音" | "모프음" =>
8273 {
8274 let x = self.arg_num(&args, 0, 0.0)? as f32;
8275 let y = self.arg_num(&args, 1, 0.0)? as f32;
8276 let z = self.arg_num(&args, 2, 0.0)? as f32;
8277 let w = self.arg_num(&args, 3, 1.0)? as f32;
8278 let freq = self.arg_num(&args, 4, 220.0)? as f32;
8279 let amp = self.arg_num(&args, 5, 0.3)? as f32;
8280 let dur = self.arg_num(&args, 6, 0.6)? as f32;
8281 let material = self.arg_num(&args, 7, 0.0)?.clamp(0.0, 3.0) as u8;
8282 let morph = self.arg_num(&args, 8, 0.0)? as f32;
8283 if let Some(a) = &self.audio {
8284 a.morph_note(x, y, z, w, freq, amp, dur, material, morph);
8285 }
8286 return Ok(Value::Unit);
8287 },
8288 #[cfg(not(target_arch = "wasm32"))]
8290 "audio_sample_load" | "载入采样" | "サンプル読込" | "샘플로드" | "โหลดตัวอย่างเสียง" =>
8291 {
8292 let path = self.arg_str(&args, 0, "");
8293 let resolved = if std::path::Path::new(&path).exists() {
8294 path.clone()
8295 } else if let Some(d) = &self.source_dir {
8296 d.join(&path).to_string_lossy().into_owned()
8297 } else {
8298 path.clone()
8299 };
8300 match ling_music::load(&resolved) {
8301 Ok(t) => {
8302 if let Some(a) = &self.audio {
8303 return Ok(Value::Number(a.add_sample(t.mono, t.rate) as f64));
8304 }
8305 return Ok(Value::Number(-1.0));
8306 },
8307 Err(e) => {
8308 eprintln!("audio_sample_load failed ({path}): {e}");
8309 return Ok(Value::Number(-1.0));
8310 },
8311 }
8312 },
8313 #[cfg(not(target_arch = "wasm32"))]
8314 "audio_sample_play" | "播放采样" | "サンプル再生" | "샘플재생" | "เล่นตัวอย่างเสียง" =>
8315 {
8316 let id = self.arg_num(&args, 0, 0.0)? as usize;
8317 let x = self.arg_num(&args, 1, 0.0)? as f32;
8318 let y = self.arg_num(&args, 2, 0.0)? as f32;
8319 let z = self.arg_num(&args, 3, 0.0)? as f32;
8320 let w = self.arg_num(&args, 4, 1.0)? as f32;
8321 let vol = self.arg_num(&args, 5, 1.0)? as f32;
8322 let looping = self.arg_num(&args, 6, 0.0)? > 0.5;
8323 let v = self
8324 .audio
8325 .as_ref()
8326 .map(|a| a.play_sample(id, x, y, z, w, vol, looping))
8327 .unwrap_or(0);
8328 return Ok(Value::Number(v as f64));
8329 },
8330 #[cfg(not(target_arch = "wasm32"))]
8331 "audio_sample_stop" | "停止采样" | "サンプル停止" | "샘플정지" | "หยุดตัวอย่างเสียง" =>
8332 {
8333 let v = self.arg_num(&args, 0, 0.0)? as u32;
8334 if let Some(a) = &self.audio {
8335 a.stop_sample(v);
8336 }
8337 return Ok(Value::Unit);
8338 },
8339 #[cfg(not(target_arch = "wasm32"))]
8341 "audio_fx_delay" | "回声" | "ディレイ効果" | "딜레이" | "เสียงสะท้อน" =>
8342 {
8343 let time = self.arg_num(&args, 0, 0.3)? as f32;
8344 let fb = self.arg_num(&args, 1, 0.3)? as f32;
8345 let mix = self.arg_num(&args, 2, 0.3)? as f32;
8346 if let Some(a) = &self.audio {
8347 a.fx_delay(time, fb, mix);
8348 }
8349 return Ok(Value::Unit);
8350 },
8351 #[cfg(not(target_arch = "wasm32"))]
8352 "audio_fx_reverb" | "混响" | "リバーブ" | "리버브" | "เสียงก้อง" =>
8353 {
8354 let mix = self.arg_num(&args, 0, 0.3)? as f32;
8355 if let Some(a) = &self.audio {
8356 a.fx_reverb(mix);
8357 }
8358 return Ok(Value::Unit);
8359 },
8360 #[cfg(not(target_arch = "wasm32"))]
8361 "audio_fx_lowpass" | "低通滤波" | "ローパス" | "저역통과" | "กรองความถี่ต่ำ" =>
8362 {
8363 let cutoff = self.arg_num(&args, 0, 1.0)? as f32;
8364 if let Some(a) = &self.audio {
8365 a.fx_lowpass(cutoff);
8366 }
8367 return Ok(Value::Unit);
8368 },
8369
8370 #[cfg(not(target_arch = "wasm32"))]
8377 "soft_ball" | "软球" | "ソフトボール" | "소프트볼" | "ลูกบอลนุ่ม" =>
8378 {
8379 let x = self.arg_num(&args, 0, 0.)? as f32;
8380 let y = self.arg_num(&args, 1, 0.)? as f32;
8381 let z = self.arg_num(&args, 2, 0.)? as f32;
8382 let r = self.arg_num(&args, 3, 1.0)? as f32;
8383 let b = ling_physics::soft::SoftBody::sphere(
8384 ling_physics::Vec3::new(x, y, z),
8385 r,
8386 8,
8387 12,
8388 1.0,
8389 );
8390 let id = self.soft_bodies.len();
8391 self.soft_bodies.push(b);
8392 return Ok(Value::Number(id as f64));
8393 },
8394 #[cfg(not(target_arch = "wasm32"))]
8395 "soft_step" | "软体步进" | "ソフト更新" | "소프트스텝" | "ก้าวนุ่ม" =>
8396 {
8397 let id = self.arg_num(&args, 0, 0.)? as usize;
8398 let dt = self.arg_num(&args, 1, 0.016)? as f32;
8399 let gy = self.arg_num(&args, 2, 15.0)? as f32;
8400 if let Some(b) = self.soft_bodies.get_mut(id) {
8401 b.integrate(dt, ling_physics::Vec3::new(0.0, gy, 0.0), 4);
8402 }
8403 return Ok(Value::Unit);
8404 },
8405 #[cfg(not(target_arch = "wasm32"))]
8406 "soft_bounce" | "软体落地" | "ソフト着地" | "소프트바운스" | "เด้งนุ่ม" =>
8407 {
8408 let id = self.arg_num(&args, 0, 0.)? as usize;
8409 let fy = self.arg_num(&args, 1, 0.)? as f32;
8410 let rest = self.arg_num(&args, 2, 0.5)? as f32;
8411 if let Some(b) = self.soft_bodies.get_mut(id) {
8412 b.floor_collision(fy, rest);
8413 }
8414 return Ok(Value::Unit);
8415 },
8416 #[cfg(not(target_arch = "wasm32"))]
8417 "soft_contain" | "软体边界" | "ソフト箱" | "소프트경계" | "กล่องนุ่ม" =>
8418 {
8419 let id = self.arg_num(&args, 0, 0.)? as usize;
8420 let nx = self.arg_num(&args, 1, -5.)? as f32;
8421 let ny = self.arg_num(&args, 2, -5.)? as f32;
8422 let nz = self.arg_num(&args, 3, -5.)? as f32;
8423 let mx = self.arg_num(&args, 4, 5.)? as f32;
8424 let my = self.arg_num(&args, 5, 5.)? as f32;
8425 let mz = self.arg_num(&args, 6, 5.)? as f32;
8426 let rest = self.arg_num(&args, 7, 0.6)? as f32;
8427 if let Some(b) = self.soft_bodies.get_mut(id) {
8428 b.contain(
8429 ling_physics::Vec3::new(nx, ny, nz),
8430 ling_physics::Vec3::new(mx, my, mz),
8431 rest,
8432 );
8433 }
8434 return Ok(Value::Unit);
8435 },
8436 #[cfg(not(target_arch = "wasm32"))]
8437 "soft_kick" | "软体踢" | "ソフト衝撃" | "소프트킥" | "เตะนุ่ม" =>
8438 {
8439 let id = self.arg_num(&args, 0, 0.)? as usize;
8440 let dx = self.arg_num(&args, 1, 0.)? as f32;
8441 let dy = self.arg_num(&args, 2, 0.)? as f32;
8442 let dz = self.arg_num(&args, 3, 0.)? as f32;
8443 let s = self.arg_num(&args, 4, 0.1)? as f32;
8444 if let Some(b) = self.soft_bodies.get_mut(id) {
8445 b.kick(ling_physics::Vec3::new(dx, dy, dz), s);
8446 }
8447 return Ok(Value::Unit);
8448 },
8449 #[cfg(not(target_arch = "wasm32"))]
8452 "soft_spin" | "软体自旋" | "ソフト回転" | "소프트회전" | "หมุนนุ่ม" =>
8453 {
8454 let id = self.arg_num(&args, 0, 0.)? as usize;
8455 let ax = self.arg_num(&args, 1, 0.)? as f32;
8456 let ay = self.arg_num(&args, 2, 0.)? as f32;
8457 let az = self.arg_num(&args, 3, 0.)? as f32;
8458 let rate = self.arg_num(&args, 4, 0.1)? as f32;
8459 if let Some(b) = self.soft_bodies.get_mut(id) {
8460 b.spin(ling_physics::Vec3::new(ax, ay, az), rate);
8461 }
8462 return Ok(Value::Unit);
8463 },
8464 #[cfg(not(target_arch = "wasm32"))]
8465 "soft_deform" | "形变量" | "変形量" | "변형량" | "ความบิดเบี้ยว" =>
8466 {
8467 let id = self.arg_num(&args, 0, 0.)? as usize;
8468 let d = self
8469 .soft_bodies
8470 .get(id)
8471 .map(|b| b.deformation())
8472 .unwrap_or(0.0);
8473 return Ok(Value::Number(d as f64));
8474 },
8475 #[cfg(not(target_arch = "wasm32"))]
8478 "soft_angular_speed"
8479 | "软体角速"
8480 | "ソフト角速度"
8481 | "소프트각속도"
8482 | "ความเร็วเชิงมุมนุ่ม" => {
8483 let id = self.arg_num(&args, 0, 0.)? as usize;
8484 let w = self
8485 .soft_bodies
8486 .get(id)
8487 .map(|b| b.angular_speed())
8488 .unwrap_or(0.0);
8489 return Ok(Value::Number(w as f64));
8490 },
8491 #[cfg(not(target_arch = "wasm32"))]
8492 "soft_centroid" | "软体质心" | "ソフト重心" | "소프트중심" | "จุดศูนย์กลางนุ่ม" =>
8493 {
8494 let id = self.arg_num(&args, 0, 0.)? as usize;
8495 let c = self
8496 .soft_bodies
8497 .get(id)
8498 .map(|b| b.centroid())
8499 .unwrap_or(ling_physics::Vec3::ZERO);
8500 return Ok(Value::List(Rc::new(vec![
8501 Value::Number(c.x as f64),
8502 Value::Number(c.y as f64),
8503 Value::Number(c.z as f64),
8504 ])));
8505 },
8506 #[cfg(not(target_arch = "wasm32"))]
8508 "soft_nodes" | "软体节点" | "ソフト節点" | "소프트노드" | "จุดนุ่ม" =>
8509 {
8510 let id = self.arg_num(&args, 0, 0.)? as usize;
8511 let mut out = Vec::new();
8512 if let Some(b) = self.soft_bodies.get(id) {
8513 for n in &b.nodes {
8514 out.push(Value::Number(n.pos.x as f64));
8515 out.push(Value::Number(n.pos.y as f64));
8516 out.push(Value::Number(n.pos.z as f64));
8517 }
8518 }
8519 return Ok(Value::List(Rc::new(out)));
8520 },
8521
8522 #[cfg(not(target_arch = "wasm32"))]
8524 "rb_add" | "刚体添加" | "剛体追加" | "강체추가" | "เพิ่มวัตถุแข็ง" =>
8525 {
8526 let x = self.arg_num(&args, 0, 0.)? as f32;
8527 let y = self.arg_num(&args, 1, 0.)? as f32;
8528 let z = self.arg_num(&args, 2, 0.)? as f32;
8529 let mass = self.arg_num(&args, 3, 1.0)? as f32;
8530 let mut b =
8531 ling_physics::rigid::RigidBody::new(ling_physics::Vec3::new(x, y, z), mass);
8532 b.restitution = 0.6;
8533 return Ok(Value::Number(self.rigid_world.add(b) as f64));
8534 },
8535 #[cfg(not(target_arch = "wasm32"))]
8536 "rb_torque" | "扭矩" | "トルク" | "토크" | "แรงบิด" => {
8537 let i = self.arg_num(&args, 0, 0.)? as usize;
8538 let tx = self.arg_num(&args, 1, 0.)? as f32;
8539 let ty = self.arg_num(&args, 2, 0.)? as f32;
8540 let tz = self.arg_num(&args, 3, 0.)? as f32;
8541 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
8542 b.apply_torque(ling_physics::Vec3::new(tx, ty, tz));
8543 }
8544 return Ok(Value::Unit);
8545 },
8546 #[cfg(not(target_arch = "wasm32"))]
8547 "rb_spin" | "自旋" | "スピン" | "스핀" | "หมุน" => {
8548 let i = self.arg_num(&args, 0, 0.)? as usize;
8549 let wx = self.arg_num(&args, 1, 0.)? as f32;
8550 let wy = self.arg_num(&args, 2, 0.)? as f32;
8551 let wz = self.arg_num(&args, 3, 0.)? as f32;
8552 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
8553 b.apply_spin(ling_physics::Vec3::new(wx, wy, wz));
8554 }
8555 return Ok(Value::Unit);
8556 },
8557 #[cfg(not(target_arch = "wasm32"))]
8558 "rb_impulse" | "刚体冲量" | "剛体インパルス" | "강체충격" | "แรงดลแข็ง" =>
8559 {
8560 let i = self.arg_num(&args, 0, 0.)? as usize;
8561 let ix = self.arg_num(&args, 1, 0.)? as f32;
8562 let iy = self.arg_num(&args, 2, 0.)? as f32;
8563 let iz = self.arg_num(&args, 3, 0.)? as f32;
8564 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
8565 b.apply_impulse(ling_physics::Vec3::new(ix, iy, iz));
8566 }
8567 return Ok(Value::Unit);
8568 },
8569 #[cfg(not(target_arch = "wasm32"))]
8570 "rb_floor" | "刚体落地" | "剛体着地" | "강체바닥" | "พื้นแข็ง" =>
8571 {
8572 let i = self.arg_num(&args, 0, 0.)? as usize;
8573 let fy = self.arg_num(&args, 1, 0.)? as f32;
8574 let rest = self.arg_num(&args, 2, 0.6)? as f32;
8575 let fric = self.arg_num(&args, 3, 0.6)? as f32;
8576 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
8577 b.bounce_floor(fy, rest, fric);
8578 }
8579 return Ok(Value::Unit);
8580 },
8581 #[cfg(not(target_arch = "wasm32"))]
8582 "rb_gravity" | "刚体重力" | "剛体重力" | "강체중력" | "แรงโน้มถ่วงแข็ง" =>
8583 {
8584 let gx = self.arg_num(&args, 0, 0.)? as f32;
8585 let gy = self.arg_num(&args, 1, 9.81)? as f32;
8586 let gz = self.arg_num(&args, 2, 0.)? as f32;
8587 self.rigid_world.gravity = ling_physics::Vec3::new(gx, gy, gz);
8588 return Ok(Value::Unit);
8589 },
8590 #[cfg(not(target_arch = "wasm32"))]
8591 "rb_step" | "刚体步进" | "剛体更新" | "강체스텝" | "ก้าวแข็ง" =>
8592 {
8593 let dt = self.arg_num(&args, 0, 0.016)? as f32;
8594 self.rigid_world.step(dt);
8595 return Ok(Value::Unit);
8596 },
8597 #[cfg(not(target_arch = "wasm32"))]
8598 "rb_pos" | "刚体位置" | "剛体位置" | "강체위치" | "ตำแหน่งแข็ง" =>
8599 {
8600 let i = self.arg_num(&args, 0, 0.)? as usize;
8601 let p = self
8602 .rigid_world
8603 .bodies
8604 .get(i)
8605 .map(|b| b.pos)
8606 .unwrap_or(ling_physics::Vec3::ZERO);
8607 return Ok(Value::List(Rc::new(vec![
8608 Value::Number(p.x as f64),
8609 Value::Number(p.y as f64),
8610 Value::Number(p.z as f64),
8611 ])));
8612 },
8613 #[cfg(not(target_arch = "wasm32"))]
8614 "rb_rot" | "刚体旋转" | "剛体回転" | "강체회전" | "การหมุนแข็ง" =>
8615 {
8616 let i = self.arg_num(&args, 0, 0.)? as usize;
8617 let q = self
8618 .rigid_world
8619 .bodies
8620 .get(i)
8621 .map(|b| b.orientation)
8622 .unwrap_or(ling_physics::Quat::IDENTITY);
8623 return Ok(Value::List(Rc::new(vec![
8624 Value::Number(q.x as f64),
8625 Value::Number(q.y as f64),
8626 Value::Number(q.z as f64),
8627 Value::Number(q.w as f64),
8628 ])));
8629 },
8630
8631 #[cfg(not(target_arch = "wasm32"))]
8633 "mesh_load" | "โหลดเมช" | "载入网格" | "メッシュ読込" | "메시로드" =>
8634 {
8635 let path = self.arg_str(&args, 0, "");
8636 let resolved = if std::path::Path::new(&path).exists() {
8637 path.clone()
8638 } else if let Some(d) = &self.source_dir {
8639 d.join(&path).to_string_lossy().into_owned()
8640 } else {
8641 path.clone()
8642 };
8643 let bytes = match std::fs::read(&resolved) {
8644 Ok(b) => b,
8645 Err(e) => {
8646 eprintln!("mesh_load failed ({path}): {e}");
8647 return Ok(Value::Number(-1.0));
8648 },
8649 };
8650 if bytes.len() < 16 || &bytes[0..4] != b"LMSH" {
8651 eprintln!("mesh_load: bad header ({path})");
8652 return Ok(Value::Number(-1.0));
8653 }
8654 let rd4 =
8655 |o: usize| -> [u8; 4] { [bytes[o], bytes[o + 1], bytes[o + 2], bytes[o + 3]] };
8656 let height = f32::from_le_bytes(rd4(8));
8657 let ntri = u32::from_le_bytes(rd4(12)) as usize;
8658 let need = 16usize.saturating_add(ntri.saturating_mul(9 * 4 + 3));
8659 if bytes.len() < need {
8660 eprintln!("mesh_load: truncated ({path})");
8661 return Ok(Value::Number(-1.0));
8662 }
8663 let mut pos = Vec::with_capacity(ntri * 3);
8664 let mut col = Vec::with_capacity(ntri);
8665 let mut off = 16usize;
8666 for _ in 0..ntri {
8667 for _k in 0..3 {
8668 let x = f32::from_le_bytes(rd4(off));
8669 let y = f32::from_le_bytes(rd4(off + 4));
8670 let z = f32::from_le_bytes(rd4(off + 8));
8671 off += 12;
8672 pos.push([x, y, z]);
8673 }
8674 col.push([bytes[off], bytes[off + 1], bytes[off + 2]]);
8675 off += 3;
8676 }
8677 eprintln!("mesh_load: {} ({} tris, h={:.2})", path, ntri, height);
8678 let id = self.meshes.len();
8679 self.meshes
8680 .push(crate::gfx::shapes::ColorMesh { pos, col, height });
8681 return Ok(Value::Number(id as f64));
8682 },
8683 #[cfg(target_arch = "wasm32")]
8684 "mesh_load" | "โหลดเมช" | "载入网格" | "メッシュ読込" | "메시로드" =>
8685 {
8686 return Ok(Value::Number(-1.0));
8689 },
8690 #[cfg(not(target_arch = "wasm32"))]
8691 "mesh_draw" | "วาดเมชสี" | "绘制网格" | "メッシュ描画" | "메시그리기" =>
8692 {
8693 let id = self.arg_num(&args, 0, 0.)? as usize;
8695 let cx = self.arg_num(&args, 1, 0.)? as f32;
8696 let cy = self.arg_num(&args, 2, 0.)? as f32;
8697 let cz = self.arg_num(&args, 3, 0.)? as f32;
8698 let sc = self.arg_num(&args, 4, 1.)? as f32;
8699 let yaw = self.arg_num(&args, 5, 0.)? as f32;
8700 let sway = self.arg_num(&args, 6, 0.)? as f32;
8701 let arm = self.arg_num(&args, 7, 0.)? as f32;
8702 let lean = self.arg_num(&args, 8, 0.)? as f32;
8703 let leg = self.arg_num(&args, 9, 0.)? as f32;
8704 let tuck = self.arg_num(&args, 10, 0.)? as f32;
8705 if id < self.meshes.len() {
8706 let m = &self.meshes[id];
8707 let mut gfx = self.gfx.borrow_mut();
8708 gfx.draw_color_mesh(m, cx, cy, cz, sc, yaw, sway, arm, lean, leg, tuck);
8709 }
8710 return Ok(Value::Unit);
8711 },
8712 #[cfg(target_arch = "wasm32")]
8713 "mesh_draw" | "วาดเมชสี" | "绘制网格" | "メッシュ描画" | "메시그리기" =>
8714 {
8715 return Ok(Value::Unit);
8716 },
8717
8718 "liquid_new" | "新建液体" | "液体新規" | "액체생성" | "สร้างของเหลว" =>
8720 {
8721 let w = self.arg_num(&args, 0, 64.)? as usize;
8722 let h = self.arg_num(&args, 1, 64.)? as usize;
8723 let id = self.liquids.len();
8724 self.liquids
8725 .push(ling_physics::liquid::LiquidGrid::new(w, h));
8726 return Ok(Value::Number(id as f64));
8727 },
8728 "liquid_set_colors" | "液体颜色" | "液体配色" | "액체색상" | "สีของเหลว" =>
8729 {
8730 let id = self.arg_num(&args, 0, 0.)? as usize;
8731 let wr = self.arg_num(&args, 1, 40.)? as f32;
8732 let wg = self.arg_num(&args, 2, 110.)? as f32;
8733 let wb = self.arg_num(&args, 3, 235.)? as f32;
8734 let or_ = self.arg_num(&args, 4, 240.)? as f32;
8735 let og = self.arg_num(&args, 5, 175.)? as f32;
8736 let ob = self.arg_num(&args, 6, 45.)? as f32;
8737 if let Some(g) = self.liquids.get_mut(id) {
8738 g.set_colors(wr, wg, wb, or_, og, ob);
8739 }
8740 return Ok(Value::Unit);
8741 },
8742 "liquid_splat" | "液体注入" | "液体追加" | "액체분사" | "หยดของเหลว" =>
8743 {
8744 let id = self.arg_num(&args, 0, 0.)? as usize;
8745 let x = self.arg_num(&args, 1, 0.)? as f32;
8746 let y = self.arg_num(&args, 2, 0.)? as f32;
8747 let kind = self.arg_num(&args, 3, 0.)? as i32;
8748 let amt = self.arg_num(&args, 4, 1.0)? as f32;
8749 let rad = self.arg_num(&args, 5, 4.0)? as f32;
8750 if let Some(g) = self.liquids.get_mut(id) {
8751 g.splat(x, y, kind, amt, rad);
8752 }
8753 return Ok(Value::Unit);
8754 },
8755 "liquid_gravity" | "液体重力" | "液体重力ベクトル" | "액체중력" | "แรงโน้มถ่วงเหลว" =>
8756 {
8757 let id = self.arg_num(&args, 0, 0.)? as usize;
8758 let gx = self.arg_num(&args, 1, 0.)? as f32;
8759 let gy = self.arg_num(&args, 2, 60.)? as f32;
8760 if let Some(g) = self.liquids.get_mut(id) {
8761 g.set_gravity(gx, gy);
8762 }
8763 return Ok(Value::Unit);
8764 },
8765 "liquid_step" | "液体步进" | "液体更新" | "액체스텝" | "ก้าวของเหลว" =>
8766 {
8767 let id = self.arg_num(&args, 0, 0.)? as usize;
8768 let dt = self.arg_num(&args, 1, 0.016)? as f32;
8769 if let Some(g) = self.liquids.get_mut(id) {
8770 g.step(dt);
8771 }
8772 return Ok(Value::Unit);
8773 },
8774 "liquid_step_all" | "液体全步进" | "液体全更新" | "전체액체스텝" | "ก้าวของเหลวทั้งหมด" =>
8780 {
8781 let dt = self.arg_num(&args, 0, 0.016)? as f32;
8782 ling_physics::liquid::step_all(&mut self.liquids, dt);
8783 return Ok(Value::Unit);
8784 },
8785 "liquid_rainbow" | "液体彩虹" | "液体虹" | "액체무지개" | "ของเหลวสายรุ้ง" =>
8787 {
8788 let id = self.arg_num(&args, 0, 0.)? as usize;
8789 let on = self.arg_num(&args, 1, 1.0)? > 0.5;
8790 if let Some(g) = self.liquids.get_mut(id) {
8791 g.rainbow = on;
8792 }
8793 return Ok(Value::Unit);
8794 },
8795 "liquid_mix" | "液体混合" | "液体混合度" | "액체혼합" | "การผสมของเหลว" =>
8797 {
8798 let id = self.arg_num(&args, 0, 0.)? as usize;
8799 let m = self.liquids.get(id).map(|g| g.mix_amount()).unwrap_or(0.0);
8800 return Ok(Value::Number(m as f64));
8801 },
8802 #[cfg(not(target_arch = "wasm32"))]
8804 "liquid_draw" | "绘制液体" | "液体描画" | "액체그리기" | "วาดของเหลว" =>
8805 {
8806 let id = self.arg_num(&args, 0, 0.)? as usize;
8807 let sx = self.arg_num(&args, 1, 0.)? as i32;
8808 let sy = self.arg_num(&args, 2, 0.)? as i32;
8809 let scale = (self.arg_num(&args, 3, 4.)? as i32).max(1);
8810 if id < self.liquids.len() {
8811 let (gw, gh) = {
8812 let g = &self.liquids[id];
8813 (g.w, g.h)
8814 };
8815 let mut gfx = self.gfx.borrow_mut();
8816 let (w, h) = (gfx.width as i32, gfx.height as i32);
8817 let g = &self.liquids[id];
8818 for cy in 0..gh {
8819 for cx in 0..gw {
8820 let col = g.sample_rgb(cx, cy);
8821 let bx = sx + cx as i32 * scale;
8822 let by = sy + cy as i32 * scale;
8823 for dy in 0..scale {
8824 for dx in 0..scale {
8825 let px = bx + dx;
8826 let py = by + dy;
8827 if px >= 0 && py >= 0 && px < w && py < h {
8828 gfx.buffer[(py * w + px) as usize] = col;
8829 }
8830 }
8831 }
8832 }
8833 }
8834 }
8835 return Ok(Value::Unit);
8836 },
8837 "liquid_draw_surface" | "液体贴面" | "液体曲面" | "액체곡면" | "ของเหลวบนพื้นผิว" =>
8840 {
8841 #[cfg(not(target_arch = "wasm32"))]
8842 {
8843 let id = self.arg_num(&args, 0, 0.)? as usize;
8844 let kind = self.arg_num(&args, 1, 1.)? as i32;
8845 let cx = self.arg_num(&args, 2, 0.)? as f32;
8846 let cy = self.arg_num(&args, 3, 0.)? as f32;
8847 let cz = self.arg_num(&args, 4, 0.)? as f32;
8848 let radius = self.arg_num(&args, 5, 2.0)? as f32;
8849 let height = self.arg_num(&args, 6, 3.0)? as f32;
8850 if id < self.liquids.len() {
8851 let (gw, gh) = {
8852 let g = &self.liquids[id];
8853 (g.w, g.h)
8854 };
8855 let mut gfx = self.gfx.borrow_mut();
8856 let (w, h, add) = (gfx.width, gfx.height, gfx.blend == 1);
8857 let cam = gfx.camera.clone();
8858 let near = -cam.zdist + 0.05;
8859 let g = &self.liquids[id];
8860 let tau = std::f32::consts::TAU;
8861 let pi = std::f32::consts::PI;
8862 let sp = |u: f32, v: f32| -> [f32; 3] {
8864 if kind == 0 {
8865 [
8866 cx + (u - 0.5) * 2.0 * radius,
8867 cy,
8868 cz + (v - 0.5) * 2.0 * radius,
8869 ]
8870 } else if kind == 2 {
8871 let th = u * tau;
8872 [
8873 cx + th.cos() * radius,
8874 cy + (v - 0.5) * height,
8875 cz + th.sin() * radius,
8876 ]
8877 } else if kind == 3 {
8878 let th = u * tau;
8879 let rr = radius * (1.0 - v);
8880 [
8881 cx + th.cos() * rr,
8882 cy + (v - 0.5) * height,
8883 cz + th.sin() * rr,
8884 ]
8885 } else if kind == 4 {
8886 let th = u * tau;
8887 let ph = v * pi * 0.5;
8888 [
8889 cx + ph.sin() * th.cos() * radius,
8890 cy - ph.cos() * radius,
8891 cz + ph.sin() * th.sin() * radius,
8892 ]
8893 } else {
8894 let th = u * tau;
8895 let ph = v * pi;
8896 [
8897 cx + ph.sin() * th.cos() * radius,
8898 cy + ph.cos() * radius,
8899 cz + ph.sin() * th.sin() * radius,
8900 ]
8901 }
8902 };
8903 let nrm = |u: f32, v: f32| -> [f32; 3] {
8904 if kind == 0 {
8905 [0.0, -1.0, 0.0]
8906 } else if kind == 2 {
8907 let th = u * tau;
8908 [th.cos(), 0.0, th.sin()]
8909 } else if kind == 3 {
8910 let th = u * tau;
8911 let s = (radius / height.max(0.01)).atan();
8912 [th.cos() * s.cos(), s.sin(), th.sin() * s.cos()]
8913 } else if kind == 4 {
8914 let th = u * tau;
8915 let ph = v * pi * 0.5;
8916 [ph.sin() * th.cos(), -ph.cos(), ph.sin() * th.sin()]
8917 } else {
8918 let th = u * tau;
8919 let ph = v * pi;
8920 [ph.sin() * th.cos(), ph.cos(), ph.sin() * th.sin()]
8921 }
8922 };
8923 let gwf = gw as f32;
8924 let ghf = gh as f32;
8925 let mut cyc = 0usize;
8926 while cyc < gh {
8927 let mut cxc = 0usize;
8928 while cxc < gw {
8929 let uc = (cxc as f32 + 0.5) / gwf;
8931 let vc = (cyc as f32 + 0.5) / ghf;
8932 let c = sp(uc, vc);
8933 let n = nrm(uc, vc);
8934 let dc = cam.depth(c[0], c[1], c[2]);
8935 if dc > near {
8936 let cull = kind != 0
8937 && cam.depth(
8938 c[0] + n[0] * 0.06,
8939 c[1] + n[1] * 0.06,
8940 c[2] + n[2] * 0.06,
8941 ) > dc;
8942 if !cull {
8943 let u0 = cxc as f32 / gwf;
8945 let u1 = (cxc + 1) as f32 / gwf;
8946 let v0 = cyc as f32 / ghf;
8947 let v1 = (cyc + 1) as f32 / ghf;
8948 let q = [sp(u0, v0), sp(u1, v0), sp(u1, v1), sp(u0, v1)];
8949 let mut poly: Vec<[f32; 2]> = Vec::with_capacity(5);
8950 let mut ok = true;
8951 for p in &q {
8952 if cam.depth(p[0], p[1], p[2]) <= near {
8953 ok = false;
8954 break;
8955 }
8956 let (sx, sy, _) = cam.project(p[0], p[1], p[2]);
8957 poly.push([sx, sy]);
8958 }
8959 if ok {
8960 let p0 = poly[0];
8961 poly.push(p0);
8962 let col = g.sample_rgb(cxc, cyc);
8963 crate::gfx::raster::fill_contours_aa(
8964 &mut gfx.buffer,
8965 w,
8966 h,
8967 col,
8968 add,
8969 std::slice::from_ref(&poly),
8970 );
8971 }
8972 }
8973 }
8974 cxc += 1;
8975 }
8976 cyc += 1;
8977 }
8978 }
8979 }
8980 #[cfg(target_arch = "wasm32")]
8981 {
8982 }
8985 return Ok(Value::Unit);
8986 },
8987 #[cfg(not(target_arch = "wasm32"))]
8990 "sparkle" | "闪光" | "きらめき" | "반짝임" | "ประกาย" => {
8991 let x = self.arg_num(&args, 0, 0.)? as f32;
8992 let y = self.arg_num(&args, 1, 0.)? as f32;
8993 let ww = self.arg_num(&args, 2, 200.)? as f32;
8994 let hh = self.arg_num(&args, 3, 200.)? as f32;
8995 let count = self.arg_num(&args, 4, 40.)? as i32;
8996 let t = self.arg_num(&args, 5, 0.)? as f32;
8997 let mut gfx = self.gfx.borrow_mut();
8998 let (w, h, add, color) = (gfx.width, gfx.height, gfx.blend == 1, gfx.color);
8999 let (cr, cg, cb) = (
9000 (color >> 16 & 0xFF) as f32,
9001 (color >> 8 & 0xFF) as f32,
9002 (color & 0xFF) as f32,
9003 );
9004 let mut n = 0i32;
9005 while n < count {
9006 let hsh = (n as u32).wrapping_mul(2654435761).wrapping_add(0x9E3779B9);
9007 let u = ((hsh >> 8) & 1023) as f32 / 1023.0;
9008 let v = ((hsh >> 18) & 1023) as f32 / 1023.0;
9009 let phase = (hsh & 255) as f32 / 255.0;
9010 let tw = (t * 3.0 + phase * 6.2831 + n as f32).sin() * 0.5 + 0.5;
9011 let sz = 1.5 + tw * 5.0;
9012 let px = x + u * ww;
9013 let py = y + v * hh;
9014 let b = tw * tw; let col =
9016 (((cr * b) as u32) << 16) | (((cg * b) as u32) << 8) | ((cb * b) as u32);
9017 crate::gfx::raster::draw_line_aa(
9018 &mut gfx.buffer,
9019 w,
9020 h,
9021 col,
9022 add,
9023 px - sz,
9024 py,
9025 px + sz,
9026 py,
9027 );
9028 crate::gfx::raster::draw_line_aa(
9029 &mut gfx.buffer,
9030 w,
9031 h,
9032 col,
9033 add,
9034 px,
9035 py - sz,
9036 px,
9037 py + sz,
9038 );
9039 let d = sz * 0.55;
9040 crate::gfx::raster::draw_line_aa(
9041 &mut gfx.buffer,
9042 w,
9043 h,
9044 col,
9045 add,
9046 px - d,
9047 py - d,
9048 px + d,
9049 py + d,
9050 );
9051 crate::gfx::raster::draw_line_aa(
9052 &mut gfx.buffer,
9053 w,
9054 h,
9055 col,
9056 add,
9057 px - d,
9058 py + d,
9059 px + d,
9060 py - d,
9061 );
9062 n += 1;
9063 }
9064 return Ok(Value::Unit);
9065 },
9066
9067 #[cfg(not(target_arch = "wasm32"))]
9073 "dialog_show" | "对话显示" | "会話表示" | "대화표시" | "แสดงบทสนทนา" =>
9074 {
9075 let text = self.arg_str(&args, 0, "");
9076 let cps = self.arg_num(&args, 1, 32.0)? as f32;
9077 self.dialog = Some(ling_game::dialog::Dialog::new(&text, cps));
9078 return Ok(Value::Unit);
9079 },
9080 #[cfg(not(target_arch = "wasm32"))]
9081 "dialog_step" | "对话步进" | "会話更新" | "대화스텝" | "ก้าวบทสนทนา" =>
9082 {
9083 let dt = self.arg_num(&args, 0, 0.016)? as f32;
9084 if let Some(d) = self.dialog.as_mut() {
9085 d.update(dt);
9086 }
9087 return Ok(Value::Unit);
9088 },
9089 #[cfg(not(target_arch = "wasm32"))]
9090 "dialog_advance" | "对话推进" | "会話送り" | "대화진행" | "เลื่อนบทสนทนา" =>
9091 {
9092 if let Some(d) = self.dialog.as_mut() {
9093 d.advance();
9094 }
9095 return Ok(Value::Unit);
9096 },
9097 #[cfg(not(target_arch = "wasm32"))]
9098 "dialog_active" | "对话激活" | "会話中" | "대화중" | "บทสนทนาทำงาน" =>
9099 {
9100 let a = self
9101 .dialog
9102 .as_ref()
9103 .map(|d| !d.is_closed())
9104 .unwrap_or(false);
9105 return Ok(Value::Bool(a));
9106 },
9107 #[cfg(not(target_arch = "wasm32"))]
9108 "dialog_typing" | "对话打字" | "会話タイプ中" | "대화타이핑" | "กำลังพิมพ์บทสนทนา" =>
9109 {
9110 use ling_game::dialog::Dialog;
9111
9112 let a = self
9113 .dialog
9114 .as_ref()
9115 .map(|d: &Dialog| !d.is_closed() && d.is_typing())
9116 .unwrap_or(false);
9117 return Ok(Value::Bool(a));
9118 },
9119 #[cfg(not(target_arch = "wasm32"))]
9120 "dialog_close" | "对话关闭" | "会話閉じる" | "대화닫기" | "ปิดบทสนทนา" =>
9121 {
9122 self.dialog = None;
9123 return Ok(Value::Unit);
9124 },
9125 #[cfg(not(target_arch = "wasm32"))]
9127 "dialog_color" | "对话颜色" | "会話色" | "대화색" | "สีบทสนทนา" =>
9128 {
9129 let role = (self.arg_num(&args, 0, 0.0)? as usize).min(3);
9130 let r = self.arg_num(&args, 1, 255.0)? as u32 & 0xFF;
9131 let g = self.arg_num(&args, 2, 255.0)? as u32 & 0xFF;
9132 let b = self.arg_num(&args, 3, 255.0)? as u32 & 0xFF;
9133 self.dialog_colors[role] = (r << 16) | (g << 8) | b;
9134 return Ok(Value::Unit);
9135 },
9136 #[cfg(not(target_arch = "wasm32"))]
9138 "dialog_draw" | "对话绘制" | "会話描画" | "대화그리기" | "วาดบทสนทนา" =>
9139 {
9140 let x = self.arg_num(&args, 0, 40.0)? as f32;
9141 let y = self.arg_num(&args, 1, 0.0)? as f32;
9142 let ww = self.arg_num(&args, 2, 720.0)? as f32;
9143 let hh = self.arg_num(&args, 3, 150.0)? as f32;
9144 let font = self.arg_num(&args, 4, -1.0)? as i64;
9145 let t = (crate::runtime::now_secs() - self.start_time_secs) as f32;
9146 self.render_dialog(x, y, ww, hh, font, t);
9147 return Ok(Value::Unit);
9148 },
9149
9150 #[cfg(not(target_arch = "wasm32"))]
9152 "text_poll" => {
9153 let keys = {
9154 let gfx = self.gfx.borrow();
9155 gfx.window
9156 .as_ref()
9157 .map(|w| w.get_keys_pressed(minifb::KeyRepeat::No))
9158 .unwrap_or_default()
9159 };
9160 for k in keys {
9161 if k == minifb::Key::Backspace {
9162 self.text_buffer.pop();
9163 } else if let Some(c) = key_char(k) {
9164 self.text_buffer.push(c);
9165 }
9166 }
9167 return Ok(Value::Str(self.text_buffer.clone()));
9168 },
9169 #[cfg(target_arch = "wasm32")]
9170 "text_poll" => {
9171 return Ok(Value::Str(self.text_buffer.clone()));
9172 },
9173 "text_get" => return Ok(Value::Str(self.text_buffer.clone())),
9174 "text_set" => {
9175 self.text_buffer = self.arg_str(&args, 0, "");
9176 return Ok(Value::Unit);
9177 },
9178 "text_clear" => {
9179 self.text_buffer.clear();
9180 return Ok(Value::Unit);
9181 },
9182 #[cfg(not(target_arch = "wasm32"))]
9184 "record_frame" => {
9185 let n = self.record_n;
9186 let (buf, w, h) = {
9187 let gfx = self.gfx.borrow();
9188 (gfx.buffer.clone(), gfx.width, gfx.height)
9189 };
9190 let _ = std::fs::create_dir_all("recordings");
9191 let mut out = Vec::with_capacity(w * h * 3 + 32);
9192 out.extend_from_slice(format!("P6\n{w} {h}\n255\n").as_bytes());
9193 for px in &buf {
9194 let p = *px;
9195 out.push((p >> 16) as u8);
9196 out.push((p >> 8) as u8);
9197 out.push(p as u8);
9198 }
9199 let _ = std::fs::write(format!("recordings/frame_{n:05}.ppm"), out);
9200 self.record_n += 1;
9201 return Ok(Value::Number(n as f64));
9202 },
9203 "record_count" => return Ok(Value::Number(self.record_n as f64)),
9204 #[cfg(not(target_arch = "wasm32"))]
9206 "screenshot" | "บันทึกภาพ" => {
9207 let mode = self.arg_str(&args, 0, "game");
9208 let (buf, w, h) = {
9209 let gfx = self.gfx.borrow();
9210 (gfx.buffer.clone(), gfx.width, gfx.height)
9211 };
9212 let _ = std::fs::create_dir_all("screenshots");
9213 let ts = std::time::SystemTime::now()
9214 .duration_since(std::time::UNIX_EPOCH)
9215 .map(|d| d.as_secs())
9216 .unwrap_or(0);
9217 let safe: String = mode
9218 .chars()
9219 .map(|c| if c.is_alphanumeric() { c } else { '_' })
9220 .collect();
9221 let path = format!("screenshots/ss_{ts}_{safe}_{w}x{h}.png");
9222 let mut rgb = Vec::with_capacity(w * h * 3);
9223 for px in &buf {
9224 let p = *px;
9225 rgb.push((p >> 16) as u8);
9226 rgb.push((p >> 8) as u8);
9227 rgb.push(p as u8);
9228 }
9229 if let Some(img) = image::RgbImage::from_raw(w as u32, h as u32, rgb) {
9230 let _ = img.save(&path);
9231 }
9232 return Ok(Value::Str(path));
9233 },
9234 #[cfg(not(target_arch = "wasm32"))]
9238 "mic_capture" => {
9239 if let Some(mic) = self.mic.as_ref() {
9240 let s = mic.latest_samples();
9241 self.mic_buffer.extend_from_slice(&s);
9242 let cap = 96_000usize; if self.mic_buffer.len() > cap {
9244 let drop = self.mic_buffer.len() - cap;
9245 self.mic_buffer.drain(0..drop);
9246 }
9247 }
9248 return Ok(Value::Number(self.mic_buffer.len() as f64));
9249 },
9250 #[cfg(not(target_arch = "wasm32"))]
9252 "mic_seed" => {
9253 let mut bytes = Vec::with_capacity(self.mic_buffer.len() * 4);
9254 for f in &self.mic_buffer {
9255 bytes.extend_from_slice(&f.to_le_bytes());
9256 }
9257 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(&bytes))));
9258 },
9259 #[cfg(not(target_arch = "wasm32"))]
9260 "mic_clear" => {
9261 self.mic_buffer.clear();
9262 return Ok(Value::Number(0.0));
9263 },
9264 #[cfg(not(target_arch = "wasm32"))]
9267 "flush_3d" | "render_3d" => {
9268 let mut gfx = self.gfx.borrow_mut();
9269 if !gfx.depth_queue.is_empty() {
9270 let w = gfx.width;
9271 let h = gfx.height;
9272 let dt = gfx.depth_test;
9273 let reset_z = gfx.zbuf_needs_clear;
9274 let (bm, ba) = (gfx.blend, gfx.alpha);
9275 let queue = std::mem::take(&mut gfx.depth_queue);
9276 {
9277 let g = &mut *gfx;
9278 let z = if dt { Some(&mut g.depth_buf) } else { None };
9279 queue.flush(&mut g.buffer, z, reset_z, w, h);
9280 }
9281 gfx.zbuf_needs_clear = false;
9282 gfx.depth_queue.set_state(bm, ba); }
9284 return Ok(Value::Unit);
9285 },
9286 #[cfg(target_arch = "wasm32")]
9287 "flush_3d" | "render_3d" => {
9288 let mut gfx = self.gfx.borrow_mut();
9289 if !gfx.depth_queue.is_empty() {
9290 let w = gfx.width;
9291 let h = gfx.height;
9292 let dt = gfx.depth_test;
9293 let reset_z = gfx.zbuf_needs_clear;
9294 let (bm, ba) = (gfx.blend, gfx.alpha);
9295 let queue = std::mem::take(&mut gfx.depth_queue);
9296 {
9297 let g = &mut *gfx;
9298 let z = if dt { Some(&mut g.depth_buf) } else { None };
9299 queue.flush(&mut g.buffer, z, reset_z, w, h);
9300 }
9301 gfx.zbuf_needs_clear = false;
9302 gfx.depth_queue.set_state(bm, ba);
9303 }
9304 return Ok(Value::Unit);
9305 },
9306
9307 #[cfg(not(target_arch = "wasm32"))]
9310 "screen_distort" | "บิดจอ" | "屏幕扭曲" | "画面歪み" | "화면왜곡" =>
9311 {
9312 let amount = self.arg_num(&args, 0, 8.0)? as f32;
9313 let t = self.arg_num(&args, 1, 0.0)? as f32;
9314 let step = self.arg_num(&args, 2, 1.0)?.max(1.0) as usize;
9317 let _d = std::time::Instant::now();
9318 self.gfx.borrow_mut().distort(amount, t, step);
9319 ling_phase_add(phase::DISTORT, _d.elapsed().as_nanos());
9320 return Ok(Value::Unit);
9321 },
9322
9323 "set_rim" | "设置边缘光" | "リム設定" | "림라이트" | "ตั้งขอบเรือง" =>
9324 {
9325 let s = self.arg_num(&args, 0, 0.6)? as f32;
9326 let r = self.arg_num(&args, 1, 115.)? as f32 / 255.0;
9327 let g = self.arg_num(&args, 2, 217.)? as f32 / 255.0;
9328 let b = self.arg_num(&args, 3, 255.)? as f32 / 255.0;
9329 let mut gfx = self.gfx.borrow_mut();
9330 gfx.shade.rim = s;
9331 gfx.shade.rim_color = [r, g, b];
9332 return Ok(Value::Unit);
9333 },
9334
9335 n if crate::gfx::shapes::canon(n).is_some() => {
9344 let kind = crate::gfx::shapes::canon(n).unwrap();
9345 let cx = self.arg_num(&args, 0, 0.)? as f32;
9346 let cy = self.arg_num(&args, 1, 0.)? as f32;
9347 let cz = self.arg_num(&args, 2, 0.)? as f32;
9348 let sx = self.arg_num(&args, 3, 1.)? as f32;
9349 let sy = self.arg_num(&args, 4, 1.)? as f32;
9350 let sz = self.arg_num(&args, 5, 1.)? as f32;
9351 let rx = self.arg_num(&args, 6, 0.)? as f32;
9352 let ry = self.arg_num(&args, 7, 0.)? as f32;
9353 let rz = self.arg_num(&args, 8, 0.)? as f32;
9354 let mode = self.arg_num(&args, 9, 0.)? as i32;
9355 let e0 = self.arg_num(&args, 10, 0.)? as f32;
9356 let e1 = self.arg_num(&args, 11, 0.)? as f32;
9357 let e2 = self.arg_num(&args, 12, 0.)? as f32;
9358 if let Some(mesh) = crate::gfx::shapes::build(
9359 kind,
9360 [cx, cy, cz, sx, sy, sz, rx, ry, rz],
9361 e0,
9362 e1,
9363 e2,
9364 ) {
9365 let mut gfx = self.gfx.borrow_mut();
9366 gfx.emit_mesh(&mesh, mode);
9367 }
9368 return Ok(Value::Unit);
9369 },
9370
9371 _ => {},
9372 }
9373
9374 if let Some(field_names) = self.structs.get(name).cloned() {
9376 if args.len() != field_names.len() {
9377 return Err(EvalErr::from(format!(
9378 "{name} expects {} field(s), got {}",
9379 field_names.len(),
9380 args.len()
9381 )));
9382 }
9383 let fields = field_names.into_iter().zip(args).collect();
9384 return Ok(Value::Struct { name: name.to_string(), fields });
9385 }
9386
9387 if let Some((enum_name, arity)) = self.enum_variants.get(name).cloned() {
9389 if args.len() != arity {
9390 return Err(EvalErr::from(format!(
9391 "{name} expects {arity} value(s), got {}",
9392 args.len()
9393 )));
9394 }
9395 let variant = name.rsplit("::").next().unwrap_or(name).to_string();
9396 return Ok(Value::Variant { enum_name, variant, payload: args });
9397 }
9398
9399 #[cfg(target_arch = "wasm32")]
9400 if let Some(v) = wasm_unsupported_builtin(name) {
9401 return Ok(v);
9402 }
9403
9404 Err(EvalErr::from(format!("unknown function '{name}'")))
9405 }
9406
9407 fn call_value(&mut self, v: Value, args: Vec<Value>) -> EvalResult {
9408 match v {
9409 Value::Fn(params, body, mut captured) => {
9410 for (p, a) in params.iter().zip(args) {
9411 captured.insert(p.clone(), a);
9412 }
9413 match self.framed("<closure>", |me| me.exec_block(&body, &mut captured)) {
9414 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
9415 Err(EvalErr::Return(v)) => Ok(v),
9416 Err(e) => Err(e),
9417 }
9418 },
9419 other => Err(EvalErr::from(format!("cannot call {:?}", other))),
9420 }
9421 }
9422
9423 fn call_method(&self, recv: Value, method: &str, args: Vec<Value>) -> EvalResult {
9424 match (&recv, method) {
9425 (Value::Str(s), "is_empty" | "是空") => Ok(Value::Bool(s.is_empty())),
9426 (Value::Str(s), "len" | "长") => Ok(Value::Number(s.len() as f64)),
9427 (Value::Str(s), "to_string" | "转文") => Ok(Value::Str(s.clone())),
9428 (Value::Str(s), "contains" | "包含") => {
9429 if let Some(Value::Str(sub)) = args.first() {
9430 Ok(Value::Bool(s.contains(sub.as_str())))
9431 } else {
9432 Ok(Value::Bool(false))
9433 }
9434 },
9435 (Value::Str(s), "push_str" | "推_文") => {
9436 let mut s2 = s.clone();
9437 if let Some(Value::Str(a)) = args.first() {
9438 s2.push_str(a);
9439 }
9440 Ok(Value::Str(s2))
9441 },
9442 (Value::List(v), "len" | "长") => Ok(Value::Number(v.len() as f64)),
9443 (Value::List(v), "push" | "推") => {
9444 let mut v2: Vec<Value> = (**v).clone();
9445 if let Some(a) = args.first() {
9446 v2.push(a.clone());
9447 }
9448 Ok(Value::List(Rc::new(v2)))
9449 },
9450 (Value::Struct { fields, .. }, _) if args.is_empty() => fields
9452 .iter()
9453 .find(|(k, _)| k == method)
9454 .map(|(_, v)| v.clone())
9455 .ok_or_else(|| EvalErr::from(format!("no field '{method}' on {recv}"))),
9456 (Value::Variant { variant, .. }, "tag" | "标签" | "タグ" | "태그" | "ป้าย")
9458 if args.is_empty() =>
9459 {
9460 Ok(Value::Str(variant.clone()))
9461 },
9462 (Value::Ok(inner), _) | (Value::Err(inner), _) => Ok(*inner.clone()),
9463 _ => Err(EvalErr::from(format!("no method '{method}' on {recv}"))),
9464 }
9465 }
9466
9467 fn match_pattern(&self, pat: &Pattern, val: &Value) -> Option<Env> {
9470 match (pat, val) {
9471 (Pattern::Wildcard, _) => Some(new_env()),
9472 (Pattern::Str(s), Value::Str(v)) if s == v => Some(new_env()),
9473 (Pattern::Number(n), Value::Number(v)) if (n - v).abs() < 1e-12 => Some(new_env()),
9474 (Pattern::Bool(b), Value::Bool(v)) if b == v => Some(new_env()),
9475 (Pattern::Ident(name), _) => {
9476 let mut e = new_env();
9477 e.insert(name.clone(), val.clone());
9478 Some(e)
9479 },
9480 (Pattern::Constructor(ctor, inner_pat), _) => {
9481 let (matches, inner_val) = match (ctor.as_str(), val) {
9482 ("ok" | "好", Value::Ok(v)) => (true, Some(v.as_ref().clone())),
9483 ("bad" | "坏", Value::Err(v)) => (true, Some(v.as_ref().clone())),
9484 ("ok" | "好", v) if !matches!(v, Value::Err(_)) => (true, Some(v.clone())),
9485 _ => (false, None),
9486 };
9487 if !matches {
9488 return None;
9489 }
9490 match (inner_pat, inner_val) {
9491 (Some(p), Some(v)) => self.match_pattern(p, &v),
9492 (None, _) => Some(new_env()),
9493 (Some(p), None) => self.match_pattern(p, &Value::Unit),
9494 }
9495 },
9496 (Pattern::Variant(vname, sub_pats), Value::Variant { variant, payload, .. }) => {
9498 if vname != variant || sub_pats.len() != payload.len() {
9499 return None;
9500 }
9501 let mut bindings = new_env();
9502 for (p, v) in sub_pats.iter().zip(payload.iter()) {
9503 bindings.extend(self.match_pattern(p, v)?);
9504 }
9505 Some(bindings)
9506 },
9507 (Pattern::Variant(vname, sub), Value::Ok(v)) if (vname == "ok" || vname == "好") => {
9510 match sub.as_slice() {
9511 [] => Some(new_env()),
9512 [p] => self.match_pattern(p, v),
9513 _ => None,
9514 }
9515 },
9516 (Pattern::Variant(vname, sub), Value::Err(v))
9517 if (vname == "bad" || vname == "坏" || vname == "err") =>
9518 {
9519 match sub.as_slice() {
9520 [] => Some(new_env()),
9521 [p] => self.match_pattern(p, v),
9522 _ => None,
9523 }
9524 },
9525 _ => None,
9526 }
9527 }
9528
9529 fn value_to_iter(&self, val: Value) -> Result<Vec<Value>, EvalErr> {
9532 match val {
9533 Value::List(v) => Ok(Rc::try_unwrap(v).unwrap_or_else(|rc| (*rc).clone())),
9534 Value::Str(s) => Ok(s.chars().map(|c| Value::Str(c.to_string())).collect()),
9535 Value::Number(n) => Ok((0..n as i64).map(|i| Value::Number(i as f64)).collect()),
9536 other => Err(EvalErr::from(format!("cannot iterate over {:?}", other))),
9537 }
9538 }
9539
9540 pub(crate) fn is_truthy(&self, val: &Value) -> bool {
9541 match val {
9542 Value::Bool(b) => *b,
9543 Value::Unit => false,
9544 Value::Number(n) => *n != 0.0,
9545 Value::Str(s) => !s.is_empty(),
9546 Value::List(v) => !v.is_empty(),
9547 Value::Ok(_) => true,
9548 Value::Err(_) => false,
9549 Value::Fn(_, _, _) => true,
9550 Value::Struct { .. } => true,
9551 Value::Variant { .. } => true,
9552 }
9553 }
9554
9555 fn to_number(&self, val: &Value) -> Result<f64, EvalErr> {
9556 match val {
9557 Value::Number(n) => Ok(*n),
9558 Value::Str(s) => s
9559 .parse()
9560 .map_err(|_| EvalErr::from(format!("cannot convert '{s}' to number"))),
9561 other => Err(EvalErr::from(format!("expected number, got {:?}", other))),
9562 }
9563 }
9564
9565 fn arg_num(&self, args: &[Value], n: usize, default: f64) -> Result<f64, EvalErr> {
9567 match args.get(n) {
9568 Some(v) => self.to_number(v),
9569 None => Ok(default),
9570 }
9571 }
9572
9573 fn arg_str(&self, args: &[Value], n: usize, default: &str) -> String {
9574 args.get(n)
9575 .map(|v| v.to_string())
9576 .unwrap_or_else(|| default.to_string())
9577 }
9578
9579 #[allow(dead_code)]
9581 fn arg_list_f32(&self, args: &[Value], n: usize) -> Vec<f32> {
9582 match args.get(n) {
9583 Some(Value::List(v)) => v
9584 .iter()
9585 .filter_map(|x| match x {
9586 Value::Number(n) => Some(*n as f32),
9587 _ => None,
9588 })
9589 .collect(),
9590 _ => Vec::new(),
9591 }
9592 }
9593
9594 #[cfg(not(target_arch = "wasm32"))]
9597 fn color_at(&self, args: &[Value], i: usize, default: u32) -> u32 {
9598 match (args.get(i), args.get(i + 1), args.get(i + 2)) {
9599 (Some(a), Some(b), Some(c)) => {
9600 match (self.to_number(a), self.to_number(b), self.to_number(c)) {
9601 (Ok(r), Ok(g), Ok(bl)) => {
9602 ((r as u32 & 0xFF) << 16) | ((g as u32 & 0xFF) << 8) | (bl as u32 & 0xFF)
9603 },
9604 _ => default,
9605 }
9606 },
9607 _ => default,
9608 }
9609 }
9610
9611 #[cfg(not(target_arch = "wasm32"))]
9613 fn pitch_arg(&self, args: &[Value], i: usize, default: i32) -> i32 {
9614 match args.get(i) {
9615 Some(Value::Str(s)) => ling_music::note::parse_pitch(s).unwrap_or(default),
9616 Some(Value::Number(n)) => *n as i32,
9617 _ => default,
9618 }
9619 }
9620
9621 #[cfg(not(target_arch = "wasm32"))]
9623 fn mouse_now(&self) -> (f32, f32, bool) {
9624 let gfx = self.gfx.borrow();
9625 let (mx, my) = gfx
9626 .window
9627 .as_ref()
9628 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
9629 .unwrap_or((0.0, 0.0));
9630 let down = gfx
9631 .window
9632 .as_ref()
9633 .map(|w| w.get_mouse_down(minifb::MouseButton::Left))
9634 .unwrap_or(false);
9635 (mx, my, down)
9636 }
9637
9638 #[cfg(not(target_arch = "wasm32"))]
9642 fn draw_ui(&self, d: &ling_ui::widgets::Draw) {
9643 let mut gfx = self.gfx.borrow_mut();
9644 let (w, h, add) = (gfx.width, gfx.height, gfx.blend == 1);
9645 for (c, poly) in &d.fills {
9646 crate::gfx::raster::fill_contours_aa(
9647 &mut gfx.buffer,
9648 w,
9649 h,
9650 *c,
9651 add,
9652 std::slice::from_ref(poly),
9653 );
9654 }
9655 for (c, pl) in &d.strokes {
9656 for s in pl.windows(2) {
9657 crate::gfx::raster::draw_line_aa(
9658 &mut gfx.buffer,
9659 w,
9660 h,
9661 *c,
9662 add,
9663 s[0][0],
9664 s[0][1],
9665 s[1][0],
9666 s[1][1],
9667 );
9668 }
9669 }
9670 }
9671
9672 fn tex_rect(&self, args: &[Value]) -> Result<(usize, usize, usize, usize), EvalErr> {
9674 let tx = self.arg_num(args, 0, 0.0)? as usize;
9675 let ty = self.arg_num(args, 1, 0.0)? as usize;
9676 let tw = self.arg_num(args, 2, 256.0)? as usize;
9677 let th = self.arg_num(args, 3, 256.0)? as usize;
9678 Ok((tx, ty, tw.max(1), th.max(1)))
9679 }
9680
9681 pub(crate) fn apply_binop(&self, op: &BinOp, l: Value, r: Value) -> EvalResult {
9682 match op {
9683 BinOp::Add => match (l, r) {
9684 (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
9685 (Value::Str(a), Value::Str(b)) => Ok(Value::Str(a + &b)),
9686 (Value::Str(a), b) => Ok(Value::Str(a + &b.to_string())),
9687 (a, Value::Str(b)) => Ok(Value::Str(a.to_string() + &b)),
9688 (a, b) => Err(EvalErr::from(format!("cannot add {:?} and {:?}", a, b))),
9689 },
9690 BinOp::Sub => Ok(Value::Number(self.to_number(&l)? - self.to_number(&r)?)),
9691 BinOp::Mul => Ok(Value::Number(self.to_number(&l)? * self.to_number(&r)?)),
9692 BinOp::Div => Ok(Value::Number(self.to_number(&l)? / self.to_number(&r)?)),
9693 BinOp::Rem => Ok(Value::Number(self.to_number(&l)? % self.to_number(&r)?)),
9694 BinOp::Eq => Ok(Value::Bool(values_equal(&l, &r))),
9695 BinOp::Ne => Ok(Value::Bool(!values_equal(&l, &r))),
9696 BinOp::Lt => Ok(Value::Bool(self.to_number(&l)? < self.to_number(&r)?)),
9697 BinOp::Gt => Ok(Value::Bool(self.to_number(&l)? > self.to_number(&r)?)),
9698 BinOp::Le => Ok(Value::Bool(self.to_number(&l)? <= self.to_number(&r)?)),
9699 BinOp::Ge => Ok(Value::Bool(self.to_number(&l)? >= self.to_number(&r)?)),
9700 BinOp::And => Ok(Value::Bool(self.is_truthy(&l) && self.is_truthy(&r))),
9701 BinOp::Or => Ok(Value::Bool(self.is_truthy(&l) || self.is_truthy(&r))),
9702 }
9703 }
9704
9705 fn builtin_format(&self, args: &[Value]) -> Result<String, EvalErr> {
9706 if args.is_empty() {
9707 return Ok(String::new());
9708 }
9709 let fmt = match &args[0] {
9710 Value::Str(s) => s.clone(),
9711 other => return Ok(other.to_string()),
9712 };
9713
9714 let mut result = String::new();
9715 let mut arg_idx = 1usize;
9716 let mut chars = fmt.chars().peekable();
9717 while let Some(c) = chars.next() {
9718 if c == '{' {
9719 if chars.peek() == Some(&'}') {
9720 chars.next();
9721 if arg_idx < args.len() {
9722 result.push_str(&args[arg_idx].to_string());
9723 arg_idx += 1;
9724 }
9725 } else {
9726 let mut spec = String::new();
9727 for ch in chars.by_ref() {
9728 if ch == '}' {
9729 break;
9730 }
9731 spec.push(ch);
9732 }
9733 if arg_idx < args.len() {
9734 if spec.starts_with(":.") {
9735 if let Value::Number(n) = &args[arg_idx] {
9736 let prec: usize =
9737 spec[2..].trim_end_matches('f').parse().unwrap_or(2);
9738 result.push_str(&format!("{:.prec$}", n));
9739 arg_idx += 1;
9740 continue;
9741 }
9742 }
9743 result.push_str(&args[arg_idx].to_string());
9744 arg_idx += 1;
9745 }
9746 }
9747 } else {
9748 result.push(c);
9749 }
9750 }
9751 Ok(result)
9752 }
9753}
9754
9755#[cfg(not(target_arch = "wasm32"))]
9756#[cfg(not(target_arch = "wasm32"))]
9758fn parse_pad_button(name: &str) -> Option<ling_input::GamepadButton> {
9759 use ling_input::GamepadButton as B;
9760 Some(match name.to_ascii_lowercase().as_str() {
9761 "a" | "south" | "cross" => B::South,
9762 "b" | "east" | "circle" => B::East,
9763 "x" | "west" | "square" => B::West,
9764 "y" | "north" | "triangle" => B::North,
9765 "lb" | "l1" | "left_shoulder" => B::LeftShoulder,
9766 "rb" | "r1" | "right_shoulder" => B::RightShoulder,
9767 "lt" | "l2" | "left_trigger" => B::LeftTrigger,
9768 "rt" | "r2" | "right_trigger" => B::RightTrigger,
9769 "start" | "menu" | "options" => B::Start,
9770 "select" | "back" | "share" | "view" => B::Select,
9771 "guide" | "home" => B::Guide,
9772 "l3" | "left_stick" => B::LeftStick,
9773 "r3" | "right_stick" => B::RightStick,
9774 "up" | "dpad_up" => B::DpadUp,
9775 "down" | "dpad_down" => B::DpadDown,
9776 "left" | "dpad_left" => B::DpadLeft,
9777 "right" | "dpad_right" => B::DpadRight,
9778 _ => return None,
9779 })
9780}
9781
9782#[cfg(not(target_arch = "wasm32"))]
9783fn str_to_minifb_key(name: &str) -> Option<minifb::Key> {
9784 use minifb::Key;
9785 Some(match name {
9786 "numpad0" | "kp0" => Key::NumPad0,
9787 "numpad1" | "kp1" => Key::NumPad1,
9788 "numpad2" | "kp2" => Key::NumPad2,
9789 "numpad3" | "kp3" => Key::NumPad3,
9790 "numpad4" | "kp4" => Key::NumPad4,
9791 "numpad5" | "kp5" => Key::NumPad5,
9792 "numpad6" | "kp6" => Key::NumPad6,
9793 "numpad7" | "kp7" => Key::NumPad7,
9794 "numpad8" | "kp8" => Key::NumPad8,
9795 "numpad9" | "kp9" => Key::NumPad9,
9796 "numpad+" | "kp+" => Key::NumPadPlus,
9797 "numpad-" | "kp-" => Key::NumPadMinus,
9798 "numpad*" | "kp*" => Key::NumPadAsterisk,
9799 "numpad/" | "kp/" => Key::NumPadSlash,
9800 "left" => Key::Left,
9801 "right" => Key::Right,
9802 "up" => Key::Up,
9803 "down" => Key::Down,
9804 "space" => Key::Space,
9805 "enter" => Key::Enter,
9806 "escape" => Key::Escape,
9807 "pageup" => Key::PageUp,
9808 "pagedown" => Key::PageDown,
9809 "lshift" | "leftshift" => Key::LeftShift,
9810 "rshift" | "rightshift" => Key::RightShift,
9811 "lctrl" | "leftctrl" => Key::LeftCtrl,
9812 "rctrl" | "rightctrl" => Key::RightCtrl,
9813 "lalt" | "leftalt" => Key::LeftAlt,
9814 "ralt" | "rightalt" => Key::RightAlt,
9815 "tab" => Key::Tab,
9816 "backspace" => Key::Backspace,
9817 "delete" => Key::Delete,
9818 "insert" => Key::Insert,
9819 "home" => Key::Home,
9820 "end" => Key::End,
9821 "a" => Key::A,
9822 "b" => Key::B,
9823 "c" => Key::C,
9824 "d" => Key::D,
9825 "e" => Key::E,
9826 "f" => Key::F,
9827 "g" => Key::G,
9828 "h" => Key::H,
9829 "i" => Key::I,
9830 "j" => Key::J,
9831 "k" => Key::K,
9832 "l" => Key::L,
9833 "m" => Key::M,
9834 "n" => Key::N,
9835 "o" => Key::O,
9836 "p" => Key::P,
9837 "q" => Key::Q,
9838 "r" => Key::R,
9839 "s" => Key::S,
9840 "t" => Key::T,
9841 "u" => Key::U,
9842 "v" => Key::V,
9843 "w" => Key::W,
9844 "x" => Key::X,
9845 "y" => Key::Y,
9846 "z" => Key::Z,
9847 "0" => Key::Key0,
9848 "1" => Key::Key1,
9849 "2" => Key::Key2,
9850 "3" => Key::Key3,
9851 "4" => Key::Key4,
9852 "5" => Key::Key5,
9853 "6" => Key::Key6,
9854 "7" => Key::Key7,
9855 "8" => Key::Key8,
9856 "9" => Key::Key9,
9857 _ => return None,
9858 })
9859}
9860
9861pub(crate) fn values_equal(a: &Value, b: &Value) -> bool {
9862 match (a, b) {
9863 (Value::Number(x), Value::Number(y)) => (x - y).abs() < 1e-12,
9864 (Value::Str(x), Value::Str(y)) => x == y,
9865 (Value::Bool(x), Value::Bool(y)) => x == y,
9866 (Value::Unit, Value::Unit) => true,
9867 _ => false,
9868 }
9869}
9870
9871#[cfg(not(target_arch = "wasm32"))]
9878fn hide_console_window() {
9879 #[cfg(windows)]
9880 unsafe {
9881 extern "system" {
9882 fn GetConsoleWindow() -> isize;
9883 fn ShowWindow(hwnd: isize, nCmdShow: i32) -> i32;
9884 }
9885 let hwnd = GetConsoleWindow();
9886 if hwnd != 0 {
9887 ShowWindow(hwnd, 0); }
9889 }
9890}
9891
9892#[cfg(all(not(target_arch = "wasm32"), windows))]
9897fn make_borderless_fullscreen(hwnd: isize, screen_w: i32, screen_h: i32) {
9898 if hwnd == 0 {
9899 return;
9900 }
9901 unsafe {
9902 extern "system" {
9903 fn SetWindowLongPtrW(hwnd: isize, index: i32, new: isize) -> isize;
9904 fn SetWindowPos(
9905 hwnd: isize,
9906 insert_after: isize,
9907 x: i32,
9908 y: i32,
9909 cx: i32,
9910 cy: i32,
9911 flags: u32,
9912 ) -> i32;
9913 fn ShowWindow(hwnd: isize, cmd: i32) -> i32;
9914 }
9915 const GWL_STYLE: i32 = -16;
9916 const GWL_EXSTYLE: i32 = -20;
9917 SetWindowLongPtrW(hwnd, GWL_STYLE, 0x9000_0000isize);
9920 SetWindowLongPtrW(hwnd, GWL_EXSTYLE, 0);
9922 SetWindowPos(hwnd, -1isize, 0, 0, screen_w, screen_h, 0x0020 | 0x0040);
9924 ShowWindow(hwnd, 3); }
9926}
9927
9928#[cfg(all(not(target_arch = "wasm32"), windows))]
9931fn monitor_info() -> (i32, i32, i32) {
9932 unsafe {
9933 extern "system" {
9934 fn GetSystemMetrics(index: i32) -> i32;
9935 fn GetDC(hwnd: isize) -> isize;
9936 fn ReleaseDC(hwnd: isize, hdc: isize) -> i32;
9937 fn GetDeviceCaps(hdc: isize, index: i32) -> i32;
9938 }
9939 let w = GetSystemMetrics(0).max(1); let h = GetSystemMetrics(1).max(1); let hdc = GetDC(0);
9942 let mut hz = if hdc != 0 { GetDeviceCaps(hdc, 116) } else { 0 }; if hdc != 0 {
9944 ReleaseDC(0, hdc);
9945 }
9946 if hz <= 1 {
9947 hz = 60; }
9949 (w, h, hz)
9950 }
9951}
9952
9953#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
9957fn monitor_info() -> (i32, i32, i32) {
9958 let (w, h) = native_screen_size();
9959 (w as i32, h as i32, linux_refresh_hz().unwrap_or(60))
9960}
9961
9962#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
9965fn linux_refresh_hz() -> Option<i32> {
9966 let out = std::process::Command::new("xrandr").arg("--current").output().ok()?;
9967 if !out.status.success() {
9968 return None;
9969 }
9970 let text = String::from_utf8_lossy(&out.stdout);
9971 text.split_whitespace()
9973 .find(|tok| tok.contains('*'))
9974 .and_then(|tok| tok.trim_matches(|c: char| !c.is_ascii_digit() && c != '.').parse::<f64>().ok())
9975 .map(|hz| hz.round() as i32)
9976 .filter(|&hz| hz >= 24 && hz <= 1000)
9977}
9978
9979#[cfg(target_arch = "wasm32")]
9981fn monitor_info() -> (i32, i32, i32) {
9982 let (w, h) = crate::gfx::webgl::canvas_size();
9983 (w as i32, h as i32, 60)
9984}
9985
9986#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
9989fn native_screen_size() -> (f64, f64) {
9990 (1920.0, 1080.0)
9993}
9994
9995struct LingProfileState {
10007 enabled: bool,
10008 every: u64,
10009 frames: u64,
10010 calls: std::collections::HashMap<String, (u64, u128)>, }
10012
10013thread_local! {
10014 static LING_PROFILE: std::cell::RefCell<LingProfileState> = std::cell::RefCell::new({
10015 let enabled = std::env::var("LING_PROFILE").map(|v| v != "0" && !v.is_empty()).unwrap_or(false);
10016 let every = std::env::var("LING_PROFILE_EVERY").ok()
10017 .and_then(|v| v.parse::<u64>().ok()).filter(|&n| n > 0).unwrap_or(240);
10018 if enabled {
10019 eprintln!("[ling-profile] ON — report every {every} frames (set LING_PROFILE_EVERY to change)");
10020 }
10021 LingProfileState { enabled, every, frames: 0, calls: std::collections::HashMap::new() }
10022 });
10023}
10024
10025#[inline]
10026fn ling_profile_enabled() -> bool {
10027 LING_PROFILE.with(|p| p.borrow().enabled)
10028}
10029
10030thread_local! {
10031 static LING_FPS: std::cell::RefCell<(bool, f64, u32, f64)> = std::cell::RefCell::new(
10032 (std::env::var("LING_FPS").map(|v| v != "0" && !v.is_empty()).unwrap_or(false), 0.0, 0, 0.0)
10033 );
10034}
10035
10036#[cfg(not(target_arch = "wasm32"))]
10037fn ling_fps_tick() {
10038 LING_FPS.with(|s| {
10039 let mut s = s.borrow_mut();
10040 if !s.0 { return; }
10041 let now = crate::runtime::now_secs();
10042 if s.1 > 0.0 {
10043 s.3 += now - s.1;
10044 s.2 += 1;
10045 if s.2 >= 120 {
10046 let avg = s.3 / s.2 as f64;
10047 eprintln!("[fps] {:.1} fps ({:.2} ms/frame, wall, {} frames)", 1.0 / avg, avg * 1000.0, s.2);
10048 s.2 = 0;
10049 s.3 = 0.0;
10050 }
10051 }
10052 s.1 = now;
10053 });
10054}
10055
10056thread_local! {
10061 static LING_PHASE: std::cell::RefCell<(bool, u64, [u128; 5])> = std::cell::RefCell::new(
10062 (std::env::var_os("LING_PHASE").is_some(), 0, [0; 5])
10063 );
10064}
10065
10066pub mod phase {
10068 pub const FLUSH: usize = 0;
10069 pub const TOON: usize = 1;
10070 pub const BLIT: usize = 2;
10071 pub const DISTORT: usize = 3;
10072 pub const SORT: usize = 4;
10073}
10074
10075#[cfg(not(target_arch = "wasm32"))]
10076#[inline]
10077pub fn ling_phase_add(idx: usize, nanos: u128) {
10078 LING_PHASE.with(|p| {
10079 let mut p = p.borrow_mut();
10080 if p.0 {
10081 p.2[idx] += nanos;
10082 }
10083 });
10084}
10085
10086#[cfg(not(target_arch = "wasm32"))]
10087fn ling_phase_frame() {
10088 LING_PHASE.with(|p| {
10089 let mut p = p.borrow_mut();
10090 if !p.0 { return; }
10091 p.1 += 1;
10092 if p.1 >= 120 {
10093 let f = p.1 as f64;
10094 let ms = |i: usize| p.2[i] as f64 / 1e6 / f;
10095 eprintln!(
10096 "[phase] sort={:.2} flush={:.2} toon={:.2} blit={:.2} distort={:.2} ms/frame",
10097 ms(phase::SORT), ms(phase::FLUSH), ms(phase::TOON), ms(phase::BLIT), ms(phase::DISTORT)
10098 );
10099 p.1 = 0;
10100 p.2 = [0; 5];
10101 }
10102 });
10103}
10104
10105fn ling_profile_record(name: &str, nanos: u128) {
10106 let is_frame = matches!(
10108 name,
10109 "present" | "แสดงผล" | "gfx_present" | "show" | "显" | "呈现" | "表示" | "표시"
10110 );
10111 LING_PROFILE.with(|p| {
10112 let mut p = p.borrow_mut();
10113 let e = p.calls.entry(name.to_string()).or_insert((0, 0));
10114 e.0 += 1;
10115 e.1 += nanos;
10116 if is_frame {
10117 p.frames += 1;
10118 if p.frames % p.every == 0 {
10119 ling_profile_print(&p);
10120 }
10121 }
10122 });
10123}
10124
10125fn ling_profile_print(p: &LingProfileState) {
10126 let mut rows: Vec<(&String, u64, u128)> =
10127 p.calls.iter().map(|(n, (c, ns))| (n, *c, *ns)).collect();
10128 rows.sort_by(|a, b| b.2.cmp(&a.2)); let total_ns: u128 = p.calls.values().map(|(_, ns)| *ns).sum();
10130 let total_calls: u64 = p.calls.values().map(|(c, _)| *c).sum();
10131 let fr = p.frames.max(1) as f64;
10132 eprintln!(
10133 "\n┌─ LING PROFILE ── frames={} ─ builtin calls by total inclusive time ─────────────",
10134 p.frames
10135 );
10136 eprintln!(
10137 "│ {:<24} {:>9} {:>9} {:>10} {:>9} {:>6}",
10138 "builtin", "calls", "calls/fr", "total_ms", "ms/frame", "%time"
10139 );
10140 eprintln!("├──────────────────────────────────────────────────────────────────────────────");
10141 for (name, count, ns) in rows.iter().take(30) {
10142 let ms = *ns as f64 / 1e6;
10143 let pct = if total_ns > 0 { *ns as f64 / total_ns as f64 * 100.0 } else { 0.0 };
10144 eprintln!(
10145 "│ {:<24} {:>9} {:>9.1} {:>10.1} {:>9.3} {:>5.1}%",
10146 truncate_name(name),
10147 count,
10148 *count as f64 / fr,
10149 ms,
10150 ms / fr,
10151 pct
10152 );
10153 }
10154 eprintln!("├──────────────────────────────────────────────────────────────────────────────");
10155 eprintln!(
10156 "│ TOTAL {} builtin calls, {:.1} ms over {} frames → {:.0} calls/frame, {:.2} ms/frame in builtins",
10157 total_calls,
10158 total_ns as f64 / 1e6,
10159 p.frames,
10160 total_calls as f64 / fr,
10161 total_ns as f64 / 1e6 / fr
10162 );
10163 eprintln!("└──────────────────────────────────────────────────────────────────────────────");
10164}
10165
10166fn truncate_name(s: &str) -> String {
10169 let max = 24;
10170 if s.chars().count() <= max {
10171 s.to_string()
10172 } else {
10173 let mut t: String = s.chars().take(max - 1).collect();
10174 t.push('…');
10175 t
10176 }
10177}