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#[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
11mod llm;
12#[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
13mod vision;
14
15#[cfg(not(target_arch = "wasm32"))]
18pub fn init_aot_runtime() {
19 let interp = Interpreter::new();
20 jit_abi::init(interp);
21}
22
23pub fn now_secs() -> f64 {
26 #[cfg(target_arch = "wasm32")]
27 {
28 js_sys::Date::now() / 1000.0
29 }
30 #[cfg(not(target_arch = "wasm32"))]
31 {
32 std::time::SystemTime::now()
33 .duration_since(std::time::UNIX_EPOCH)
34 .map(|d| d.as_secs_f64())
35 .unwrap_or(0.0)
36 }
37}
38
39static LINE_HUE_RATE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
43static LINE_HUE_START: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
44
45pub fn set_line_hue_rate(rate: f64) {
47 LINE_HUE_RATE.store(rate.to_bits(), std::sync::atomic::Ordering::Relaxed);
48 LINE_HUE_START.store(now_secs().to_bits(), std::sync::atomic::Ordering::Relaxed);
49}
50
51pub fn line_hue_phase() -> Option<f64> {
55 let rate = f64::from_bits(LINE_HUE_RATE.load(std::sync::atomic::Ordering::Relaxed));
56 if rate <= 0.0 {
57 return None;
58 }
59 let start = f64::from_bits(LINE_HUE_START.load(std::sync::atomic::Ordering::Relaxed));
60 Some((now_secs() - start) * rate)
61}
62
63#[cfg(target_arch = "wasm32")]
66thread_local! {
67 static WASM_MODULES: std::cell::RefCell<std::collections::HashMap<String, String>> =
68 std::cell::RefCell::new(std::collections::HashMap::new());
69}
70
71#[cfg(target_arch = "wasm32")]
74pub fn register_wasm_module(path: &str, source: &str) {
75 WASM_MODULES.with(|m| m.borrow_mut().insert(path.to_string(), source.to_string()));
76}
77
78#[cfg(target_arch = "wasm32")]
80pub(crate) fn get_wasm_module(path: &str) -> Option<String> {
81 WASM_MODULES.with(|m| m.borrow().get(path).cloned())
82}
83
84#[cfg(target_arch = "wasm32")]
85#[inline]
86fn wasm_sleep_ms(ms: i32) {
87 if ms <= 0 {
88 return;
89 }
90
91 let global = js_sys::global();
94 let has_sab = js_sys::Reflect::has(
95 &global,
96 &wasm_bindgen::JsValue::from_str("SharedArrayBuffer"),
97 )
98 .unwrap_or(false);
99 let has_atomics =
100 js_sys::Reflect::has(&global, &wasm_bindgen::JsValue::from_str("Atomics")).unwrap_or(false);
101 if has_sab && has_atomics {
102 let sab = js_sys::SharedArrayBuffer::new(4);
103 let i32a = js_sys::Int32Array::new(&sab);
104 if js_sys::Atomics::wait_with_timeout(&i32a, 0, 0, ms as f64).is_ok() {
105 return;
106 }
107 }
108
109 let end = js_sys::Date::now() + ms as f64;
110 while js_sys::Date::now() < end {}
111}
112
113#[cfg(target_arch = "wasm32")]
114fn wasm_fetch_sync(
115 path: &str,
116 response_type: &str,
117 return_expr: &str,
118) -> Result<wasm_bindgen::JsValue, String> {
119 let quoted = js_sys::JSON::stringify(&wasm_bindgen::JsValue::from_str(path))
120 .ok()
121 .and_then(|s| s.as_string())
122 .unwrap_or_else(|| "\"\"".to_string());
123
124 let script = format!(
125 "(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}})()"
126 );
127
128 js_sys::eval(&script).map_err(|e| {
129 e.as_string()
130 .unwrap_or_else(|| format!("JS eval failed: {:?}", e))
131 })
132}
133
134#[cfg(target_arch = "wasm32")]
135fn wasm_fetch_bytes(path: &str) -> Result<Vec<u8>, String> {
136 let value = wasm_fetch_sync(
137 path,
138 "arraybuffer",
139 "new Uint8Array(xhr.response || new ArrayBuffer(0))",
140 )?;
141 let arr = js_sys::Uint8Array::new(&value);
142 let mut out = vec![0u8; arr.length() as usize];
143 arr.copy_to(&mut out);
144 Ok(out)
145}
146
147#[cfg(target_arch = "wasm32")]
148fn wasm_fetch_text(path: &str) -> Result<String, String> {
149 let value = wasm_fetch_sync(path, "text", "String(xhr.responseText || '')")?;
150 Ok(value.as_string().unwrap_or_default())
151}
152
153#[cfg(not(target_arch = "wasm32"))]
154mod net;
155#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
156pub mod web;
157use crate::gfx::{GfxState, Light};
158use crate::parser::ast::*;
159#[cfg(target_arch = "wasm32")]
160use js_sys;
161use std::cell::RefCell;
162use std::collections::HashMap;
163use std::rc::Rc;
164use crate::gfx::raster::draw_line;
167#[cfg(not(target_arch = "wasm32"))]
168use crate::gfx::raster::fill_triangle;
169#[cfg(not(target_arch = "wasm32"))]
170use ling_audio::{AudioEngine, ToneParams, Wave};
171
172#[cfg(not(target_arch = "wasm32"))]
173use ling_audio::FftAnalyzer;
174
175#[cfg(not(target_arch = "wasm32"))]
176use ling_mic;
177
178#[derive(Debug, Clone)]
181pub enum Value {
182 Str(String),
183 Number(f64),
184 Bool(bool),
185 Unit,
186 List(Rc<Vec<Value>>),
187 Ok(Box<Value>),
188 Err(Box<Value>),
189 Fn(Vec<String>, Vec<Stmt>, Env),
190 Struct {
192 name: String,
193 fields: Vec<(String, Value)>,
194 },
195 Variant {
197 enum_name: String,
198 variant: String,
199 payload: Vec<Value>,
200 },
201}
202
203use rustc_hash::FxHashMap;
206type Env = FxHashMap<String, Value>;
207
208#[inline]
209fn new_env() -> Env {
210 FxHashMap::default()
211}
212
213impl std::fmt::Display for Value {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 match self {
216 Value::Str(s) => write!(f, "{s}"),
217 Value::Number(n) => {
218 if n.fract() == 0.0 && n.abs() < 1e15 {
219 write!(f, "{}", *n as i64)
220 } else {
221 write!(f, "{n}")
222 }
223 },
224 Value::Bool(b) => write!(f, "{b}"),
225 Value::Unit => write!(f, "()"),
226 Value::List(v) => {
227 write!(f, "[")?;
228 for (i, x) in v.iter().enumerate() {
229 if i > 0 {
230 write!(f, ", ")?;
231 }
232 write!(f, "{x}")?;
233 }
234 write!(f, "]")
235 },
236 Value::Ok(v) => write!(f, "Ok({v})"),
237 Value::Err(v) => write!(f, "Err({v})"),
238 Value::Fn(_, _, _) => write!(f, "<fn>"),
239 Value::Struct { name, fields } => {
240 write!(f, "{name} {{ ")?;
241 for (i, (k, v)) in fields.iter().enumerate() {
242 if i > 0 {
243 write!(f, ", ")?;
244 }
245 write!(f, "{k}: {v}")?;
246 }
247 write!(f, " }}")
248 },
249 Value::Variant { variant, payload, .. } => {
250 write!(f, "{variant}")?;
251 if !payload.is_empty() {
252 write!(f, "(")?;
253 for (i, v) in payload.iter().enumerate() {
254 if i > 0 {
255 write!(f, ", ")?;
256 }
257 write!(f, "{v}")?;
258 }
259 write!(f, ")")?;
260 }
261 Ok(())
262 },
263 }
264 }
265}
266
267#[cfg(target_arch = "wasm32")]
268fn wasm_unsupported_builtin(name: &str) -> Option<Value> {
269 Some(match name {
270 "audio_blip"
272 | "提示音"
273 | "ビープ音"
274 | "효과음"
275 | "เสียงบี๊บ"
276 | "ui_sound"
277 | "界面音"
278 | "UI音"
279 | "인터페이스음"
280 | "เสียงปุ่ม"
281 | "audio_stop_sfx"
282 | "停止音效"
283 | "効果音停止"
284 | "효과음정지"
285 | "หยุดเอฟเฟกต์ทั้งหมด"
286 | "بوق_کوتاه"
287 | "نغمة_قصيرة"
288 | "ביפ"
289 | "بلپ_آواز" => Value::Unit,
290
291 "music_load"
293 | "载入音乐"
294 | "音楽読込"
295 | "음악로드"
296 | "โหลดเพลง"
297 | "music_patch"
298 | "乐器音色"
299 | "音色読込"
300 | "악기패치"
301 | "แพตช์เครื่องดนตรี"
302 | "music_lrc"
303 | "载入歌词"
304 | "歌詞読込"
305 | "가사로드"
306 | "โหลดเนื้อเพลง"
307 | "music_midi_load"
308 | "载入MIDI"
309 | "MIDI読込"
310 | "미디로드"
311 | "โหลดมิดี"
312 | "بارگذاری_موسیقی"
313 | "تحميل_الموسيقى"
314 | "טעינת_מוזיקה"
315 | "موسیقی_لوڈ" => Value::Number(-1.0),
316
317 "music_duration"
318 | "音乐时长"
319 | "音楽長さ"
320 | "음악길이"
321 | "ความยาวเพลง"
322 | "music_bpm"
323 | "节拍速度"
324 | "テンポ"
325 | "템포"
326 | "จังหวะต่อนาที"
327 | "music_pos"
328 | "音乐位置"
329 | "音楽位置"
330 | "음악위치"
331 | "ตำแหน่งเพลง"
332 | "music_mic_pitch"
333 | "麦克风音高"
334 | "マイク音程"
335 | "마이크음정"
336 | "ระดับเสียงไมค์"
337 | "music_hz"
338 | "音符频率"
339 | "音符周波数"
340 | "음표주파수"
341 | "ความถี่โน้ต"
342 | "music_pitch_score"
343 | "音准评分"
344 | "音程スコア"
345 | "음정점수"
346 | "คะแนนเสียง"
347 | "music_judge"
348 | "判定"
349 | "判定する"
350 | "판정"
351 | "ตัดสินจังหวะ"
352 | "music_midi_count"
353 | "MIDI数量"
354 | "MIDI数"
355 | "미디수"
356 | "จำนวนมิดี"
357 | "مدت_موسیقی"
358 | "مدة_الموسيقى"
359 | "משך_מוזיקה"
360 | "موسیقی_دورانیہ" => Value::Number(0.0),
361
362 "music_key"
363 | "调性"
364 | "調性"
365 | "조성"
366 | "คีย์เพลง"
367 | "music_lyric"
368 | "当前歌词"
369 | "現在歌詞"
370 | "현재가사"
371 | "เนื้อเพลงปัจจุบัน"
372 | "music_note_name"
373 | "音名"
374 | "音名称"
375 | "음이름"
376 | "ชื่อโน้ต"
377 | "music_grade_name"
378 | "判定名"
379 | "判定名称"
380 | "판정이름"
381 | "ชื่อการตัดสิน"
382 | "گام_موسیقی"
383 | "مقام_الموسيقى"
384 | "סולם_מוזיקלי"
385 | "موسیقی_کلید" => Value::Str(String::new()),
386
387 "music_onsets"
388 | "音符起点"
389 | "オンセット"
390 | "온셋"
391 | "จุดเริ่มเสียง"
392 | "music_beat_grid"
393 | "节拍网格"
394 | "ビートグリッド"
395 | "비트그리드"
396 | "กริดจังหวะ"
397 | "music_midi_notes"
398 | "MIDI音符"
399 | "MIDIノート"
400 | "미디음표"
401 | "โน้ตมิดี"
402 | "music_midi_bars"
403 | "MIDI音条"
404 | "MIDIバー"
405 | "미디바"
406 | "แท่งมิดี"
407 | "music_fft"
408 | "音乐频谱"
409 | "音楽スペクトル"
410 | "음악스펙트럼"
411 | "สเปกตรัมเพลง"
412 | "آغازهای_نت"
413 | "بدايات_النغمات"
414 | "התחלות_תווים"
415 | "نوٹ_شروعات" => Value::List(Vec::new().into()),
416
417 "music_play"
418 | "播放音乐"
419 | "音楽再生"
420 | "음악재생"
421 | "เล่นเพลง"
422 | "music_pause"
423 | "暂停音乐"
424 | "音楽一時停止"
425 | "음악일시정지"
426 | "หยุดเพลงชั่วคราว"
427 | "music_stop"
428 | "停止音乐"
429 | "音楽停止"
430 | "음악정지"
431 | "หยุดเพลง"
432 | "music_seek"
433 | "定位音乐"
434 | "音楽シーク"
435 | "음악탐색"
436 | "ค้นหาเพลง"
437 | "music_volume"
438 | "音乐音量"
439 | "音楽音量"
440 | "음악음량"
441 | "ระดับเพลง"
442 | "music_note"
443 | "弹音符"
444 | "音符演奏"
445 | "음표연주"
446 | "เล่นโน้ต"
447 | "music_note_on"
448 | "音符开始"
449 | "音符オン"
450 | "음표켜기"
451 | "โน้ตเริ่ม"
452 | "music_note_off"
453 | "音符结束"
454 | "音符オフ"
455 | "음표끄기"
456 | "โน้ตจบ"
457 | "پخش_موسیقی"
458 | "شغّل_الموسيقى"
459 | "נגן_מוזיקה"
460 | "موسیقی_چلاؤ" => Value::Unit,
461
462 "liquid_new"
464 | "新建液体"
465 | "液体新規"
466 | "액체생성"
467 | "สร้างของเหลว"
468 | "مایع_جدید"
469 | "سائل_جديد"
470 | "נוזל_חדש"
471 | "نیا_مائع" => Value::Number(0.0),
472 "liquid_mix"
473 | "液体混合"
474 | "液体混合度"
475 | "액체혼합"
476 | "การผสมของเหลว"
477 | "ترکیب_مایع"
478 | "مزج_سائل"
479 | "ערבוב_נוזל"
480 | "مائع_ملاؤ" => Value::Number(0.0),
481 "liquid_set_colors"
482 | "液体颜色"
483 | "液体配色"
484 | "액체색상"
485 | "สีของเหลว"
486 | "liquid_splat"
487 | "液体注入"
488 | "液体追加"
489 | "액체분사"
490 | "หยดของเหลว"
491 | "liquid_gravity"
492 | "液体重力"
493 | "液体重力ベクトル"
494 | "액체중력"
495 | "แรงโน้มถ่วงเหลว"
496 | "liquid_step"
497 | "液体步进"
498 | "液体更新"
499 | "액체스텝"
500 | "ก้าวของเหลว"
501 | "liquid_step_all"
502 | "液体全步进"
503 | "液体全更新"
504 | "전체액체스텝"
505 | "ก้าวของเหลวทั้งหมด"
506 | "liquid_rainbow"
507 | "液体彩虹"
508 | "液体虹"
509 | "액체무지개"
510 | "ของเหลวสายรุ้ง"
511 | "liquid_draw"
512 | "绘制液体"
513 | "液体描画"
514 | "액체그리기"
515 | "วาดของเหลว"
516 | "liquid_draw_surface"
517 | "液体贴面"
518 | "液体曲面"
519 | "액체곡면"
520 | "ของเหลวบนพื้นผิว"
521 | "تنظیم_رنگ_مایع"
522 | "عيّن_ألوان_السائل"
523 | "קבע_צבעי_נוזל"
524 | "مائع_رنگ_مقرر_کرو" => Value::Unit,
525
526 "nn_new"
528 | "建神经网"
529 | "ニューラル作成"
530 | "신경망생성"
531 | "สร้างโครงข่าย"
532 | "nn_load"
533 | "载入网"
534 | "網読込"
535 | "신경망불러오기"
536 | "โหลดโครงข่าย"
537 | "شبکه_جدید"
538 | "شبكة_جديدة"
539 | "רשת_חדשה"
540 | "نئی_نیورل_نیٹ" => Value::Number(-1.0),
541 "nn_forward"
542 | "神经前向"
543 | "順伝播"
544 | "순전파"
545 | "ส่งต่อโครงข่าย"
546 | "پیشروی_شبکه"
547 | "تمرير_أمامي"
548 | "העברה_קדימה"
549 | "فارورڈ_پاس" => Value::List(Vec::new().into()),
550 "nn_train"
551 | "训练网"
552 | "ニューラル学習"
553 | "신경망학습"
554 | "ฝึกโครงข่าย"
555 | "nn_dense"
556 | "密集层"
557 | "密層追加"
558 | "밀집층"
559 | "ชั้นหนาแน่น"
560 | "آموزش_شبکه"
561 | "درّب_الشبكة"
562 | "אמן_רשת"
563 | "نیٹ_ٹریننگ" => Value::Number(0.0),
564 "nn_save"
565 | "保存网"
566 | "網保存"
567 | "신경망저장"
568 | "บันทึกโครงข่าย"
569 | "ذخیره_شبکه"
570 | "احفظ_الشبكة"
571 | "שמור_רשת"
572 | "نیٹ_محفوظ_کرو" => Value::Bool(false),
573
574 "bt_build"
576 | "建行为树"
577 | "行動木構築"
578 | "행동트리구성"
579 | "สร้างต้นไม้พฤติกรรม"
580 | "ساخت_درخت_رفتار"
581 | "ابنِ_شجرة_السلوك"
582 | "בנה_עץ_התנהגות"
583 | "بی_ٹی_تعمیر" => Value::Number(-1.0),
584 "bt_tick"
585 | "行为树滴答"
586 | "行動木更新"
587 | "행동트리틱"
588 | "เดินต้นไม้พฤติกรรม"
589 | "تیک_درخت_رفتار"
590 | "نبضة_شجرة_السلوك"
591 | "טיק_עץ_התנהגות"
592 | "بی_ٹی_ٹک" => Value::Str(String::new()),
593 "bt_status"
594 | "行为树状态"
595 | "行動木状態"
596 | "행동트리상태"
597 | "สถานะต้นไม้พฤติกรรม"
598 | "وضعیت_درخت_رفتار"
599 | "حالة_شجرة_السلوك"
600 | "סטטוס_עץ_התנהגות"
601 | "بی_ٹی_حالت" => Value::Number(0.0),
602 "bt_set"
603 | "设事实"
604 | "事実設定"
605 | "사실설정"
606 | "ตั้งข้อเท็จจริง"
607 | "تنظیم_واقعیت"
608 | "عيّن_حقيقة"
609 | "קבע_עובדה"
610 | "بی_ٹی_سیٹ" => Value::Unit,
611
612 "dialog_new"
614 | "建对话模型"
615 | "対話モデル作成"
616 | "대화모델생성"
617 | "สร้างโมเดลสนทนา"
618 | "dialog_load_model"
619 | "对话载模"
620 | "対話モデル読込"
621 | "대화모델불러오기"
622 | "โหลดโมเดลสนทนา"
623 | "dialog_train"
624 | "对话训练"
625 | "対話訓練"
626 | "대화훈련"
627 | "ฝึกสนทนา"
628 | "dialog_load"
629 | "对话载入"
630 | "対話読込"
631 | "대화불러오기"
632 | "โหลดชุดสนทนา"
633 | "مدل_گفتگوی_جدید"
634 | "نموذج_حوار_جديد"
635 | "מודל_דיאלוג_חדש"
636 | "نیا_مکالمہ_ماڈل" => Value::Number(-1.0),
637 "dialog_say"
638 | "对话生成"
639 | "対話生成"
640 | "대화생성"
641 | "พูดสนทนา"
642 | "بگو"
643 | "قل"
644 | "אמור"
645 | "کہو" => Value::Str(String::new()),
646 "dialog_save"
647 | "对话存模"
648 | "対話モデル保存"
649 | "대화모델저장"
650 | "บันทึกโมเดลสนทนา"
651 | "ذخیره_مدل_گفتگو"
652 | "احفظ_نموذج_الحوار"
653 | "שמור_מודל_דיאלוג"
654 | "مکالمہ_ماڈل_محفوظ" => Value::Bool(false),
655 "dialog_learn"
656 | "对话学习"
657 | "対話学習"
658 | "대화학습"
659 | "เรียนรู้สนทนา"
660 | "یادگیری_گفتگو"
661 | "تعلّم_الحوار"
662 | "למד_דיאלוג"
663 | "مکالمہ_سیکھو" => Value::Unit,
664
665 "net_connect"
667 | "联网"
668 | "ネット接続"
669 | "네트연결"
670 | "เชื่อมเน็ต"
671 | "net_listen"
672 | "监听"
673 | "待機"
674 | "리슨"
675 | "รอรับ"
676 | "net_send"
677 | "发送"
678 | "送信"
679 | "전송"
680 | "ส่ง"
681 | "اتصال_شبکه"
682 | "اتصل_بالشبكة"
683 | "התחבר_לרשת"
684 | "نیٹ_کنیکٹ" => Value::Number(-1.0),
685 "net_recv"
686 | "接收"
687 | "受信"
688 | "수신"
689 | "รับ"
690 | "net_status"
691 | "连接状态"
692 | "接続状態"
693 | "연결상태"
694 | "สถานะการเชื่อม"
695 | "دریافت_شبکه"
696 | "استقبل_من_الشبكة"
697 | "קבל_מרשת"
698 | "نیٹ_وصول" => Value::Str(String::new()),
699 "net_discover"
700 | "发现"
701 | "探索"
702 | "검색"
703 | "ค้นหาเครือข่าย"
704 | "کشف_شبکه"
705 | "اكتشف_الشبكة"
706 | "גלה_רשת"
707 | "نیٹ_دریافت" => Value::List(Vec::new().into()),
708 "net_close"
709 | "断开"
710 | "切断"
711 | "연결종료"
712 | "ตัดเชื่อม"
713 | "net_test"
714 | "测连接"
715 | "接続テスト"
716 | "연결테스트"
717 | "ทดสอบเน็ต"
718 | "بستن_شبکه"
719 | "أغلق_الشبكة"
720 | "סגור_רשת"
721 | "نیٹ_بند" => Value::Number(0.0),
722
723 _ => Value::Unit,
725 })
726}
727
728#[derive(Debug)]
731pub(crate) enum EvalErr {
732 Runtime(String),
733 Return(Value),
734 #[allow(dead_code)] Break,
736}
737
738impl From<String> for EvalErr {
739 fn from(s: String) -> Self {
740 EvalErr::Runtime(s)
741 }
742}
743
744type EvalResult = Result<Value, EvalErr>;
745
746#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
748fn base32_encode(data: &[u8]) -> String {
749 const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
750 let mut out = String::new();
751 let mut buffer: u32 = 0;
752 let mut bits = 0u32;
753 for &b in data {
754 buffer = (buffer << 8) | b as u32;
755 bits += 8;
756 while bits >= 5 {
757 bits -= 5;
758 out.push(ALPHABET[((buffer >> bits) & 0x1f) as usize] as char);
759 }
760 }
761 if bits > 0 {
762 out.push(ALPHABET[((buffer << (5 - bits)) & 0x1f) as usize] as char);
763 }
764 out
765}
766
767#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
769fn base32_decode(s: &str) -> Option<Vec<u8>> {
770 let mut buffer: u32 = 0;
771 let mut bits = 0u32;
772 let mut out = Vec::new();
773 for c in s.chars() {
774 if c == '=' || c.is_whitespace() {
775 continue;
776 }
777 let v = match c.to_ascii_uppercase() {
778 'A'..='Z' => c.to_ascii_uppercase() as u32 - 'A' as u32,
779 '2'..='7' => c as u32 - '2' as u32 + 26,
780 _ => return None,
781 };
782 buffer = (buffer << 5) | v;
783 bits += 5;
784 if bits >= 8 {
785 bits -= 8;
786 out.push((buffer >> bits) as u8);
787 }
788 }
789 Some(out)
790}
791
792#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
794fn totp_code(secret_b32: &str, step: u64) -> Option<String> {
795 let key = base32_decode(secret_b32)?;
796 let msg = step.to_be_bytes();
797 let mac = hmac_sha1(&key, &msg);
798 let offset = (mac[19] & 0x0f) as usize;
799 let bin = ((mac[offset] as u32 & 0x7f) << 24)
800 | ((mac[offset + 1] as u32) << 16)
801 | ((mac[offset + 2] as u32) << 8)
802 | (mac[offset + 3] as u32);
803 Some(format!("{:06}", bin % 1_000_000))
804}
805
806#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
808fn totp_check(secret_b32: &str, code: &str) -> bool {
809 if code.len() != 6 || !code.bytes().all(|b| b.is_ascii_digit()) {
810 return false;
811 }
812 let now_step = (crate::runtime::now_secs() as u64) / 30;
813 for delta in [-1i64, 0, 1] {
814 let step = (now_step as i64 + delta) as u64;
815 if let Some(expected) = totp_code(secret_b32, step) {
816 if expected.as_bytes().ct_eq_str(code.as_bytes()) {
818 return true;
819 }
820 }
821 }
822 false
823}
824
825#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
827fn hmac_sha1(key: &[u8], msg: &[u8]) -> [u8; 20] {
828 use hmac::{Mac, SimpleHmac};
829 let mut mac = SimpleHmac::<sha1::Sha1>::new_from_slice(key).expect("hmac key");
830 mac.update(msg);
831 let out = mac.finalize().into_bytes();
832 let mut arr = [0u8; 20];
833 arr.copy_from_slice(&out);
834 arr
835}
836
837#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
839trait CtEqStr {
840 fn ct_eq_str(&self, other: &[u8]) -> bool;
841}
842#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
843impl CtEqStr for [u8] {
844 fn ct_eq_str(&self, other: &[u8]) -> bool {
845 if self.len() != other.len() {
846 return false;
847 }
848 let mut diff = 0u8;
849 for (a, b) in self.iter().zip(other.iter()) {
850 diff |= a ^ b;
851 }
852 diff == 0
853 }
854}
855
856fn url_decode(s: &str) -> String {
858 let bytes = s.as_bytes();
859 let mut out = Vec::with_capacity(bytes.len());
860 let mut i = 0;
861 while i < bytes.len() {
862 match bytes[i] {
863 b'+' => {
864 out.push(b' ');
865 i += 1;
866 },
867 b'%' if i + 2 < bytes.len() => {
868 let hi = (bytes[i + 1] as char).to_digit(16);
869 let lo = (bytes[i + 2] as char).to_digit(16);
870 if let (Some(h), Some(l)) = (hi, lo) {
871 out.push((h * 16 + l) as u8);
872 i += 3;
873 } else {
874 out.push(bytes[i]);
875 i += 1;
876 }
877 },
878 b => {
879 out.push(b);
880 i += 1;
881 },
882 }
883 }
884 String::from_utf8_lossy(&out).into_owned()
885}
886
887#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
889fn values_to_sql_params(args: &[Value]) -> Vec<ling_http::rusqlite::types::Value> {
890 use ling_http::rusqlite::types::Value as Sql;
891 args.iter()
892 .map(|v| match v {
893 Value::Number(n) if n.fract() == 0.0 && n.abs() < 9e15 => Sql::Integer(*n as i64),
894 Value::Number(n) => Sql::Real(*n),
895 Value::Bool(b) => Sql::Integer(*b as i64),
896 other => Sql::Text(other.to_string()),
897 })
898 .collect()
899}
900
901struct SvgWriter {
906 path: String,
907 width: f64,
908 height: f64,
909 elements: Vec<String>,
910}
911
912impl SvgWriter {
913 fn new(path: String, width: f64, height: f64) -> Self {
914 let bg = format!("<rect width=\"{width}\" height=\"{height}\" fill=\"#0a0a0a\"/>");
915 Self { path, width, height, elements: vec![bg] }
916 }
917
918 fn save(&self) -> std::io::Result<()> {
919 let w = self.width;
920 let h = self.height;
921 let mut out = format!(
922 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
923 <svg xmlns=\"http://www.w3.org/2000/svg\" \
924 width=\"{w}\" height=\"{h}\" viewBox=\"0 0 {w} {h}\">\n"
925 );
926 for elem in &self.elements {
927 out.push_str(" ");
928 out.push_str(elem);
929 out.push('\n');
930 }
931 out.push_str("</svg>\n");
932 if let Some(parent) = std::path::Path::new(&self.path).parent() {
934 if !parent.as_os_str().is_empty() {
935 let _ = std::fs::create_dir_all(parent);
936 }
937 }
938 std::fs::write(&self.path, out.as_bytes())
939 }
940}
941
942fn hsl_to_hex(h: f64, s: f64, l: f64) -> String {
943 let s = s / 100.0;
944 let l = l / 100.0;
945 let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
946 let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
947 let m = l - c / 2.0;
948 let (r1, g1, b1) = if h < 60.0 {
949 (c, x, 0.0)
950 } else if h < 120.0 {
951 (x, c, 0.0)
952 } else if h < 180.0 {
953 (0.0, c, x)
954 } else if h < 240.0 {
955 (0.0, x, c)
956 } else if h < 300.0 {
957 (x, 0.0, c)
958 } else {
959 (c, 0.0, x)
960 };
961 let r = ((r1 + m) * 255.0).round() as u8;
962 let g = ((g1 + m) * 255.0).round() as u8;
963 let b = ((b1 + m) * 255.0).round() as u8;
964 format!("#{r:02x}{g:02x}{b:02x}")
965}
966
967fn tex_hash(x: i32, y: i32, seed: u32) -> f32 {
970 let mut h = (x as u32)
971 .wrapping_add((y as u32).wrapping_mul(2654435769))
972 .wrapping_add(seed.wrapping_mul(1234567891));
973 h ^= h >> 16;
974 h = h.wrapping_mul(0x45d9f3b);
975 h ^= h >> 16;
976 h as f32 / u32::MAX as f32
977}
978
979fn tex_vnoise(x: f32, y: f32, seed: u32) -> f32 {
980 let xi = x.floor() as i32;
981 let yi = y.floor() as i32;
982 let sm = |t: f32| t * t * (3.0 - 2.0 * t);
983 let xf = sm(x - xi as f32);
984 let yf = sm(y - yi as f32);
985 let a = tex_hash(xi, yi, seed);
986 let b = tex_hash(xi + 1, yi, seed);
987 let c = tex_hash(xi, yi + 1, seed);
988 let d = tex_hash(xi + 1, yi + 1, seed);
989 a + (b - a) * xf + (c - a) * yf + (a - b - c + d) * xf * yf
990}
991
992fn tex_fbm(x: f32, y: f32, octaves: u32, seed: u32) -> f32 {
993 let mut v = 0.0f32;
994 let mut amp = 0.5f32;
995 let mut f = 1.0f32;
996 for i in 0..octaves {
997 v += tex_vnoise(x * f, y * f, seed.wrapping_add(i * 7919)) * amp;
998 amp *= 0.5;
999 f *= 2.0;
1000 }
1001 v
1002}
1003
1004fn tex_palette(name: &str, t: f32) -> [f32; 3] {
1005 let (a, b, c, d): ([f32; 3], [f32; 3], [f32; 3], [f32; 3]) = match name {
1006 "fire" => (
1007 [0.8, 0.4, 0.1],
1008 [0.7, 0.3, 0.1],
1009 [1.0, 0.5, 0.3],
1010 [0.0, 0.5, 0.8],
1011 ),
1012 "ocean" => (
1013 [0.1, 0.4, 0.7],
1014 [0.3, 0.3, 0.4],
1015 [0.8, 1.0, 0.5],
1016 [0.3, 0.0, 0.6],
1017 ),
1018 "psychedelic" => (
1019 [0.5, 0.5, 0.5],
1020 [0.8, 0.8, 0.8],
1021 [1.0, 1.3, 0.7],
1022 [0.0, 0.15, 0.3],
1023 ),
1024 "neon" => (
1025 [0.5, 0.5, 0.5],
1026 [0.5, 0.5, 0.5],
1027 [2.0, 1.0, 0.0],
1028 [0.5, 0.2, 0.25],
1029 ),
1030 "forest" => (
1031 [0.3, 0.5, 0.2],
1032 [0.2, 0.3, 0.1],
1033 [1.0, 0.5, 0.8],
1034 [0.1, 0.3, 0.6],
1035 ),
1036 _ => (
1037 [0.5, 0.5, 0.5],
1038 [0.5, 0.5, 0.5],
1039 [1.0, 1.0, 1.0],
1040 [0.0, 0.333, 0.667],
1041 ),
1042 };
1043 [0, 1, 2]
1044 .map(|i| (a[i] + b[i] * (std::f32::consts::TAU * (c[i] * t + d[i])).cos()).clamp(0.0, 1.0))
1045}
1046
1047#[cfg(not(target_arch = "wasm32"))]
1049fn key_char(k: minifb::Key, shift: bool) -> Option<char> {
1055 use minifb::Key::*;
1056 let base = match k {
1057 A => 'a',
1058 B => 'b',
1059 C => 'c',
1060 D => 'd',
1061 E => 'e',
1062 F => 'f',
1063 G => 'g',
1064 H => 'h',
1065 I => 'i',
1066 J => 'j',
1067 K => 'k',
1068 L => 'l',
1069 M => 'm',
1070 N => 'n',
1071 O => 'o',
1072 P => 'p',
1073 Q => 'q',
1074 R => 'r',
1075 S => 's',
1076 T => 't',
1077 U => 'u',
1078 V => 'v',
1079 W => 'w',
1080 X => 'x',
1081 Y => 'y',
1082 Z => 'z',
1083 Key0 => '0',
1084 Key1 => '1',
1085 Key2 => '2',
1086 Key3 => '3',
1087 Key4 => '4',
1088 Key5 => '5',
1089 Key6 => '6',
1090 Key7 => '7',
1091 Key8 => '8',
1092 Key9 => '9',
1093 Space => ' ',
1094 Minus => '-',
1095 Equal => '=',
1096 Period => '.',
1097 Comma => ',',
1098 Slash => '/',
1099 Backslash => '\\',
1100 Semicolon => ';',
1101 Apostrophe => '\'',
1102 LeftBracket => '[',
1103 RightBracket => ']',
1104 Backquote => '`',
1105 _ => return None,
1106 };
1107 if !shift {
1108 return Some(base);
1109 }
1110 Some(match base {
1111 'a'..='z' => base.to_ascii_uppercase(),
1112 '0' => ')',
1113 '1' => '!',
1114 '2' => '@',
1115 '3' => '#',
1116 '4' => '$',
1117 '5' => '%',
1118 '6' => '^',
1119 '7' => '&',
1120 '8' => '*',
1121 '9' => '(',
1122 '-' => '_',
1123 '=' => '+',
1124 '.' => '>',
1125 ',' => '<',
1126 '/' => '?',
1127 '\\' => '|',
1128 ';' => ':',
1129 '\'' => '"',
1130 '[' => '{',
1131 ']' => '}',
1132 '`' => '~',
1133 other => other,
1134 })
1135}
1136
1137#[cfg(all(not(target_arch = "wasm32"), windows))]
1141fn vk_char(vk: i32, shift: bool) -> Option<char> {
1142 let base = match vk {
1143 0x41..=0x5A => (b'a' + (vk - 0x41) as u8) as char, 0x30..=0x39 => (b'0' + (vk - 0x30) as u8) as char, 0x20 => ' ', 0xBD => '-', 0xBB => '=', 0xBE => '.', 0xBC => ',', 0xBF => '/', 0xDC => '\\', 0xBA => ';', 0xDE => '\'', 0xDB => '[', 0xDD => ']', 0xC0 => '`', _ => return None,
1158 };
1159 if !shift {
1160 return Some(base);
1161 }
1162 Some(match base {
1163 'a'..='z' => base.to_ascii_uppercase(),
1164 '0' => ')',
1165 '1' => '!',
1166 '2' => '@',
1167 '3' => '#',
1168 '4' => '$',
1169 '5' => '%',
1170 '6' => '^',
1171 '7' => '&',
1172 '8' => '*',
1173 '9' => '(',
1174 '-' => '_',
1175 '=' => '+',
1176 '.' => '>',
1177 ',' => '<',
1178 '/' => '?',
1179 '\\' => '|',
1180 ';' => ':',
1181 '\'' => '"',
1182 '[' => '{',
1183 ']' => '}',
1184 '`' => '~',
1185 other => other,
1186 })
1187}
1188
1189#[cfg(all(not(target_arch = "wasm32"), windows))]
1192const TEXT_POLL_VKS: &[i32] = &[
1193 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
1194 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
1195 0x36, 0x37, 0x38, 0x39, 0x20, 0xBD, 0xBB, 0xBE, 0xBC, 0xBF, 0xDC, 0xBA, 0xDE, 0xDB, 0xDD, 0xC0,
1196];
1197#[cfg(all(not(target_arch = "wasm32"), windows))]
1200const VK_BACK: i32 = 0x08;
1201#[cfg(all(not(target_arch = "wasm32"), windows))]
1202const VK_SHIFT: i32 = 0x10;
1203
1204#[cfg(all(not(target_arch = "wasm32"), windows))]
1211fn os_key_down(vk: i32) -> bool {
1212 unsafe {
1213 extern "system" {
1214 fn GetAsyncKeyState(vkey: i32) -> i16;
1215 }
1216 (GetAsyncKeyState(vk) as u16 & 0x8000) != 0
1217 }
1218}
1219
1220#[cfg(all(not(target_arch = "wasm32"), windows))]
1228fn window_is_foreground(hwnd: isize) -> bool {
1229 unsafe {
1230 extern "system" {
1231 fn GetForegroundWindow() -> isize;
1232 }
1233 hwnd != 0 && GetForegroundWindow() == hwnd
1234 }
1235}
1236
1237#[cfg(all(not(target_arch = "wasm32"), windows))]
1240const KEY_REPEAT_DELAY: f64 = 0.45;
1241#[cfg(all(not(target_arch = "wasm32"), windows))]
1243const KEY_REPEAT_RATE: f64 = 0.045;
1244
1245#[cfg(all(not(target_arch = "wasm32"), windows))]
1252fn key_repeat_fire(
1253 now: f64,
1254 down: bool,
1255 was_down: bool,
1256 down_since: &mut f64,
1257 last_fire: &mut f64,
1258) -> bool {
1259 if !down {
1260 return false;
1261 }
1262 if !was_down {
1263 *down_since = now;
1264 *last_fire = now;
1265 return true;
1266 }
1267 if now - *down_since >= KEY_REPEAT_DELAY && now - *last_fire >= KEY_REPEAT_RATE {
1268 *last_fire = now;
1269 return true;
1270 }
1271 false
1272}
1273
1274#[cfg(all(not(target_arch = "wasm32"), windows))]
1278fn str_to_vk(name: &str) -> Option<i32> {
1279 Some(match name {
1280 "numpad0" | "kp0" => 0x60,
1281 "numpad1" | "kp1" => 0x61,
1282 "numpad2" | "kp2" => 0x62,
1283 "numpad3" | "kp3" => 0x63,
1284 "numpad4" | "kp4" => 0x64,
1285 "numpad5" | "kp5" => 0x65,
1286 "numpad6" | "kp6" => 0x66,
1287 "numpad7" | "kp7" => 0x67,
1288 "numpad8" | "kp8" => 0x68,
1289 "numpad9" | "kp9" => 0x69,
1290 "numpad+" | "kp+" => 0x6B,
1291 "numpad-" | "kp-" => 0x6D,
1292 "numpad*" | "kp*" => 0x6A,
1293 "numpad/" | "kp/" => 0x6F,
1294 "left" => 0x25,
1295 "up" => 0x26,
1296 "right" => 0x27,
1297 "down" => 0x28,
1298 "space" => 0x20,
1299 "enter" => 0x0D,
1300 "escape" => 0x1B,
1301 "pageup" => 0x21,
1302 "pagedown" => 0x22,
1303 "lshift" | "leftshift" => 0xA0,
1304 "rshift" | "rightshift" => 0xA1,
1305 "lctrl" | "leftctrl" => 0xA2,
1306 "rctrl" | "rightctrl" => 0xA3,
1307 "lalt" | "leftalt" => 0xA4,
1308 "ralt" | "rightalt" => 0xA5,
1309 "tab" => 0x09,
1310 "backspace" => VK_BACK,
1311 "delete" => 0x2E,
1312 "insert" => 0x2D,
1313 "home" => 0x24,
1314 "end" => 0x23,
1315 "a" => 0x41,
1316 "b" => 0x42,
1317 "c" => 0x43,
1318 "d" => 0x44,
1319 "e" => 0x45,
1320 "f" => 0x46,
1321 "g" => 0x47,
1322 "h" => 0x48,
1323 "i" => 0x49,
1324 "j" => 0x4A,
1325 "k" => 0x4B,
1326 "l" => 0x4C,
1327 "m" => 0x4D,
1328 "n" => 0x4E,
1329 "o" => 0x4F,
1330 "p" => 0x50,
1331 "q" => 0x51,
1332 "r" => 0x52,
1333 "s" => 0x53,
1334 "t" => 0x54,
1335 "u" => 0x55,
1336 "v" => 0x56,
1337 "w" => 0x57,
1338 "x" => 0x58,
1339 "y" => 0x59,
1340 "z" => 0x5A,
1341 "0" => 0x30,
1342 "1" => 0x31,
1343 "2" => 0x32,
1344 "3" => 0x33,
1345 "4" => 0x34,
1346 "5" => 0x35,
1347 "6" => 0x36,
1348 "7" => 0x37,
1349 "8" => 0x38,
1350 "9" => 0x39,
1351 _ => return None,
1352 })
1353}
1354
1355fn hex_encode(bytes: &[u8]) -> String {
1357 let mut s = String::with_capacity(bytes.len() * 2);
1358 for b in bytes {
1359 s.push_str(&format!("{b:02x}"));
1360 }
1361 s
1362}
1363
1364#[cfg(not(target_arch = "wasm32"))]
1366fn decode_blob(s: &str) -> Result<Vec<u8>, String> {
1367 use base64::Engine as _;
1368 use std::io::Read as _;
1369 let comp = base64::engine::general_purpose::STANDARD
1370 .decode(s.trim())
1371 .map_err(|e| format!("base64: {e}"))?;
1372 let mut out = Vec::new();
1373 flate2::read::ZlibDecoder::new(&comp[..])
1374 .read_to_end(&mut out)
1375 .map_err(|e| format!("inflate: {e}"))?;
1376 Ok(out)
1377}
1378
1379#[cfg(not(target_arch = "wasm32"))]
1381fn hex_decode(s: &str) -> Vec<u8> {
1382 let s = s.trim();
1383 (0..s.len() / 2)
1384 .filter_map(|i| u8::from_str_radix(s.get(i * 2..i * 2 + 2)?, 16).ok())
1385 .collect()
1386}
1387
1388#[cfg(not(target_arch = "wasm32"))]
1390fn hex_to_32(s: &str) -> [u8; 32] {
1391 let v = hex_decode(s);
1392 let mut out = [0u8; 32];
1393 let n = v.len().min(32);
1394 out[..n].copy_from_slice(&v[..n]);
1395 out
1396}
1397
1398#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1406fn build_pdf_from_images(
1407 paths: &[String],
1408 out_path: &str,
1409) -> Result<(), Box<dyn std::error::Error>> {
1410 use std::io::Write as _;
1411
1412 struct PageImg {
1413 w: u32,
1414 h: u32,
1415 compressed: Vec<u8>,
1416 }
1417
1418 let mut pages = Vec::with_capacity(paths.len());
1419 for p in paths {
1420 let img = image::open(p)?.to_rgb8();
1421 let (w, h) = (img.width(), img.height());
1422 let mut enc = flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
1423 enc.write_all(img.as_raw())?;
1424 pages.push(PageImg { w, h, compressed: enc.finish()? });
1425 }
1426
1427 let n = pages.len();
1428 let page_nums: Vec<u32> = (0..n).map(|i| 3 + (i as u32) * 3).collect();
1431 let total_objs = 2 + n * 3;
1432 let mut off = vec![0usize; total_objs + 1]; let mut buf: Vec<u8> = Vec::new();
1435 buf.extend_from_slice(b"%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
1436
1437 off[1] = buf.len();
1438 buf.extend_from_slice(b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n");
1439
1440 off[2] = buf.len();
1441 let kids = page_nums
1442 .iter()
1443 .map(|n| format!("{n} 0 R"))
1444 .collect::<Vec<_>>()
1445 .join(" ");
1446 buf.extend_from_slice(
1447 format!("2 0 obj\n<< /Type /Pages /Kids [{kids}] /Count {n} >>\nendobj\n").as_bytes(),
1448 );
1449
1450 for (i, page) in pages.iter().enumerate() {
1451 let page_num = page_nums[i];
1452 let content_num = page_num + 1;
1453 let image_num = page_num + 2;
1454 let (w, h) = (page.w, page.h);
1455
1456 off[page_num as usize] = buf.len();
1457 buf.extend_from_slice(format!(
1458 "{page_num} 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 {w} {h}] /Resources << /XObject << /Im0 {image_num} 0 R >> >> /Contents {content_num} 0 R >>\nendobj\n"
1459 ).as_bytes());
1460
1461 let content = format!("q {w} 0 0 {h} 0 0 cm /Im0 Do Q");
1462 off[content_num as usize] = buf.len();
1463 buf.extend_from_slice(
1464 format!(
1465 "{content_num} 0 obj\n<< /Length {} >>\nstream\n{content}\nendstream\nendobj\n",
1466 content.len()
1467 )
1468 .as_bytes(),
1469 );
1470
1471 off[image_num as usize] = buf.len();
1472 buf.extend_from_slice(format!(
1473 "{image_num} 0 obj\n<< /Type /XObject /Subtype /Image /Width {w} /Height {h} /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /FlateDecode /Length {} >>\nstream\n",
1474 page.compressed.len()
1475 ).as_bytes());
1476 buf.extend_from_slice(&page.compressed);
1477 buf.extend_from_slice(b"\nendstream\nendobj\n");
1478 }
1479
1480 let xref_offset = buf.len();
1481 buf.extend_from_slice(format!("xref\n0 {}\n", total_objs + 1).as_bytes());
1482 buf.extend_from_slice(b"0000000000 65535 f \n");
1483 for entry in off.iter().skip(1) {
1484 buf.extend_from_slice(format!("{entry:010} 00000 n \n").as_bytes());
1485 }
1486 buf.extend_from_slice(
1487 format!(
1488 "trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref_offset}\n%%EOF",
1489 total_objs + 1
1490 )
1491 .as_bytes(),
1492 );
1493
1494 std::fs::write(out_path, buf)?;
1495 Ok(())
1496}
1497
1498fn tex_rgb(r: f32, g: f32, b: f32) -> u32 {
1499 ((r * 255.0) as u32) << 16 | ((g * 255.0) as u32) << 8 | (b * 255.0) as u32
1500}
1501
1502const PERM: [u8; 512] = [
1505 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
1506 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
1507 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 35, 63, 189, 114, 56, 42, 123,
1508 165, 38, 72, 93, 69, 139, 138, 78, 149, 159, 56, 89, 152, 78, 61, 140, 63, 26, 142, 76, 124,
1509 132, 72, 11, 90, 44, 82, 59, 96, 41, 148, 126, 157, 13, 49, 27, 176, 33, 47, 14, 97, 78, 71,
1510 40, 87, 183, 4, 122, 92, 7, 72, 3, 246, 17, 225, 87, 91, 106, 203, 190, 57, 74, 76, 88, 207,
1511 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146,
1512 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196,
1513 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94,
1514 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55,
1515 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180,
1516 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185,
1517 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,
1518 140, 161, 137, 13, 191, 230, 66, 104, 153, 199, 167, 147, 99, 179, 92,
1519 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
1521 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
1522 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 35, 63, 189, 114, 56, 42, 123,
1523 165, 38, 72, 93, 69, 139, 138, 78, 149, 159, 56, 89, 152, 78, 61, 140, 63, 26, 142, 76, 124,
1524 132, 72, 11, 90, 44, 82, 59, 96, 41, 148, 126, 157, 13, 49, 27, 176, 33, 47, 14, 97, 78, 71,
1525 40, 87, 183, 4, 122, 92, 7, 72, 3, 246, 17, 225, 87, 91, 106, 203, 190, 57, 74, 76, 88, 207,
1526 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146,
1527 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196,
1528 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94,
1529 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55,
1530 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174,
1531];
1532
1533fn fade(t: f32) -> f32 {
1534 t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
1535}
1536
1537fn grad(hash: u8, x: f32, y: f32, z: f32) -> f32 {
1538 let h = hash & 15;
1539 let u = if h < 8 { x } else { y };
1540 let v = if h < 8 { y } else { z };
1541 (if (h & 1) == 0 { u } else { -u }) + (if (h & 2) == 0 { v } else { -v })
1542}
1543
1544fn perlin3(x: f32, y: f32, z: f32) -> f32 {
1545 let xi = (x.floor() as i32) & 255;
1546 let yi = (y.floor() as i32) & 255;
1547 let zi = (z.floor() as i32) & 255;
1548
1549 let xf = x - x.floor();
1550 let yf = y - y.floor();
1551 let zf = z - z.floor();
1552
1553 let u = fade(xf);
1554 let v = fade(yf);
1555 let w = fade(zf);
1556
1557 let p0 = PERM[xi as usize] as usize;
1558 let p1 = PERM[((xi + 1) & 255) as usize] as usize;
1559 let pa = PERM[(p0 + yi as usize) & 255] as usize;
1560 let pb = PERM[(p0 + ((yi + 1) & 255) as usize) & 255] as usize;
1561 let pc = PERM[(p1 + yi as usize) & 255] as usize;
1562 let pd = PERM[(p1 + ((yi + 1) & 255) as usize) & 255] as usize;
1563
1564 let g000 = grad(PERM[(pa + zi as usize) & 255], xf, yf, zf);
1565 let g001 = grad(
1566 PERM[(pa + ((zi + 1) & 255) as usize) & 255],
1567 xf,
1568 yf,
1569 zf - 1.0,
1570 );
1571 let g010 = grad(PERM[(pb + zi as usize) & 255], xf, yf - 1.0, zf);
1572 let g011 = grad(
1573 PERM[(pb + ((zi + 1) & 255) as usize) & 255],
1574 xf,
1575 yf - 1.0,
1576 zf - 1.0,
1577 );
1578 let g100 = grad(PERM[(pc + zi as usize) & 255], xf - 1.0, yf, zf);
1579 let g101 = grad(
1580 PERM[(pc + ((zi + 1) & 255) as usize) & 255],
1581 xf - 1.0,
1582 yf,
1583 zf - 1.0,
1584 );
1585 let g110 = grad(PERM[(pd + zi as usize) & 255], xf - 1.0, yf - 1.0, zf);
1586 let g111 = grad(
1587 PERM[(pd + ((zi + 1) & 255) as usize) & 255],
1588 xf - 1.0,
1589 yf - 1.0,
1590 zf - 1.0,
1591 );
1592
1593 let l00 = g000 + u * (g100 - g000);
1594 let l01 = g001 + u * (g101 - g001);
1595 let l10 = g010 + u * (g110 - g010);
1596 let l11 = g011 + u * (g111 - g011);
1597
1598 let l0 = l00 + v * (l10 - l00);
1599 let l1 = l01 + v * (l11 - l01);
1600
1601 l0 + w * (l1 - l0)
1602}
1603
1604fn fast_rand_f64(state: &mut u64) -> f64 {
1605 *state = state
1606 .wrapping_mul(6364136223846793005)
1607 .wrapping_add(1442695040888963407);
1608 ((*state >> 32) as u32) as f64 / 4294967296.0
1609}
1610
1611#[inline]
1615fn put_px(buf: &mut [u32], idx: usize, color: u32, blend: u8) {
1616 if idx >= buf.len() {
1617 return;
1618 }
1619 if blend == 0 {
1620 buf[idx] = color;
1621 } else {
1622 let old = buf[idx];
1623 let r = (((old >> 16) & 255) + ((color >> 16) & 255)).min(255);
1624 let g = (((old >> 8) & 255) + ((color >> 8) & 255)).min(255);
1625 let b = ((old & 255) + (color & 255)).min(255);
1626 buf[idx] = (r << 16) | (g << 8) | b;
1627 }
1628}
1629
1630#[inline]
1632fn rgb(r: f64, g: f64, b: f64) -> u32 {
1633 let r = (r as i64).clamp(0, 255) as u32;
1634 let g = (g as i64).clamp(0, 255) as u32;
1635 let b = (b as i64).clamp(0, 255) as u32;
1636 (r << 16) | (g << 8) | b
1637}
1638
1639#[allow(clippy::too_many_arguments)]
1640fn draw_circle_outline(
1641 buf: &mut [u32],
1642 w: i32,
1643 h: i32,
1644 cx: i32,
1645 cy: i32,
1646 r: i32,
1647 color: u32,
1648 blend: u8,
1649) {
1650 let r = r.clamp(0, 20000); if r == 0 {
1652 return;
1653 }
1654 let mut x = 0;
1655 let mut y = r;
1656 let mut d = 3 - 2 * r;
1657 while x <= y {
1658 plot_circle_points(buf, w, h, cx, cy, x, y, color, blend);
1659 if d < 0 {
1660 d += 4 * x + 6;
1661 } else {
1662 d += 4 * (x - y) + 10;
1663 y -= 1;
1664 }
1665 x += 1;
1666 }
1667}
1668
1669#[allow(clippy::too_many_arguments)]
1670fn plot_circle_points(
1671 buf: &mut [u32],
1672 w: i32,
1673 h: i32,
1674 cx: i32,
1675 cy: i32,
1676 x: i32,
1677 y: i32,
1678 color: u32,
1679 blend: u8,
1680) {
1681 let points = [
1682 (cx + x, cy + y),
1683 (cx - x, cy + y),
1684 (cx + x, cy - y),
1685 (cx - x, cy - y),
1686 (cx + y, cy + x),
1687 (cx - y, cy + x),
1688 (cx + y, cy - x),
1689 (cx - y, cy - x),
1690 ];
1691 for &(px, py) in &points {
1692 if px >= 0 && px < w && py >= 0 && py < h {
1693 put_px(buf, (py * w + px) as usize, color, blend);
1694 }
1695 }
1696}
1697
1698#[allow(clippy::too_many_arguments)]
1699fn draw_circle_filled(
1700 buf: &mut [u32],
1701 w: i32,
1702 h: i32,
1703 cx: i32,
1704 cy: i32,
1705 r: i32,
1706 color: u32,
1707 blend: u8,
1708) {
1709 if r <= 0 {
1710 return;
1711 }
1712 for dy in -r..=r {
1713 let dx_max = ((r * r - dy * dy) as f64).sqrt() as i32;
1714 let py = cy + dy;
1715 if py < 0 || py >= h {
1716 continue;
1717 }
1718 for dx in -dx_max..=dx_max {
1719 let px = cx + dx;
1720 if px >= 0 && px < w {
1721 put_px(buf, (py * w + px) as usize, color, blend);
1722 }
1723 }
1724 }
1725}
1726
1727#[cfg(test)]
1728mod draw_tests {
1729 use super::*;
1730
1731 #[test]
1732 fn filled_circle_actually_writes_pixels() {
1733 let mut buf = vec![0u32; 100 * 100];
1734 draw_circle_filled(&mut buf, 100, 100, 50, 50, 10, 0xFF00FF, 0);
1735 assert_eq!(buf[50 * 100 + 50], 0xFF00FF, "centre pixel must be filled");
1736 assert_eq!(buf[0], 0, "far corner must stay clear");
1737 let n = buf.iter().filter(|&&p| p != 0).count();
1738 assert!(n > 200 && n < 500, "r=10 disc area ≈ 314, got {n}");
1739 }
1740
1741 #[test]
1742 fn circle_outline_writes_a_ring() {
1743 let mut buf = vec![0u32; 100 * 100];
1744 draw_circle_outline(&mut buf, 100, 100, 50, 50, 20, 0x00FF00, 0);
1745 assert_eq!(buf[50 * 100 + 50], 0, "outline must NOT fill the centre");
1746 assert!(
1747 buf.iter().any(|&p| p == 0x00FF00),
1748 "outline must draw a ring"
1749 );
1750 }
1751
1752 #[test]
1753 fn additive_blend_accumulates_channels() {
1754 let mut buf = vec![0x202020u32; 1];
1755 put_px(&mut buf, 0, 0x404040, 1);
1756 assert_eq!(buf[0], 0x606060);
1757 }
1758}
1759
1760#[derive(Clone, Copy)]
1766pub struct UiTheme {
1767 pub primary: u32,
1768 pub accent: u32,
1769 pub track: u32,
1770 pub warn: u32,
1771 pub text: u32,
1772 pub bg: u32,
1773}
1774
1775impl Default for UiTheme {
1776 fn default() -> Self {
1777 Self {
1778 primary: 0x00D2FF, accent: 0x28FFB4, track: 0x2C3E64, warn: 0xFF5A5A, text: 0xBEEBFF, bg: 0x0A1018, }
1785 }
1786}
1787
1788#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
1789pub struct Interpreter {
1790 globals: HashMap<String, Expr>,
1791 global_seed: Env,
1794 functions: FxHashMap<String, Rc<FnDef>>,
1795 pub(crate) structs: HashMap<String, Vec<String>>,
1797 enum_variants: HashMap<String, (String, usize)>,
1799 _modules: HashMap<String, Vec<FnDef>>,
1800 gfx: RefCell<GfxState>,
1801 svg: RefCell<Option<SvgWriter>>,
1802 pub source_dir: Option<std::path::PathBuf>,
1804 loaded_files: std::collections::HashSet<String>,
1806 #[cfg(not(target_arch = "wasm32"))]
1808 audio: Option<AudioEngine>,
1809 #[cfg(not(target_arch = "wasm32"))]
1810 fft: RefCell<FftAnalyzer>,
1811 fft_bands_cache: RefCell<Vec<f32>>,
1812 start_time_secs: f64,
1815 frame_num: u64,
1817 #[cfg(target_arch = "wasm32")]
1819 wasm_target_fps: f64,
1820 #[cfg(target_arch = "wasm32")]
1822 wasm_next_present_ms: f64,
1823 rand_state: u64,
1825 #[cfg(not(target_arch = "wasm32"))]
1827 mic: Option<ling_mic::MicInput>,
1828 #[cfg(not(target_arch = "wasm32"))]
1830 crypto_ids: Vec<ling_crypto::KnotIdentity>,
1831 #[cfg(not(target_arch = "wasm32"))]
1833 ed25519_ids: Vec<ling_crypto::Ed25519Keypair>,
1834 #[cfg(not(target_arch = "wasm32"))]
1836 mldsa_ids: Vec<ling_crypto::MlDsa65Keypair>,
1837 text_buffer: String,
1839 record_n: u32,
1841 #[cfg(not(target_arch = "wasm32"))]
1843 mic_buffer: Vec<f32>,
1844 #[cfg(not(target_arch = "wasm32"))]
1846 fonts: Vec<ling_graphics::VectorFont>,
1847 images: Vec<image::RgbaImage>,
1850 ui_theme: UiTheme,
1852 mouse_was_down: bool,
1854 #[cfg(not(target_arch = "wasm32"))]
1856 music: Option<ling_music::MusicEngine>,
1857 #[cfg(not(target_arch = "wasm32"))]
1858 music_init: bool,
1859 tracks: Vec<ling_music::DecodedAudio>,
1861 lyrics: Vec<ling_music::Lyrics>,
1863 midis: Vec<ling_music::MidiSong>,
1865 soft_bodies: Vec<ling_physics::soft::SoftBody>,
1867 rigid_world: ling_physics::rigid::PhysicsWorld,
1869 liquids: Vec<ling_physics::liquid::LiquidGrid>,
1871 meshes: Vec<crate::gfx::shapes::ColorMesh>,
1872 gltf_models: std::cell::RefCell<Vec<ling_physics::gltf::GltfModel>>,
1874 dialog: Option<ling_game::dialog::Dialog>,
1876 dialog_colors: [u32; 4],
1878 frames: Vec<String>,
1880 error_trace: Option<Vec<String>>,
1883 #[cfg(not(target_arch = "wasm32"))]
1887 input: RefCell<Option<InputState>>,
1888 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1893 http_routes: Vec<(String, String, Value)>,
1894 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1898 db: Option<ling_http::rusqlite::Connection>,
1899 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1902 http_static_dirs: Vec<(String, String)>,
1903 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1907 async_jobs: web::AsyncJobs,
1908 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1912 oauth_jobs: web::OAuthJobs,
1913}
1914
1915#[cfg(not(target_arch = "wasm32"))]
1917struct InputState {
1918 sensorium: ling_input::Sensorium,
1919 backend: ling_input::backend::GilrsBackend,
1920}
1921
1922impl Default for Interpreter {
1923 fn default() -> Self {
1924 Self::new()
1925 }
1926}
1927
1928impl Interpreter {
1929 pub fn new() -> Self {
1930 #[cfg(not(target_arch = "wasm32"))]
1931 let audio = AudioEngine::new()
1932 .map_err(|e| eprintln!("audio init failed (no sound): {e}"))
1933 .ok();
1934 Self {
1935 globals: HashMap::new(),
1936 global_seed: new_env(),
1937 functions: FxHashMap::default(),
1938 structs: HashMap::new(),
1939 enum_variants: HashMap::new(),
1940 _modules: HashMap::new(),
1941 gfx: RefCell::new(GfxState::new()),
1942 svg: RefCell::new(None),
1943 source_dir: None,
1944 loaded_files: std::collections::HashSet::new(),
1945 #[cfg(not(target_arch = "wasm32"))]
1946 audio,
1947 #[cfg(not(target_arch = "wasm32"))]
1948 fft: RefCell::new(FftAnalyzer::new(2048, 44100)),
1949 fft_bands_cache: RefCell::new(vec![]),
1950 start_time_secs: crate::runtime::now_secs(),
1951 frame_num: 0,
1952 #[cfg(target_arch = "wasm32")]
1953 wasm_target_fps: 60.0,
1954 #[cfg(target_arch = "wasm32")]
1955 wasm_next_present_ms: 0.0,
1956 rand_state: 0x123456789ABCDEF,
1957 #[cfg(not(target_arch = "wasm32"))]
1958 mic: None,
1959 #[cfg(not(target_arch = "wasm32"))]
1960 crypto_ids: Vec::new(),
1961 #[cfg(not(target_arch = "wasm32"))]
1962 ed25519_ids: Vec::new(),
1963 #[cfg(not(target_arch = "wasm32"))]
1964 mldsa_ids: Vec::new(),
1965 text_buffer: String::new(),
1966 record_n: 0,
1967 #[cfg(not(target_arch = "wasm32"))]
1968 mic_buffer: Vec::new(),
1969 #[cfg(not(target_arch = "wasm32"))]
1970 fonts: Vec::new(),
1971 images: Vec::new(),
1972 ui_theme: UiTheme::default(),
1973 mouse_was_down: false,
1974 #[cfg(not(target_arch = "wasm32"))]
1975 music: None,
1976 #[cfg(not(target_arch = "wasm32"))]
1977 music_init: false,
1978 tracks: Vec::new(),
1979 lyrics: Vec::new(),
1980 midis: Vec::new(),
1981 soft_bodies: Vec::new(),
1982 rigid_world: ling_physics::rigid::PhysicsWorld::new(),
1983 liquids: Vec::new(),
1984 meshes: Vec::new(),
1985 gltf_models: std::cell::RefCell::new(Vec::new()),
1986 dialog: None,
1987 dialog_colors: [0xE6F2FF, 0xFFD24A, 0x4AD2FF, 0x6CFF8C], frames: Vec::new(),
1989 error_trace: None,
1990 #[cfg(not(target_arch = "wasm32"))]
1991 input: RefCell::new(None),
1992 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1993 http_routes: Vec::new(),
1994 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1995 db: None,
1996 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1997 http_static_dirs: Vec::new(),
1998 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1999 async_jobs: web::AsyncJobs::new(),
2000 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
2001 oauth_jobs: web::OAuthJobs::new(),
2002 }
2003 }
2004
2005 #[cfg(not(target_arch = "wasm32"))]
2009 fn pad_poll(&self) -> usize {
2010 let mut slot = self.input.borrow_mut();
2011 if slot.is_none() {
2012 match ling_input::backend::GilrsBackend::new() {
2013 Ok(backend) => {
2014 *slot = Some(InputState { sensorium: ling_input::Sensorium::new(4), backend });
2015 },
2016 Err(_) => return 0,
2017 }
2018 }
2019 let st = slot.as_mut().unwrap();
2020 st.sensorium.begin_frame();
2021 st.sensorium.pump(&mut st.backend);
2022 st.sensorium.update(1.0 / 60.0);
2023 st.sensorium.devices.count()
2024 }
2025
2026 #[cfg(not(target_arch = "wasm32"))]
2029 fn with_pad<T>(&self, slot: usize, default: T, f: impl FnOnce(&ling_input::Gamepad) -> T) -> T {
2030 let inp = self.input.borrow();
2031 match inp.as_ref().and_then(|s| s.sensorium.player(slot)) {
2032 Some(p) => f(p),
2033 None => default,
2034 }
2035 }
2036
2037 pub fn take_error_trace(&mut self) -> Vec<String> {
2040 self.error_trace.take().unwrap_or_default()
2041 }
2042
2043 #[cfg(target_arch = "wasm32")]
2044 fn wasm_pace_frame(&mut self) {
2045 let fps = self.wasm_target_fps.max(1.0);
2046 let frame_ms = 1000.0 / fps;
2047 let now = js_sys::Date::now();
2048 if self.wasm_next_present_ms <= 0.0 {
2049 self.wasm_next_present_ms = now + frame_ms;
2050 return;
2051 }
2052
2053 let wait_ms = (self.wasm_next_present_ms - now).floor() as i32;
2054 if wait_ms > 0 {
2055 wasm_sleep_ms(wait_ms);
2056 }
2057
2058 let after = js_sys::Date::now();
2059 if after > self.wasm_next_present_ms + frame_ms * 3.0 {
2060 self.wasm_next_present_ms = after + frame_ms;
2061 } else {
2062 self.wasm_next_present_ms += frame_ms;
2063 }
2064 }
2065
2066 fn framed<T, F>(&mut self, name: &str, body: F) -> Result<T, EvalErr>
2069 where
2070 F: FnOnce(&mut Self) -> Result<T, EvalErr>,
2071 {
2072 self.frames.push(name.to_string());
2073 let result = body(self);
2074 if matches!(result, Err(EvalErr::Runtime(_))) && self.error_trace.is_none() {
2075 self.error_trace = Some(self.frames.clone());
2076 }
2077 self.frames.pop();
2078 result
2079 }
2080
2081 #[cfg(not(target_arch = "wasm32"))]
2085 fn render_dialog(&mut self, x: f32, y: f32, w: f32, h: f32, font: i64, t: f32) {
2086 let (runs, typing) = match &self.dialog {
2087 Some(d) if !d.is_closed() => {
2088 let runs: Vec<(String, usize, bool)> = d
2089 .visible_runs()
2090 .into_iter()
2091 .map(|r| (r.text, r.role.index(), r.newline_before))
2092 .collect();
2093 (runs, d.is_typing())
2094 },
2095 _ => return,
2096 };
2097 let colors = self.dialog_colors;
2098 let b = 12.0;
2100 let corners: Vec<[f32; 2]> = vec![
2101 [x + b, y],
2102 [x + w - b, y],
2103 [x + w, y + b],
2104 [x + w, y + h - b],
2105 [x + w - b, y + h],
2106 [x + b, y + h],
2107 [x, y + h - b],
2108 [x, y + b],
2109 [x + b, y],
2110 ];
2111 {
2112 let mut gfx = self.gfx.borrow_mut();
2113 let (bw, bh) = (gfx.width, gfx.height);
2114 crate::gfx::raster::fill_contours_aa(
2115 &mut gfx.buffer,
2116 bw,
2117 bh,
2118 0x0A1018,
2119 false,
2120 std::slice::from_ref(&corners),
2121 );
2122 for seg in corners.windows(2) {
2123 crate::gfx::raster::draw_line_aa(
2124 &mut gfx.buffer,
2125 bw,
2126 bh,
2127 0x00D2FF,
2128 false,
2129 seg[0][0],
2130 seg[0][1],
2131 seg[1][0],
2132 seg[1][1],
2133 );
2134 }
2135 }
2136 let px = 22.0f32;
2138 let pad = 20.0f32;
2139 let line_h = px * 1.45;
2140 let mut cx = x + pad;
2141 let mut cy = y + pad;
2142 let use_font = font >= 0 && (font as usize) < self.fonts.len();
2143 for (text, role, nl) in &runs {
2144 if *nl {
2145 cx = x + pad;
2146 cy += line_h;
2147 }
2148 for word in text.split_inclusive(' ') {
2149 let wpx = if use_font {
2150 self.fonts[font as usize].measure(word, px)
2151 } else {
2152 ling_ui::holo::text_width(word, px * 0.6, px * 0.24)
2153 };
2154 if cx + wpx > x + w - pad && cx > x + pad + 1.0 {
2155 cx = x + pad;
2156 cy += line_h;
2157 }
2158 if cy + line_h > y + h {
2159 break;
2160 }
2161 let col = colors[(*role).min(3)];
2162 if use_font {
2163 let glyphs = self.font_layout_2d_glyphs(font as usize, cx, cy, px, word);
2164 let mut gfx = self.gfx.borrow_mut();
2165 let (bw, bh, add) = (gfx.width, gfx.height, gfx.blend == 1);
2166 for contours in &glyphs {
2167 crate::gfx::raster::fill_contours_aa(
2168 &mut gfx.buffer,
2169 bw,
2170 bh,
2171 col,
2172 add,
2173 contours,
2174 );
2175 }
2176 } else {
2177 let segs = ling_ui::holo::text_lines(word, cx, cy, px * 0.6, px, px * 0.24);
2178 let mut gfx = self.gfx.borrow_mut();
2179 let (bw, bh) = (gfx.width, gfx.height);
2180 for s in segs {
2181 draw_line(&mut gfx.buffer, bw, bh, col, s[0], s[1], s[2], s[3]);
2182 }
2183 }
2184 cx += wpx;
2185 }
2186 }
2187 if !typing && (t * 3.0).sin() > 0.0 {
2189 let ax = x + w - 26.0;
2190 let ay = y + h - 22.0;
2191 let mut gfx = self.gfx.borrow_mut();
2192 let (bw, bh) = (gfx.width, gfx.height);
2193 crate::gfx::raster::fill_contours_aa(
2194 &mut gfx.buffer,
2195 bw,
2196 bh,
2197 0x00D2FF,
2198 false,
2199 std::slice::from_ref(&vec![
2200 [ax - 7.0, ay],
2201 [ax + 7.0, ay],
2202 [ax, ay + 9.0],
2203 [ax - 7.0, ay],
2204 ]),
2205 );
2206 }
2207 }
2208
2209 #[cfg(not(target_arch = "wasm32"))]
2212 fn ensure_music(&mut self) -> bool {
2213 if self.music.is_some() {
2214 return true;
2215 }
2216 if self.music_init {
2217 return false;
2218 }
2219 self.music_init = true;
2220 match ling_music::MusicEngine::new() {
2221 Ok(m) => {
2222 self.music = Some(m);
2223 true
2224 },
2225 Err(e) => {
2226 eprintln!("music engine init failed (no music playback): {e}");
2227 false
2228 },
2229 }
2230 }
2231
2232 #[cfg(target_arch = "wasm32")]
2233 fn wasm_resolve_source_path(&self, path: &str) -> String {
2234 let p = path.trim();
2235 if p.is_empty() {
2236 return String::new();
2237 }
2238 if p.contains("://") || p.starts_with('/') || p.starts_with("./") || p.starts_with("../") {
2239 return p.to_string();
2240 }
2241 if let Some(d) = &self.source_dir {
2242 let base = d.to_string_lossy().replace('\\', "/");
2243 if !base.is_empty() {
2244 return format!(
2245 "{}/{}",
2246 base.trim_end_matches('/'),
2247 p.trim_start_matches("./")
2248 );
2249 }
2250 }
2251 p.to_string()
2252 }
2253
2254 #[cfg(target_arch = "wasm32")]
2255 fn wasm_music_builtin(&mut self, name: &str, args: &[Value]) -> Result<Option<Value>, EvalErr> {
2256 match name {
2257 "music_load"
2259 | "载入音乐"
2260 | "音楽読込"
2261 | "음악로드"
2262 | "โหลดเพลง"
2263 | "بارگذاری_موسیقی"
2264 | "تحميل_الموسيقى"
2265 | "טעינת_מוזיקה"
2266 | "موسیقی_لوڈ"
2267 | "charger_musique"
2268 | "musik_laden"
2269 | "загрузить_музыку" => {
2270 let path = self.arg_str(args, 0, "");
2271 let resolved = self.wasm_resolve_source_path(&path);
2272 match wasm_fetch_bytes(&resolved)
2273 .and_then(|bytes| ling_music::from_bytes(&bytes).map_err(|e| e.to_string()))
2274 {
2275 Ok(t) => {
2276 let id = self.tracks.len();
2277 self.tracks.push(t);
2278 return Ok(Some(Value::Number(id as f64)));
2279 },
2280 Err(e) => {
2281 eprintln!("music_load failed ({path}): {e}");
2282 return Ok(Some(Value::Number(-1.0)));
2283 },
2284 }
2285 },
2286 "music_duration"
2287 | "音乐时长"
2288 | "音楽長さ"
2289 | "음악길이"
2290 | "ความยาวเพลง"
2291 | "مدت_موسیقی"
2292 | "مدة_الموسيقى"
2293 | "משך_מוזיקה"
2294 | "موسیقی_دورانیہ"
2295 | "durée_musique"
2296 | "musik_dauer"
2297 | "длительность_музыки" => {
2298 let id = self.arg_num(args, 0, 0.0)? as i64;
2299 let d = self
2300 .tracks
2301 .get(id as usize)
2302 .map(|t| t.duration)
2303 .unwrap_or(0.0);
2304 return Ok(Some(Value::Number(d as f64)));
2305 },
2306 "music_bpm"
2307 | "节拍速度"
2308 | "テンポ"
2309 | "템포"
2310 | "จังหวะต่อนาที"
2311 | "ضربان_در_دقیقه"
2312 | "نبضات_بالدقيقة"
2313 | "פעימות_לדקה"
2314 | "بی_پی_ایم"
2315 | "bpm_musique"
2316 | "musik_bpm"
2317 | "музыка_bpm" => {
2318 let id = self.arg_num(args, 0, 0.0)? as i64;
2319 let b = self
2320 .tracks
2321 .get(id as usize)
2322 .map(|t| ling_music::analysis::bpm(&t.mono, t.rate))
2323 .unwrap_or(0.0);
2324 return Ok(Some(Value::Number(b as f64)));
2325 },
2326 "music_key"
2327 | "调性"
2328 | "調性"
2329 | "조성"
2330 | "คีย์เพลง"
2331 | "گام_موسیقی"
2332 | "مقام_الموسيقى"
2333 | "סולם_מוזיקלי"
2334 | "موسیقی_کلید"
2335 | "tonalité_musique"
2336 | "musik_tonart"
2337 | "тональность_музыки" => {
2338 let id = self.arg_num(args, 0, 0.0)? as i64;
2339 let k = self
2340 .tracks
2341 .get(id as usize)
2342 .map(|t| ling_music::analysis::key_name(&t.mono, t.rate))
2343 .unwrap_or_default();
2344 return Ok(Some(Value::Str(k)));
2345 },
2346 "music_onsets"
2347 | "音符起点"
2348 | "オンセット"
2349 | "온셋"
2350 | "จุดเริ่มเสียง"
2351 | "آغازهای_نت"
2352 | "بدايات_النغمات"
2353 | "התחלות_תווים"
2354 | "نوٹ_شروعات"
2355 | "attaques_musique"
2356 | "musik_einsätze"
2357 | "атаки_музыки" => {
2358 let id = self.arg_num(args, 0, 0.0)? as i64;
2359 let v = self
2360 .tracks
2361 .get(id as usize)
2362 .map(|t| ling_music::analysis::onsets(&t.mono, t.rate))
2363 .unwrap_or_default();
2364 return Ok(Some(Value::List(
2365 v.into_iter()
2366 .map(|x| Value::Number(x as f64))
2367 .collect::<Vec<_>>()
2368 .into(),
2369 )));
2370 },
2371 "music_beat_grid"
2372 | "节拍网格"
2373 | "ビートグリッド"
2374 | "비트그리드"
2375 | "กริดจังหวะ"
2376 | "شبکه_ضرب"
2377 | "شبكة_الإيقاع"
2378 | "רשת_פעימות"
2379 | "بیٹ_گرڈ"
2380 | "grille_temps_musique"
2381 | "musik_taktraster"
2382 | "сетка_ритма_музыки" => {
2383 let id = self.arg_num(args, 0, 0.0)? as i64;
2384 let beats = self
2385 .tracks
2386 .get(id as usize)
2387 .map(|t| {
2388 let b = ling_music::analysis::bpm(&t.mono, t.rate);
2389 ling_music::analysis::beat_grid(&t.mono, t.rate, b)
2390 })
2391 .unwrap_or_default();
2392 return Ok(Some(Value::List(
2393 beats
2394 .into_iter()
2395 .map(|x| Value::Number(x as f64))
2396 .collect::<Vec<_>>()
2397 .into(),
2398 )));
2399 },
2400 "music_lrc"
2401 | "载入歌词"
2402 | "歌詞読込"
2403 | "가사로드"
2404 | "โหลดเนื้อเพลง"
2405 | "بارگذاری_متن_ترانه"
2406 | "تحميل_كلمات_الأغنية"
2407 | "טעינת_מילות_שיר"
2408 | "گیت_متن_لوڈ"
2409 | "lrc_musique"
2410 | "musik_lrc"
2411 | "lrc_музыки" => {
2412 let path = self.arg_str(args, 0, "");
2413 let resolved = self.wasm_resolve_source_path(&path);
2414 match wasm_fetch_text(&resolved) {
2415 Ok(text) => {
2416 let id = self.lyrics.len();
2417 self.lyrics.push(ling_music::Lyrics::parse(&text));
2418 return Ok(Some(Value::Number(id as f64)));
2419 },
2420 Err(e) => {
2421 eprintln!("music_lrc failed ({path}): {e}");
2422 return Ok(Some(Value::Number(-1.0)));
2423 },
2424 }
2425 },
2426 "music_lyric"
2427 | "当前歌词"
2428 | "現在歌詞"
2429 | "현재가사"
2430 | "เนื้อเพลงปัจจุบัน"
2431 | "متن_ترانه_فعلی"
2432 | "كلمات_الأغنية_الحالية"
2433 | "מילות_שיר_נוכחיות"
2434 | "موجودہ_گیت_متن"
2435 | "paroles_musique"
2436 | "musik_liedtext"
2437 | "текст_песни" => {
2438 let id = self.arg_num(args, 0, 0.0)? as i64;
2439 let t = self.arg_num(args, 1, 0.0)? as f32;
2440 let line = self
2441 .lyrics
2442 .get(id as usize)
2443 .map(|l| l.line_at(t).to_string())
2444 .unwrap_or_default();
2445 return Ok(Some(Value::Str(line)));
2446 },
2447 "music_midi_load"
2448 | "载入MIDI"
2449 | "MIDI読込"
2450 | "미디로드"
2451 | "โหลดมิดี"
2452 | "بارگذاری_MIDI"
2453 | "تحميل_MIDI"
2454 | "טעינת_MIDI"
2455 | "MIDI_لوڈ"
2456 | "charger_midi_musique"
2457 | "musik_midi_laden"
2458 | "загрузить_midi_музыки" => {
2459 let path = self.arg_str(args, 0, "");
2460 let resolved = self.wasm_resolve_source_path(&path);
2461 match wasm_fetch_bytes(&resolved).and_then(|bytes| {
2462 ling_music::midi::from_bytes(&bytes).map_err(|e| e.to_string())
2463 }) {
2464 Ok(m) => {
2465 let id = self.midis.len();
2466 self.midis.push(m);
2467 return Ok(Some(Value::Number(id as f64)));
2468 },
2469 Err(e) => {
2470 eprintln!("music_midi_load failed ({path}): {e}");
2471 return Ok(Some(Value::Number(-1.0)));
2472 },
2473 }
2474 },
2475 "music_midi_count"
2476 | "MIDI数量"
2477 | "MIDI数"
2478 | "미디수"
2479 | "จำนวนมิดี"
2480 | "تعداد_MIDI"
2481 | "عدد_MIDI"
2482 | "מספר_MIDI"
2483 | "MIDI_تعداد"
2484 | "nombre_midi_musique"
2485 | "musik_midi_anzahl"
2486 | "число_midi_музыки" => {
2487 let id = self.arg_num(args, 0, 0.0)? as i64;
2488 let n = self
2489 .midis
2490 .get(id as usize)
2491 .map(|m| m.notes.len())
2492 .unwrap_or(0);
2493 return Ok(Some(Value::Number(n as f64)));
2494 },
2495 "music_midi_notes"
2496 | "MIDI音符"
2497 | "MIDIノート"
2498 | "미디음표"
2499 | "โน้ตมิดี"
2500 | "نتهای_MIDI"
2501 | "نغمات_MIDI"
2502 | "תווי_MIDI"
2503 | "MIDI_نوٹس"
2504 | "notes_midi_musique"
2505 | "musik_midi_noten"
2506 | "ноты_midi_музыки" => {
2507 let id = self.arg_num(args, 0, 0.0)? as i64;
2508 let mut out = Vec::new();
2509 if let Some(m) = self.midis.get(id as usize) {
2510 for n in &m.notes {
2511 out.push(Value::Number(n.time as f64));
2512 out.push(Value::Number(n.midi as f64));
2513 }
2514 }
2515 return Ok(Some(Value::List(out.into())));
2516 },
2517 "music_midi_bars"
2518 | "MIDI音条"
2519 | "MIDIバー"
2520 | "미디바"
2521 | "แท่งมิดี"
2522 | "میلههای_MIDI"
2523 | "أعمدة_MIDI"
2524 | "עמודות_MIDI"
2525 | "MIDI_بارز"
2526 | "mesures_midi_musique"
2527 | "musik_midi_takte"
2528 | "такты_midi_музыки" => {
2529 let id = self.arg_num(args, 0, 0.0)? as i64;
2530 let mut out = Vec::new();
2531 if let Some(m) = self.midis.get(id as usize) {
2532 for n in &m.notes {
2533 out.push(Value::Number(n.time as f64));
2534 out.push(Value::Number(n.midi as f64));
2535 out.push(Value::Number(n.dur as f64));
2536 }
2537 }
2538 return Ok(Some(Value::List(out.into())));
2539 },
2540 "music_judge"
2541 | "判定"
2542 | "判定する"
2543 | "판정"
2544 | "ตัดสินจังหวะ"
2545 | "داوری_ضرب"
2546 | "حكم_الإيقاع"
2547 | "שיפוט_קצב"
2548 | "بیٹ_فیصلہ"
2549 | "juger_musique"
2550 | "musik_bewerten"
2551 | "оценить_музыку" => {
2552 let delta_ms = self.arg_num(args, 0, 9999.0)? as f32;
2553 return Ok(Some(Value::Number(
2554 ling_music::Grade::judge(delta_ms).index() as f64,
2555 )));
2556 },
2557 "music_grade_name"
2558 | "判定名"
2559 | "判定名称"
2560 | "판정이름"
2561 | "ชื่อการตัดสิน"
2562 | "نام_رتبه"
2563 | "اسم_التقييم"
2564 | "שם_דירוג"
2565 | "گریڈ_نام"
2566 | "nom_grade_musique"
2567 | "musik_bewertungsname"
2568 | "имя_оценки_музыки" => {
2569 let idx = self.arg_num(args, 0, 4.0)? as i32;
2570 return Ok(Some(Value::Str(
2571 ling_music::Grade::from_index(idx).name().to_string(),
2572 )));
2573 },
2574 "music_note_name"
2575 | "音名"
2576 | "音名称"
2577 | "음이름"
2578 | "ชื่อโน้ต"
2579 | "نام_نت"
2580 | "اسم_النغمة"
2581 | "שם_תו"
2582 | "نوٹ_نام"
2583 | "nom_note_musique"
2584 | "musik_notenname"
2585 | "имя_ноты_музыки" => {
2586 let hz = self.arg_num(args, 0, 0.0)? as f32;
2587 return Ok(Some(Value::Str(ling_music::note::hz_to_name(hz))));
2588 },
2589 "music_hz"
2590 | "音符频率"
2591 | "音符周波数"
2592 | "음표주파수"
2593 | "ความถี่โน้ต"
2594 | "فرکانس_نت"
2595 | "تردد_النغمة"
2596 | "תדר_תו"
2597 | "نوٹ_ہرٹز"
2598 | "hz_musique"
2599 | "musik_hz"
2600 | "музыка_гц" => {
2601 let midi = match args.get(0) {
2602 Some(Value::Str(s)) => ling_music::note::parse_pitch(s).unwrap_or(69),
2603 Some(Value::Number(n)) => *n as i32,
2604 _ => 69,
2605 };
2606 return Ok(Some(Value::Number(
2607 ling_music::note::midi_to_hz(midi as f32) as f64,
2608 )));
2609 },
2610 "music_pitch_score"
2611 | "音准评分"
2612 | "音程スコア"
2613 | "음정점수"
2614 | "คะแนนเสียง"
2615 | "امتیاز_زیروبمی"
2616 | "درجة_طبقة_الصوت"
2617 | "ציון_גובה_צליל"
2618 | "پچ_اسکور"
2619 | "score_hauteur_musique"
2620 | "musik_tonhöhen_punktzahl"
2621 | "счёт_высоты_тона" => {
2622 let hz = self.arg_num(args, 0, 0.0)? as f32;
2623 let target = self.arg_num(args, 1, 0.0)? as f32;
2624 return Ok(Some(Value::Number(
2625 ling_music::karaoke::pitch_score(hz, target) as f64,
2626 )));
2627 },
2628
2629 "music_play"
2631 | "播放音乐"
2632 | "音楽再生"
2633 | "음악재생"
2634 | "เล่นเพลง"
2635 | "پخش_موسیقی"
2636 | "شغّل_الموسيقى"
2637 | "נגן_מוזיקה"
2638 | "موسیقی_چلاؤ"
2639 | "jouer_musique"
2640 | "musik_abspielen"
2641 | "играть_музыку" => {
2642 let id = self.arg_num(args, 0, 0.0)? as usize;
2643 if let Some(t) = self.tracks.get(id) {
2644 crate::gfx::audio_web::play_music(id, &t.stereo, t.channels, t.rate, 1.0);
2645 }
2646 return Ok(Some(Value::Unit));
2647 },
2648 "music_pause"
2649 | "暂停音乐"
2650 | "音楽一時停止"
2651 | "음악일시정지"
2652 | "หยุดเพลงชั่วคราว"
2653 | "music_stop"
2654 | "停止音乐"
2655 | "音楽停止"
2656 | "음악정지"
2657 | "หยุดเพลง"
2658 | "مکث_موسیقی"
2659 | "ألبث_الموسيقى"
2660 | "השהה_מוזיקה"
2661 | "موسیقی_روکو_مؤقت"
2662 | "pause_musique"
2663 | "musik_pausieren"
2664 | "пауза_музыки" => {
2665 let id = self.arg_num(args, 0, 0.0)? as usize;
2666 crate::gfx::audio_web::stop_music(id);
2667 return Ok(Some(Value::Unit));
2668 },
2669 "music_seek"
2670 | "定位音乐"
2671 | "音楽シーク"
2672 | "음악탐색"
2673 | "ค้นหาเพลง"
2674 | "جستجوی_موسیقی"
2675 | "ابحث_في_الموسيقى"
2676 | "חפש_במוזיקה"
2677 | "موسیقی_تلاش"
2678 | "chercher_musique"
2679 | "musik_suchen"
2680 | "перемотать_музыку" => {
2681 return Ok(Some(Value::Unit));
2683 },
2684 "music_pos"
2685 | "音乐位置"
2686 | "音楽位置"
2687 | "음악위치"
2688 | "ตำแหน่งเพลง"
2689 | "موقعیت_موسیقی"
2690 | "موضع_الموسيقى"
2691 | "מיקום_מוזיקה"
2692 | "موسیقی_مقام"
2693 | "position_musique"
2694 | "musik_position"
2695 | "позиция_музыки" => {
2696 return Ok(Some(Value::Number(
2697 crate::gfx::audio_web::current_music_position(),
2698 )));
2699 },
2700 "music_volume"
2701 | "音乐音量"
2702 | "音楽音量"
2703 | "음악음량"
2704 | "ระดับเพลง"
2705 | "بلندی_موسیقی"
2706 | "مستوى_الموسيقى"
2707 | "עוצמת_מוזיקה"
2708 | "موسیقی_شدت"
2709 | "volume_musique"
2710 | "musik_lautstärke"
2711 | "громкость_музыки" => {
2712 let vol = self.arg_num(args, 0, 0.8)? as f32;
2713 crate::gfx::audio_web::set_music_volume(0, vol);
2715 return Ok(Some(Value::Unit));
2716 },
2717
2718 "music_fft"
2720 | "音乐频谱"
2721 | "音楽スペクトル"
2722 | "음악스펙트럼"
2723 | "สเปกตรัมเพลง"
2724 | "طیف_موسیقی"
2725 | "طيف_الموسيقى"
2726 | "ספקטרום_מוזיקה"
2727 | "میوزک_اسپیکٹرم"
2728 | "fft_musique"
2729 | "musik_fft"
2730 | "fft_музыки" => {
2731 let id = self.arg_num(args, 0, 0.0)? as usize;
2732 let nbands = self.arg_num(args, 1, 16.0)? as usize;
2733 let pos = crate::gfx::audio_web::current_music_position() as f32;
2734 let bands = if let Some(t) = self.tracks.get(id) {
2735 ling_music::analysis::fft_bands_at_pos(&t.mono, t.rate, pos, nbands)
2736 } else {
2737 vec![0.0f32; nbands]
2738 };
2739 return Ok(Some(Value::List(
2740 bands
2741 .into_iter()
2742 .map(|x| Value::Number(x as f64))
2743 .collect::<Vec<_>>()
2744 .into(),
2745 )));
2746 },
2747
2748 _ => {},
2749 }
2750 Ok(None)
2751 }
2752
2753 #[cfg(not(target_arch = "wasm32"))]
2757 fn font_layout_2d(
2758 &mut self,
2759 id: usize,
2760 x: f32,
2761 y: f32,
2762 px: f32,
2763 text: &str,
2764 ) -> Vec<Vec<[f32; 2]>> {
2765 let mut out = Vec::new();
2766 for g in self.font_layout_2d_glyphs(id, x, y, px, text) {
2767 out.extend(g);
2768 }
2769 out
2770 }
2771
2772 #[cfg(not(target_arch = "wasm32"))]
2775 fn font_layout_2d_glyphs(
2776 &mut self,
2777 id: usize,
2778 x: f32,
2779 y: f32,
2780 px: f32,
2781 text: &str,
2782 ) -> Vec<Vec<Vec<[f32; 2]>>> {
2783 let font = &mut self.fonts[id];
2784 let asc = font.ascent();
2785 let tol = 0.3 / px;
2786 let mut pen = 0.0f32;
2787 let mut glyphs = Vec::new();
2788 for ch in text.chars() {
2789 let go = font.glyph_outline(ch, tol);
2790 let mut contours = Vec::with_capacity(go.polylines.len());
2791 for pl in &go.polylines {
2792 let mapped: Vec<[f32; 2]> = pl
2793 .iter()
2794 .map(|p| [x + (pen + p[0]) * px, y + (asc - p[1]) * px])
2795 .collect();
2796 contours.push(mapped);
2797 }
2798 glyphs.push(contours);
2799 pen += go.advance;
2800 }
2801 glyphs
2802 }
2803
2804 pub fn register_program(&mut self, program: &Program) -> Result<(), String> {
2809 for item in &program.items {
2810 self.register_item("", item)?;
2811 }
2812 let mut env = new_env();
2813 let non_do: Vec<_> = self
2814 .globals
2815 .iter()
2816 .filter(|(_, e)| !matches!(e, Expr::Do(_)))
2817 .map(|(k, e)| (k.clone(), e.clone()))
2818 .collect();
2819 let mut pending: Vec<(String, Expr)> = Vec::new();
2820 for (k, expr) in &non_do {
2821 let mut tmp = new_env();
2822 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
2823 env.insert(k.clone(), v);
2824 } else {
2825 pending.push((k.clone(), expr.clone()));
2826 }
2827 }
2828 for (k, expr) in &pending {
2829 let mut tmp = env.clone();
2830 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
2831 env.insert(k.clone(), v);
2832 }
2833 }
2834 self.global_seed = env;
2835 Ok(())
2836 }
2837
2838 pub fn run_program(&mut self, program: &Program) -> Result<(), String> {
2839 self.register_program(program)?;
2840 let entry = self
2841 .find_entry()
2842 .ok_or("no entry point — need `bind start = do {...}` or `ผูก เริ่ม = ทำ {...}`")?;
2843 let mut env = self.global_seed.clone();
2844 self.framed("start", |me| me.eval_expr(&entry, &mut env))
2845 .map(|_| ())
2846 .map_err(|e| match e {
2847 EvalErr::Runtime(s) => s,
2848 EvalErr::Return(_) => "unexpected top-level return".to_string(),
2849 EvalErr::Break => "unexpected break at top level".to_string(),
2850 })
2851 }
2852
2853 fn register_item(&mut self, ns: &str, item: &Item) -> Result<(), String> {
2854 match item {
2855 Item::Bind(name, expr) => {
2856 let key = if ns.is_empty() {
2857 name.clone()
2858 } else {
2859 format!("{ns}::{name}")
2860 };
2861 self.globals.insert(key, expr.clone());
2862 },
2863 Item::Fn(def) => {
2864 let key = if ns.is_empty() {
2865 def.name.clone()
2866 } else {
2867 format!("{ns}::{}", def.name)
2868 };
2869 self.functions.insert(key, Rc::new(def.clone()));
2870 },
2871 Item::Mod(name, body) => {
2872 let child_ns = if ns.is_empty() {
2873 name.clone()
2874 } else {
2875 format!("{ns}::{name}")
2876 };
2877 for child in body {
2878 self.register_item(&child_ns, child)?;
2879 }
2880 },
2881 Item::TypeAlias(_, _) => {},
2882 Item::Struct(name, fields) => {
2883 self.structs.insert(name.clone(), fields.clone());
2884 if !ns.is_empty() {
2885 self.structs.insert(format!("{ns}::{name}"), fields.clone());
2886 }
2887 },
2888 Item::Enum(name, variants) => {
2889 for v in variants {
2890 self.enum_variants
2891 .insert(v.name.clone(), (name.clone(), v.arity));
2892 self.enum_variants
2893 .insert(format!("{name}::{}", v.name), (name.clone(), v.arity));
2894 if !ns.is_empty() {
2895 self.enum_variants
2896 .insert(format!("{ns}::{name}::{}", v.name), (name.clone(), v.arity));
2897 }
2898 }
2899 },
2900 Item::Use { path, alias } => {
2901 self.load_module(path, alias.as_deref(), ns)?;
2902 },
2903 }
2904 Ok(())
2905 }
2906
2907 fn load_module(
2912 &mut self,
2913 path: &str,
2914 alias: Option<&str>,
2915 parent_ns: &str,
2916 ) -> Result<(), String> {
2917 #[cfg(target_arch = "wasm32")]
2919 let (source, sub_dir) = {
2920 if self.loaded_files.contains(path) {
2922 return Ok(());
2923 }
2924 self.loaded_files.insert(path.to_string());
2925
2926 let src = crate::runtime::get_wasm_module(path)
2927 .or_else(|| crate::runtime::get_wasm_module(&format!("{}.ling", path)))
2928 .ok_or_else(|| format!("use: cannot find module '{path}'"))?;
2929 (src, None::<std::path::PathBuf>)
2930 };
2931
2932 #[cfg(not(target_arch = "wasm32"))]
2934 let (source, sub_dir) = {
2935 let base_dir = self
2936 .source_dir
2937 .clone()
2938 .unwrap_or_else(|| std::path::PathBuf::from("."));
2939 let raw = std::path::Path::new(path);
2940 let candidates: Vec<std::path::PathBuf> = vec![
2941 base_dir.join(format!("{}.ling", path)),
2942 base_dir.join(format!("{}.灵", path)),
2943 base_dir.join(format!("{}.령", path)),
2944 base_dir.join(format!("{}.霊", path)),
2945 base_dir.join(format!("{}.ลิง", path)),
2946 base_dir.join(raw),
2947 std::path::PathBuf::from(format!("{}.ling", path)),
2948 std::path::PathBuf::from(path),
2949 ];
2950
2951 let resolved = candidates
2952 .into_iter()
2953 .find(|p| p.exists())
2954 .ok_or_else(|| format!("use: cannot find module '{path}'"))?;
2955
2956 let canonical = resolved
2957 .canonicalize()
2958 .unwrap_or_else(|_| resolved.clone())
2959 .to_string_lossy()
2960 .to_string();
2961
2962 if self.loaded_files.contains(&canonical) {
2964 return Ok(());
2965 }
2966 self.loaded_files.insert(canonical.clone());
2967
2968 let src = std::fs::read_to_string(&resolved)
2969 .map_err(|e| format!("use: failed to read '{path}': {e}"))?;
2970 let dir = resolved.parent().map(|p| p.to_path_buf());
2971 (src, dir)
2972 };
2973
2974 let program = crate::parser::parse(&source)
2975 .map_err(|e| format!("use: parse error in '{path}': {e}"))?;
2976
2977 let target_ns = match (parent_ns.is_empty(), alias) {
2979 (_, Some(a)) if !parent_ns.is_empty() => format!("{parent_ns}::{a}"),
2980 (_, Some(a)) => a.to_string(),
2981 (false, None) => parent_ns.to_string(),
2982 (true, None) => String::new(),
2983 };
2984
2985 let prev_dir = self.source_dir.clone();
2987 self.source_dir = sub_dir;
2988
2989 for item in &program.items {
2990 self.register_item(&target_ns, item)?;
2991 }
2992
2993 self.source_dir = prev_dir;
2994 Ok(())
2995 }
2996
2997 fn find_entry(&self) -> Option<Expr> {
2998 for key in crate::entry::ENTRY_NAMES {
3000 if let Some(e) = self.globals.get(*key) {
3001 return Some(e.clone());
3002 }
3003 }
3004 self.globals
3005 .values()
3006 .find(|e| matches!(e, Expr::Do(_)))
3007 .cloned()
3008 }
3009
3010 fn eval_expr(&mut self, expr: &Expr, env: &mut Env) -> EvalResult {
3013 match expr {
3014 Expr::Str(s) => Ok(Value::Str(s.clone())),
3015 Expr::Number(n) => Ok(Value::Number(*n)),
3016 Expr::Bool(b) => Ok(Value::Bool(*b)),
3017 Expr::Unit => Ok(Value::Unit),
3018 Expr::Array(elems) => {
3019 let vs: Vec<_> = elems
3020 .iter()
3021 .map(|e| self.eval_expr(e, env))
3022 .collect::<Result<_, _>>()?;
3023 Ok(Value::List(Rc::new(vs)))
3024 },
3025
3026 Expr::Ident(name) => self.lookup(name, env),
3027
3028 Expr::Path(segs) => {
3029 if segs.len() == 1 {
3030 return self.lookup(&segs[0], env);
3031 }
3032 Ok(Value::Str(segs.join("::")))
3033 },
3034
3035 Expr::Ref(inner) => self.eval_expr(inner, env),
3036 Expr::Await(inner) => self.eval_expr(inner, env),
3037
3038 Expr::Do(stmts) => {
3039 let mut local = env.clone();
3040 Ok(self.exec_block(stmts, &mut local)?.unwrap_or(Value::Unit))
3041 },
3042
3043 Expr::BinOp(op, lhs, rhs) => {
3044 let l = self.eval_expr(lhs, env)?;
3045 let r = self.eval_expr(rhs, env)?;
3046 self.apply_binop(op, l, r)
3047 },
3048
3049 Expr::If { cond, then, elseifs, else_body } => {
3050 let cond_val = self.eval_expr(cond, env)?;
3051 if self.is_truthy(&cond_val) {
3052 return Ok(self.exec_block(then, env)?.unwrap_or(Value::Unit));
3053 }
3054 for (ei_cond, ei_body) in elseifs {
3055 let ei_cond_val = self.eval_expr(ei_cond, env)?;
3056 if self.is_truthy(&ei_cond_val) {
3057 return Ok(self.exec_block(ei_body, env)?.unwrap_or(Value::Unit));
3058 }
3059 }
3060 if let Some(eb) = else_body {
3061 return Ok(self.exec_block(eb, env)?.unwrap_or(Value::Unit));
3062 }
3063 Ok(Value::Unit)
3064 },
3065
3066 Expr::While { cond, body } => {
3067 loop {
3071 let cv = self.eval_expr(cond, env)?;
3072 if !self.is_truthy(&cv) {
3073 break;
3074 }
3075 match self.exec_block(body, env) {
3076 Ok(_) => {},
3077 Err(EvalErr::Break) => break,
3078 Err(e) => return Err(e),
3079 }
3080 }
3081 Ok(Value::Unit)
3082 },
3083
3084 Expr::For { var, iter, body } => {
3085 let iter_val = self.eval_expr(iter, env)?;
3086 let items = self.value_to_iter(iter_val)?;
3087 for item in items {
3088 let mut local = env.clone();
3089 local.insert(var.clone(), item);
3090 match self.exec_block(body, &mut local) {
3091 Ok(_) => {},
3092 Err(EvalErr::Break) => break,
3093 Err(e) => return Err(e),
3094 }
3095 }
3096 Ok(Value::Unit)
3097 },
3098
3099 Expr::Match(subject, arms) => {
3100 let subj = self.eval_expr(subject, env)?;
3101 for arm in arms {
3102 if let Some(bindings) = self.match_pattern(&arm.pattern, &subj) {
3103 let mut local = env.clone();
3104 local.extend(bindings);
3105 return self.eval_expr(&arm.body, &mut local);
3106 }
3107 }
3108 Ok(Value::Unit)
3109 },
3110
3111 Expr::Range(lo, hi) => {
3112 let lo_v = self.eval_expr(lo, env)?;
3113 let hi_v = self.eval_expr(hi, env)?;
3114 let lo_n = self.to_number(&lo_v)? as i64;
3115 let hi_n = self.to_number(&hi_v)? as i64;
3116 Ok(Value::List(Rc::new(
3117 (lo_n..hi_n).map(|i| Value::Number(i as f64)).collect(),
3118 )))
3119 },
3120
3121 Expr::Index(base, idx) => {
3122 let b = self.eval_expr(base, env)?;
3123 let i = self.eval_expr(idx, env)?;
3124 let n = self.to_number(&i)? as usize;
3125 match b {
3126 Value::List(v) => v
3127 .get(n)
3128 .cloned()
3129 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
3130 Value::Str(s) => s
3131 .chars()
3132 .nth(n)
3133 .map(|c| Value::Str(c.to_string()))
3134 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
3135 other => Err(EvalErr::from(format!("cannot index {:?}", other))),
3136 }
3137 },
3138
3139 Expr::Call(callee, args) => {
3140 let arg_vals: Vec<Value> = args
3141 .iter()
3142 .map(|a| self.eval_expr(a, env))
3143 .collect::<Result<_, _>>()?;
3144 match callee.as_ref() {
3145 Expr::Ident(name) => self.call_named(name, arg_vals, env),
3146 Expr::Path(segs) => self.call_named(&segs.join("::"), arg_vals, env),
3147 _ => {
3148 let v = self.eval_expr(callee, env)?;
3149 self.call_value(v, arg_vals)
3150 },
3151 }
3152 },
3153
3154 Expr::MethodCall { receiver, method, args } => {
3155 let recv = self.eval_expr(receiver, env)?;
3156 let arg_vals: Vec<Value> = args
3157 .iter()
3158 .map(|a| self.eval_expr(a, env))
3159 .collect::<Result<_, _>>()?;
3160 self.call_method(recv, method, arg_vals)
3161 },
3162
3163 Expr::Closure(params, body) => Ok(Value::Fn(
3164 params.clone(),
3165 vec![Stmt::Expr(*body.clone())],
3166 env.clone(),
3167 )),
3168
3169 Expr::Asm(_) => Ok(Value::Unit),
3170 }
3171 }
3172
3173 fn exec_block(&mut self, stmts: &[Stmt], env: &mut Env) -> Result<Option<Value>, EvalErr> {
3176 let mut last: Option<Value> = None;
3177 for stmt in stmts {
3178 match stmt {
3179 Stmt::Bind(name, expr) => {
3180 match self.try_inplace_list_update(name, expr, env)? {
3181 Some(v) => env.insert(name.clone(), v),
3182 None => {
3183 let v = self.eval_expr(expr, env)?;
3184 env.insert(name.clone(), v)
3185 },
3186 };
3187 last = None;
3188 },
3189 Stmt::Return(expr) => {
3190 let v = self.eval_expr(expr, env)?;
3191 return Err(EvalErr::Return(v));
3192 },
3193 Stmt::Expr(expr) => {
3194 last = Some(self.eval_expr(expr, env)?);
3195 },
3196 }
3197 }
3198 Ok(last)
3199 }
3200
3201 fn try_inplace_list_update(
3208 &mut self,
3209 name: &str,
3210 expr: &Expr,
3211 env: &mut Env,
3212 ) -> Result<Option<Value>, EvalErr> {
3213 let Expr::Call(callee, args) = expr else { return Ok(None) };
3214 let Expr::Ident(fname) = callee.as_ref() else { return Ok(None) };
3215 let is_push = matches!(
3216 fname.as_str(),
3217 "list_push" | "เพิ่มรายการ" | "列表添加" | "リスト追加" | "목록추가"
3218 );
3219 let is_set = matches!(
3220 fname.as_str(),
3221 "list_set" | "ตั้งรายการ" | "设元素" | "要素設定" | "요소설정"
3222 );
3223 if !is_push && !is_set {
3224 return Ok(None);
3225 }
3226 match args.first() {
3229 Some(Expr::Ident(a0)) if a0 == name => {},
3230 _ => return Ok(None),
3231 }
3232 if self.functions.contains_key(fname.as_str()) {
3233 return Ok(None);
3234 }
3235 if is_push {
3236 if args.len() != 2 {
3237 return Ok(None);
3238 }
3239 let val = self.eval_expr(&args[1], env)?;
3240 match env.remove(name) {
3241 Some(Value::List(mut v)) => {
3242 Rc::make_mut(&mut v).push(val);
3243 Ok(Some(Value::List(v)))
3244 },
3245 other => {
3246 if let Some(o) = other {
3247 env.insert(name.to_string(), o);
3248 }
3249 Ok(None)
3250 },
3251 }
3252 } else {
3253 if args.len() != 3 {
3254 return Ok(None);
3255 }
3256 let idx_v = self.eval_expr(&args[1], env)?;
3257 let idx = self.to_number(&idx_v).unwrap_or(0.0) as usize;
3258 let val = self.eval_expr(&args[2], env)?;
3259 match env.remove(name) {
3260 Some(Value::List(mut v)) => {
3261 if idx < v.len() {
3262 Rc::make_mut(&mut v)[idx] = val;
3263 }
3264 Ok(Some(Value::List(v)))
3265 },
3266 other => {
3267 if let Some(o) = other {
3268 env.insert(name.to_string(), o);
3269 }
3270 Ok(None)
3271 },
3272 }
3273 }
3274 }
3275
3276 fn lookup(&self, name: &str, env: &Env) -> EvalResult {
3279 if let Some(v) = env.get(name) {
3280 return Ok(v.clone());
3281 }
3282 if let Some(v) = self.global_seed.get(name) {
3285 return Ok(v.clone());
3286 }
3287 if self.functions.contains_key(name) {
3288 let def = &self.functions[name];
3289 return Ok(Value::Fn(def.params.clone(), def.body.clone(), new_env()));
3290 }
3291 if let Some((enum_name, 0)) = self.enum_variants.get(name).cloned() {
3293 let variant = name.rsplit("::").next().unwrap_or(name).to_string();
3294 return Ok(Value::Variant { enum_name, variant, payload: Vec::new() });
3295 }
3296 match name {
3298 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" | "پی" | "باي" | "פאי" | "پائی"
3299 | "пи" => return Ok(Value::Number(std::f64::consts::PI)),
3300 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" | "تاو" | "טאו" | "ٹاؤ" | "тау" => {
3301 return Ok(Value::Number(std::f64::consts::TAU))
3302 },
3303 _ => {},
3304 }
3305 Err(EvalErr::from(format!("undefined: '{name}'")))
3306 }
3307
3308 pub(crate) fn call_named(&mut self, name: &str, args: Vec<Value>, env: &Env) -> EvalResult {
3317 if !ling_profile_enabled() {
3318 return self.call_named_inner(name, args, env);
3319 }
3320 let t0 = crate::runtime::now_secs();
3321 let r = self.call_named_inner(name, args, env);
3322 ling_profile_record(
3323 name,
3324 ((crate::runtime::now_secs() - t0) * 1_000_000_000.0) as u128,
3325 );
3326 r
3327 }
3328
3329 fn call_named_inner(&mut self, name: &str, args: Vec<Value>, env: &Env) -> EvalResult {
3330 if let Some(def) = self.functions.get(name).cloned() {
3333 let mut call_env =
3334 FxHashMap::with_capacity_and_hasher(def.params.len(), Default::default());
3335 let _ = env; for (param, arg) in def.params.iter().zip(args) {
3337 call_env.insert(param.clone(), arg);
3338 }
3339 return match self.framed(name, |me| me.exec_block(&def.body, &mut call_env)) {
3340 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
3341 Err(EvalErr::Return(v)) => Ok(v),
3342 Err(e) => Err(e),
3343 };
3344 }
3345
3346 #[cfg(target_arch = "wasm32")]
3347 if let Some(v) = self.wasm_music_builtin(name, &args)? {
3348 return Ok(v);
3349 }
3350
3351 match name {
3352 "__ling_global" => {
3355 if let Some(Value::Str(g)) = args.first() {
3356 if let Some(v) = self.global_seed.get(g.as_str()) {
3357 return Ok(v.clone());
3358 }
3359 }
3360 return Ok(Value::Unit);
3361 },
3362 "print" | "println" | "印" | "打印" | "印刷" | "พิมพ์" | "출력" | "вывести"
3364 | "imprimir" | "afficher" | "چاپ" | "اطبع" | "הדפס" | "چھاپو" | "drucken"
3365 | "печать" => {
3366 let s = args
3367 .iter()
3368 .map(|v| v.to_string())
3369 .collect::<Vec<_>>()
3370 .join("");
3371 println!("{s}");
3372 return Ok(Value::Unit);
3373 },
3374 "print_color" | "พิมพ์สี" | "چاپ_رنگی" | "اطبع_بلون" | "הדפס_בצבע" | "رنگین_چھاپو" =>
3377 {
3378 #[cfg(windows)]
3379 {
3380 use std::sync::Once;
3381 static VT: Once = Once::new();
3382 VT.call_once(|| {
3383 extern "system" {
3384 fn GetStdHandle(n: u32) -> *mut std::ffi::c_void;
3385 fn GetConsoleMode(h: *mut std::ffi::c_void, m: *mut u32) -> i32;
3386 fn SetConsoleMode(h: *mut std::ffi::c_void, m: u32) -> i32;
3387 }
3388 unsafe {
3389 let h = GetStdHandle(0xFFFF_FFF5u32); let mut mode = 0u32;
3391 if GetConsoleMode(h, &mut mode) != 0 {
3392 SetConsoleMode(h, mode | 0x0004); }
3394 }
3395 });
3396 }
3397 let col = self.arg_num(&args, 0, 7.0)? as i64;
3398 let s = args
3399 .iter()
3400 .skip(1)
3401 .map(|v| v.to_string())
3402 .collect::<Vec<_>>()
3403 .join("");
3404 let code = 90 + col.clamp(0, 7);
3405 println!("\x1b[1;{code}m{s}\x1b[0m");
3406 return Ok(Value::Unit);
3407 },
3408 "format"
3410 | "格式"
3411 | "フォーマット"
3412 | "서식"
3413 | "รูปแบบ"
3414 | "форматировать"
3415 | "formatear"
3416 | "formater"
3417 | "قالببندی"
3418 | "نسّق"
3419 | "פרמט"
3420 | "فارمیٹ"
3421 | "formatieren" => {
3422 return Ok(Value::Str(self.builtin_format(&args)?));
3423 },
3424 "格式::拼接" | "format::join" => match args.first() {
3426 Some(Value::List(items)) => {
3427 return Ok(Value::Str(items.iter().map(|v| v.to_string()).collect()));
3428 },
3429 _ => return Ok(Value::Str(self.builtin_format(&args)?)),
3430 },
3431 "ok" | "好" | "良し" | "좋아" | "โอเค" | "تایید" | "تمام" | "בסדר" | "ٹھیک" | "bon"
3433 | "gut" | "хорошо" => {
3434 let val = args.into_iter().next().unwrap_or(Value::Unit);
3435 return Ok(Value::Ok(Box::new(val)));
3436 },
3437 "bad" | "坏" | "err" | "悪い" | "나쁨" | "ผิด" | "بد" | "سيء" | "רע" | "برا"
3438 | "mauvais" | "schlecht" | "плохо" => {
3439 let val = args.into_iter().next().unwrap_or(Value::Unit);
3440 return Ok(Value::Err(Box::new(val)));
3441 },
3442 "向量::从" | "Vec::from" => {
3444 if let Some(Value::List(v)) = args.first() {
3445 return Ok(Value::List(v.clone()));
3446 }
3447 return Ok(Value::List(Rc::new(args)));
3448 },
3449 "向量::有容量" | "Vec::with_capacity" => {
3450 return Ok(Value::List(Rc::new(Vec::new())))
3451 },
3452 "计时::获取当前小时" | "Timer::hour" => return Ok(Value::Number(14.0)),
3454 "计时::现在" | "Timer::now" => return Ok(Value::Number(1000.0)),
3455 "sleep" | "หยุด" | "นอน" | "sleep_ms" | "睡眠" | "眠る" | "スリープ" | "잠자기"
3457 | "잠" | "流水::睡眠" | "Flow::sleep" | "خواب" | "نم" | "שינה" | "سو_جاؤ"
3458 | "dormir" | "schlafen" | "спать" => {
3459 if let Some(ms_val) = args.first() {
3460 if let Ok(ms) = self.to_number(ms_val) {
3461 #[cfg(target_arch = "wasm32")]
3462 wasm_sleep_ms(ms.max(0.0) as i32);
3463 #[cfg(not(target_arch = "wasm32"))]
3464 std::thread::sleep(std::time::Duration::from_millis(ms as u64));
3465 }
3466 }
3467 return Ok(Value::Unit);
3468 },
3469 "流水::并行" | "Flow::parallel" => {
3471 if let Some(Value::Fn(params, body, mut cap)) = args.first().cloned() {
3472 let _ = params;
3473 match self.exec_block(&body, &mut cap) {
3474 Ok(Some(v)) => return Ok(v),
3475 Ok(None) => return Ok(Value::Unit),
3476 Err(EvalErr::Return(v)) => return Ok(v),
3477 Err(e) => return Err(e),
3478 }
3479 }
3480 return Ok(Value::Unit);
3481 },
3482
3483 "sin" | "ไซน์" | "正弦" | "サイン" | "사인" | "سینوس" | "جا" | "סינוס" | "سائن"
3492 | "sinus" | "синус" => {
3493 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sin()));
3494 },
3495 "cos" | "โคไซน์" | "余弦" | "コサイン" | "코사인" | "کسینوس" | "جتا" | "קוסינוס"
3496 | "کوسائن" | "cosinus" | "kosinus" | "косинус" => {
3497 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cos()));
3498 },
3499
3500 "tanh" | "tanhf" | "双曲正切" | "双曲線正接" | "쌍곡탄젠트" => {
3503 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tanh()));
3504 },
3505
3506 "tan"
3507 | "แทนเจนต์"
3508 | "正切"
3509 | "タンジェント"
3510 | "탄젠트"
3511 | "تانژانت"
3512 | "ظا"
3513 | "טנגנס"
3514 | "ٹینجنٹ"
3515 | "tangente"
3516 | "tangens"
3517 | "тангенс" => {
3518 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tan()));
3519 },
3520 "asin"
3521 | "arcsin"
3522 | "反正弦"
3523 | "アークサイン"
3524 | "아크사인"
3525 | "อาร์กไซน์"
3526 | "آرکسینوس"
3527 | "قوس_جا"
3528 | "ארקסינוס"
3529 | "آرک_سائن"
3530 | "arcsinus"
3531 | "arkussinus"
3532 | "арксинус" => {
3533 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.asin()));
3534 },
3535 "acos"
3536 | "arccos"
3537 | "反余弦"
3538 | "アークコサイン"
3539 | "아크코사인"
3540 | "อาร์กโคไซน์"
3541 | "آرککسینوس"
3542 | "قوس_جتا"
3543 | "ארקוקוסינוס"
3544 | "آرک_کوسائن"
3545 | "arccosinus"
3546 | "arkuskosinus"
3547 | "арккосинус" => {
3548 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.acos()));
3549 },
3550 "atan"
3551 | "arctan"
3552 | "反正切"
3553 | "アークタンジェント"
3554 | "아크탄젠트"
3555 | "อาร์กแทนเจนต์"
3556 | "آرکتانژانت"
3557 | "قوس_ظا"
3558 | "ארקטנגנס"
3559 | "آرک_ٹینجنٹ"
3560 | "arctangente"
3561 | "arkustangens"
3562 | "арктангенс" => {
3563 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.atan()));
3564 },
3565 "atan2" | "arctan2" | "反正切2" | "アークタンジェント2" | "아크탄젠트2" =>
3566 {
3567 let y = self.arg_num(&args, 0, 0.0)?;
3568 let x = self.arg_num(&args, 1, 1.0)?;
3569 return Ok(Value::Number(y.atan2(x)));
3570 },
3571
3572 "sqrt"
3574 | "รากที่สอง"
3575 | "平方根"
3576 | "根"
3577 | "제곱근"
3578 | "جذر"
3579 | "שורש"
3580 | "racine_carrée"
3581 | "quadratwurzel"
3582 | "корень" => {
3583 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sqrt()));
3584 },
3585 "cbrt"
3586 | "立方根"
3587 | "세제곱근"
3588 | "รากที่สาม"
3589 | "ریشه_سوم"
3590 | "جذر_تكعيبي"
3591 | "שורש_שלישי"
3592 | "مکعب_جذر"
3593 | "racine_cubique"
3594 | "kubikwurzel"
3595 | "кубический_корень" => {
3596 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cbrt()));
3597 },
3598 "pow"
3599 | "ยกกำลัง"
3600 | "幂"
3601 | "べき乗"
3602 | "거듭제곱"
3603 | "توان"
3604 | "أس"
3605 | "חזקה"
3606 | "قوت"
3607 | "puissance"
3608 | "potenz"
3609 | "степень" => {
3610 let base = self.arg_num(&args, 0, 0.0)?;
3611 let exp = self.arg_num(&args, 1, 1.0)?;
3612 return Ok(Value::Number(base.powf(exp)));
3613 },
3614 "exp" | "指数" | "指数関数" | "지수" => {
3615 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.exp()));
3616 },
3617 "hypot" | "斜边" | "斜辺" | "빗변" => {
3618 let x = self.arg_num(&args, 0, 0.0)?;
3619 let y = self.arg_num(&args, 1, 0.0)?;
3620 return Ok(Value::Number(x.hypot(y)));
3621 },
3622
3623 "ln"
3625 | "log"
3626 | "ลอการิทึม"
3627 | "对数"
3628 | "対数"
3629 | "로그"
3630 | "لگاریتم_طبیعی"
3631 | "لوغاريتم_طبيعي"
3632 | "לוגריתם_טבעי"
3633 | "فطری_لوگ" => {
3634 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.ln()));
3635 },
3636 "log2" | "对数2" | "対数2" | "로그2" => {
3637 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log2()));
3638 },
3639 "log10" | "对数10" | "対数10" | "로그10" => {
3640 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log10()));
3641 },
3642
3643 "abs"
3645 | "ค่าสัมบูรณ์"
3646 | "绝对值"
3647 | "绝对"
3648 | "絶対値"
3649 | "절댓값"
3650 | "절대값"
3651 | "قدرمطلق"
3652 | "مطلق"
3653 | "ערך_מוחלט"
3654 | "مطلق_قدر"
3655 | "valeur_absolue"
3656 | "betrag"
3657 | "модуль_числа" => {
3658 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.abs()));
3659 },
3660 "floor" | "ปัดลง" | "向下取整" | "下整" | "床関数" | "내림" | "کف" | "أرضية"
3661 | "רצפה" | "فرش" | "plancher" | "abrunden" | "вниз" => {
3662 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.floor()));
3663 },
3664 "ceil"
3665 | "ปัดขึ้น"
3666 | "向上取整"
3667 | "上整"
3668 | "天井関数"
3669 | "올림"
3670 | "سقف"
3671 | "תקרה"
3672 | "چھت"
3673 | "plafond"
3674 | "aufrunden"
3675 | "вверх" => {
3676 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.ceil()));
3677 },
3678 "round" | "ปัดเศษ" | "四舍五入" | "四舍" | "四捨五入" | "반올림" | "گرد_کردن"
3679 | "تقريب" | "עיגול" | "گول" | "arrondir" | "runden" | "округлить" =>
3680 {
3681 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.round()));
3682 },
3683 "trunc"
3684 | "int"
3685 | "ตัดทศนิยม"
3686 | "取整"
3687 | "整数化"
3688 | "整数"
3689 | "截整"
3690 | "정수화"
3691 | "정수"
3692 | "切り捨て"
3693 | "버림"
3694 | "برش"
3695 | "اقتطاع"
3696 | "קיטום"
3697 | "کٹائی"
3698 | "tronquer"
3699 | "abschneiden"
3700 | "усечь" => {
3701 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.trunc()));
3702 },
3703 "fract" | "小数部分" | "小数部" | "소수부" => {
3704 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.fract()));
3705 },
3706
3707 "min" | "ต่ำสุด" | "最小" | "최솟값" | "کمینه" | "أصغر" | "מינימום" | "کم_ترین"
3709 | "minimum" | "минимум" => {
3710 let a = self.arg_num(&args, 0, 0.0)?;
3711 let b = self.arg_num(&args, 1, 0.0)?;
3712 return Ok(Value::Number(a.min(b)));
3713 },
3714 "max"
3715 | "สูงสุด"
3716 | "最大"
3717 | "최댓값"
3718 | "بیشینه"
3719 | "أكبر"
3720 | "מקסימום"
3721 | "زیادہ_ترین"
3722 | "maximum"
3723 | "максимум" => {
3724 let a = self.arg_num(&args, 0, 0.0)?;
3725 let b = self.arg_num(&args, 1, 0.0)?;
3726 return Ok(Value::Number(a.max(b)));
3727 },
3728 "clamp"
3729 | "จำกัด"
3730 | "截取"
3731 | "範囲制限"
3732 | "범위제한"
3733 | "محدود"
3734 | "قيّد"
3735 | "הגבל"
3736 | "limiter"
3737 | "begrenzen"
3738 | "ограничить" => {
3739 let x = self.arg_num(&args, 0, 0.0)?;
3740 let lo = self.arg_num(&args, 1, 0.0)?;
3741 let hi = self.arg_num(&args, 2, 1.0)?;
3742 return Ok(Value::Number(x.clamp(lo, hi)));
3743 },
3744
3745 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" | "پی" | "باي" | "פאי" | "پائی"
3747 | "пи" => return Ok(Value::Number(std::f64::consts::PI)),
3748 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" | "تاو" | "טאו" | "ٹاؤ" | "тау" => {
3749 return Ok(Value::Number(std::f64::consts::TAU))
3750 },
3751
3752 "vnoise"
3758 | "noise2"
3759 | "นอยส์2ดี"
3760 | "柏林噪声2D"
3761 | "バリューノイズ2D"
3762 | "값노이즈2D"
3763 | "نویز_برداری"
3764 | "ضجيج_متجه"
3765 | "רעש_וקטורי"
3766 | "ویکٹر_نوائز"
3767 | "bruit_v"
3768 | "v_rauschen"
3769 | "шум_v" => {
3770 let x = self.arg_num(&args, 0, 0.0)? as f32;
3771 let y = self.arg_num(&args, 1, 0.0)? as f32;
3772 let seed = self.arg_num(&args, 2, 0.0)? as u32;
3773 return Ok(Value::Number(tex_vnoise(x, y, seed) as f64));
3774 },
3775
3776 "fbm"
3777 | "นอยส์ออร์แกนิก"
3778 | "分形噪声"
3779 | "フラクタルノイズ"
3780 | "프랙탈노이즈"
3781 | "نویز_فراکتالی"
3782 | "ضجيج_عضوي"
3783 | "רעש_פרקטלי"
3784 | "فریکٹل_نوائز" => {
3785 let x = self.arg_num(&args, 0, 0.0)? as f32;
3786 let y = self.arg_num(&args, 1, 0.0)? as f32;
3787 let octaves = self.arg_num(&args, 2, 4.0)? as u32;
3788 let seed = self.arg_num(&args, 3, 0.0)? as u32;
3789 return Ok(Value::Number(tex_fbm(x, y, octaves, seed) as f64));
3790 },
3791
3792 "perlin"
3793 | "perlin3"
3794 | "เพอร์ลิน3ดี"
3795 | "柏林噪声3D"
3796 | "パーリンノイズ3D"
3797 | "펄린노이즈3D"
3798 | "نویز_پرلین"
3799 | "بيرلين"
3800 | "רעש_פרלין"
3801 | "پرلن_نوائز"
3802 | "перлин" => {
3803 let x = self.arg_num(&args, 0, 0.0)? as f32;
3804 let y = self.arg_num(&args, 1, 0.0)? as f32;
3805 let z = self.arg_num(&args, 2, 0.0)? as f32;
3806 return Ok(Value::Number(perlin3(x, y, z) as f64));
3807 },
3808
3809 "lerp"
3811 | "ค่าระหว่าง"
3812 | "线性插值"
3813 | "線形補間"
3814 | "선형보간"
3815 | "میانیابی"
3816 | "استيفاء"
3817 | "אינטרפולציה"
3818 | "درمیانی_قدر"
3819 | "interpoler"
3820 | "interpolieren"
3821 | "интерполировать" => {
3822 let a = self.arg_num(&args, 0, 0.0)?;
3823 let b = self.arg_num(&args, 1, 1.0)?;
3824 let t = self.arg_num(&args, 2, 0.0)?;
3825 return Ok(Value::Number(a + (b - a) * t));
3826 },
3827
3828 "smoothstep"
3829 | "เปลี่ยนแบบนุ่ม"
3830 | "平滑步进"
3831 | "スムーズステップ"
3832 | "스무스스텝"
3833 | "گام_نرم"
3834 | "تدرج_ناعم"
3835 | "מדרגה_חלקה"
3836 | "ہموار_قدم"
3837 | "lissage"
3838 | "glättung"
3839 | "сглаживание" => {
3840 let lo = self.arg_num(&args, 0, 0.0)?;
3841 let hi = self.arg_num(&args, 1, 1.0)?;
3842 let x = self.arg_num(&args, 2, 0.5)?;
3843 let t = ((x - lo) / (hi - lo)).clamp(0.0, 1.0);
3844 return Ok(Value::Number(t * t * (3.0 - 2.0 * t)));
3845 },
3846
3847 "rand" | "สุ่ม" | "随机" | "乱数" | "난수" | "تصادفی" | "عشوائي" | "אקראי"
3848 | "بے_ترتیب" | "aléatoire" | "zufall" | "случайное" => {
3849 let val = fast_rand_f64(&mut self.rand_state);
3850 return Ok(Value::Number(val));
3851 },
3852
3853 "sign"
3854 | "เครื่องหมาย"
3855 | "符号"
3856 | "符号関数"
3857 | "부호"
3858 | "علامت"
3859 | "إشارة"
3860 | "סימן"
3861 | "نشان"
3862 | "signe"
3863 | "vorzeichen"
3864 | "знак" => {
3865 let x = self.arg_num(&args, 0, 0.0)?;
3866 return Ok(Value::Number(x.signum()));
3867 },
3868
3869 "hsv_to_rgb"
3870 | "เอชเอสวีเป็นRGB"
3871 | "HSV转RGB"
3872 | "HSV変換RGB"
3873 | "HSV변환RGB"
3874 | "HSV_به_RGB"
3875 | "HSV_إلى_RGB"
3876 | "HSV_ל_RGB"
3877 | "HSV_سے_RGB"
3878 | "hsv_vers_rgb"
3879 | "hsv_zu_rgb"
3880 | "hsv_в_rgb" => {
3881 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;
3885 let x = c * (1.0 - (((h / 60.0) % 2.0) - 1.0).abs());
3886 let m = v - c;
3887 let (r1, g1, b1) = if h < 60.0 {
3888 (c, x, 0.0)
3889 } else if h < 120.0 {
3890 (x, c, 0.0)
3891 } else if h < 180.0 {
3892 (0.0, c, x)
3893 } else if h < 240.0 {
3894 (0.0, x, c)
3895 } else if h < 300.0 {
3896 (x, 0.0, c)
3897 } else {
3898 (c, 0.0, x)
3899 };
3900 let r = ((r1 + m) * 255.0).round();
3901 let g = ((g1 + m) * 255.0).round();
3902 let b = ((b1 + m) * 255.0).round();
3903 return Ok(Value::List(Rc::new(vec![
3904 Value::Number(r),
3905 Value::Number(g),
3906 Value::Number(b),
3907 ])));
3908 },
3909
3910 "lerp_color"
3911 | "ไล่สี"
3912 | "颜色插值"
3913 | "色補間"
3914 | "색보간"
3915 | "میانیابی_رنگ"
3916 | "استيفاء_اللون"
3917 | "אינטרפולציית_צבע"
3918 | "رنگ_درمیانی_قدر"
3919 | "interpoler_couleur"
3920 | "farbe_interpolieren"
3921 | "интерполировать_цвет" => {
3922 let r1 = self.arg_num(&args, 0, 0.0)?;
3923 let g1 = self.arg_num(&args, 1, 0.0)?;
3924 let b1 = self.arg_num(&args, 2, 0.0)?;
3925 let r2 = self.arg_num(&args, 3, 255.0)?;
3926 let g2 = self.arg_num(&args, 4, 255.0)?;
3927 let b2 = self.arg_num(&args, 5, 255.0)?;
3928 let t = self.arg_num(&args, 6, 0.0)?;
3929 let r = r1 + (r2 - r1) * t;
3930 let g = g1 + (g2 - g1) * t;
3931 let b = b1 + (b2 - b1) * t;
3932 let c = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
3933 self.gfx.borrow_mut().color = c;
3934 return Ok(Value::Unit);
3935 },
3936
3937 "time_now"
3939 | "เวลาปัจจุบัน"
3940 | "当前时间"
3941 | "経過時間"
3942 | "현재시간"
3943 | "زمان_اکنون"
3944 | "الوقت_الآن"
3945 | "הזמן_עכשיו"
3946 | "ابھی_کا_وقت"
3947 | "temps_actuel"
3948 | "aktuelle_zeit"
3949 | "текущее_время" => {
3950 return Ok(Value::Number(
3951 crate::runtime::now_secs() - self.start_time_secs,
3952 ));
3953 },
3954
3955 "epoch_now"
3959 | "เวลาโลก"
3960 | "datetime"
3961 | "现在时刻"
3962 | "現在時刻"
3963 | "현재시각"
3964 | "مهر_زمانی_اکنون"
3965 | "طابع_الوقت_الآن"
3966 | "חותמת_זמן_עכשיו"
3967 | "ایپاک_وقت" => {
3968 return Ok(Value::Number(crate::runtime::now_secs()));
3969 },
3970
3971 "frame_count"
3972 | "เฟรม"
3973 | "帧数"
3974 | "フレーム数"
3975 | "프레임수"
3976 | "شمار_فریم"
3977 | "عدد_الإطارات"
3978 | "ספירת_פריימים"
3979 | "فریم_شمار"
3980 | "nombre_images"
3981 | "bildanzahl"
3982 | "число_кадров" => {
3983 return Ok(Value::Number(self.frame_num as f64));
3984 },
3985
3986 "mic_open"
3988 | "เปิดไมค์"
3989 | "开麦克风"
3990 | "マイク開く"
3991 | "마이크열기"
3992 | "باز_کردن_میکروفون"
3993 | "افتح_الميكروفون"
3994 | "פתח_מיקרופון"
3995 | "مائیکروفون_کھولو"
3996 | "ouvrir_micro"
3997 | "mikrofon_öffnen"
3998 | "открыть_микрофон" => {
3999 #[cfg(not(target_arch = "wasm32"))]
4000 {
4001 match ling_mic::MicInput::open(Default::default()) {
4002 Ok(mic) => {
4003 let _ = mic.start(|_samples: &[f32]| {}); self.mic = Some(mic);
4005 return Ok(Value::Number(1.0)); },
4007 Err(_e) => {
4010 self.mic = None;
4011 return Ok(Value::Number(0.0));
4012 },
4013 }
4014 }
4015 #[cfg(target_arch = "wasm32")]
4016 return Ok(Value::Unit);
4017 },
4018
4019 "mic_rms"
4020 | "เสียงRMS"
4021 | "麦克风音量"
4022 | "マイクRMS"
4023 | "마이크RMS"
4024 | "RMS_میکروفون"
4025 | "RMS_الميكروفون"
4026 | "RMS_מיקרופון"
4027 | "مائیکروفون_RMS"
4028 | "rms_micro"
4029 | "mikrofon_rms"
4030 | "rms_микрофона" => {
4031 #[cfg(not(target_arch = "wasm32"))]
4032 {
4033 let rms = self
4034 .mic
4035 .as_ref()
4036 .map(|m: &ling_mic::MicInput| m.rms())
4037 .unwrap_or(0.0);
4038 return Ok(Value::Number(rms as f64));
4039 }
4040 #[cfg(target_arch = "wasm32")]
4041 return Ok(Value::Number(0.0));
4042 },
4043
4044 "mic_peak"
4045 | "เสียงพีค"
4046 | "麦克风峰值"
4047 | "マイクピーク"
4048 | "마이크피크"
4049 | "اوج_میکروفون"
4050 | "ذروة_الميكروفون"
4051 | "שיא_מיקרופון"
4052 | "مائیکروفون_چوٹی"
4053 | "crête_micro"
4054 | "mikrofon_spitze"
4055 | "пик_микрофона" => {
4056 #[cfg(not(target_arch = "wasm32"))]
4057 {
4058 let peak = self
4059 .mic
4060 .as_ref()
4061 .map(|m: &ling_mic::MicInput| m.peak())
4062 .unwrap_or(0.0);
4063 return Ok(Value::Number(peak as f64));
4064 }
4065 #[cfg(target_arch = "wasm32")]
4066 return Ok(Value::Number(0.0));
4067 },
4068
4069 "mic_fft"
4070 | "วิเคราะห์เสียงสด"
4071 | "实时频谱"
4072 | "リアルタイムFFT"
4073 | "실시간FFT"
4074 | "FFT_میکروفون"
4075 | "FFT_الميكروفون"
4076 | "FFT_מיקרופון"
4077 | "مائیکروفون_FFT"
4078 | "fft_micro"
4079 | "mikrofon_fft"
4080 | "fft_микрофона" => {
4081 #[cfg(not(target_arch = "wasm32"))]
4082 {
4083 let n = self.arg_num(&args, 0, 8.0)? as usize;
4084 if let Some(mic) = self.mic.as_ref() {
4085 let samples = mic.latest_samples();
4086 self.fft.borrow_mut().push_samples(&samples);
4087 }
4088 let bands = self.fft.borrow().freq_bands(n);
4089 let result: Vec<Value> =
4090 bands.iter().map(|&v| Value::Number(v as f64)).collect();
4091 return Ok(Value::List(Rc::new(result)));
4092 }
4093 #[cfg(target_arch = "wasm32")]
4094 return Ok(Value::List(Vec::new().into()));
4095 },
4096
4097 "set_blend"
4099 | "โหมดผสม"
4100 | "混合模式"
4101 | "ブレンドモード"
4102 | "블렌드모드"
4103 | "تنظیم_ترکیب"
4104 | "عيّن_المزج"
4105 | "קבע_מיזוג"
4106 | "بلینڈ_مقرر_کرو"
4107 | "définir_mélange"
4108 | "mischmodus_setzen"
4109 | "задать_смешивание" => {
4110 let mode = self.arg_num(&args, 0, 0.0)? as u8;
4111 let mut gfx = self.gfx.borrow_mut();
4112 gfx.blend = mode;
4113 let a = gfx.alpha;
4114 gfx.depth_queue.set_state(mode, a); return Ok(Value::Unit);
4116 },
4117
4118 "set_antialias"
4122 | "ตั้งลบรอยหยัก"
4123 | "抗锯齿"
4124 | "アンチエイリアス"
4125 | "안티에일리어싱"
4126 | "تنظیم_ضدلبهدندانه"
4127 | "عيّن_مضاد_التسنن"
4128 | "קבע_החלקת_קצוות"
4129 | "اینٹی_الائیسنگ_مقرر_کرو" => {
4130 let on = self.arg_num(&args, 0, 1.0)? > 0.5;
4131 self.gfx.borrow_mut().antialias = on;
4132 return Ok(Value::Unit);
4133 },
4134 "get_antialias"
4136 | "อ่านลบรอยหยัก"
4137 | "读取抗锯齿"
4138 | "アンチエイリアス取得"
4139 | "안티에일리어싱상태"
4140 | "خواندن_ضدلبهدندانه"
4141 | "اقرأ_مضاد_التسنن"
4142 | "קרא_החלקת_קצוות"
4143 | "اینٹی_الائیسنگ_پڑھو" => {
4144 return Ok(Value::Bool(self.gfx.borrow().antialias));
4145 },
4146
4147 "set_font_antialias" | "글꼴안티에일리어싱" => {
4151 let on = self.arg_num(&args, 0, 1.0)? > 0.5;
4152 self.gfx.borrow_mut().font_antialias = on;
4153 return Ok(Value::Unit);
4154 },
4155 "get_font_antialias" | "글꼴안티에일리어싱상태" => {
4157 return Ok(Value::Bool(self.gfx.borrow().font_antialias));
4158 },
4159
4160 "draw_circle"
4162 | "วาดวงกลม"
4163 | "画圆"
4164 | "円描画"
4165 | "원그리기"
4166 | "رسم_دایره"
4167 | "ارسم_دائرة"
4168 | "צייר_עיגול"
4169 | "دائرہ_کھینچو"
4170 | "dessiner_cercle"
4171 | "kreis_zeichnen"
4172 | "рисовать_круг" => {
4173 let cx = self.arg_num(&args, 0, 0.0)? as i32;
4174 let cy = self.arg_num(&args, 1, 0.0)? as i32;
4175 let r = self.arg_num(&args, 2, 10.0)? as i32;
4176 let mut gfx = self.gfx.borrow_mut();
4177 let (w, h, color, blend) =
4178 (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
4179 if gfx.antialias {
4180 let (uw, uh) = (gfx.width, gfx.height);
4181 let segs = ((r.max(1) as u32) * 4).clamp(24, 512);
4182 crate::gfx::raster::draw_arc(
4183 &mut gfx.buffer,
4184 uw,
4185 uh,
4186 color,
4187 true,
4188 blend == 1,
4189 cx as f32,
4190 cy as f32,
4191 r as f32,
4192 0.0,
4193 std::f32::consts::TAU,
4194 segs,
4195 );
4196 } else {
4197 draw_circle_outline(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
4198 }
4199 return Ok(Value::Unit);
4200 },
4201
4202 "draw_filled_circle"
4203 | "draw_disc"
4204 | "วาดวงกลมทึบ"
4205 | "画实心圆"
4206 | "塗りつぶし円"
4207 | "원채우기"
4208 | "رسم_دایره_توپر"
4209 | "ارسم_دائرة_ممتلئة"
4210 | "צייר_עיגול_מלא"
4211 | "بھرا_دائرہ_کھینچو" => {
4212 let cx = self.arg_num(&args, 0, 0.0)? as i32;
4213 let cy = self.arg_num(&args, 1, 0.0)? as i32;
4214 let r = self.arg_num(&args, 2, 10.0)? as i32;
4215 let mut gfx = self.gfx.borrow_mut();
4216 let (w, h, color, blend) =
4217 (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
4218 draw_circle_filled(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
4219 return Ok(Value::Unit);
4220 },
4221
4222 "draw_arc"
4226 | "arc"
4227 | "วาดส่วนโค้ง"
4228 | "画弧"
4229 | "円弧描画"
4230 | "호그리기"
4231 | "رسم_کمان"
4232 | "ارسم_قوسا"
4233 | "צייר_קשת"
4234 | "آرک_کھینچو" => {
4235 let cx = self.arg_num(&args, 0, 0.0)? as f32;
4236 let cy = self.arg_num(&args, 1, 0.0)? as f32;
4237 let r = self.arg_num(&args, 2, 10.0)? as f32;
4238 let a0 = self.arg_num(&args, 3, 0.0)? as f32;
4239 let a1 = self.arg_num(&args, 4, std::f64::consts::TAU)? as f32;
4240 let default_segs = ((r.abs() * (a1 - a0).abs()).ceil() as u32).clamp(8, 1024);
4241 let segs = self.arg_num(&args, 5, default_segs as f64)? as u32;
4242 let mut gfx = self.gfx.borrow_mut();
4243 let color = gfx.color;
4244 #[cfg(not(target_arch = "wasm32"))]
4245 {
4246 let (uw, uh, aa, add) = (gfx.width, gfx.height, gfx.antialias, gfx.blend == 1);
4247 crate::gfx::raster::draw_arc(
4248 &mut gfx.buffer,
4249 uw,
4250 uh,
4251 color,
4252 aa,
4253 add,
4254 cx,
4255 cy,
4256 r,
4257 a0,
4258 a1,
4259 segs,
4260 );
4261 }
4262 #[cfg(target_arch = "wasm32")]
4263 {
4264 let segs_f = segs.max(1);
4265 let step = (a1 - a0) / segs_f as f32;
4266 let mut px = cx + r * a0.cos();
4267 let mut py = cy + r * a0.sin();
4268 let mut i = 1u32;
4269 while i <= segs_f {
4270 let a = a0 + step * i as f32;
4271 let nx = cx + r * a.cos();
4272 let ny = cy + r * a.sin();
4273 gfx.depth_queue.push_line(0.0, color, px, py, nx, ny);
4274 px = nx;
4275 py = ny;
4276 i += 1;
4277 }
4278 }
4279 return Ok(Value::Unit);
4280 },
4281
4282 "set_alpha"
4288 | "ตั้งความโปร่งใส"
4289 | "设透明"
4290 | "アルファ設定"
4291 | "투명도설정"
4292 | "تنظیم_شفافیت"
4293 | "عيّن_الشفافية"
4294 | "קבע_שקיפות"
4295 | "شفافیت_مقرر_کرو"
4296 | "définir_alpha"
4297 | "alpha_setzen"
4298 | "задать_альфа" => {
4299 let a = self.arg_num(&args, 0, 1.0)? as f32;
4300 let mut gfx = self.gfx.borrow_mut();
4301 gfx.alpha = a.clamp(0.0, 1.0);
4302 let (m, al) = (gfx.blend, gfx.alpha);
4303 gfx.depth_queue.set_state(m, al); return Ok(Value::Unit);
4305 },
4306
4307 "mesh_hue" | "หมุนสีเมช" | "فام_مش" | "صبغة_الشبكة" | "גוון_רשת" | "میش_ہیو" =>
4310 {
4311 let h = self.arg_num(&args, 0, 0.0)? as f32;
4312 let g = self.arg_num(&args, 1, 1.0)? as f32;
4313 let mut gfx = self.gfx.borrow_mut();
4314 gfx.mesh_hue = h;
4315 gfx.mesh_hue_gain = g.max(0.0);
4316 return Ok(Value::Unit);
4317 },
4318
4319 "set_frame_blur"
4322 | "frame_blur"
4323 | "เบลอเฟรม"
4324 | "تنظیم_تاری_فریم"
4325 | "عيّن_ضبابية_الإطار"
4326 | "קבע_טשטוש_פריים"
4327 | "فریم_بلر_مقرر_کرو" => {
4328 let a = self.arg_num(&args, 0, 0.0)? as f32;
4329 let mut gfx = self.gfx.borrow_mut();
4330 gfx.frame_blur = a.clamp(0.0, 0.95);
4331 if gfx.frame_blur <= 0.0 {
4332 gfx.prev_frame = Vec::new();
4333 }
4334 return Ok(Value::Unit);
4335 },
4336
4337 "set_line_hue_cycle"
4341 | "ตั้งวนสีเส้น"
4342 | "تنظیم_چرخه_فام_خط"
4343 | "عيّن_دورة_صبغة_الخط"
4344 | "קבע_מחזור_גוון_קו"
4345 | "لائن_ہیو_سائیکل_مقرر_کرو" => {
4346 let rate = self.arg_num(&args, 0, 0.0)?;
4347 crate::runtime::set_line_hue_rate(rate);
4348 return Ok(Value::Unit);
4349 },
4350
4351 "set_color_space"
4355 | "ปริภูมิสี"
4356 | "色彩空间"
4357 | "色空間"
4358 | "색공간"
4359 | "تنظیم_فضای_رنگ"
4360 | "عيّن_فضاء_اللون"
4361 | "קבע_מרחב_צבע"
4362 | "کلر_اسپیس_مقرر_کرو"
4363 | "définir_espace_couleur"
4364 | "farbraum_setzen"
4365 | "задать_цветовое_пространство" => {
4366 let m = self.arg_num(&args, 0, 0.0)? as i64;
4367 self.gfx.borrow_mut().linear_blend = m != 0;
4368 return Ok(Value::Unit);
4369 },
4370
4371 "set_gradient_space"
4374 | "ปริภูมิไล่สี"
4375 | "渐变空间"
4376 | "グラデ空間"
4377 | "그라데이션공간"
4378 | "تنظیم_فضای_گرادیان"
4379 | "عيّن_فضاء_التدرج"
4380 | "קבע_מרחב_גרדיאנט"
4381 | "گریڈینٹ_اسپیس_مقرر_کرو"
4382 | "définir_espace_dégradé"
4383 | "verlaufsraum_setzen"
4384 | "задать_пространство_градиента" => {
4385 let m = self.arg_num(&args, 0, 1.0)? as i64;
4386 self.gfx.borrow_mut().grad_oklab = m != 0;
4387 return Ok(Value::Unit);
4388 },
4389
4390 "mix_color"
4394 | "ผสมสี"
4395 | "混合颜色"
4396 | "色混合"
4397 | "색혼합"
4398 | "ترکیب_رنگ"
4399 | "امزج_اللون"
4400 | "ערבב_צבע"
4401 | "رنگ_ملاؤ"
4402 | "mélanger_couleur"
4403 | "farbe_mischen"
4404 | "смешать_цвет" => {
4405 let c0 = rgb(
4406 self.arg_num(&args, 0, 0.0)?,
4407 self.arg_num(&args, 1, 0.0)?,
4408 self.arg_num(&args, 2, 0.0)?,
4409 );
4410 let c1 = rgb(
4411 self.arg_num(&args, 3, 255.0)?,
4412 self.arg_num(&args, 4, 255.0)?,
4413 self.arg_num(&args, 5, 255.0)?,
4414 );
4415 let t = self.arg_num(&args, 6, 0.5)? as f32;
4416 self.gfx.borrow_mut().color = crate::gfx::color::mix_oklab(c0, c1, t);
4417 return Ok(Value::Unit);
4418 },
4419
4420 "set_depth_test"
4424 | "ทดสอบความลึก"
4425 | "深度测试"
4426 | "深度テスト"
4427 | "깊이테스트"
4428 | "تنظیم_آزمون_عمق"
4429 | "عيّن_اختبار_العمق"
4430 | "קבע_בדיקת_עומק"
4431 | "ڈیپتھ_ٹیسٹ_مقرر_کرو"
4432 | "définir_test_profondeur"
4433 | "tiefentest_setzen"
4434 | "задать_тест_глубины" => {
4435 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
4436 self.gfx.borrow_mut().depth_test = on;
4437 return Ok(Value::Unit);
4438 },
4439
4440 "set_flat_shade"
4443 | "ตั้งแฟลตเชด"
4444 | "平面着色"
4445 | "フラット着色"
4446 | "평면음영"
4447 | "تنظیم_سایهپردازی_تخت"
4448 | "عيّن_تظليلا_مسطحا"
4449 | "קבע_הצללה_שטוחה"
4450 | "فلیٹ_شیڈ_مقرر_کرو" => {
4451 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
4452 self.gfx.borrow_mut().flat_shade = on;
4453 return Ok(Value::Unit);
4454 },
4455
4456 "set_normal_override" => {
4459 let x = self.arg_num(&args, 0, 0.0)? as f32;
4460 let y = self.arg_num(&args, 1, -1.0)? as f32;
4461 let z = self.arg_num(&args, 2, 0.0)? as f32;
4462 self.gfx.borrow_mut().normal_override = Some([x, y, z]);
4463 return Ok(Value::Unit);
4464 },
4465
4466 "reset_normal_override" => {
4467 self.gfx.borrow_mut().normal_override = None;
4468 return Ok(Value::Unit);
4469 },
4470
4471 "clear_depth"
4475 | "ล้างความลึก"
4476 | "清深度"
4477 | "深度クリア"
4478 | "깊이지우기"
4479 | "پاککردن_عمق"
4480 | "امسح_العمق"
4481 | "נקה_עומק"
4482 | "گہرائی_صاف_کرو" => {
4483 self.gfx.borrow_mut().zbuf_needs_clear = true;
4484 return Ok(Value::Unit);
4485 },
4486
4487 "depth_blur"
4493 | "เบลอความลึก"
4494 | "dof"
4495 | "depth_of_field"
4496 | "景深"
4497 | "تاری_عمق"
4498 | "ضبابية_العمق"
4499 | "טשטוש_עומק"
4500 | "ڈیپتھ_بلر" => {
4501 let focus = self.arg_num(&args, 0, 30.0)? as f32;
4502 let range = self.arg_num(&args, 1, 60.0)? as f32;
4503 let radius = self.arg_num(&args, 2, 3.0)?.max(0.0) as usize;
4504 let oil = self.arg_num(&args, 3, 0.0)? as f32;
4507 let mut gfx = self.gfx.borrow_mut();
4508 let w = gfx.width;
4509 let h = gfx.height;
4510 if gfx.depth_buf.len() == w * h {
4511 let g = &mut *gfx;
4512 crate::gfx::raster::depth_of_field(
4513 &mut g.buffer,
4514 &g.depth_buf,
4515 w,
4516 h,
4517 focus,
4518 range,
4519 radius,
4520 oil,
4521 );
4522 }
4523 return Ok(Value::Unit);
4524 },
4525
4526 "light_pool"
4532 | "แอ่งแสง"
4533 | "光池"
4534 | "ライトプール"
4535 | "빛웅덩이"
4536 | "برکه_نور"
4537 | "بركة_ضوء"
4538 | "בריכת_אור"
4539 | "روشنی_تالاب"
4540 | "bassin_lumière"
4541 | "lichtpfütze"
4542 | "лужа_света" => {
4543 let x = self.arg_num(&args, 0, 0.0)? as f32;
4544 let y = self.arg_num(&args, 1, 0.0)? as f32;
4545 let z = self.arg_num(&args, 2, 0.0)? as f32;
4546 let radius = self.arg_num(&args, 3, 20.0)? as f32;
4547 let r = self.arg_num(&args, 4, 255.0)? as f32 / 255.0;
4548 let g = self.arg_num(&args, 5, 255.0)? as f32 / 255.0;
4549 let b = self.arg_num(&args, 6, 255.0)? as f32 / 255.0;
4550 let inten = self.arg_num(&args, 7, 1.0)? as f32;
4551 self.gfx
4552 .borrow_mut()
4553 .emit_light_pool(x, y, z, radius, [r, g, b], inten);
4554 return Ok(Value::Unit);
4555 },
4556
4557 "light_beam"
4562 | "ลำแสงไฟ"
4563 | "光柱"
4564 | "ライトビーム"
4565 | "빛기둥"
4566 | "پرتو_نور"
4567 | "شعاع_ضوء"
4568 | "קרן_אור"
4569 | "روشنی_شعاع"
4570 | "faisceau_lumière"
4571 | "lichtstrahl"
4572 | "луч_света" => {
4573 let x = self.arg_num(&args, 0, 0.0)? as f32;
4574 let y = self.arg_num(&args, 1, 0.0)? as f32;
4575 let z = self.arg_num(&args, 2, 0.0)? as f32;
4576 let fy = self.arg_num(&args, 3, 0.0)? as f32;
4577 let radius = self.arg_num(&args, 4, 14.0)? as f32;
4578 let r = self.arg_num(&args, 5, 255.0)? as f32 / 255.0;
4579 let g = self.arg_num(&args, 6, 255.0)? as f32 / 255.0;
4580 let b = self.arg_num(&args, 7, 255.0)? as f32 / 255.0;
4581 let inten = self.arg_num(&args, 8, 1.0)? as f32;
4582 self.gfx
4583 .borrow_mut()
4584 .emit_light_beam(x, y, z, fy, radius, [r, g, b], inten);
4585 return Ok(Value::Unit);
4586 },
4587
4588 "grad_triangle"
4592 | "สามเหลี่ยมไล่สี"
4593 | "渐变三角"
4594 | "グラデ三角"
4595 | "그라데삼각"
4596 | "مثلث_گرادیان"
4597 | "مثلث_متدرج"
4598 | "משולש_גרדיאנט"
4599 | "گریڈینٹ_مثلث"
4600 | "triangle_dégradé"
4601 | "dreieck_verlauf"
4602 | "градиент_треугольник" => {
4603 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
4604 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
4605 let c0 = rgb(
4606 self.arg_num(&args, 2, 255.0)?,
4607 self.arg_num(&args, 3, 255.0)?,
4608 self.arg_num(&args, 4, 255.0)?,
4609 );
4610 let x1 = self.arg_num(&args, 5, 0.0)? as f32;
4611 let y1 = self.arg_num(&args, 6, 0.0)? as f32;
4612 let c1 = rgb(
4613 self.arg_num(&args, 7, 255.0)?,
4614 self.arg_num(&args, 8, 255.0)?,
4615 self.arg_num(&args, 9, 255.0)?,
4616 );
4617 let x2 = self.arg_num(&args, 10, 0.0)? as f32;
4618 let y2 = self.arg_num(&args, 11, 0.0)? as f32;
4619 let c2 = rgb(
4620 self.arg_num(&args, 12, 255.0)?,
4621 self.arg_num(&args, 13, 255.0)?,
4622 self.arg_num(&args, 14, 255.0)?,
4623 );
4624 let mut gfx = self.gfx.borrow_mut();
4625 let (w, h, alpha, mode, lin, ok) = (
4626 gfx.width,
4627 gfx.height,
4628 gfx.alpha,
4629 gfx.blend,
4630 gfx.linear_blend,
4631 gfx.grad_oklab,
4632 );
4633 crate::gfx::raster::fill_triangle_grad(
4634 &mut gfx.buffer,
4635 w,
4636 h,
4637 alpha,
4638 mode,
4639 lin,
4640 ok,
4641 x0,
4642 y0,
4643 c0,
4644 x1,
4645 y1,
4646 c1,
4647 x2,
4648 y2,
4649 c2,
4650 );
4651 return Ok(Value::Unit);
4652 },
4653
4654 "grad_rect"
4657 | "สี่เหลี่ยมไล่สี"
4658 | "渐变矩形"
4659 | "グラデ矩形"
4660 | "그라데사각"
4661 | "مستطیل_گرادیان"
4662 | "مستطيل_متدرج"
4663 | "מלבן_גרדיאנט"
4664 | "گریڈینٹ_مستطیل"
4665 | "rectangle_dégradé"
4666 | "rechteck_verlauf"
4667 | "градиент_прямоугольник" => {
4668 let x = self.arg_num(&args, 0, 0.0)? as f32;
4669 let y = self.arg_num(&args, 1, 0.0)? as f32;
4670 let rw = self.arg_num(&args, 2, 0.0)? as f32;
4671 let rh = self.arg_num(&args, 3, 0.0)? as f32;
4672 let c0 = rgb(
4673 self.arg_num(&args, 4, 255.0)?,
4674 self.arg_num(&args, 5, 255.0)?,
4675 self.arg_num(&args, 6, 255.0)?,
4676 );
4677 let c1 = rgb(
4678 self.arg_num(&args, 7, 0.0)?,
4679 self.arg_num(&args, 8, 0.0)?,
4680 self.arg_num(&args, 9, 0.0)?,
4681 );
4682 let dir = self.arg_num(&args, 10, 1.0)? as u8;
4683 let mut gfx = self.gfx.borrow_mut();
4684 let (w, h, alpha, mode, lin, ok) = (
4685 gfx.width,
4686 gfx.height,
4687 gfx.alpha,
4688 gfx.blend,
4689 gfx.linear_blend,
4690 gfx.grad_oklab,
4691 );
4692 crate::gfx::raster::fill_rect_grad(
4693 &mut gfx.buffer,
4694 w,
4695 h,
4696 alpha,
4697 mode,
4698 lin,
4699 ok,
4700 x,
4701 y,
4702 rw,
4703 rh,
4704 c0,
4705 c1,
4706 dir,
4707 );
4708 return Ok(Value::Unit);
4709 },
4710
4711 "shadow_blob"
4715 | "เงาวงรี"
4716 | "阴影斑"
4717 | "影ブロブ"
4718 | "그림자블롭"
4719 | "لکه_سایه"
4720 | "بقعة_ظل"
4721 | "כתם_צל"
4722 | "سایہ_دھبہ"
4723 | "tache_ombre"
4724 | "schattenklecks"
4725 | "пятно_тени" => {
4726 let cx = self.arg_num(&args, 0, 0.0)? as f32;
4727 let cy = self.arg_num(&args, 1, 0.0)? as f32;
4728 let rx = self.arg_num(&args, 2, 16.0)? as f32;
4729 let ry = self.arg_num(&args, 3, 8.0)? as f32;
4730 let a = self.arg_num(&args, 4, 0.5)? as f32;
4731 let mut gfx = self.gfx.borrow_mut();
4732 let (w, h, color, soft, mode, lin) = (
4733 gfx.width,
4734 gfx.height,
4735 gfx.color,
4736 gfx.shadow.soft,
4737 gfx.blend,
4738 gfx.linear_blend,
4739 );
4740 crate::gfx::raster::fill_disc_soft(
4741 &mut gfx.buffer,
4742 w,
4743 h,
4744 cx,
4745 cy,
4746 rx,
4747 ry,
4748 color,
4749 a,
4750 soft,
4751 mode,
4752 lin,
4753 );
4754 return Ok(Value::Unit);
4755 },
4756
4757 "cast_shadow"
4762 | "ทอดเงา"
4763 | "投射阴影"
4764 | "影を落とす"
4765 | "그림자드리우기"
4766 | "افکندن_سایه"
4767 | "ألقِ_ظلا"
4768 | "הטל_צל"
4769 | "سایہ_ڈالو"
4770 | "projeter_ombre"
4771 | "schatten_werfen"
4772 | "отбросить_тень" => {
4773 let cx = self.arg_num(&args, 0, 0.0)? as f32;
4774 let cy = self.arg_num(&args, 1, 0.0)? as f32;
4775 let height = (self.arg_num(&args, 2, 0.0)? as f32).max(0.0);
4776 let mut gfx = self.gfx.borrow_mut();
4777 let sp = gfx.shadow;
4778 let radius = (sp.base + sp.grow * height).max(0.5);
4779 let alpha = (sp.alpha - sp.fade * height).clamp(0.04, 1.0);
4780 let soft = (sp.soft + height * 0.004).clamp(0.0, 0.95);
4781 let (w, h, color, mode, lin) = (
4782 gfx.width,
4783 gfx.height,
4784 gfx.color,
4785 gfx.blend,
4786 gfx.linear_blend,
4787 );
4788 crate::gfx::raster::fill_disc_soft(
4789 &mut gfx.buffer,
4790 w,
4791 h,
4792 cx,
4793 cy,
4794 radius,
4795 radius * 0.62,
4796 color,
4797 alpha,
4798 soft,
4799 mode,
4800 lin,
4801 );
4802 return Ok(Value::Unit);
4803 },
4804
4805 "shadow_params"
4808 | "ตั้งค่าเงา"
4809 | "阴影参数"
4810 | "影設定"
4811 | "그림자설정"
4812 | "پارامترهای_سایه"
4813 | "معاملات_الظل"
4814 | "פרמטרי_צל"
4815 | "سایہ_پیرامیٹرز"
4816 | "paramètres_ombre"
4817 | "schattenparameter"
4818 | "параметры_тени" => {
4819 let cur = self.gfx.borrow().shadow;
4820 let base = self.arg_num(&args, 0, cur.base as f64)? as f32;
4821 let grow = self.arg_num(&args, 1, cur.grow as f64)? as f32;
4822 let alpha = self.arg_num(&args, 2, cur.alpha as f64)? as f32;
4823 let fade = self.arg_num(&args, 3, cur.fade as f64)? as f32;
4824 let soft = self.arg_num(&args, 4, cur.soft as f64)? as f32;
4825 self.gfx.borrow_mut().shadow =
4826 crate::gfx::ShadowParams { base, grow, alpha, fade, soft };
4827 return Ok(Value::Unit);
4828 },
4829
4830 "depth_triangle"
4835 | "สามเหลี่ยมเรียงลึก"
4836 | "深度三角"
4837 | "深度三角形"
4838 | "깊이삼각"
4839 | "مثلث_عمق"
4840 | "مثلث_العمق"
4841 | "משולש_עומק"
4842 | "گہرائی_مثلث"
4843 | "triangle_profondeur"
4844 | "tiefendreieck"
4845 | "глубина_треугольник" => {
4846 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
4847 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
4848 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
4849 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
4850 let x2 = self.arg_num(&args, 4, 0.0)? as f32;
4851 let y2 = self.arg_num(&args, 5, 0.0)? as f32;
4852 let z = self.arg_num(&args, 6, 0.0)? as f32;
4853 let mut gfx = self.gfx.borrow_mut();
4854 let color = gfx.color;
4855 gfx.depth_queue
4856 .push_triangle(z, color, x0, y0, x1, y1, x2, y2);
4857 return Ok(Value::Unit);
4858 },
4859
4860 "depth_line"
4863 | "เส้นเรียงลึก"
4864 | "深度线"
4865 | "深度線"
4866 | "깊이선"
4867 | "خط_عمق"
4868 | "خط_العمق"
4869 | "קו_עומק"
4870 | "گہرائی_لکیر"
4871 | "ligne_profondeur"
4872 | "tiefenlinie"
4873 | "глубина_линия" => {
4874 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
4875 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
4876 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
4877 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
4878 let z = self.arg_num(&args, 4, 0.0)? as f32;
4879 let mut gfx = self.gfx.borrow_mut();
4880 let color = gfx.color;
4881 gfx.depth_queue.push_line(z, color, x0, y0, x1, y1);
4882 return Ok(Value::Unit);
4883 },
4884
4885 "เปิดหน้าต่าง"
4892 | "open_window"
4893 | "gfx_window"
4894 | "开窗"
4895 | "ウィンドウ開く"
4896 | "창열기"
4897 | "باز_کردن_پنجره"
4898 | "افتح_نافذة"
4899 | "פתח_חלון"
4900 | "ونڈو_کھولو" => {
4901 let w = self.arg_num(&args, 0, 800.0)? as usize;
4902 let h = self.arg_num(&args, 1, 600.0)? as usize;
4903 #[cfg(not(target_arch = "wasm32"))]
4904 {
4905 let title = args
4906 .get(2)
4907 .map(|v| v.to_string())
4908 .unwrap_or_else(|| "Ling".into());
4909 let mut gfx = self.gfx.borrow_mut();
4910 let mut win = minifb::Window::new(
4911 &title,
4912 w,
4913 h,
4914 minifb::WindowOptions {
4915 resize: false,
4916 scale: minifb::Scale::X1,
4917 ..Default::default()
4918 },
4919 )
4920 .map_err(|e| EvalErr::from(format!("cannot open window: {e}")))?;
4921 apply_frame_pacing(&mut win, gfx.vsync);
4922 gfx.buffer = vec![0u32; w * h];
4923 gfx.width = w;
4924 gfx.height = h;
4925 gfx.window = Some(win);
4926 gfx.topmost_window = false;
4927 gfx.sync_projection();
4928 }
4929 #[cfg(target_arch = "wasm32")]
4930 {
4931 let mut gfx = self.gfx.borrow_mut();
4932 gfx.width = w;
4933 gfx.height = h;
4934 gfx.buffer.resize(w * h, 0); gfx.sync_projection();
4936 crate::gfx::webgl::resize(w as u32, h as u32);
4937 }
4938 return Ok(Value::Unit);
4939 },
4940
4941 "เติม" | "fill" | "gfx_fill" | "clear" | "填" | "塗り潰し" | "채우기" | "清"
4943 | "消去" | "지우기" | "پر_کن" | "املأ" | "מלא" | "بھرو" => {
4944 let r = self.arg_num(&args, 0, 0.0)? as u32;
4945 let g = self.arg_num(&args, 1, 0.0)? as u32;
4946 let b = self.arg_num(&args, 2, 0.0)? as u32;
4947 #[cfg(not(target_arch = "wasm32"))]
4948 {
4949 let c = (r << 16) | (g << 8) | b;
4950 let mut gfx = self.gfx.borrow_mut();
4951 gfx.buffer.fill(c);
4952 gfx.zbuf_needs_clear = true; gfx.edge_set.clear(); }
4955 #[cfg(target_arch = "wasm32")]
4956 {
4957 let mut gfx = self.gfx.borrow_mut();
4958 gfx.fill_r = r as f32 / 255.0;
4959 gfx.fill_g = g as f32 / 255.0;
4960 gfx.fill_b = b as f32 / 255.0;
4961 let c = (r << 16) | (g << 8) | b;
4962 gfx.buffer.fill(c);
4963 gfx.zbuf_needs_clear = true;
4964 gfx.edge_set.clear();
4965 }
4966 return Ok(Value::Unit);
4967 },
4968
4969 "set_color_hsl"
4972 | "颜色HSL"
4973 | "色相"
4974 | "HSL色"
4975 | "HSL색설정"
4976 | "สีHSLวาด"
4977 | "تنظیم_رنگ_HSL"
4978 | "عيّن_اللون_HSL"
4979 | "קבע_צבע_HSL"
4980 | "HSL_رنگ_مقرر_کرو"
4981 | "définir_couleur_hsl"
4982 | "farbe_hsl_setzen"
4983 | "задать_цвет_hsl" => {
4984 let h = self.arg_num(&args, 0, 0.0)?;
4985 let s = self.arg_num(&args, 1, 70.0)?;
4986 let l = self.arg_num(&args, 2, 50.0)?;
4987 let hex = hsl_to_hex(h, s, l);
4988 let r = u32::from_str_radix(&hex[1..3], 16).unwrap_or(255);
4989 let g = u32::from_str_radix(&hex[3..5], 16).unwrap_or(255);
4990 let b = u32::from_str_radix(&hex[5..7], 16).unwrap_or(255);
4991 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
4992 return Ok(Value::Unit);
4993 },
4994
4995 "สีดินสอ"
4997 | "set_color"
4998 | "gfx_color"
4999 | "color"
5000 | "设色"
5001 | "色設定"
5002 | "색설정"
5003 | "تنظیم_رنگ"
5004 | "عيّن_اللون"
5005 | "קבע_צבע"
5006 | "رنگ_مقرر_کرو" => {
5007 let r = self.arg_num(&args, 0, 255.0)? as u32;
5008 let g = self.arg_num(&args, 1, 255.0)? as u32;
5009 let b = self.arg_num(&args, 2, 255.0)? as u32;
5010 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
5011 return Ok(Value::Unit);
5012 },
5013
5014 "วาดสามเหลี่ยม"
5016 | "draw_triangle"
5017 | "gfx_triangle"
5018 | "triangle"
5019 | "画三角"
5020 | "三角形描画"
5021 | "삼각형그리기"
5022 | "رسم_مثلث"
5023 | "ارسم_مثلثا"
5024 | "צייר_משולש"
5025 | "مثلث_کھینچو" => {
5026 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
5027 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
5028 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
5029 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
5030 let x2 = self.arg_num(&args, 4, 0.0)? as f32;
5031 let y2 = self.arg_num(&args, 5, 0.0)? as f32;
5032 let mut gfx = self.gfx.borrow_mut();
5033 let color = gfx.color;
5034 #[cfg(not(target_arch = "wasm32"))]
5035 {
5036 let w = gfx.width;
5037 let h = gfx.height;
5038 fill_triangle(&mut gfx.buffer, w, h, color, x0, y0, x1, y1, x2, y2);
5039 }
5040 #[cfg(target_arch = "wasm32")]
5041 gfx.depth_queue
5042 .push_triangle(0.0, color, x0, y0, x1, y1, x2, y2);
5043 return Ok(Value::Unit);
5044 },
5045
5046 "วาดเส้น"
5048 | "draw_line"
5049 | "gfx_line"
5050 | "line"
5051 | "画线"
5052 | "線描く"
5053 | "선그리기"
5054 | "رسم_خط"
5055 | "ارسم_خط"
5056 | "צייר_קו"
5057 | "لکیر_کھینچو" => {
5058 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
5059 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
5060 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
5061 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
5062 let mut gfx = self.gfx.borrow_mut();
5063 let color = gfx.color;
5064 #[cfg(not(target_arch = "wasm32"))]
5065 {
5066 let w = gfx.width;
5067 let h = gfx.height;
5068 let aa = gfx.antialias;
5069 let add = gfx.blend == 1;
5070 if aa {
5071 crate::gfx::raster::draw_line_aa(
5072 &mut gfx.buffer,
5073 w,
5074 h,
5075 color,
5076 add,
5077 x0,
5078 y0,
5079 x1,
5080 y1,
5081 );
5082 } else {
5083 draw_line(&mut gfx.buffer, w, h, color, x0, y0, x1, y1);
5084 }
5085 }
5086 #[cfg(target_arch = "wasm32")]
5087 gfx.depth_queue.push_line(0.0, color, x0, y0, x1, y1);
5088 return Ok(Value::Unit);
5089 },
5090
5091 "วาดจุด"
5093 | "draw_pixel"
5094 | "gfx_pixel"
5095 | "pixel"
5096 | "画点"
5097 | "点描く"
5098 | "점그리기"
5099 | "رسم_نقطه"
5100 | "ارسم_نقطة"
5101 | "צייר_פיקסל"
5102 | "پکسل_کھینچو" => {
5103 let px = self.arg_num(&args, 0, 0.0)? as i32;
5104 let py = self.arg_num(&args, 1, 0.0)? as i32;
5105 #[cfg(not(target_arch = "wasm32"))]
5106 {
5107 let mut gfx = self.gfx.borrow_mut();
5108 let color = gfx.color;
5109 let w = gfx.width;
5110 let h = gfx.height;
5111 if px >= 0 && py >= 0 && (px as usize) < w && (py as usize) < h {
5112 gfx.buffer[py as usize * w + px as usize] = color;
5113 }
5114 }
5115 #[cfg(target_arch = "wasm32")]
5116 {
5117 let mut gfx = self.gfx.borrow_mut();
5119 let color = gfx.color;
5120 let x = px as f32;
5121 let y = py as f32;
5122 gfx.depth_queue
5123 .push_triangle(0.0, color, x, y, x + 1.0, y, x + 1.0, y + 1.0);
5124 gfx.depth_queue
5125 .push_triangle(0.0, color, x, y, x + 1.0, y + 1.0, x, y + 1.0);
5126 }
5127 return Ok(Value::Unit);
5128 },
5129
5130 "แสดงผล" | "present" | "gfx_present" | "show" | "显" | "呈现" | "表示" | "표시"
5132 | "نمایش" | "اعرض" | "הצג" | "دکھاؤ" => {
5133 #[cfg(not(target_arch = "wasm32"))]
5146 let pre_update_mouse_down = self
5147 .gfx
5148 .borrow()
5149 .window
5150 .as_ref()
5151 .map(|w| w.get_mouse_down(minifb::MouseButton::Left))
5152 .unwrap_or(false);
5153 #[cfg(not(target_arch = "wasm32"))]
5154 {
5155 ling_fps_tick();
5156 ling_phase_frame();
5157 {
5159 let mut gfx = self.gfx.borrow_mut();
5160 if !gfx.depth_queue.is_empty() {
5161 let w = gfx.width;
5162 let h = gfx.height;
5163 let dt = gfx.depth_test;
5164 let reset_z = gfx.zbuf_needs_clear;
5165 let (bm, ba) = (gfx.blend, gfx.alpha);
5166 let aa = gfx.antialias;
5167 let queue = std::mem::take(&mut gfx.depth_queue);
5168 {
5169 let g = &mut *gfx;
5170 let z = if dt { Some(&mut g.depth_buf) } else { None };
5171 queue.flush(&mut g.buffer, z, reset_z, w, h, aa);
5172 }
5173 gfx.depth_queue.set_state(bm, ba);
5174 gfx.zbuf_needs_clear = false;
5175 }
5176 let _t = std::time::Instant::now();
5177 if !gfx.post_done {
5178 gfx.toon_post_process();
5179 }
5180 gfx.post_done = false;
5181 ling_phase_add(phase::TOON, _t.elapsed().as_nanos());
5182 let w = gfx.width;
5183 let h = gfx.height;
5184 let g = &mut *gfx;
5185 if g.frame_blur > 0.0 {
5186 let a = (g.frame_blur.clamp(0.0, 0.995) * 256.0) as u32;
5191 if g.prev_frame.len() != g.buffer.len() {
5192 g.prev_frame = g.buffer.clone();
5193 }
5194 for (dst, prev) in g.buffer.iter_mut().zip(g.prev_frame.iter_mut()) {
5195 let c = *dst;
5196 let pv = *prev;
5197 let pr = (((pv >> 16) & 0xFF) * a) >> 8;
5198 let pg = (((pv >> 8) & 0xFF) * a) >> 8;
5199 let pb = ((pv & 0xFF) * a) >> 8;
5200 let cr = (c >> 16) & 0xFF;
5201 let cg = (c >> 8) & 0xFF;
5202 let cb = c & 0xFF;
5203 let outp = (cr.max(pr) << 16) | (cg.max(pg) << 8) | cb.max(pb);
5204 *dst = outp;
5205 *prev = outp;
5206 }
5207 }
5208 if let Some(win) = g.window.as_mut() {
5209 let _b = std::time::Instant::now();
5210 win.update_with_buffer(&g.buffer, w, h)
5211 .map_err(|e| EvalErr::from(format!("present error: {e}")))?;
5212 ling_phase_add(phase::BLIT, _b.elapsed().as_nanos());
5213 }
5214 }
5215 let mouse_pos = {
5217 let gfx = self.gfx.borrow();
5218 gfx.window
5219 .as_ref()
5220 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
5221 };
5222 let mut gfx = self.gfx.borrow_mut();
5223 if gfx.mouse_captured {
5224 let w = gfx.width as f32;
5225 let h = gfx.height as f32;
5226 if let Some((mx, my)) = mouse_pos {
5227 if gfx.last_mx.is_nan() {
5228 gfx.mouse_dx = 0.0;
5229 gfx.mouse_dy = 0.0;
5230 gfx.last_mx = mx;
5231 gfx.last_my = my;
5232 } else {
5233 gfx.mouse_dx = mx - gfx.last_mx;
5234 gfx.mouse_dy = my - gfx.last_my;
5235 let margin = 6.0;
5238 let (mut nx, mut ny, mut warp) = (mx, my, false);
5239 if mx < margin {
5240 nx = w - margin - 2.0;
5241 warp = true;
5242 } else if mx > w - margin {
5243 nx = margin + 2.0;
5244 warp = true;
5245 }
5246 if my < margin {
5247 ny = h - margin - 2.0;
5248 warp = true;
5249 } else if my > h - margin {
5250 ny = margin + 2.0;
5251 warp = true;
5252 }
5253 if warp {
5254 #[cfg(windows)]
5255 unsafe {
5256 #[repr(C)]
5257 struct RECT {
5258 left: i32,
5259 top: i32,
5260 right: i32,
5261 bottom: i32,
5262 }
5263 extern "system" {
5264 fn GetForegroundWindow() -> isize;
5265 fn GetWindowRect(hwnd: isize, lpRect: *mut RECT)
5266 -> i32;
5267 fn SetCursorPos(x: i32, y: i32) -> i32;
5268 }
5269 let hwnd = GetForegroundWindow();
5270 let mut rect =
5271 RECT { left: 0, top: 0, right: 0, bottom: 0 };
5272 if GetWindowRect(hwnd, &mut rect) != 0 {
5273 SetCursorPos(
5274 rect.left + nx as i32,
5275 rect.top + ny as i32,
5276 );
5277 }
5278 }
5279 gfx.last_mx = nx;
5280 gfx.last_my = ny;
5281 } else {
5282 gfx.last_mx = mx;
5283 gfx.last_my = my;
5284 }
5285 }
5286 } else {
5287 gfx.mouse_dx = 0.0;
5288 gfx.mouse_dy = 0.0;
5289 }
5290 } else if let Some((mx, my)) = mouse_pos {
5291 if gfx.last_mx.is_nan() {
5292 gfx.mouse_dx = 0.0;
5293 gfx.mouse_dy = 0.0;
5294 } else {
5295 gfx.mouse_dx = mx - gfx.last_mx;
5296 gfx.mouse_dy = my - gfx.last_my;
5297 }
5298 gfx.last_mx = mx;
5299 gfx.last_my = my;
5300 } else {
5301 gfx.mouse_dx = 0.0;
5302 gfx.mouse_dy = 0.0;
5303 }
5304
5305 let is_active = gfx.window.as_mut().map(|w| w.is_active()).unwrap_or(true);
5313 if is_active && !gfx.was_active {
5314 gfx.focus_grace_frames = 5;
5315 #[cfg(windows)]
5318 if gfx.topmost_window {
5319 if let Some(w) = gfx.window.as_ref() {
5320 set_window_topmost(w.get_window_handle() as isize, true);
5321 }
5322 }
5323 } else if !is_active && gfx.was_active {
5324 #[cfg(windows)]
5330 if gfx.topmost_window {
5331 if let Some(w) = gfx.window.as_ref() {
5332 set_window_topmost(w.get_window_handle() as isize, false);
5333 }
5334 }
5335 }
5336 gfx.was_active = is_active;
5337 if gfx.focus_grace_frames > 0 {
5338 gfx.focus_grace_frames -= 1;
5339 }
5340 }
5341 #[cfg(target_arch = "wasm32")]
5342 {
5343 {
5344 let mut gfx = self.gfx.borrow_mut();
5348 let w = gfx.width;
5349 let h = gfx.height;
5350 if gfx.buffer.len() != w * h {
5351 gfx.buffer.resize(w * h, 0);
5352 }
5353 if !gfx.depth_queue.is_empty() {
5354 let dt = gfx.depth_test;
5355 let reset_z = gfx.zbuf_needs_clear;
5356 let aa = gfx.antialias;
5357 let queue = std::mem::take(&mut gfx.depth_queue);
5358 {
5359 let g = &mut *gfx;
5360 let z = if dt { Some(&mut g.depth_buf) } else { None };
5361 queue.flush(&mut g.buffer, z, reset_z, w, h, aa);
5362 }
5363 gfx.zbuf_needs_clear = false;
5364 }
5365 if !gfx.post_done {
5366 gfx.toon_post_process();
5367 }
5368 gfx.post_done = false;
5369 crate::gfx::webgl::blit_rgb(&gfx.buffer, w, h);
5370 }
5371 self.wasm_pace_frame();
5372 }
5373 #[cfg(not(target_arch = "wasm32"))]
5377 {
5378 self.mouse_was_down = pre_update_mouse_down;
5379 }
5380 self.frame_num += 1;
5382 return Ok(Value::Unit);
5383 },
5384
5385 "เปิดหน้าต่างเต็มจอ"
5387 | "open_fullscreen"
5388 | "fullscreen"
5389 | "全屏"
5390 | "全画面"
5391 | "전체화면"
5392 | "باز_کردن_تمامصفحه"
5393 | "افتح_ملء_الشاشة"
5394 | "פתח_מסך_מלא"
5395 | "فل_سکرین_کھولو" => {
5396 #[cfg(target_arch = "wasm32")]
5399 let (default_w, default_h) = {
5400 let (cw, ch) = crate::gfx::webgl::canvas_size();
5401 (cw as f64, ch as f64)
5402 };
5403 #[cfg(all(not(target_arch = "wasm32"), windows))]
5405 let (default_w, default_h) = unsafe {
5406 extern "system" {
5407 fn GetSystemMetrics(nIndex: i32) -> i32;
5408 }
5409 (GetSystemMetrics(0) as f64, GetSystemMetrics(1) as f64)
5410 };
5411 #[cfg(all(not(target_arch = "wasm32"), not(windows)))]
5412 let (default_w, default_h) = native_screen_size();
5413
5414 let w = args
5415 .get(1)
5416 .map(|v| self.to_number(v).unwrap_or(default_w) as usize)
5417 .unwrap_or(default_w as usize);
5418 let h = args
5419 .get(2)
5420 .map(|v| self.to_number(v).unwrap_or(default_h) as usize)
5421 .unwrap_or(default_h as usize);
5422 #[cfg(not(target_arch = "wasm32"))]
5423 {
5424 let title = args
5425 .first()
5426 .map(|v| v.to_string())
5427 .unwrap_or_else(|| "Ling".into());
5428 let mut gfx = self.gfx.borrow_mut();
5429 let mut win = minifb::Window::new(
5430 &title,
5431 w,
5432 h,
5433 minifb::WindowOptions {
5434 borderless: true,
5435 title: false,
5436 resize: false,
5437 topmost: true,
5438 scale: minifb::Scale::X1,
5439 ..Default::default()
5440 },
5441 )
5442 .map_err(|e| EvalErr::from(format!("cannot open fullscreen: {e}")))?;
5443 apply_frame_pacing(&mut win, gfx.vsync);
5444 #[cfg(windows)]
5446 let hwnd = win.get_window_handle() as isize;
5447 gfx.buffer = vec![0u32; w * h];
5448 gfx.width = w;
5449 gfx.height = h;
5450 gfx.window = Some(win);
5451 gfx.topmost_window = true;
5452 #[cfg(windows)]
5453 {
5454 gfx.hwnd = hwnd;
5455 }
5456 gfx.sync_projection();
5457 #[cfg(windows)]
5459 make_borderless_fullscreen(hwnd, w as i32, h as i32);
5460 #[cfg(windows)]
5461 force_window_focus(hwnd);
5462 }
5463 #[cfg(target_arch = "wasm32")]
5464 {
5465 let mut gfx = self.gfx.borrow_mut();
5466 gfx.width = w;
5467 gfx.height = h;
5468 gfx.buffer.resize(w * h, 0); gfx.sync_projection();
5470 crate::gfx::webgl::resize(w as u32, h as u32);
5471 }
5472 return Ok(Value::Unit);
5473 },
5474
5475 "get_width"
5477 | "ความกว้าง"
5478 | "宽"
5479 | "幅取得"
5480 | "너비"
5481 | "عرض"
5482 | "العرض"
5483 | "רוחב"
5484 | "چوڑائی"
5485 | "obtenir_largeur"
5486 | "breite_abrufen"
5487 | "получить_ширину" => {
5488 return Ok(Value::Number(self.gfx.borrow().width as f64));
5489 },
5490 "get_height"
5491 | "ความสูง"
5492 | "高"
5493 | "高取得"
5494 | "높이"
5495 | "ارتفاع"
5496 | "الارتفاع"
5497 | "גובה"
5498 | "اونچائی"
5499 | "obtenir_hauteur"
5500 | "höhe_abrufen"
5501 | "получить_высоту" => {
5502 return Ok(Value::Number(self.gfx.borrow().height as f64));
5503 },
5504
5505 "monitor_width"
5508 | "screen_width"
5509 | "屏宽"
5510 | "画面幅"
5511 | "화면너비"
5512 | "ความกว้างจอ"
5513 | "عرض_مانیتور"
5514 | "عرض_الشاشة"
5515 | "רוחב_צג"
5516 | "مانیٹر_چوڑائی"
5517 | "largeur_moniteur"
5518 | "monitor_breite"
5519 | "ширина_монитора" => {
5520 return Ok(Value::Number(monitor_info().0 as f64));
5521 },
5522 "monitor_height"
5524 | "screen_height"
5525 | "屏高"
5526 | "画面高"
5527 | "화면높이"
5528 | "ความสูงจอ"
5529 | "ارتفاع_مانیتور"
5530 | "ارتفاع_الشاشة"
5531 | "גובה_צג"
5532 | "مانیٹر_اونچائی"
5533 | "hauteur_moniteur"
5534 | "monitor_höhe"
5535 | "высота_монитора" => {
5536 return Ok(Value::Number(monitor_info().1 as f64));
5537 },
5538 "monitor_refresh"
5540 | "monitor_hz"
5541 | "monitor_fps"
5542 | "refresh_rate"
5543 | "刷新率"
5544 | "リフレッシュレート"
5545 | "주사율"
5546 | "อัตรารีเฟรช"
5547 | "نرخ_بروزرسانی_مانیتور"
5548 | "معدل_تحديث_الشاشة"
5549 | "קצב_רענון_צג"
5550 | "مانیٹر_ریفریش_ریٹ"
5551 | "fréquence_moniteur"
5552 | "monitor_bildwiederholrate"
5553 | "частота_монитора" => {
5554 return Ok(Value::Number(monitor_info().2 as f64));
5555 },
5556 "monitor_info"
5558 | "screen_info"
5559 | "屏幕信息"
5560 | "画面情報"
5561 | "화면정보"
5562 | "ข้อมูลจอ"
5563 | "اطلاعات_مانیتور"
5564 | "معلومات_الشاشة"
5565 | "מידע_צג"
5566 | "مانیٹر_معلومات"
5567 | "info_moniteur"
5568 | "bildschirminfo"
5569 | "инфо_монитора" => {
5570 let (w, h, hz) = monitor_info();
5571 return Ok(Value::List(Rc::new(vec![
5572 Value::Number(w as f64),
5573 Value::Number(h as f64),
5574 Value::Number(hz as f64),
5575 ])));
5576 },
5577 "set_fps"
5579 | "set_target_fps"
5580 | "target_fps"
5581 | "设帧率"
5582 | "フレームレート設定"
5583 | "프레임설정"
5584 | "ตั้งเฟรมเรต"
5585 | "تنظیم_نرخ_فریم"
5586 | "عيّن_معدل_الإطارات"
5587 | "קבע_קצב_פריימים"
5588 | "ایف_پی_ایس_مقرر_کرو"
5589 | "définir_fps"
5590 | "fps_setzen"
5591 | "задать_fps" => {
5592 #[cfg(not(target_arch = "wasm32"))]
5593 {
5594 let fps = self.arg_num(&args, 0, 60.0)?.max(1.0) as usize;
5595 let mut gfx = self.gfx.borrow_mut();
5596 if let Some(win) = gfx.window.as_mut() {
5597 win.set_target_fps(fps);
5598 }
5599 }
5600 #[cfg(target_arch = "wasm32")]
5601 {
5602 self.wasm_target_fps = self.arg_num(&args, 0, 60.0)?.max(1.0);
5603 self.wasm_next_present_ms = 0.0;
5604 }
5605 return Ok(Value::Unit);
5606 },
5607
5608 "set_vsync"
5612 | "vsync"
5613 | "垂直同步"
5614 | "垂直同期"
5615 | "수직동기"
5616 | "ตั้งวีซิงก์"
5617 | "تنظیم_ویسینک"
5618 | "عيّن_تزامن_رأسي"
5619 | "קבע_וי_סינק"
5620 | "وی_سینک_مقرر_کرو"
5621 | "définir_vsync"
5622 | "vsync_setzen"
5623 | "задать_vsync" => {
5624 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
5625 #[cfg(not(target_arch = "wasm32"))]
5626 {
5627 let mut gfx = self.gfx.borrow_mut();
5628 gfx.vsync = on;
5629 if let Some(win) = gfx.window.as_mut() {
5630 apply_frame_pacing(win, on);
5631 }
5632 }
5633 #[cfg(target_arch = "wasm32")]
5634 {
5635 self.wasm_target_fps = if on { monitor_info().2 as f64 } else { 240.0 };
5636 self.wasm_next_present_ms = 0.0;
5637 }
5638 return Ok(Value::Unit);
5639 },
5640
5641 "หน้าต่างเปิดอยู่"
5643 | "window_is_open"
5644 | "gfx_is_open"
5645 | "is_open"
5646 | "窗开"
5647 | "開いている"
5648 | "창열림"
5649 | "پنجره_باز_است"
5650 | "النافذة_مفتوحة"
5651 | "החלון_פתוח"
5652 | "ونڈو_کھلی_ہے" => {
5653 #[cfg(not(target_arch = "wasm32"))]
5654 {
5655 let gfx = self.gfx.borrow();
5656 if gfx.want_quit {
5657 return Ok(Value::Bool(false));
5658 }
5659 #[cfg(windows)]
5665 let escape_down = if gfx.topmost_window {
5666 window_is_foreground(gfx.hwnd) && os_key_down(0x1B) } else {
5668 gfx.window
5669 .as_ref()
5670 .map(|w| w.is_key_down(minifb::Key::Escape))
5671 .unwrap_or(false)
5672 };
5673 #[cfg(not(windows))]
5674 let escape_down = gfx
5675 .window
5676 .as_ref()
5677 .map(|w| w.is_key_down(minifb::Key::Escape))
5678 .unwrap_or(false);
5679 let open =
5680 gfx.window.as_ref().map(|w| w.is_open()).unwrap_or(false) && !escape_down;
5681 return Ok(Value::Bool(open));
5682 }
5683 #[cfg(target_arch = "wasm32")]
5684 return Ok(Value::Bool(true));
5685 },
5686
5687 "quit" | "exit_game" | "close_window" => {
5690 #[cfg(not(target_arch = "wasm32"))]
5691 {
5692 self.gfx.borrow_mut().want_quit = true;
5693 }
5694 return Ok(Value::Unit);
5695 },
5696
5697 "key_down"
5699 | "กดค้าง"
5700 | "按键"
5701 | "キー押す"
5702 | "키누름"
5703 | "کلید_فشرده"
5704 | "المفتاح_مضغوط"
5705 | "מקש_לחוץ"
5706 | "بٹن_دبا_ہوا"
5707 | "touche_enfoncée"
5708 | "taste_gedrückt"
5709 | "клавиша_нажата" => {
5710 #[cfg(not(target_arch = "wasm32"))]
5711 {
5712 let name = self.arg_str(&args, 0, "");
5713 let mut gfx = self.gfx.borrow_mut();
5714 #[cfg(windows)]
5722 if gfx.topmost_window {
5723 if !window_is_foreground(gfx.hwnd) {
5724 return Ok(Value::Bool(false));
5725 }
5726 return Ok(Value::Bool(
5727 str_to_vk(&name).map(os_key_down).unwrap_or(false),
5728 ));
5729 }
5730 if gfx.input_suppressed() {
5731 return Ok(Value::Bool(false));
5732 }
5733 let down = gfx
5734 .window
5735 .as_ref()
5736 .and_then(|w| str_to_minifb_key(&name).map(|k| w.is_key_down(k)))
5737 .unwrap_or(false);
5738 return Ok(Value::Bool(down));
5739 }
5740 #[cfg(target_arch = "wasm32")]
5741 {
5742 let name = self.arg_str(&args, 0, "");
5743 return Ok(Value::Bool(crate::gfx::wasm_is_key_down(&name)));
5744 }
5745 },
5746
5747 "key_pressed"
5749 | "กดปุ่ม"
5750 | "键按"
5751 | "キー押した"
5752 | "키눌림"
5753 | "فشردن_کلید"
5754 | "ضغط_المفتاح"
5755 | "לחיצת_מקש"
5756 | "بٹن_دبانا"
5757 | "touche_appuyée"
5758 | "taste_getippt"
5759 | "клавиша_нажатие" => {
5760 #[cfg(not(target_arch = "wasm32"))]
5761 {
5762 let name = self.arg_str(&args, 0, "");
5763 let pressed = {
5764 let mut gfx = self.gfx.borrow_mut();
5765 #[cfg(windows)]
5766 let topmost = gfx.topmost_window;
5767 #[cfg(not(windows))]
5768 let topmost = false;
5769 if topmost {
5770 #[cfg(windows)]
5771 {
5772 if !window_is_foreground(gfx.hwnd) {
5773 false
5774 } else {
5775 match str_to_vk(&name) {
5776 Some(vk) => {
5777 let idx = (vk as usize) & 0xFF;
5778 let down = os_key_down(vk);
5779 let was = gfx.raw_keys_prev[idx];
5780 gfx.raw_keys_prev[idx] = down;
5781 down && !was
5782 },
5783 None => false,
5784 }
5785 }
5786 }
5787 #[cfg(not(windows))]
5788 {
5789 false
5790 }
5791 } else if gfx.input_suppressed() {
5792 false
5793 } else {
5794 gfx.window
5795 .as_ref()
5796 .and_then(|w| {
5797 str_to_minifb_key(&name)
5798 .map(|k| w.is_key_pressed(k, minifb::KeyRepeat::No))
5799 })
5800 .unwrap_or(false)
5801 }
5802 };
5803 let pressed =
5805 pressed || ((name == "enter" || name == "return") && gamepad::start_edge());
5806 return Ok(Value::Bool(pressed));
5807 }
5808 #[cfg(target_arch = "wasm32")]
5809 {
5810 let name = self.arg_str(&args, 0, "");
5811 let pressed = crate::gfx::wasm_is_key_pressed(&name);
5812 return Ok(Value::Bool(pressed));
5813 }
5814 },
5815
5816 "mouse_dx"
5818 | "เมาส์X"
5819 | "鼠ΔX"
5820 | "マウスΔX"
5821 | "마우스ΔX"
5822 | "دلتا_ماوس_ایکس"
5823 | "فارق_الفأرة_س"
5824 | "דלתא_עכבר_X"
5825 | "ماؤس_ڈیلٹا_ایکس"
5826 | "souris_dx"
5827 | "maus_dx"
5828 | "мышь_dx" => {
5829 #[cfg(not(target_arch = "wasm32"))]
5830 return Ok(Value::Number(self.gfx.borrow().mouse_dx as f64));
5831 #[cfg(target_arch = "wasm32")]
5832 return Ok(Value::Number(crate::gfx::wasm_mouse_dx() as f64));
5833 },
5834 #[cfg(not(target_arch = "wasm32"))]
5836 "mouse_scroll"
5837 | "ล้อเมาส์"
5838 | "滚轮"
5839 | "ホイール"
5840 | "스크롤"
5841 | "غلتک_ماوس"
5842 | "عجلة_الفأرة"
5843 | "גלגלת_עכבר"
5844 | "ماؤس_اسکرول" => {
5845 let gfx = self.gfx.borrow();
5846 let s = gfx
5847 .window
5848 .as_ref()
5849 .and_then(|w| w.get_scroll_wheel())
5850 .map(|(_, y)| y as f64)
5851 .unwrap_or(0.0);
5852 return Ok(Value::Number(s));
5853 },
5854 #[cfg(target_arch = "wasm32")]
5855 "mouse_scroll"
5856 | "ล้อเมาส์"
5857 | "滚轮"
5858 | "ホイール"
5859 | "스크롤"
5860 | "غلتک_ماوس"
5861 | "عجلة_الفأرة"
5862 | "גלגלת_עכבר"
5863 | "ماؤس_اسکرول" => {
5864 return Ok(Value::Number(0.0));
5865 },
5866 "mouse_dy"
5867 | "เมาส์Y"
5868 | "鼠ΔY"
5869 | "マウスΔY"
5870 | "마우스ΔY"
5871 | "دلتا_ماوس_ایگرگ"
5872 | "فارق_الفأرة_ص"
5873 | "דלתא_עכבר_Y"
5874 | "ماؤس_ڈیلٹا_وائی"
5875 | "souris_dy"
5876 | "maus_dy"
5877 | "мышь_dy" => {
5878 #[cfg(not(target_arch = "wasm32"))]
5879 return Ok(Value::Number(self.gfx.borrow().mouse_dy as f64));
5880 #[cfg(target_arch = "wasm32")]
5881 return Ok(Value::Number(crate::gfx::wasm_mouse_dy() as f64));
5882 },
5883
5884 "pad_poll"
5887 | "手柄轮询"
5888 | "パッド更新"
5889 | "패드폴링"
5890 | "อัปเดตแพด"
5891 | "بررسی_دسته"
5892 | "استطلع_اليد"
5893 | "בדוק_בקר"
5894 | "گیم_پیڈ_پول"
5895 | "interroger_manette"
5896 | "gamepad_abfragen"
5897 | "опросить_геймпад" => {
5898 #[cfg(not(target_arch = "wasm32"))]
5899 return Ok(Value::Number(self.pad_poll() as f64));
5900 #[cfg(target_arch = "wasm32")]
5901 return Ok(Value::Number(input_web::poll() as f64));
5902 },
5903 "pad_count"
5905 | "手柄数"
5906 | "パッド数"
5907 | "패드수"
5908 | "จำนวนแพด"
5909 | "تعداد_دسته"
5910 | "عدد_أيدي_التحكم"
5911 | "מספר_בקרים"
5912 | "گیم_پیڈ_تعداد"
5913 | "nombre_manettes"
5914 | "gamepad_anzahl"
5915 | "число_геймпадов" => {
5916 #[cfg(not(target_arch = "wasm32"))]
5917 {
5918 let inp = self.input.borrow();
5919 let n = inp.as_ref().map_or(0, |s| s.sensorium.devices.count());
5920 return Ok(Value::Number(n as f64));
5921 }
5922 #[cfg(target_arch = "wasm32")]
5923 return Ok(Value::Number(input_web::count() as f64));
5924 },
5925 "pad_connected"
5927 | "手柄连接"
5928 | "パッド接続"
5929 | "패드연결"
5930 | "แพดเชื่อม"
5931 | "دسته_متصل"
5932 | "يد_التحكم_متصلة"
5933 | "בקר_מחובר"
5934 | "گیم_پیڈ_منسلک"
5935 | "manette_connectée"
5936 | "gamepad_verbunden"
5937 | "геймпад_подключён" => {
5938 #[cfg(not(target_arch = "wasm32"))]
5939 {
5940 let i = self.arg_num(&args, 0, 0.0)? as usize;
5941 let inp = self.input.borrow();
5942 let c = inp
5943 .as_ref()
5944 .is_some_and(|s| s.sensorium.devices.for_player(i as u8).is_some());
5945 return Ok(Value::Bool(c));
5946 }
5947 #[cfg(target_arch = "wasm32")]
5948 {
5949 let i = self.arg_num(&args, 0, 0.0)? as usize;
5950 return Ok(Value::Bool(input_web::is_connected(i)));
5951 }
5952 },
5953 "pad_button"
5955 | "手柄按键"
5956 | "パッドボタン"
5957 | "패드버튼"
5958 | "ปุ่มแพด"
5959 | "دکمه_دسته"
5960 | "زر_اليد"
5961 | "כפתור_בקר"
5962 | "گیم_پیڈ_بٹن"
5963 | "bouton_manette"
5964 | "gamepad_taste"
5965 | "кнопка_геймпада" => {
5966 #[cfg(not(target_arch = "wasm32"))]
5967 {
5968 let i = self.arg_num(&args, 0, 0.0)? as usize;
5969 let name = self.arg_str(&args, 1, "");
5970 let down = parse_pad_button(&name)
5971 .is_some_and(|b| self.with_pad(i, false, |p| p.is_down(b)));
5972 return Ok(Value::Bool(down));
5973 }
5974 #[cfg(target_arch = "wasm32")]
5975 {
5976 let i = self.arg_num(&args, 0, 0.0)? as usize;
5977 let name = self.arg_str(&args, 1, "");
5978 return Ok(Value::Bool(input_web::button_down(i, &name)));
5979 }
5980 },
5981 "pad_pressed"
5984 | "手柄按下"
5985 | "パッド押下"
5986 | "패드눌림"
5987 | "แพดกด"
5988 | "دکمه_دسته_فشرده"
5989 | "زر_اليد_مضغوط"
5990 | "כפתור_בקר_לחוץ"
5991 | "گیم_پیڈ_دبایا"
5992 | "manette_appuyée"
5993 | "gamepad_gedrückt"
5994 | "геймпад_нажат" => {
5995 #[cfg(not(target_arch = "wasm32"))]
5996 {
5997 let i = self.arg_num(&args, 0, 0.0)? as usize;
5998 let name = self.arg_str(&args, 1, "");
5999 let p = parse_pad_button(&name)
6000 .is_some_and(|b| self.with_pad(i, false, |g| g.just_pressed(b)));
6001 return Ok(Value::Bool(p));
6002 }
6003 #[cfg(target_arch = "wasm32")]
6004 {
6005 let i = self.arg_num(&args, 0, 0.0)? as usize;
6006 let name = self.arg_str(&args, 1, "");
6007 return Ok(Value::Bool(input_web::button_down(i, &name)));
6008 }
6009 },
6010 "pad_lx"
6012 | "手柄左X"
6013 | "パッド左X"
6014 | "패드왼X"
6015 | "แพดซ้ายX"
6016 | "آنالوگ_چپ_ایکس"
6017 | "عصا_اليسرى_س"
6018 | "ג'ויסטיק_שמאל_X"
6019 | "بائیں_اسٹک_ایکس"
6020 | "manette_axe_gauche_x"
6021 | "gamepad_lx"
6022 | "геймпад_ось_лево_x" => {
6023 #[cfg(not(target_arch = "wasm32"))]
6024 {
6025 let i = self.arg_num(&args, 0, 0.0)? as usize;
6026 return Ok(Value::Number(
6027 self.with_pad(i, 0.0, |p| p.left_stick.x as f64),
6028 ));
6029 }
6030 #[cfg(target_arch = "wasm32")]
6031 {
6032 let i = self.arg_num(&args, 0, 0.0)? as usize;
6033 return Ok(Value::Number(input_web::axis_lx(i) as f64));
6034 }
6035 },
6036 "pad_ly"
6037 | "手柄左Y"
6038 | "パッド左Y"
6039 | "패드왼Y"
6040 | "แพดซ้ายY"
6041 | "آنالوگ_چپ_ایگرگ"
6042 | "عصا_اليسرى_ص"
6043 | "ג'ויסטיק_שמאל_Y"
6044 | "بائیں_اسٹک_وائی"
6045 | "manette_axe_gauche_y"
6046 | "gamepad_ly"
6047 | "геймпад_ось_лево_y" => {
6048 #[cfg(not(target_arch = "wasm32"))]
6049 {
6050 let i = self.arg_num(&args, 0, 0.0)? as usize;
6051 return Ok(Value::Number(
6052 self.with_pad(i, 0.0, |p| p.left_stick.y as f64),
6053 ));
6054 }
6055 #[cfg(target_arch = "wasm32")]
6056 {
6057 let i = self.arg_num(&args, 0, 0.0)? as usize;
6058 return Ok(Value::Number(input_web::axis_ly(i) as f64));
6059 }
6060 },
6061 "pad_rx"
6062 | "手柄右X"
6063 | "パッド右X"
6064 | "패드오X"
6065 | "แพดขวาX"
6066 | "آنالوگ_راست_ایکس"
6067 | "عصا_اليمنى_س"
6068 | "ג'ויסטיק_ימין_X"
6069 | "دائیں_اسٹک_ایکس"
6070 | "manette_axe_droit_x"
6071 | "gamepad_rx"
6072 | "геймпад_ось_право_x" => {
6073 #[cfg(not(target_arch = "wasm32"))]
6074 {
6075 let i = self.arg_num(&args, 0, 0.0)? as usize;
6076 return Ok(Value::Number(
6077 self.with_pad(i, 0.0, |p| p.right_stick.x as f64),
6078 ));
6079 }
6080 #[cfg(target_arch = "wasm32")]
6081 {
6082 let i = self.arg_num(&args, 0, 0.0)? as usize;
6083 return Ok(Value::Number(input_web::axis_rx(i) as f64));
6084 }
6085 },
6086 "pad_ry"
6087 | "手柄右Y"
6088 | "パッド右Y"
6089 | "패드오Y"
6090 | "แพดขวาY"
6091 | "آنالوگ_راست_ایگرگ"
6092 | "عصا_اليمنى_ص"
6093 | "ג'ויסטיק_ימין_Y"
6094 | "دائیں_اسٹک_وائی"
6095 | "manette_axe_droit_y"
6096 | "gamepad_ry"
6097 | "геймпад_ось_право_y" => {
6098 #[cfg(not(target_arch = "wasm32"))]
6099 {
6100 let i = self.arg_num(&args, 0, 0.0)? as usize;
6101 return Ok(Value::Number(
6102 self.with_pad(i, 0.0, |p| p.right_stick.y as f64),
6103 ));
6104 }
6105 #[cfg(target_arch = "wasm32")]
6106 {
6107 let i = self.arg_num(&args, 0, 0.0)? as usize;
6108 return Ok(Value::Number(input_web::axis_ry(i) as f64));
6109 }
6110 },
6111 "pad_lt"
6113 | "手柄左扳机"
6114 | "パッド左トリガー"
6115 | "패드왼트리거"
6116 | "ไกแพดซ้าย"
6117 | "ماشه_چپ"
6118 | "زناد_اليسار"
6119 | "הדק_שמאל"
6120 | "بائیں_ٹریگر"
6121 | "manette_gâchette_gauche"
6122 | "gamepad_lt"
6123 | "геймпад_триггер_лево" => {
6124 #[cfg(not(target_arch = "wasm32"))]
6125 {
6126 let i = self.arg_num(&args, 0, 0.0)? as usize;
6127 return Ok(Value::Number(
6128 self.with_pad(i, 0.0, |p| p.left_trigger as f64),
6129 ));
6130 }
6131 #[cfg(target_arch = "wasm32")]
6132 {
6133 let i = self.arg_num(&args, 0, 0.0)? as usize;
6134 return Ok(Value::Number(input_web::trigger_lt(i) as f64));
6135 }
6136 },
6137 "pad_rt"
6138 | "手柄右扳机"
6139 | "パッド右トリガー"
6140 | "패드오트리거"
6141 | "ไกแพดขวา"
6142 | "ماشه_راست"
6143 | "زناد_اليمين"
6144 | "הדק_ימין"
6145 | "دائیں_ٹریگر"
6146 | "manette_gâchette_droite"
6147 | "gamepad_rt"
6148 | "геймпад_триггер_право" => {
6149 #[cfg(not(target_arch = "wasm32"))]
6150 {
6151 let i = self.arg_num(&args, 0, 0.0)? as usize;
6152 return Ok(Value::Number(
6153 self.with_pad(i, 0.0, |p| p.right_trigger as f64),
6154 ));
6155 }
6156 #[cfg(target_arch = "wasm32")]
6157 {
6158 let i = self.arg_num(&args, 0, 0.0)? as usize;
6159 return Ok(Value::Number(input_web::trigger_rt(i) as f64));
6160 }
6161 },
6162 "pad_rumble"
6164 | "手柄震动"
6165 | "パッド振動"
6166 | "패드진동"
6167 | "แพดสั่น"
6168 | "لرزش_دسته"
6169 | "اهتزاز_اليد"
6170 | "רטט_בקר"
6171 | "گیم_پیڈ_تھرتھراہٹ"
6172 | "vibration_manette"
6173 | "gamepad_vibration"
6174 | "вибрация_геймпада" => {
6175 #[cfg(not(target_arch = "wasm32"))]
6176 {
6177 use ling_input::backend::InputBackend;
6178 let i = self.arg_num(&args, 0, 0.0)? as usize;
6179 let lo = self.arg_num(&args, 1, 0.0)? as f32;
6180 let hi = self.arg_num(&args, 2, lo as f64)? as f32;
6181 let mut inp = self.input.borrow_mut();
6182 if let Some(s) = inp.as_mut() {
6183 if let Some(dev) = s.sensorium.devices.for_player(i as u8).map(|d| d.id) {
6184 s.backend.set_rumble(
6185 dev,
6186 ling_input::Rumble { low: lo, high: hi, ..Default::default() },
6187 );
6188 }
6189 }
6190 return Ok(Value::Unit);
6191 }
6192 #[cfg(target_arch = "wasm32")]
6193 return Ok(Value::Unit);
6194 },
6195
6196 "set_camera_pos"
6198 | "ตั้งตำแหน่งกล้อง"
6199 | "镜坐标"
6200 | "カメラ座標"
6201 | "카메라좌표"
6202 | "تنظیم_موقعیت_دوربین"
6203 | "عيّن_موضع_الكاميرا"
6204 | "קבע_מיקום_מצלמה"
6205 | "کیمرہ_مقام_مقرر_کرو"
6206 | "définir_position_caméra"
6207 | "kameraposition_setzen"
6208 | "задать_позицию_камеры" => {
6209 let x = self.arg_num(&args, 0, 0.0)? as f32;
6210 let y = self.arg_num(&args, 1, 0.0)? as f32;
6211 let z = self.arg_num(&args, 2, 0.0)? as f32;
6212 {
6213 let mut gfx = self.gfx.borrow_mut();
6214 gfx.camera.tx = x;
6215 gfx.camera.ty = y;
6216 gfx.camera.tz = z;
6217 }
6218 #[cfg(not(target_arch = "wasm32"))]
6219 if let Some(audio) = &self.audio {
6220 audio.set_listener_pos(x, y, z);
6221 }
6222 return Ok(Value::Unit);
6223 },
6224
6225 "move_camera" => {
6227 let dx = self.arg_num(&args, 0, 0.0)? as f32;
6228 let dy = self.arg_num(&args, 1, 0.0)? as f32;
6229 let dz = self.arg_num(&args, 2, 0.0)? as f32;
6230 let mut gfx = self.gfx.borrow_mut();
6231 gfx.camera.tx += dx;
6232 gfx.camera.ty += dy;
6233 gfx.camera.tz += dz;
6234 return Ok(Value::Unit);
6235 },
6236
6237 "set_zdist"
6239 | "ตั้งระยะห่าง"
6240 | "镜距"
6241 | "Z距離設定"
6242 | "Z거리설정"
6243 | "تنظیم_فاصله_عمق"
6244 | "عيّن_مسافة_العمق"
6245 | "קבע_מרחק_עומק"
6246 | "گہرائی_فاصلہ_مقرر_کرو"
6247 | "définir_distance_z"
6248 | "z_abstand_setzen"
6249 | "задать_дистанцию_z" => {
6250 let d = self.arg_num(&args, 0, 5.0)? as f32;
6251 self.gfx.borrow_mut().camera.zdist = d;
6252 return Ok(Value::Unit);
6253 },
6254
6255 "capture_mouse"
6257 | "จับเมาส์"
6258 | "捕鼠"
6259 | "マウス捕捉"
6260 | "마우스잡기"
6261 | "ضبط_ماوس"
6262 | "امسك_الفأرة"
6263 | "לכוד_עכבר"
6264 | "ماؤس_پکڑو"
6265 | "capturer_souris"
6266 | "maus_erfassen"
6267 | "захватить_мышь" => {
6268 #[cfg(not(target_arch = "wasm32"))]
6269 {
6270 let mut gfx = self.gfx.borrow_mut();
6271 gfx.mouse_captured = true;
6272 gfx.last_mx = f32::NAN;
6273 if let Some(win) = gfx.window.as_mut() {
6274 win.set_cursor_visibility(false);
6275 }
6276 }
6277 return Ok(Value::Unit);
6278 },
6279
6280 "release_mouse" => {
6282 #[cfg(not(target_arch = "wasm32"))]
6283 {
6284 let mut gfx = self.gfx.borrow_mut();
6285 gfx.mouse_captured = false;
6286 gfx.last_mx = f32::NAN;
6287 if let Some(win) = gfx.window.as_mut() {
6288 win.set_cursor_visibility(true);
6289 }
6290 #[cfg(windows)]
6291 unsafe {
6292 extern "system" {
6294 fn ClipCursor(lpRect: *const std::ffi::c_void) -> i32;
6295 }
6296 ClipCursor(std::ptr::null());
6297 }
6298 }
6299 return Ok(Value::Unit);
6300 },
6301
6302 "cursor_hide" => {
6308 #[cfg(not(target_arch = "wasm32"))]
6309 if let Some(win) = self.gfx.borrow_mut().window.as_mut() {
6310 win.set_cursor_visibility(false);
6311 }
6312 return Ok(Value::Unit);
6313 },
6314 "cursor_show" => {
6315 #[cfg(not(target_arch = "wasm32"))]
6316 if let Some(win) = self.gfx.borrow_mut().window.as_mut() {
6317 win.set_cursor_visibility(true);
6318 }
6319 return Ok(Value::Unit);
6320 },
6321
6322 "set_camera"
6329 | "ตั้งกล้อง"
6330 | "设镜"
6331 | "设置摄像机"
6332 | "カメラ設定"
6333 | "카메라설정"
6334 | "تنظیم_دوربین"
6335 | "عيّن_الكاميرا"
6336 | "קבע_מצלמה"
6337 | "کیمرہ_مقرر_کرو"
6338 | "définir_caméra"
6339 | "kamera_setzen"
6340 | "задать_камеру" => {
6341 let cry = self.arg_num(&args, 0, 1.0)? as f32;
6342 let sry = self.arg_num(&args, 1, 0.0)? as f32;
6343 let crx = self.arg_num(&args, 2, 1.0)? as f32;
6344 let srx = self.arg_num(&args, 3, 0.0)? as f32;
6345 let mut gfx = self.gfx.borrow_mut();
6346 gfx.camera.cry = cry;
6347 gfx.camera.sry = sry;
6348 gfx.camera.crx = crx;
6349 gfx.camera.srx = srx;
6350 return Ok(Value::Unit);
6351 },
6352
6353 "set_projection"
6356 | "ตั้งโปรเจกชัน"
6357 | "投影"
6358 | "投影設定"
6359 | "투영설정"
6360 | "تنظیم_فرافکنی"
6361 | "عيّن_الإسقاط"
6362 | "קבע_הטלה"
6363 | "پروجیکشن_مقرر_کرو"
6364 | "définir_projection"
6365 | "projektion_setzen"
6366 | "задать_проекцию" => {
6367 let cx = self.arg_num(&args, 0, 960.0)? as f32;
6368 let cy = self.arg_num(&args, 1, 540.0)? as f32;
6369 let focal = self.arg_num(&args, 2, 1080.0)? as f32;
6370 let zdist = self.arg_num(&args, 3, 5.0)? as f32;
6371 let mut gfx = self.gfx.borrow_mut();
6372 gfx.camera.cx = cx;
6373 gfx.camera.cy = cy;
6374 gfx.camera.focal = focal;
6375 gfx.camera.zdist = zdist;
6376 return Ok(Value::Unit);
6377 },
6378
6379 "gltf_load" => {
6381 let path = self.arg_str(&args, 0, "");
6382 match ling_physics::gltf::GltfModel::load(&path) {
6383 Ok(m) => {
6384 self.gltf_models.borrow_mut().push(m);
6385 let h = self.gltf_models.borrow().len() - 1;
6386 return Ok(Value::Number(h as f64));
6387 },
6388 Err(e) => {
6389 eprintln!("mesh_load failed ({path}): {e}");
6390 return Ok(Value::Number(-1.0));
6391 },
6392 }
6393 },
6394 "gltf_anim_count" => {
6396 let h = self.arg_num(&args, 0, -1.0)? as i64;
6397 let n = self
6398 .gltf_models
6399 .borrow()
6400 .get(h as usize)
6401 .map(|m| m.animations.len())
6402 .unwrap_or(0);
6403 return Ok(Value::Number(n as f64));
6404 },
6405 "gltf_anim_name" => {
6407 let h = self.arg_num(&args, 0, -1.0)? as i64;
6408 let i = self.arg_num(&args, 1, 0.0)? as usize;
6409 let s = self
6410 .gltf_models
6411 .borrow()
6412 .get(h as usize)
6413 .and_then(|m| m.animations.get(i))
6414 .map(|a| a.name.clone())
6415 .unwrap_or_default();
6416 return Ok(Value::Str(s));
6417 },
6418 "gltf_anim_dur" => {
6420 let h = self.arg_num(&args, 0, -1.0)? as i64;
6421 let i = self.arg_num(&args, 1, 0.0)? as usize;
6422 let d = self
6423 .gltf_models
6424 .borrow()
6425 .get(h as usize)
6426 .and_then(|m| m.animations.get(i))
6427 .map(|a| a.duration)
6428 .unwrap_or(0.0);
6429 return Ok(Value::Number(d as f64));
6430 },
6431 "gltf_tris" => {
6433 let h = self.arg_num(&args, 0, -1.0)? as i64;
6434 let n: usize = self
6435 .gltf_models
6436 .borrow()
6437 .get(h as usize)
6438 .map(|m| m.meshes.iter().map(|mm| mm.indices.len()).sum::<usize>() / 3)
6439 .unwrap_or(0);
6440 return Ok(Value::Number(n as f64));
6441 },
6442
6443 "gltf_joint_count" => {
6445 let h = self.arg_num(&args, 0, -1.0)? as i64;
6446 let n = self
6447 .gltf_models
6448 .borrow()
6449 .get(h as usize)
6450 .and_then(|m| m.skins.first())
6451 .map(|s| s.joints.len())
6452 .unwrap_or(0);
6453 return Ok(Value::Number(n as f64));
6454 },
6455 "gltf_joint_name" => {
6457 let h = self.arg_num(&args, 0, -1.0)? as i64;
6458 let j = self.arg_num(&args, 1, 0.0)? as usize;
6459 let models = self.gltf_models.borrow();
6460 let s = models
6461 .get(h as usize)
6462 .and_then(|m| {
6463 m.skins
6464 .first()
6465 .and_then(|sk| sk.joints.get(j))
6466 .and_then(|jt| m.nodes.get(jt.node_idx))
6467 .map(|n| n.name.clone())
6468 })
6469 .unwrap_or_default();
6470 return Ok(Value::Str(s));
6471 },
6472
6473 "gltf_draw" => {
6478 let hh = self.arg_num(&args, 0, -1.0)? as i64;
6479 let ox = self.arg_num(&args, 1, 0.0)? as f32;
6480 let oy = self.arg_num(&args, 2, 0.0)? as f32;
6481 let oz = self.arg_num(&args, 3, 0.0)? as f32;
6482 let scale = self.arg_num(&args, 4, 1.0)? as f32;
6483 let yaw = self.arg_num(&args, 5, 0.0)? as f32;
6484 let (sy, cyy) = yaw.sin_cos();
6485 let models = self.gltf_models.borrow();
6486 let model = match models.get(hh as usize) {
6487 Some(m) => m,
6488 None => return Ok(Value::Unit),
6489 };
6490 let mut gfx = self.gfx.borrow_mut();
6491 let cp = {
6492 let c = &gfx.camera;
6493 ling_gpu::CameraParams {
6494 cry: c.cry,
6495 sry: c.sry,
6496 crx: c.crx,
6497 srx: c.srx,
6498 cx: c.cx,
6499 cy: c.cy,
6500 focal: c.focal,
6501 zdist: c.zdist,
6502 tx: c.tx,
6503 ty: c.ty,
6504 tz: c.tz,
6505 }
6506 };
6507 let near = -gfx.camera.zdist + 0.02;
6508 let ambient = gfx.ambient;
6509 for mesh in &model.meshes {
6510 let nlow = mesh.name.to_lowercase();
6511 let base: u32 = if nlow.contains("hair") {
6512 0x7a4a28
6513 } else if nlow.contains("cloth") || nlow.contains("top") {
6514 0x4a86e0
6515 } else if nlow.contains("wing") {
6516 0xe6ecf5
6517 } else if nlow.contains("star") {
6518 0xffd24d
6519 } else {
6520 0xf2d6b8
6521 };
6522 let nv = mesh.verts.len();
6523 if nv == 0 {
6524 continue;
6525 }
6526 let mut world = vec![0.0f32; nv * 3];
6527 for (i, v) in mesh.verts.iter().enumerate() {
6528 let gx = v.pos.x * scale;
6529 let gy = -v.pos.y * scale;
6530 let gz = -v.pos.z * scale;
6531 let rx = gx * cyy + gz * sy;
6532 let rz = -gx * sy + gz * cyy;
6533 world[i * 3] = ox + rx;
6534 world[i * 3 + 1] = oy + gy;
6535 world[i * 3 + 2] = oz + rz;
6536 }
6537 let mut proj = vec![0.0f32; nv * 3];
6538 ling_gpu::backend().project_points(&world, &cp, &mut proj);
6539 let idx = &mesh.indices;
6540 let nt = idx.len() / 3;
6541 for t in 0..nt {
6542 let ia = idx[t * 3] as usize;
6543 let ib = idx[t * 3 + 1] as usize;
6544 let ic = idx[t * 3 + 2] as usize;
6545 if ia >= nv || ib >= nv || ic >= nv {
6546 continue;
6547 }
6548 let (da, db, dc) = (proj[ia * 3 + 2], proj[ib * 3 + 2], proj[ic * 3 + 2]);
6549 if (da + db + dc) / 3.0 <= near {
6550 continue;
6551 }
6552 let col = {
6553 let (ax, ay, az) =
6554 (world[ia * 3], world[ia * 3 + 1], world[ia * 3 + 2]);
6555 let (bx, by, bz) =
6556 (world[ib * 3], world[ib * 3 + 1], world[ib * 3 + 2]);
6557 let (px, py, pz) =
6558 (world[ic * 3], world[ic * 3 + 1], world[ic * 3 + 2]);
6559 let (ux, uy, uz) = (bx - ax, by - ay, bz - az);
6560 let (vx, vy, vz) = (px - ax, py - ay, pz - az);
6561 let normal = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];
6562 let centroid = [
6563 (ax + bx + px) / 3.0,
6564 (ay + by + py) / 3.0,
6565 (az + bz + pz) / 3.0,
6566 ];
6567 if gfx.flat_shade {
6568 base
6569 } else {
6570 crate::gfx::light::compute_lit_color(
6571 base,
6572 normal,
6573 centroid,
6574 &gfx.lights,
6575 ambient,
6576 )
6577 }
6578 };
6579 let depth = (da + db + dc) / 3.0;
6580 let col = gfx.fog_apply(col, depth);
6581 gfx.depth_queue.push_triangle_zv(
6582 col,
6583 proj[ia * 3],
6584 proj[ia * 3 + 1],
6585 da,
6586 proj[ib * 3],
6587 proj[ib * 3 + 1],
6588 db,
6589 proj[ic * 3],
6590 proj[ic * 3 + 1],
6591 dc,
6592 );
6593 }
6594 }
6595 return Ok(Value::Unit);
6596 },
6597
6598 "gltf_autorig" => {
6600 let hh = self.arg_num(&args, 0, -1.0)? as i64;
6601 let mut models = self.gltf_models.borrow_mut();
6602 if let Some(m) = models.get_mut(hh as usize) {
6603 return Ok(Value::Number(m.autorig() as f64));
6604 }
6605 return Ok(Value::Number(0.0));
6606 },
6607
6608 "gltf_pose_draw" => {
6612 let hh = self.arg_num(&args, 0, -1.0)? as i64;
6613 let ox = self.arg_num(&args, 1, 0.0)? as f32;
6614 let oy = self.arg_num(&args, 2, 0.0)? as f32;
6615 let oz = self.arg_num(&args, 3, 0.0)? as f32;
6616 let scale = self.arg_num(&args, 4, 1.0)? as f32;
6617 let yaw = self.arg_num(&args, 5, 0.0)? as f32;
6618 let euler: Vec<f32> = match args.get(6) {
6619 Some(Value::List(v)) => v
6620 .iter()
6621 .map(|x| self.to_number(x).unwrap_or(0.0) as f32)
6622 .collect(),
6623 _ => Vec::new(),
6624 };
6625 let (sy, cyy) = yaw.sin_cos();
6626 let models = self.gltf_models.borrow();
6627 let model = match models.get(hh as usize) {
6628 Some(m) => m,
6629 None => return Ok(Value::Unit),
6630 };
6631 let skinned = model.skin_local(&euler);
6632 let mut gfx = self.gfx.borrow_mut();
6633 let cp = {
6634 let c = &gfx.camera;
6635 ling_gpu::CameraParams {
6636 cry: c.cry,
6637 sry: c.sry,
6638 crx: c.crx,
6639 srx: c.srx,
6640 cx: c.cx,
6641 cy: c.cy,
6642 focal: c.focal,
6643 zdist: c.zdist,
6644 tx: c.tx,
6645 ty: c.ty,
6646 tz: c.tz,
6647 }
6648 };
6649 let near = -gfx.camera.zdist + 0.02;
6650 let ambient = gfx.ambient;
6651 for (mi, mesh) in model.meshes.iter().enumerate() {
6652 let nlow = mesh.name.to_lowercase();
6653 let base: u32 = if nlow.contains("hair") {
6654 0x7a4a28
6655 } else if nlow.contains("cloth") || nlow.contains("top") {
6656 0x4a86e0
6657 } else if nlow.contains("wing") {
6658 0xe6ecf5
6659 } else if nlow.contains("star") {
6660 0xffd24d
6661 } else {
6662 0xf2d6b8
6663 };
6664 let sk = match skinned.get(mi) {
6665 Some(s) => s,
6666 None => continue,
6667 };
6668 let nv = sk.len();
6669 if nv == 0 {
6670 continue;
6671 }
6672 let mut world = vec![0.0f32; nv * 3];
6673 for i in 0..nv {
6674 let gx = sk[i][0] * scale;
6675 let gy = -sk[i][1] * scale;
6676 let gz = -sk[i][2] * scale;
6677 let rx = gx * cyy + gz * sy;
6678 let rz = -gx * sy + gz * cyy;
6679 world[i * 3] = ox + rx;
6680 world[i * 3 + 1] = oy + gy;
6681 world[i * 3 + 2] = oz + rz;
6682 }
6683 let mut proj = vec![0.0f32; nv * 3];
6684 ling_gpu::backend().project_points(&world, &cp, &mut proj);
6685 let idx = &mesh.indices;
6686 let nt = idx.len() / 3;
6687 for t in 0..nt {
6688 let ia = idx[t * 3] as usize;
6689 let ib = idx[t * 3 + 1] as usize;
6690 let ic = idx[t * 3 + 2] as usize;
6691 if ia >= nv || ib >= nv || ic >= nv {
6692 continue;
6693 }
6694 let (da, db, dc) = (proj[ia * 3 + 2], proj[ib * 3 + 2], proj[ic * 3 + 2]);
6695 if (da + db + dc) / 3.0 <= near {
6696 continue;
6697 }
6698 let col = {
6699 let (ax, ay, az) =
6700 (world[ia * 3], world[ia * 3 + 1], world[ia * 3 + 2]);
6701 let (bx, by, bz) =
6702 (world[ib * 3], world[ib * 3 + 1], world[ib * 3 + 2]);
6703 let (px, py, pz) =
6704 (world[ic * 3], world[ic * 3 + 1], world[ic * 3 + 2]);
6705 let (ux, uy, uz) = (bx - ax, by - ay, bz - az);
6706 let (vx, vy, vz) = (px - ax, py - ay, pz - az);
6707 let normal = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];
6708 let centroid = [
6709 (ax + bx + px) / 3.0,
6710 (ay + by + py) / 3.0,
6711 (az + bz + pz) / 3.0,
6712 ];
6713 if gfx.flat_shade {
6714 base
6715 } else {
6716 crate::gfx::light::compute_lit_color(
6717 base,
6718 normal,
6719 centroid,
6720 &gfx.lights,
6721 ambient,
6722 )
6723 }
6724 };
6725 let depth = (da + db + dc) / 3.0;
6726 let col = gfx.fog_apply(col, depth);
6727 gfx.depth_queue.push_triangle_zv(
6728 col,
6729 proj[ia * 3],
6730 proj[ia * 3 + 1],
6731 da,
6732 proj[ib * 3],
6733 proj[ib * 3 + 1],
6734 db,
6735 proj[ic * 3],
6736 proj[ic * 3 + 1],
6737 dc,
6738 );
6739 }
6740 }
6741 return Ok(Value::Unit);
6742 },
6743
6744 "draw_mesh"
6751 | "วาดเมช"
6752 | "رسم_مش"
6753 | "ارسم_شبكة"
6754 | "צייר_רשת"
6755 | "میش_کھینچو"
6756 | "dessiner_maillage"
6757 | "netz_zeichnen"
6758 | "рисовать_меш" => {
6759 let pos = match args.first() {
6760 Some(Value::List(v)) => v,
6761 _ => return Ok(Value::Unit),
6762 };
6763 let idx = match args.get(1) {
6764 Some(Value::List(v)) => v,
6765 _ => return Ok(Value::Unit),
6766 };
6767 let ox = self.arg_num(&args, 2, 0.0)? as f32;
6768 let oy = self.arg_num(&args, 3, 0.0)? as f32;
6769 let oz = self.arg_num(&args, 4, 0.0)? as f32;
6770 let scale = self.arg_num(&args, 5, 1.0)? as f32;
6771 let mode = self.arg_num(&args, 6, 0.0)? as i64;
6772 let nv = pos.len() / 3;
6773 if nv == 0 {
6774 return Ok(Value::Unit);
6775 }
6776 let mut world = vec![0.0f32; nv * 3];
6777 for i in 0..nv {
6778 world[i * 3] = ox + self.to_number(&pos[i * 3]).unwrap_or(0.0) as f32 * scale;
6779 world[i * 3 + 1] =
6780 oy + self.to_number(&pos[i * 3 + 1]).unwrap_or(0.0) as f32 * scale;
6781 world[i * 3 + 2] =
6782 oz + self.to_number(&pos[i * 3 + 2]).unwrap_or(0.0) as f32 * scale;
6783 }
6784 let mut gfx = self.gfx.borrow_mut();
6785 let cp = {
6786 let c = &gfx.camera;
6787 ling_gpu::CameraParams {
6788 cry: c.cry,
6789 sry: c.sry,
6790 crx: c.crx,
6791 srx: c.srx,
6792 cx: c.cx,
6793 cy: c.cy,
6794 focal: c.focal,
6795 zdist: c.zdist,
6796 tx: c.tx,
6797 ty: c.ty,
6798 tz: c.tz,
6799 }
6800 };
6801 let near = -gfx.camera.zdist + 0.02;
6802 let base = gfx.color;
6803 let ambient = gfx.ambient;
6804 let mut proj = vec![0.0f32; nv * 3]; ling_gpu::backend().project_points(&world, &cp, &mut proj);
6806 let nt = idx.len() / 3;
6807 for t in 0..nt {
6808 let ia = self.to_number(&idx[t * 3]).unwrap_or(0.0) as usize;
6809 let ib = self.to_number(&idx[t * 3 + 1]).unwrap_or(0.0) as usize;
6810 let ic = self.to_number(&idx[t * 3 + 2]).unwrap_or(0.0) as usize;
6811 if ia >= nv || ib >= nv || ic >= nv {
6812 continue;
6813 }
6814 let (da, db, dc) = (proj[ia * 3 + 2], proj[ib * 3 + 2], proj[ic * 3 + 2]);
6815 if (da + db + dc) / 3.0 <= near {
6816 continue;
6817 } let col = if mode == 1 {
6819 let h = t as f32 * 0.6;
6820 let r = ((h.sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
6821 let g = (((h + 2.094).sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
6822 let b = (((h + 4.189).sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
6823 (r << 16) | (g << 8) | b
6824 } else {
6825 let (ax, ay, az) = (world[ia * 3], world[ia * 3 + 1], world[ia * 3 + 2]);
6826 let (bx, by, bz) = (world[ib * 3], world[ib * 3 + 1], world[ib * 3 + 2]);
6827 let (px, py, pz) = (world[ic * 3], world[ic * 3 + 1], world[ic * 3 + 2]);
6828 let (ux, uy, uz) = (bx - ax, by - ay, bz - az);
6829 let (vx, vy, vz) = (px - ax, py - ay, pz - az);
6830 let normal = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];
6831 let centroid = [
6832 (ax + bx + px) / 3.0,
6833 (ay + by + py) / 3.0,
6834 (az + bz + pz) / 3.0,
6835 ];
6836 if gfx.flat_shade {
6837 base
6838 } else {
6839 crate::gfx::light::compute_lit_color(
6840 base,
6841 normal,
6842 centroid,
6843 &gfx.lights,
6844 ambient,
6845 )
6846 }
6847 };
6848 let depth = (da + db + dc) / 3.0;
6849 let col = gfx.fog_apply(col, depth);
6850 gfx.depth_queue.push_triangle_zv(
6854 col,
6855 proj[ia * 3],
6856 proj[ia * 3 + 1],
6857 da,
6858 proj[ib * 3],
6859 proj[ib * 3 + 1],
6860 db,
6861 proj[ic * 3],
6862 proj[ic * 3 + 1],
6863 dc,
6864 );
6865 }
6866 return Ok(Value::Unit);
6867 },
6868
6869 "add_light"
6873 | "เพิ่มแสง"
6874 | "加灯"
6875 | "ライト追加"
6876 | "조명추가"
6877 | "افزودن_نور"
6878 | "أضف_ضوء"
6879 | "הוסף_אור"
6880 | "روشنی_شامل_کرو"
6881 | "ajouter_lumière"
6882 | "licht_hinzufügen"
6883 | "добавить_свет" => {
6884 let x = self.arg_num(&args, 0, 0.0)? as f32;
6885 let y = self.arg_num(&args, 1, -3.0)? as f32;
6886 let z = self.arg_num(&args, 2, 3.0)? as f32;
6887 let mut r = self.arg_num(&args, 3, 1.0)? as f32;
6888 let mut g = self.arg_num(&args, 4, 1.0)? as f32;
6889 let mut b = self.arg_num(&args, 5, 1.0)? as f32;
6890 if r > 1.5 || g > 1.5 || b > 1.5 {
6893 r /= 255.0;
6894 g /= 255.0;
6895 b /= 255.0;
6896 }
6897 let intensity = self.arg_num(&args, 6, 1.0)? as f32;
6898 let radius = self.arg_num(&args, 7, 0.0)? as f32;
6899 self.gfx
6900 .borrow_mut()
6901 .lights
6902 .push(Light { x, y, z, r, g, b, intensity, radius });
6903 return Ok(Value::Unit);
6904 },
6905
6906 "clear_lights"
6908 | "ล้างแสง"
6909 | "清灯"
6910 | "ライト消去"
6911 | "조명초기화"
6912 | "پاککردن_نورها"
6913 | "امسح_الأضواء"
6914 | "נקה_אורות"
6915 | "روشنیاں_صاف_کرو"
6916 | "effacer_lumières"
6917 | "lichter_löschen"
6918 | "очистить_свет" => {
6919 self.gfx.borrow_mut().lights.clear();
6920 return Ok(Value::Unit);
6921 },
6922
6923 "set_material"
6933 | "ตั้งวัสดุ"
6934 | "设置材质"
6935 | "マテリアル設定"
6936 | "재질설정"
6937 | "تنظیم_متریال"
6938 | "عيّن_المادة"
6939 | "קבע_חומר"
6940 | "میٹریل_مقرر_کرو" => {
6941 let key = self.arg_str(&args, 0, "");
6942 let val = self.arg_num(&args, 1, 0.0)?;
6943 let mut gfx = self.gfx.borrow_mut();
6944 let mat = gfx
6945 .material
6946 .get_or_insert_with(crate::gfx::LingMaterial::default);
6947 match key.as_str() {
6948 "albedo" => mat.albedo = val as u32,
6949 "roughness" => mat.roughness = val as f32,
6950 "metallic" => mat.metallic = val as f32,
6951 "emission" => mat.emission = val as u32,
6952 "emission_strength" => mat.emission_strength = val as f32,
6953 "specular" => mat.specular = val as f32,
6954 "specular_tint" => mat.specular_tint = val as f32,
6955 "subsurface" => mat.subsurface = val as f32,
6956 "subsurface_color" => mat.subsurface_color = val as u32,
6957 "clearcoat" => mat.clearcoat = val as f32,
6958 "clearcoat_roughness" => mat.clearcoat_roughness = val as f32,
6959 "transmission" => mat.transmission = val as f32,
6960 "ior" => mat.ior = val as f32,
6961 "iridescence" => mat.iridescence = val as f32,
6962 "sheen" => mat.sheen = val as f32,
6963 "anisotropy" => mat.anisotropy = val as f32,
6964 "anisotropy_angle" => mat.anisotropy_angle = val as f32,
6965 "toon_bands" => mat.toon_bands = val as u32,
6966 "shadow_softness" => mat.shadow_softness = val as f32,
6967 "outline_px" => mat.outline_px = val as f32,
6968 "outline_color" => mat.outline_color = val as u32,
6969 "highlight_color" => mat.highlight_color = val as u32,
6970 _ => {},
6971 }
6972 return Ok(Value::Unit);
6973 },
6974
6975 "reset_material"
6978 | "รีเซ็ตวัสดุ"
6979 | "重置材质"
6980 | "マテリアルリセット"
6981 | "재질초기화"
6982 | "بازنشانی_متریال"
6983 | "أعد_ضبط_المادة"
6984 | "אפס_חומר"
6985 | "میٹریل_ری_سیٹ" => {
6986 self.gfx.borrow_mut().material = None;
6987 return Ok(Value::Unit);
6988 },
6989
6990 "toon_outlines"
6996 | "ตั้งเส้นขอบการ์ตูน"
6997 | "卡通轮廓"
6998 | "トゥーンアウトライン"
6999 | "툰아웃라인"
7000 | "خطوط_کارتونی"
7001 | "حدود_كرتونية"
7002 | "קווי_מתאר_מצוירים"
7003 | "ٹون_آؤٹ_لائنز" => {
7004 let px = self.arg_num(&args, 0, 0.0)? as f32;
7005 let color = self.arg_num(&args, 1, 0.0)? as u32;
7006 let thresh = self.arg_num(&args, 2, 0.05)? as f32;
7007 let mut gfx = self.gfx.borrow_mut();
7008 gfx.toon.outline_px = px;
7009 gfx.toon.outline_color = color;
7010 gfx.toon.outline_thresh = thresh;
7011 return Ok(Value::Unit);
7012 },
7013
7014 "tone_stop"
7020 | "ตั้งจุดโทน"
7021 | "色调停止"
7022 | "トーンストップ"
7023 | "톤스톱"
7024 | "نقطه_توقف_تن_رنگ"
7025 | "نقطة_توقف_اللون"
7026 | "נקודת_עצירת_גוון"
7027 | "ٹون_اسٹاپ" => {
7028 let t = self.arg_num(&args, 0, 0.0)? as f32;
7029 let val = self.arg_num(&args, 1, 1.0)? as f32;
7030 let mut gfx = self.gfx.borrow_mut();
7031 gfx.toon.ramp.stops.push(crate::gfx::toon::ToneStop {
7032 t: t.clamp(0.0, 1.0),
7033 value: val.clamp(0.0, 1.0),
7034 });
7035 gfx.toon
7036 .ramp
7037 .stops
7038 .sort_by(|a, b| a.t.partial_cmp(&b.t).unwrap_or(std::cmp::Ordering::Equal));
7039 return Ok(Value::Unit);
7040 },
7041
7042 "tone_smooth"
7045 | "ตั้งโทนนุ่ม"
7046 | "色调平滑"
7047 | "トーンスムーズ"
7048 | "톤스무스"
7049 | "تن_رنگ_نرم"
7050 | "تدرج_لون_ناعم"
7051 | "גוון_חלק"
7052 | "ٹون_ہموار" => {
7053 let v = self.arg_num(&args, 0, 0.0)? as f32;
7054 self.gfx.borrow_mut().toon.ramp.smooth = v > 0.5;
7055 return Ok(Value::Unit);
7056 },
7057
7058 "tone_bezier"
7066 | "ตั้งโทนเบซิเยร์"
7067 | "色调贝塞尔"
7068 | "トーンベジェ"
7069 | "톤베지어"
7070 | "تن_رنگ_بزیه"
7071 | "تدرج_لون_بيزيه"
7072 | "גוון_בזייה"
7073 | "بیزیئر_ٹون" => {
7074 let y1 = self.arg_num(&args, 0, 1.0 / 3.0)? as f32;
7075 let y2 = self.arg_num(&args, 1, 2.0 / 3.0)? as f32;
7076 self.gfx.borrow_mut().toon.ramp.bezier = Some([y1, y2]);
7077 return Ok(Value::Unit);
7078 },
7079
7080 "tone_bezier_off"
7082 | "ปิดโทนเบซิเยร์"
7083 | "关闭色调贝塞尔"
7084 | "トーンベジェオフ"
7085 | "톤베지어끄기"
7086 | "خاموشکردن_بزیه"
7087 | "إيقاف_تدرج_بيزيه"
7088 | "כבה_גוון_בזייה"
7089 | "بیزیئر_ٹون_بند" => {
7090 self.gfx.borrow_mut().toon.ramp.bezier = None;
7091 return Ok(Value::Unit);
7092 },
7093
7094 "tone_ramp_reset"
7096 | "รีเซ็ตการไล่โทน"
7097 | "重置色调渐变"
7098 | "トーンランプリセット"
7099 | "톤램프리셋"
7100 | "بازنشانی_شیب_تن_رنگ"
7101 | "أعد_ضبط_تدرج_اللون"
7102 | "אפס_שיפוע_גוון"
7103 | "ٹون_ریمپ_ری_سیٹ" => {
7104 self.gfx.borrow_mut().toon.ramp = crate::gfx::toon::ToneRamp::default();
7105 return Ok(Value::Unit);
7106 },
7107
7108 "tone_ramp_clear"
7110 | "ล้างการไล่โทน"
7111 | "清除色调渐变"
7112 | "トーンランプクリア"
7113 | "톤램프클리어"
7114 | "پاککردن_شیب_تن_رنگ"
7115 | "امسح_تدرج_اللون"
7116 | "נקה_שיפוע_גוון"
7117 | "ٹون_ریمپ_صاف" => {
7118 self.gfx.borrow_mut().toon.ramp.stops.clear();
7119 return Ok(Value::Unit);
7120 },
7121
7122 "tone_soft"
7130 | "โทนขอบนุ่ม"
7131 | "色调柔边"
7132 | "トーンソフト"
7133 | "톤소프트"
7134 | "تن_رنگ_لبهنرم"
7135 | "تدرج_ناعم_الحواف"
7136 | "גוון_קצה_רך"
7137 | "نرم_کنارہ_ٹون"
7138 | "tonalité_douce"
7139 | "weicher_ton"
7140 | "мягкий_тон" => {
7141 let s = self.arg_num(&args, 0, 0.32)? as f32;
7142 let sh = self.arg_num(&args, 1, 0.65)? as f32;
7143 let mut gfx = self.gfx.borrow_mut();
7144 gfx.toon.ramp.soft = s.clamp(0.0, 1.0);
7145 gfx.toon.ramp.sheen = sh.clamp(0.0, 1.0);
7146 return Ok(Value::Unit);
7147 },
7148
7149 "set_ssao"
7154 | "ตั้งเงาสัมผัส"
7155 | "环境光遮蔽"
7156 | "アンビエントオクルージョン"
7157 | "앰비언트오클루전"
7158 | "تنظیم_انسداد_محیطی"
7159 | "عيّن_تظليل_محيطي"
7160 | "קבע_הצללה_סביבתית"
7161 | "ایس_ایس_اے_او_مقرر_کرو"
7162 | "définir_ssao"
7163 | "ssao_setzen"
7164 | "задать_ssao" => {
7165 let s = self.arg_num(&args, 0, 0.35)? as f32;
7166 let r = self.arg_num(&args, 1, 6.0)? as f32;
7167 let z = self.arg_num(&args, 2, 12.0)? as f32;
7168 let mut gfx = self.gfx.borrow_mut();
7169 gfx.toon.ao_strength = s.clamp(0.0, 1.0);
7170 gfx.toon.ao_radius = r.max(1.0);
7171 gfx.toon.ao_range = z.max(0.01);
7172 return Ok(Value::Unit);
7173 },
7174
7175 "set_fxaa"
7181 | "ลบรอยหยัก"
7182 | "屏幕抗锯齿"
7183 | "画面アンチエイリアス"
7184 | "화면안티앨리어싱"
7185 | "تنظیم_ضدلبهدندانه_سریع"
7186 | "عيّن_مضاد_التسنن_السريع"
7187 | "קבע_החלקת_מסך"
7188 | "ایف_ایکس_اے_اے_مقرر_کرو"
7189 | "définir_fxaa"
7190 | "fxaa_setzen"
7191 | "задать_fxaa" => {
7192 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
7193 self.gfx.borrow_mut().toon.fxaa = on;
7194 return Ok(Value::Unit);
7195 },
7196
7197 "set_bloom"
7202 | "ตั้งบลูม"
7203 | "泛光"
7204 | "ブルーム"
7205 | "블룸"
7206 | "تنظیم_درخشش"
7207 | "عيّن_التوهج"
7208 | "קבע_זוהר"
7209 | "بلوم_مقرر_کرو"
7210 | "définir_bloom"
7211 | "bloom_setzen"
7212 | "задать_блум" => {
7213 let s = self.arg_num(&args, 0, 0.45)? as f32;
7214 let t = self.arg_num(&args, 1, 0.74)? as f32;
7215 let mut gfx = self.gfx.borrow_mut();
7216 gfx.toon.bloom_strength = s.max(0.0);
7217 gfx.toon.bloom_thresh = t.clamp(0.0, 0.99);
7218 return Ok(Value::Unit);
7219 },
7220
7221 "shadow_smooth"
7224 | "ตั้งเงานุ่ม"
7225 | "柔化阴影"
7226 | "影ソフト"
7227 | "그림자부드럽게"
7228 | "سایه_نرم"
7229 | "ظل_ناعم"
7230 | "צל_חלק"
7231 | "نرم_سایہ" => {
7232 let s = self.arg_num(&args, 0, 0.0)? as f32;
7233 let mut gfx = self.gfx.borrow_mut();
7234 gfx.toon.ramp.smooth = s > 0.05;
7235 if s > 0.05 {
7236 let y1 = (0.333 + s * 0.2).clamp(0.0, 1.0);
7237 let y2 = (0.667 - s * 0.2).clamp(0.0, 1.0);
7238 gfx.toon.ramp.bezier = Some([y1, y2]);
7239 } else {
7240 gfx.toon.ramp.bezier = None;
7241 }
7242 return Ok(Value::Unit);
7243 },
7244
7245 "toon_highlight"
7247 | "ตั้งไฮไลท์การ์ตูน"
7248 | "卡通高光"
7249 | "トゥーンハイライト"
7250 | "툰하이라이트"
7251 | "هایلایت_کارتونی"
7252 | "إبراز_كرتوني"
7253 | "הדגשה_מצוירת"
7254 | "ٹون_ہائی_لائٹ" => {
7255 let _strength = self.arg_num(&args, 0, 0.0)? as f32;
7257 let _thresh = self.arg_num(&args, 2, 0.78)? as f32;
7258 return Ok(Value::Unit);
7260 },
7261
7262 "set_ambient"
7264 | "ตั้งแสงรอบข้าง"
7265 | "环境光"
7266 | "環境光設定"
7267 | "환경광설정"
7268 | "تنظیم_نور_محیطی"
7269 | "عيّن_الإضاءة_المحيطة"
7270 | "קבע_תאורה_סביבתית"
7271 | "ماحولیاتی_روشنی_مقرر_کرو"
7272 | "définir_ambiante"
7273 | "umgebungslicht_setzen"
7274 | "задать_фон" => {
7275 let v = self.arg_num(&args, 0, 0.15)? as f32;
7276 self.gfx.borrow_mut().ambient = v;
7277 return Ok(Value::Unit);
7278 },
7279
7280 "set_fog"
7283 | "ตั้งหมอก"
7284 | "雾"
7285 | "霧設定"
7286 | "안개설정"
7287 | "تنظیم_مه"
7288 | "عيّن_الضباب"
7289 | "קבע_ערפל"
7290 | "دھند_مقرر_کرو" => {
7291 let r = self.arg_num(&args, 0, 0.0)?.clamp(0.0, 255.0) as u32;
7292 let g = self.arg_num(&args, 1, 0.0)?.clamp(0.0, 255.0) as u32;
7293 let b = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 255.0) as u32;
7294 let start = self.arg_num(&args, 3, 0.0)? as f32;
7295 let end = self.arg_num(&args, 4, 0.0)? as f32;
7296 let mut gfx = self.gfx.borrow_mut();
7297 gfx.fog_color = (r << 16) | (g << 8) | b;
7298 gfx.fog_start = start;
7299 gfx.fog_end = end;
7300 return Ok(Value::Unit);
7301 },
7302
7303 "วาดสามเหลี่ยม3มิติ"
7307 | "draw_triangle_3d"
7308 | "triangle3d"
7309 | "رسم_مثلث_سهبعدی"
7310 | "ارسم_مثلثا_ثلاثي_الأبعاد"
7311 | "צייר_משולש_תלת_ממדי"
7312 | "تھری_ڈی_مثلث_کھینچو" => {
7313 let ax = self.arg_num(&args, 0, 0.0)? as f32;
7314 let ay = self.arg_num(&args, 1, 0.0)? as f32;
7315 let az = self.arg_num(&args, 2, 0.0)? as f32;
7316 let bx = self.arg_num(&args, 3, 0.0)? as f32;
7317 let by = self.arg_num(&args, 4, 0.0)? as f32;
7318 let bz = self.arg_num(&args, 5, 0.0)? as f32;
7319 let cx = self.arg_num(&args, 6, 0.0)? as f32;
7320 let cy = self.arg_num(&args, 7, 0.0)? as f32;
7321 let cz = self.arg_num(&args, 8, 0.0)? as f32;
7322
7323 let mut gfx = self.gfx.borrow_mut();
7324
7325 if gfx.mesh_capture.is_some() {
7327 let col = gfx.color;
7328 gfx.mesh_capture
7329 .as_mut()
7330 .unwrap()
7331 .push(([ax, ay, az, bx, by, bz, cx, cy, cz], col));
7332 return Ok(Value::Unit);
7333 }
7334
7335 gfx.submit_triangle(ax, ay, az, bx, by, bz, cx, cy, cz);
7336 return Ok(Value::Unit);
7337 },
7338
7339 "เริ่มอบเมช"
7341 | "mesh_bake_begin"
7342 | "شروع_پخت_مش"
7343 | "ابدأ_خبز_الشبكة"
7344 | "התחל_אפיית_רשת"
7345 | "میش_بیک_شروع" => {
7346 self.gfx.borrow_mut().mesh_capture = Some(Vec::new());
7347 return Ok(Value::Unit);
7348 },
7349
7350 "เมชแคชรับ"
7352 | "mesh_cache_get"
7353 | "دریافت_کش_مش"
7354 | "اجلب_مخبأ_الشبكة"
7355 | "קבל_מטמון_רשת"
7356 | "میش_کیش_حاصل_کرو" => {
7357 let key = self.arg_num(&args, 0, 0.0)? as i64;
7358 let h = self.gfx.borrow().mesh_cache.get(&key).copied();
7359 return Ok(Value::Number(h.map(|x| x as f64).unwrap_or(-1.0)));
7360 },
7361
7362 "เมชแคชตั้ง"
7364 | "mesh_cache_put"
7365 | "ذخیره_در_کش_مش"
7366 | "ضع_في_مخبأ_الشبكة"
7367 | "שמור_במטמון_רשת"
7368 | "میش_کیش_رکھو" => {
7369 let key = self.arg_num(&args, 0, 0.0)? as i64;
7370 let h = self.arg_num(&args, 1, 0.0)? as usize;
7371 let mut gfx = self.gfx.borrow_mut();
7372 const CAP: usize = 256;
7373 if gfx.mesh_cache.len() >= CAP {
7374 let evict: Vec<usize> = gfx.mesh_cache.values().copied().collect();
7375 gfx.mesh_cache.clear();
7376 for id in evict {
7377 if id < gfx.meshes.len() {
7378 gfx.meshes[id].clear();
7379 gfx.mesh_free.push(id);
7380 }
7381 }
7382 }
7383 gfx.mesh_cache.insert(key, h);
7384 return Ok(Value::Unit);
7385 },
7386
7387 "เมชแคชล้าง"
7389 | "mesh_cache_clear"
7390 | "پاککردن_کش_مش"
7391 | "امسح_مخبأ_الشبكة"
7392 | "נקה_מטמון_רשת"
7393 | "میش_کیش_صاف" => {
7394 let mut gfx = self.gfx.borrow_mut();
7395 let evict: Vec<usize> = gfx.mesh_cache.values().copied().collect();
7396 gfx.mesh_cache.clear();
7397 for id in evict {
7398 if id < gfx.meshes.len() {
7399 gfx.meshes[id].clear();
7400 gfx.mesh_free.push(id);
7401 }
7402 }
7403 return Ok(Value::Unit);
7404 },
7405
7406 "จบอบเมช"
7408 | "mesh_bake_end"
7409 | "پایان_پخت_مش"
7410 | "أنهِ_خبز_الشبكة"
7411 | "סיים_אפיית_רשת"
7412 | "میش_بیک_ختم" => {
7413 let mut gfx = self.gfx.borrow_mut();
7414 let tris = gfx.mesh_capture.take().unwrap_or_default();
7415 let id = gfx.mesh_register(tris);
7416 return Ok(Value::Number(id as f64));
7417 },
7418
7419 "วาดอบเมช"
7423 | "mesh_bake_draw"
7424 | "วาดอบเมชสี"
7425 | "mesh_bake_draw_col"
7426 | "رسم_مش_پخته"
7427 | "ارسم_شبكة_مخبوزة"
7428 | "צייר_רשת_אפויה"
7429 | "بیکڈ_میش_کھینچو" => {
7430 let baked_col = matches!(name, "วาดอบเมชสี" | "mesh_bake_draw_col");
7431 let id = self.arg_num(&args, 0, 0.0)? as usize;
7432 let ox = self.arg_num(&args, 1, 0.0)? as f32;
7433 let oy = self.arg_num(&args, 2, 0.0)? as f32;
7434 let oz = self.arg_num(&args, 3, 0.0)? as f32;
7435 let rx = self.arg_num(&args, 4, 1.0)? as f32;
7436 let ry = self.arg_num(&args, 5, 0.0)? as f32;
7437 let rz = self.arg_num(&args, 6, 0.0)? as f32;
7438 let ux = self.arg_num(&args, 7, 0.0)? as f32;
7439 let uy = self.arg_num(&args, 8, 1.0)? as f32;
7440 let uz = self.arg_num(&args, 9, 0.0)? as f32;
7441 let s = self.arg_num(&args, 10, 1.0)? as f32;
7442 self.gfx
7443 .borrow_mut()
7444 .mesh_draw(id, ox, oy, oz, rx, ry, rz, ux, uy, uz, s, baked_col);
7445 return Ok(Value::Unit);
7446 },
7447
7448 "draw_quad_3d"
7452 | "quad3d"
7453 | "วาดสี่เหลี่ยม3มิติ"
7454 | "draw_pent_3d"
7455 | "pent3d"
7456 | "วาดห้าเหลี่ยม3มิติ"
7457 | "draw_hex_3d"
7458 | "hex3d"
7459 | "วาดหกเหลี่ยม3มิติ"
7460 | "draw_polygon_3d"
7461 | "polygon3d"
7462 | "วาดรูปหลายเหลี่ยม3มิติ"
7463 | "رسم_چهارضلعی_سهبعدی"
7464 | "ارسم_رباعيا_ثلاثي_الأبعاد"
7465 | "צייר_מרובע_תלת_ממדי"
7466 | "تھری_ڈی_چوکور_کھینچو" => {
7467 let mut wxs: [f32; 8] = [0.0; 8];
7469 let mut wys: [f32; 8] = [0.0; 8];
7470 let mut wzs: [f32; 8] = [0.0; 8];
7471 let n_verts;
7472
7473 if args.len() == 1 {
7474 let list = match &args[0] {
7476 Value::List(l) => l.clone(),
7477 _ => {
7478 return Err(EvalErr::from("draw_polygon_3d: expected list".to_string()))
7479 },
7480 };
7481 let coords: Vec<f32> = list
7482 .iter()
7483 .map(|v| match v {
7484 Value::Number(n) => *n as f32,
7485 _ => 0.0,
7486 })
7487 .collect();
7488 n_verts = (coords.len() / 3).min(8);
7489 for i in 0..n_verts {
7490 wxs[i] = coords[i * 3];
7491 wys[i] = coords[i * 3 + 1];
7492 wzs[i] = coords[i * 3 + 2];
7493 }
7494 } else {
7495 n_verts = (args.len() / 3).min(8);
7497 for i in 0..n_verts {
7498 wxs[i] = self.arg_num(&args, i * 3, 0.0)? as f32;
7499 wys[i] = self.arg_num(&args, i * 3 + 1, 0.0)? as f32;
7500 wzs[i] = self.arg_num(&args, i * 3 + 2, 0.0)? as f32;
7501 }
7502 }
7503 if n_verts < 3 {
7504 return Ok(Value::Unit);
7505 }
7506
7507 let mut gfx = self.gfx.borrow_mut();
7508
7509 if gfx.mesh_capture.is_some() {
7515 let col = gfx.color;
7516 let cap = gfx.mesh_capture.as_mut().unwrap();
7517 for i in 1..n_verts - 1 {
7518 cap.push((
7519 [
7520 wxs[0],
7521 wys[0],
7522 wzs[0],
7523 wxs[i],
7524 wys[i],
7525 wzs[i],
7526 wxs[i + 1],
7527 wys[i + 1],
7528 wzs[i + 1],
7529 ],
7530 col,
7531 ));
7532 }
7533 return Ok(Value::Unit);
7534 }
7535
7536 let normal = crate::gfx::poly::face_normal(
7538 wxs[0], wys[0], wzs[0], wxs[1], wys[1], wzs[1], wxs[2], wys[2], wzs[2],
7539 );
7540
7541 let mut wcs: [u32; 8] = [0; 8];
7543 if gfx.flat_shade {
7544 let c = gfx.color;
7545 for wc in wcs.iter_mut().take(n_verts) {
7546 *wc = c;
7547 }
7548 } else if let Some(ref mat) = gfx.material.clone() {
7549 let cam = [gfx.camera.cx, gfx.camera.cy, gfx.camera.zdist];
7550 let lights: Vec<_> = gfx.lights.clone();
7551 let ambient = gfx.ambient;
7552 for i in 0..n_verts {
7553 let v = [wxs[i], wys[i], wzs[i]];
7554 let vd = [cam[0] - v[0], cam[1] - v[1], cam[2] - v[2]];
7555 wcs[i] = crate::gfx::material::shade(mat, normal, vd, v, &lights, ambient);
7556 }
7557 } else {
7558 let base = gfx.color;
7559 let lights: Vec<_> = gfx.lights.clone();
7560 let ambient = gfx.ambient;
7561 for i in 0..n_verts {
7562 wcs[i] = crate::gfx::light::compute_lit_color_linear(
7563 base,
7564 normal,
7565 [wxs[i], wys[i], wzs[i]],
7566 &lights,
7567 ambient,
7568 );
7569 }
7570 }
7571
7572 let near = -gfx.camera.zdist + 0.05;
7574 let mut clip_in: [(f32, f32, f32, f32, u32); crate::gfx::poly::MAX_CLIP_VERTS] =
7575 [(0.0, 0.0, 0.0, 0.0, 0); crate::gfx::poly::MAX_CLIP_VERTS];
7576 for i in 0..n_verts {
7577 let d = gfx.camera.depth(wxs[i], wys[i], wzs[i]);
7578 clip_in[i] = (wxs[i], wys[i], wzs[i], d, wcs[i]);
7579 }
7580 let mut clip_out: [(f32, f32, f32, f32, u32); crate::gfx::poly::MAX_CLIP_VERTS] =
7581 [(0.0, 0.0, 0.0, 0.0, 0); crate::gfx::poly::MAX_CLIP_VERTS];
7582 let pn = crate::gfx::poly::clip_near(&clip_in, n_verts, near, &mut clip_out);
7583 if pn < 3 {
7584 return Ok(Value::Unit);
7585 }
7586
7587 let mut proj: [(f32, f32, f32, u32); crate::gfx::poly::MAX_CLIP_VERTS] =
7589 [(0.0, 0.0, 0.0, 0); crate::gfx::poly::MAX_CLIP_VERTS];
7590 for i in 0..pn {
7591 let (sx, sy, sz) =
7592 gfx.camera
7593 .project(clip_out[i].0, clip_out[i].1, clip_out[i].2);
7594 let fc = gfx.fog_apply(clip_out[i].4, sz);
7595 proj[i] = (sx, sy, sz, fc);
7596 }
7597
7598 let unlit = gfx.flat_shade;
7600 crate::gfx::poly::fan_emit_proj(
7601 &proj,
7602 pn,
7603 |x0, y0, z0, c0, x1, y1, z1, c1, x2, y2, z2, c2| {
7604 gfx.depth_queue.push_triangle_g_zv(
7605 x0, y0, z0, c0, x1, y1, z1, c1, x2, y2, z2, c2, 3, unlit,
7606 );
7607 },
7608 );
7609 return Ok(Value::Unit);
7610 },
7611
7612 "วาดเส้น3มิติ"
7616 | "draw_line_3d"
7617 | "line3d"
7618 | "画3D线"
7619 | "3D線描く"
7620 | "3D선그리기"
7621 | "رسم_خط_سهبعدی"
7622 | "ارسم_خطا_ثلاثي_الأبعاد"
7623 | "צייר_קו_תלת_ממדי"
7624 | "تھری_ڈی_لکیر_کھینچو" => {
7625 let ax = self.arg_num(&args, 0, 0.0)? as f32;
7626 let ay = self.arg_num(&args, 1, 0.0)? as f32;
7627 let az = self.arg_num(&args, 2, 0.0)? as f32;
7628 let bx = self.arg_num(&args, 3, 0.0)? as f32;
7629 let by = self.arg_num(&args, 4, 0.0)? as f32;
7630 let bz = self.arg_num(&args, 5, 0.0)? as f32;
7631
7632 let mut gfx = self.gfx.borrow_mut();
7633 let color = gfx.color;
7634 let near = -gfx.camera.zdist + 0.05;
7636 let mut lax = ax;
7637 let mut lay = ay;
7638 let mut laz = az;
7639 let mut lbx = bx;
7640 let mut lby = by;
7641 let mut lbz = bz;
7642 let da_raw = gfx.camera.depth(lax, lay, laz);
7643 let db_raw = gfx.camera.depth(lbx, lby, lbz);
7644 if da_raw <= near && db_raw <= near {
7645 return Ok(Value::Unit);
7646 }
7647 if da_raw <= near {
7648 let t = (near - da_raw) / (db_raw - da_raw);
7649 lax += t * (lbx - lax);
7650 lay += t * (lby - lay);
7651 laz += t * (lbz - laz);
7652 } else if db_raw <= near {
7653 let t = (near - da_raw) / (db_raw - da_raw);
7654 lbx = lax + t * (lbx - lax);
7655 lby = lay + t * (lby - lay);
7656 lbz = laz + t * (lbz - laz);
7657 }
7658 if !gfx.edge_set.try_insert(lax, lay, laz, lbx, lby, lbz) {
7660 return Ok(Value::Unit);
7661 }
7662 let (sax, say, da) = gfx.camera.project(lax, lay, laz);
7663 let (sbx, sby, db) = gfx.camera.project(lbx, lby, lbz);
7664 let depth = (da + db) / 2.0;
7665 let color = gfx.fog_apply(color, depth);
7666 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
7667 return Ok(Value::Unit);
7668 },
7669
7670 #[cfg(not(target_arch = "wasm32"))]
7680 "orb_shell"
7681 | "球壳"
7682 | "オーブ殻"
7683 | "오브껍질"
7684 | "เปลือกทรงกลม"
7685 | "پوسته_کروی"
7686 | "قشرة_كروية"
7687 | "קליפת_כדור"
7688 | "کروی_خول"
7689 | "coque_orbe"
7690 | "orb_hülle"
7691 | "оболочка_сферы" => {
7692 let cx = self.arg_num(&args, 0, 0.)? as f32;
7693 let cy = self.arg_num(&args, 1, 0.)? as f32;
7694 let cz = self.arg_num(&args, 2, 0.)? as f32;
7695 let radius = self.arg_num(&args, 3, 1.0)? as f32;
7696 let ry = self.arg_num(&args, 4, 0.)? as f32;
7697 let rx = self.arg_num(&args, 5, 0.)? as f32;
7698 let density = (self.arg_num(&args, 6, 10.)? as i32).clamp(1, 48);
7699 let tr = (self.arg_num(&args, 7, 230.)? as f32).clamp(0., 255.);
7700 let tg = (self.arg_num(&args, 8, 230.)? as f32).clamp(0., 255.);
7701 let tb = (self.arg_num(&args, 9, 235.)? as f32).clamp(0., 255.);
7702 let (cyr, syr) = (ry.cos(), ry.sin());
7703 let (cxr, sxr) = (rx.cos(), rx.sin());
7704 let tau = std::f32::consts::TAU;
7705 let pi = std::f32::consts::PI;
7706 let turns = 6.0_f32; let nseg = 96; let inv_r = if radius.abs() > 1e-5 {
7709 1.0 / radius
7710 } else {
7711 0.0
7712 };
7713 let pt = |u: f32, theta0: f32, dir: f32| -> ([f32; 3], f32) {
7716 let phi = pi * u; let th = dir * turns * tau * u + theta0;
7718 let (mut x, y, mut z) = (
7719 phi.sin() * th.cos() * radius,
7720 phi.cos() * radius,
7721 phi.sin() * th.sin() * radius,
7722 );
7723 let x1 = x * cyr + z * syr; let z1 = -x * syr + z * cyr;
7725 x = x1;
7726 z = z1;
7727 let y2 = y * cxr - z * sxr; let z2 = y * sxr + z * cxr;
7729 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.0, 1.0);
7731 ([cx + x, cy + y2, cz + z2], facing)
7732 };
7733 let mut gfx = self.gfx.borrow_mut();
7734 let near = -gfx.camera.zdist + 0.05;
7735 let seg = |gfx: &mut crate::gfx::GfxState, a: [f32; 3], b: [f32; 3], lum: f32| {
7737 let (mut lax, mut lay, mut laz) = (a[0], a[1], a[2]);
7738 let (mut lbx, mut lby, mut lbz) = (b[0], b[1], b[2]);
7739 let da = gfx.camera.depth(lax, lay, laz);
7740 let db = gfx.camera.depth(lbx, lby, lbz);
7741 if da <= near && db <= near {
7742 return;
7743 }
7744 if da <= near {
7745 let t = (near - da) / (db - da);
7746 lax += t * (lbx - lax);
7747 lay += t * (lby - lay);
7748 laz += t * (lbz - laz);
7749 } else if db <= near {
7750 let t = (near - da) / (db - da);
7751 lbx = lax + t * (lbx - lax);
7752 lby = lay + t * (lby - lay);
7753 lbz = laz + t * (lbz - laz);
7754 }
7755 let (sax, say, da2) = gfx.camera.project(lax, lay, laz);
7756 let (sbx, sby, db2) = gfx.camera.project(lbx, lby, lbz);
7757 let l = (0.12 + 0.88 * lum).clamp(0.0, 1.0);
7759 let cr = (tr * l) as u32;
7760 let cg = (tg * l) as u32;
7761 let cb = (tb * l) as u32;
7762 let color = (cr << 16) | (cg << 8) | cb;
7763 gfx.depth_queue
7764 .push_line((da2 + db2) * 0.5, color, sax, say, sbx, sby);
7765 };
7766 for &dir in &[1.0_f32, -1.0_f32] {
7768 for s in 0..density {
7769 let theta0 = s as f32 * tau / density as f32;
7770 let mut prev = pt(0.0, theta0, dir);
7771 for k in 1..=nseg {
7772 let cur = pt(k as f32 / nseg as f32, theta0, dir);
7773 seg(&mut gfx, prev.0, cur.0, (prev.1 + cur.1) * 0.5);
7774 prev = cur;
7775 }
7776 }
7777 }
7778 return Ok(Value::Unit);
7779 },
7780
7781 #[cfg(not(target_arch = "wasm32"))]
7789 "orb_particles"
7790 | "球内粒子"
7791 | "オーブ粒子"
7792 | "오브입자"
7793 | "อนุภาคทรงกลม"
7794 | "ذرات_کروی"
7795 | "جسيمات_كروية"
7796 | "חלקיקי_כדור"
7797 | "کروی_ذرات"
7798 | "particules_orbe"
7799 | "orb_partikel"
7800 | "частицы_сферы" => {
7801 let cx = self.arg_num(&args, 0, 0.)? as f32;
7802 let cy = self.arg_num(&args, 1, 0.)? as f32;
7803 let cz = self.arg_num(&args, 2, 0.)? as f32;
7804 let radius = self.arg_num(&args, 3, 1.0)? as f32;
7805 let count = (self.arg_num(&args, 4, 160.)? as i32).clamp(1, 4000);
7806 let t = self.arg_num(&args, 5, 0.)? as f32;
7807 let tr = (self.arg_num(&args, 6, 255.)? as f32).clamp(0., 255.);
7808 let tg = (self.arg_num(&args, 7, 255.)? as f32).clamp(0., 255.);
7809 let tb = (self.arg_num(&args, 8, 255.)? as f32).clamp(0., 255.);
7810 let inv_r = if radius.abs() > 1e-5 {
7811 1.0 / radius
7812 } else {
7813 0.0
7814 };
7815 let h = |mut x: u32| -> f32 {
7817 x = x.wrapping_mul(747796405).wrapping_add(2891336453);
7818 x = ((x >> ((x >> 28).wrapping_add(4))) ^ x).wrapping_mul(277803737);
7819 (((x >> 22) ^ x) & 0xFFFFFF) as f32 / 16_777_216.0
7820 };
7821 let tau = std::f32::consts::TAU;
7822 let (cyr, syr) = ((t * 0.5).cos(), (t * 0.5).sin());
7824 let (cxr, sxr) = ((t * 0.23).cos(), (t * 0.23).sin());
7825 let mut gfx = self.gfx.borrow_mut();
7826 let near = -gfx.camera.zdist + 0.05;
7827 let (sw, sh) = (gfx.width as i32, gfx.height as i32);
7828 for i in 0..count {
7829 let i = i as u32;
7830 let u = h(i.wrapping_mul(3) + 1);
7832 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) = (
7836 rr * ph.sin() * th.cos(),
7837 rr * ph.cos(),
7838 rr * ph.sin() * th.sin(),
7839 );
7840 let x1 = x * cyr + z * syr;
7842 let z1 = -x * syr + z * cyr;
7843 x = x1;
7844 z = z1;
7845 let y2 = y * cxr - z * sxr;
7846 let z2 = y * sxr + z * cxr;
7847 let (wx, wy, wz) = (cx + x, cy + y2, cz + z2);
7848 if gfx.camera.depth(wx, wy, wz) <= near {
7849 continue;
7850 }
7851 let (sx, sy, dep) = gfx.camera.project(wx, wy, wz);
7852 let sxi = sx as i32;
7853 let syi = sy as i32;
7854 if sxi < 0 || syi < 0 || sxi >= sw || syi >= sh {
7855 continue;
7856 }
7857 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.15, 1.0);
7859 let l = facing;
7860 let cr = (tr * l) as u32;
7861 let cg = (tg * l) as u32;
7862 let cb = (tb * l) as u32;
7863 let color = (cr << 16) | (cg << 8) | cb;
7864 let len = if facing > 0.7 { 1.0 } else { 0.0 };
7866 gfx.depth_queue.push_line(dep, color, sx, sy, sx + len, sy);
7867 }
7868 return Ok(Value::Unit);
7869 },
7870
7871 "project_3d"
7875 | "投影3D"
7876 | "3D投影"
7877 | "3D투영"
7878 | "ฉาย3มิติ"
7879 | "فرافکنی_سهبعدی"
7880 | "إسقاط_ثلاثي_الأبعاد"
7881 | "הטלה_תלת_ממדית"
7882 | "تھری_ڈی_پروجیکشن"
7883 | "projeter_3d"
7884 | "projizieren_3d"
7885 | "проекция_3d" => {
7886 let x = self.arg_num(&args, 0, 0.0)? as f32;
7887 let y = self.arg_num(&args, 1, 0.0)? as f32;
7888 let z = self.arg_num(&args, 2, 0.0)? as f32;
7889 let gfx = self.gfx.borrow();
7890 let near = -gfx.camera.zdist + 0.05;
7891 let d = gfx.camera.depth(x, y, z);
7892 if d <= near {
7893 return Ok(Value::List(Rc::new(vec![
7894 Value::Number(-99999.0),
7895 Value::Number(-99999.0),
7896 Value::Number(d as f64),
7897 ])));
7898 }
7899 let (sx, sy, depth) = gfx.camera.project(x, y, z);
7900 return Ok(Value::List(Rc::new(vec![
7901 Value::Number(sx as f64),
7902 Value::Number(sy as f64),
7903 Value::Number(depth as f64),
7904 ])));
7905 },
7906
7907 #[cfg(not(target_arch = "wasm32"))]
7914 "mouse_ray" => {
7915 let gfx = self.gfx.borrow();
7916 let (mx, my) = gfx
7917 .window
7918 .as_ref()
7919 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
7920 .unwrap_or((gfx.camera.cx, gfx.camera.cy));
7921 let cam = &gfx.camera;
7922 let dcx = (mx - cam.cx) / cam.focal;
7925 let dcy = (my - cam.cy) / cam.focal;
7926 let a_x = dcx;
7929 let a_y = cam.crx * dcy + cam.srx * 1.0;
7930 let a_z = 0.0 - cam.srx * dcy + cam.crx * 1.0;
7931 let dir_x = cam.cry * a_x + cam.sry * a_z;
7932 let dir_y = a_y;
7933 let dir_z = 0.0 - cam.sry * a_x + cam.cry * a_z;
7934 let dlen = (dir_x * dir_x + dir_y * dir_y + dir_z * dir_z)
7935 .sqrt()
7936 .max(1e-6);
7937 let (dir_x, dir_y, dir_z) = (dir_x / dlen, dir_y / dlen, dir_z / dlen);
7938 let ox = cam.tx;
7950 let oy = cam.ty;
7951 let oz = cam.tz;
7952 return Ok(Value::List(Rc::new(vec![
7953 Value::Number(ox as f64),
7954 Value::Number(oy as f64),
7955 Value::Number(oz as f64),
7956 Value::Number(dir_x as f64),
7957 Value::Number(dir_y as f64),
7958 Value::Number(dir_z as f64),
7959 ])));
7960 },
7961 #[cfg(target_arch = "wasm32")]
7962 "mouse_ray" => {
7963 let gfx = self.gfx.borrow();
7964 let mx = crate::gfx::wasm_mouse_x();
7965 let my = crate::gfx::wasm_mouse_y();
7966 let cam = &gfx.camera;
7967 let dcx = (mx - cam.cx) / cam.focal;
7968 let dcy = (my - cam.cy) / cam.focal;
7969 let a_x = dcx;
7970 let a_y = cam.crx * dcy + cam.srx * 1.0;
7971 let a_z = 0.0 - cam.srx * dcy + cam.crx * 1.0;
7972 let dir_x = cam.cry * a_x + cam.sry * a_z;
7973 let dir_y = a_y;
7974 let dir_z = 0.0 - cam.sry * a_x + cam.cry * a_z;
7975 let dlen = (dir_x * dir_x + dir_y * dir_y + dir_z * dir_z)
7976 .sqrt()
7977 .max(1e-6);
7978 let (dir_x, dir_y, dir_z) = (dir_x / dlen, dir_y / dlen, dir_z / dlen);
7979 let ox = cam.tx;
7980 let oy = cam.ty;
7981 let oz = cam.tz;
7982 return Ok(Value::List(Rc::new(vec![
7983 Value::Number(ox as f64),
7984 Value::Number(oy as f64),
7985 Value::Number(oz as f64),
7986 Value::Number(dir_x as f64),
7987 Value::Number(dir_y as f64),
7988 Value::Number(dir_z as f64),
7989 ])));
7990 },
7991 #[cfg(not(target_arch = "wasm32"))]
7994 "draw_poly"
7995 | "填充多边形"
7996 | "ポリゴン塗り"
7997 | "다각형채우기"
7998 | "เติมรูปหลายเหลี่ยม"
7999 | "رسم_چندضلعی"
8000 | "ارسم_مضلع"
8001 | "צייר_מצולע"
8002 | "کثیر_الاضلاع_کھینچو"
8003 | "dessiner_polygone"
8004 | "polygon_zeichnen"
8005 | "рисовать_полигон" => {
8006 let mut pts: Vec<[f32; 2]> = Vec::new();
8007 if let Some(Value::List(v)) = args.first() {
8008 let mut i = 0;
8009 while i + 1 < v.len() {
8010 let x = self.to_number(&v[i]).unwrap_or(0.0) as f32;
8011 let y = self.to_number(&v[i + 1]).unwrap_or(0.0) as f32;
8012 pts.push([x, y]);
8013 i += 2;
8014 }
8015 }
8016 if pts.len() >= 3 {
8017 if pts[0] != pts[pts.len() - 1] {
8018 let p0 = pts[0];
8019 pts.push(p0);
8020 } let mut gfx = self.gfx.borrow_mut();
8022 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
8023 crate::gfx::raster::fill_contours_aa(
8024 &mut gfx.buffer,
8025 w,
8026 h,
8027 color,
8028 add,
8029 std::slice::from_ref(&pts),
8030 );
8031 }
8032 return Ok(Value::Unit);
8033 },
8034
8035 "vtex_grid"
8044 | "ลายตาราง"
8045 | "纹格"
8046 | "格子模様"
8047 | "격자무늬"
8048 | "الگوی_شبکه"
8049 | "نقش_شبكة"
8050 | "דוגמת_רשת"
8051 | "نقش_جالی"
8052 | "motif_grille"
8053 | "muster_gitter"
8054 | "узор_сетка" => {
8055 let cx = self.arg_num(&args, 0, 0.)? as f32;
8056 let cy = self.arg_num(&args, 1, 0.)? as f32;
8057 let cz = self.arg_num(&args, 2, 0.)? as f32;
8058 let ux = self.arg_num(&args, 3, 1.)? as f32;
8059 let uy = self.arg_num(&args, 4, 0.)? as f32;
8060 let uz = self.arg_num(&args, 5, 0.)? as f32;
8061 let vx = self.arg_num(&args, 6, 0.)? as f32;
8062 let vy = self.arg_num(&args, 7, 0.)? as f32;
8063 let vz = self.arg_num(&args, 8, 1.)? as f32;
8064 let cols = self.arg_num(&args, 9, 10.)? as usize;
8065 let rows = self.arg_num(&args, 10, 10.)? as usize;
8066 let cw = self.arg_num(&args, 11, 1.)? as f32;
8067 let ch = self.arg_num(&args, 12, 1.)? as f32;
8068 let fr = self.arg_num(&args, 13, 0.)? as f32;
8069 let hue = self.arg_num(&args, 14, 0.)? as f32;
8070 let mut gfx = self.gfx.borrow_mut();
8071 let cam = gfx.camera.clone();
8072 crate::gfx::vtex::draw_grid(
8073 &mut gfx.depth_queue,
8074 &cam,
8075 cx,
8076 cy,
8077 cz,
8078 ux,
8079 uy,
8080 uz,
8081 vx,
8082 vy,
8083 vz,
8084 cols,
8085 rows,
8086 cw,
8087 ch,
8088 fr,
8089 hue,
8090 );
8091 return Ok(Value::Unit);
8092 },
8093
8094 "vtex_rings"
8096 | "ลายวงซ้อน"
8097 | "纹环"
8098 | "同心円"
8099 | "동심원"
8100 | "الگوی_حلقه"
8101 | "نقش_حلقات"
8102 | "דוגמת_טבעות"
8103 | "نقش_حلقے"
8104 | "motif_anneaux"
8105 | "muster_ringe"
8106 | "узор_кольца" => {
8107 let cx = self.arg_num(&args, 0, 0.)? as f32;
8108 let cy = self.arg_num(&args, 1, 0.)? as f32;
8109 let cz = self.arg_num(&args, 2, 0.)? as f32;
8110 let ux = self.arg_num(&args, 3, 1.)? as f32;
8111 let uy = self.arg_num(&args, 4, 0.)? as f32;
8112 let uz = self.arg_num(&args, 5, 0.)? as f32;
8113 let vx = self.arg_num(&args, 6, 0.)? as f32;
8114 let vy = self.arg_num(&args, 7, 0.)? as f32;
8115 let vz = self.arg_num(&args, 8, 1.)? as f32;
8116 let nr = self.arg_num(&args, 9, 6.)? as usize;
8117 let ns = self.arg_num(&args, 10, 6.)? as usize;
8118 let mr = self.arg_num(&args, 11, 3.)? as f32;
8119 let tw = self.arg_num(&args, 12, 0.)? as f32;
8120 let fr = self.arg_num(&args, 13, 0.)? as f32;
8121 let hue = self.arg_num(&args, 14, 0.)? as f32;
8122 let mut gfx = self.gfx.borrow_mut();
8123 let cam = gfx.camera.clone();
8124 crate::gfx::vtex::draw_rings(
8125 &mut gfx.depth_queue,
8126 &cam,
8127 cx,
8128 cy,
8129 cz,
8130 ux,
8131 uy,
8132 uz,
8133 vx,
8134 vy,
8135 vz,
8136 nr,
8137 ns,
8138 mr,
8139 tw,
8140 fr,
8141 hue,
8142 );
8143 return Ok(Value::Unit);
8144 },
8145
8146 "vtex_star"
8148 | "ลายดาว"
8149 | "纹星"
8150 | "星模様"
8151 | "별무늬"
8152 | "الگوی_ستاره"
8153 | "نقش_نجمة"
8154 | "דוגמת_כוכב"
8155 | "نقش_ستارہ"
8156 | "motif_étoile"
8157 | "muster_stern"
8158 | "узор_звезда" => {
8159 let cx = self.arg_num(&args, 0, 0.)? as f32;
8160 let cy = self.arg_num(&args, 1, 0.)? as f32;
8161 let cz = self.arg_num(&args, 2, 0.)? as f32;
8162 let ux = self.arg_num(&args, 3, 1.)? as f32;
8163 let uy = self.arg_num(&args, 4, 0.)? as f32;
8164 let uz = self.arg_num(&args, 5, 0.)? as f32;
8165 let vx = self.arg_num(&args, 6, 0.)? as f32;
8166 let vy = self.arg_num(&args, 7, 0.)? as f32;
8167 let vz = self.arg_num(&args, 8, 1.)? as f32;
8168 let np = self.arg_num(&args, 9, 6.)? as usize;
8169 let ro = self.arg_num(&args, 10, 2.)? as f32;
8170 let ri = self.arg_num(&args, 11, 1.)? as f32;
8171 let rs = self.arg_num(&args, 12, 0.01)? as f32;
8172 let fr = self.arg_num(&args, 13, 0.)? as f32;
8173 let hue = self.arg_num(&args, 14, 0.)? as f32;
8174 let mut gfx = self.gfx.borrow_mut();
8175 let cam = gfx.camera.clone();
8176 crate::gfx::vtex::draw_star(
8177 &mut gfx.depth_queue,
8178 &cam,
8179 cx,
8180 cy,
8181 cz,
8182 ux,
8183 uy,
8184 uz,
8185 vx,
8186 vy,
8187 vz,
8188 np,
8189 ro,
8190 ri,
8191 rs,
8192 fr,
8193 hue,
8194 );
8195 return Ok(Value::Unit);
8196 },
8197
8198 "vtex_spiral"
8200 | "ลายเกลียว"
8201 | "纹螺"
8202 | "螺旋"
8203 | "나선"
8204 | "الگوی_مارپیچ"
8205 | "نقش_حلزوني"
8206 | "דוגמת_ספירלה"
8207 | "نقش_سرپیچ"
8208 | "motif_spirale"
8209 | "muster_spirale"
8210 | "узор_спираль" => {
8211 let cx = self.arg_num(&args, 0, 0.)? as f32;
8212 let cy = self.arg_num(&args, 1, 0.)? as f32;
8213 let cz = self.arg_num(&args, 2, 0.)? as f32;
8214 let ux = self.arg_num(&args, 3, 1.)? as f32;
8215 let uy = self.arg_num(&args, 4, 0.)? as f32;
8216 let uz = self.arg_num(&args, 5, 0.)? as f32;
8217 let vx = self.arg_num(&args, 6, 0.)? as f32;
8218 let vy = self.arg_num(&args, 7, 0.)? as f32;
8219 let vz = self.arg_num(&args, 8, 1.)? as f32;
8220 let nt = self.arg_num(&args, 9, 3.)? as f32;
8221 let mr = self.arg_num(&args, 10, 3.)? as f32;
8222 let st = self.arg_num(&args, 11, 120.)? as usize;
8223 let fr = self.arg_num(&args, 12, 0.)? as f32;
8224 let hue = self.arg_num(&args, 13, 0.)? as f32;
8225 let mut gfx = self.gfx.borrow_mut();
8226 let cam = gfx.camera.clone();
8227 crate::gfx::vtex::draw_spiral(
8228 &mut gfx.depth_queue,
8229 &cam,
8230 cx,
8231 cy,
8232 cz,
8233 ux,
8234 uy,
8235 uz,
8236 vx,
8237 vy,
8238 vz,
8239 nt,
8240 mr,
8241 st,
8242 fr,
8243 hue,
8244 );
8245 return Ok(Value::Unit);
8246 },
8247
8248 "vtex_flower"
8250 | "ลายดอก"
8251 | "纹花"
8252 | "花模様"
8253 | "꽃무늬"
8254 | "الگوی_گل"
8255 | "نقش_زهرة"
8256 | "דוגמת_פרח"
8257 | "نقش_پھول"
8258 | "motif_fleur"
8259 | "muster_blume"
8260 | "узор_цветок" => {
8261 let cx = self.arg_num(&args, 0, 0.)? as f32;
8262 let cy = self.arg_num(&args, 1, 0.)? as f32;
8263 let cz = self.arg_num(&args, 2, 0.)? as f32;
8264 let ux = self.arg_num(&args, 3, 1.)? as f32;
8265 let uy = self.arg_num(&args, 4, 0.)? as f32;
8266 let uz = self.arg_num(&args, 5, 0.)? as f32;
8267 let vx = self.arg_num(&args, 6, 0.)? as f32;
8268 let vy = self.arg_num(&args, 7, 0.)? as f32;
8269 let vz = self.arg_num(&args, 8, 1.)? as f32;
8270 let r = self.arg_num(&args, 9, 1.)? as f32;
8271 let ns = self.arg_num(&args, 10, 24.)? as usize;
8272 let fr = self.arg_num(&args, 11, 0.)? as f32;
8273 let hue = self.arg_num(&args, 12, 0.)? as f32;
8274 let mut gfx = self.gfx.borrow_mut();
8275 let cam = gfx.camera.clone();
8276 crate::gfx::vtex::draw_flower(
8277 &mut gfx.depth_queue,
8278 &cam,
8279 cx,
8280 cy,
8281 cz,
8282 ux,
8283 uy,
8284 uz,
8285 vx,
8286 vy,
8287 vz,
8288 r,
8289 ns,
8290 fr,
8291 hue,
8292 );
8293 return Ok(Value::Unit);
8294 },
8295
8296 "vtex_letter_rain"
8298 | "ลายอักษรไหล"
8299 | "纹字雨"
8300 | "文字雨"
8301 | "글자비"
8302 | "الگوی_باران_حروف"
8303 | "نقش_مطر_الحروف"
8304 | "דוגמת_גשם_אותיות"
8305 | "نقش_حروف_بارش"
8306 | "motif_pluie_lettres"
8307 | "muster_buchstabenregen"
8308 | "узор_дождь_букв" => {
8309 let cx = self.arg_num(&args, 0, 0.)? as f32;
8310 let cy = self.arg_num(&args, 1, 0.)? as f32;
8311 let cz = self.arg_num(&args, 2, 0.)? as f32;
8312 let ux = self.arg_num(&args, 3, 1.)? as f32;
8313 let uy = self.arg_num(&args, 4, 0.)? as f32;
8314 let uz = self.arg_num(&args, 5, 0.)? as f32;
8315 let vx = self.arg_num(&args, 6, 0.)? as f32;
8316 let vy = self.arg_num(&args, 7, 0.)? as f32;
8317 let vz = self.arg_num(&args, 8, 1.)? as f32;
8318 let nc = self.arg_num(&args, 9, 16.)? as usize;
8319 let nv = self.arg_num(&args, 10, 14.)? as usize;
8320 let cw = self.arg_num(&args, 11, 0.65)? as f32;
8321 let rh = self.arg_num(&args, 12, 0.60)? as f32;
8322 let sp = self.arg_num(&args, 13, 0.025)? as f32;
8323 let fr = self.arg_num(&args, 14, 0.)? as f32;
8324 let hue = self.arg_num(&args, 15, 0.)? as f32;
8325 let mut gfx = self.gfx.borrow_mut();
8326 let cam = gfx.camera.clone();
8327 crate::gfx::vtex::draw_letter_rain(
8328 &mut gfx.depth_queue,
8329 &cam,
8330 cx,
8331 cy,
8332 cz,
8333 ux,
8334 uy,
8335 uz,
8336 vx,
8337 vy,
8338 vz,
8339 nc,
8340 nv,
8341 cw,
8342 rh,
8343 sp,
8344 fr,
8345 hue,
8346 );
8347 return Ok(Value::Unit);
8348 },
8349
8350 "vtex_hyperbolic_uv"
8352 | "ลายไฮเพอร์โบลิก"
8353 | "纹曲面"
8354 | "双曲線"
8355 | "쌍곡선"
8356 | "الگوی_هذلولی"
8357 | "نقش_زائدي"
8358 | "דוגמת_היפרבולית"
8359 | "نقش_ہائپربولک"
8360 | "motif_uv_hyperbolique"
8361 | "muster_hyperbolische_uv"
8362 | "узор_гиперболический_uv" => {
8363 let cx = self.arg_num(&args, 0, 0.)? as f32;
8364 let cy = self.arg_num(&args, 1, 0.)? as f32;
8365 let cz = self.arg_num(&args, 2, 0.)? as f32;
8366 let ux = self.arg_num(&args, 3, 1.)? as f32;
8367 let uy = self.arg_num(&args, 4, 0.)? as f32;
8368 let uz = self.arg_num(&args, 5, 0.)? as f32;
8369 let vx = self.arg_num(&args, 6, 0.)? as f32;
8370 let vy = self.arg_num(&args, 7, 0.)? as f32;
8371 let vz = self.arg_num(&args, 8, 1.)? as f32;
8372 let mr = self.arg_num(&args, 9, 5.)? as f32;
8373 let nc = self.arg_num(&args, 10, 12.)? as usize;
8374 let nr = self.arg_num(&args, 11, 18.)? as usize;
8375 let fr = self.arg_num(&args, 12, 0.)? as f32;
8376 let hue = self.arg_num(&args, 13, 0.)? as f32;
8377 let mut gfx = self.gfx.borrow_mut();
8378 let cam = gfx.camera.clone();
8379 crate::gfx::vtex::draw_hyperbolic_uv(
8380 &mut gfx.depth_queue,
8381 &cam,
8382 cx,
8383 cy,
8384 cz,
8385 ux,
8386 uy,
8387 uz,
8388 vx,
8389 vy,
8390 vz,
8391 mr,
8392 nc,
8393 nr,
8394 fr,
8395 hue,
8396 );
8397 return Ok(Value::Unit);
8398 },
8399
8400 "vtex_halftone"
8402 | "ลายจุด"
8403 | "纹半调"
8404 | "網点模様"
8405 | "망점"
8406 | "الگوی_نیمتن"
8407 | "نقش_نصفي"
8408 | "דוגמת_חצי_גוון"
8409 | "نقش_ہاف_ٹون"
8410 | "motif_demi_ton"
8411 | "muster_halbton"
8412 | "узор_растр" => {
8413 let cx = self.arg_num(&args, 0, 0.)? as f32;
8414 let cy = self.arg_num(&args, 1, 0.)? as f32;
8415 let cz = self.arg_num(&args, 2, 0.)? as f32;
8416 let ux = self.arg_num(&args, 3, 1.)? as f32;
8417 let uy = self.arg_num(&args, 4, 0.)? as f32;
8418 let uz = self.arg_num(&args, 5, 0.)? as f32;
8419 let vx = self.arg_num(&args, 6, 0.)? as f32;
8420 let vy = self.arg_num(&args, 7, 0.)? as f32;
8421 let vz = self.arg_num(&args, 8, 1.)? as f32;
8422 let cols = self.arg_num(&args, 9, 16.)? as usize;
8423 let rows = self.arg_num(&args, 10, 12.)? as usize;
8424 let cw = self.arg_num(&args, 11, 0.5)? as f32;
8425 let ch = self.arg_num(&args, 12, 0.5)? as f32;
8426 let dens = self.arg_num(&args, 13, 0.4)? as f32;
8427 let fr = self.arg_num(&args, 14, 0.)? as f32;
8428 let hue = self.arg_num(&args, 15, 0.)? as f32;
8429 let mut gfx = self.gfx.borrow_mut();
8430 let cam = gfx.camera.clone();
8431 crate::gfx::vtex::draw_halftone(
8432 &mut gfx.depth_queue,
8433 &cam,
8434 cx,
8435 cy,
8436 cz,
8437 ux,
8438 uy,
8439 uz,
8440 vx,
8441 vy,
8442 vz,
8443 cols,
8444 rows,
8445 cw,
8446 ch,
8447 dens,
8448 fr,
8449 hue,
8450 );
8451 return Ok(Value::Unit);
8452 },
8453
8454 "vtex_tessellated"
8456 | "ลายตาข่าย"
8457 | "纹镶嵌"
8458 | "網目模様"
8459 | "격자망"
8460 | "الگوی_کاشیکاری"
8461 | "نقش_مرصوف_متكرر"
8462 | "דוגמת_ריצוף_חוזר"
8463 | "نقش_ٹائلنگ"
8464 | "motif_tesselle"
8465 | "muster_tessellation"
8466 | "узор_мозаика" => {
8467 let cx = self.arg_num(&args, 0, 0.)? as f32;
8468 let cy = self.arg_num(&args, 1, 0.)? as f32;
8469 let cz = self.arg_num(&args, 2, 0.)? as f32;
8470 let ux = self.arg_num(&args, 3, 1.)? as f32;
8471 let uy = self.arg_num(&args, 4, 0.)? as f32;
8472 let uz = self.arg_num(&args, 5, 0.)? as f32;
8473 let vx = self.arg_num(&args, 6, 0.)? as f32;
8474 let vy = self.arg_num(&args, 7, 0.)? as f32;
8475 let vz = self.arg_num(&args, 8, 1.)? as f32;
8476 let cols = self.arg_num(&args, 9, 14.)? as usize;
8477 let rows = self.arg_num(&args, 10, 10.)? as usize;
8478 let cell = self.arg_num(&args, 11, 0.6)? as f32;
8479 let amp = self.arg_num(&args, 12, 0.25)? as f32;
8480 let freq = self.arg_num(&args, 13, 4.)? as f32;
8481 let fr = self.arg_num(&args, 14, 0.)? as f32;
8482 let hue = self.arg_num(&args, 15, 0.)? as f32;
8483 let mut gfx = self.gfx.borrow_mut();
8484 let cam = gfx.camera.clone();
8485 crate::gfx::vtex::draw_tessellated(
8486 &mut gfx.depth_queue,
8487 &cam,
8488 cx,
8489 cy,
8490 cz,
8491 ux,
8492 uy,
8493 uz,
8494 vx,
8495 vy,
8496 vz,
8497 cols,
8498 rows,
8499 cell,
8500 amp,
8501 freq,
8502 fr,
8503 hue,
8504 );
8505 return Ok(Value::Unit);
8506 },
8507
8508 "vtex_lotus"
8510 | "ลายดอกบัว"
8511 | "纹莲"
8512 | "蓮模様"
8513 | "연꽃무늬"
8514 | "الگوی_لوتوس"
8515 | "نقش_لوتس"
8516 | "דוגמת_לוטוס"
8517 | "نقش_کنول"
8518 | "motif_lotus"
8519 | "muster_lotus"
8520 | "узор_лотос" => {
8521 let cx = self.arg_num(&args, 0, 0.)? as f32;
8522 let cy = self.arg_num(&args, 1, 0.)? as f32;
8523 let cz = self.arg_num(&args, 2, 0.)? as f32;
8524 let ux = self.arg_num(&args, 3, 1.)? as f32;
8525 let uy = self.arg_num(&args, 4, 0.)? as f32;
8526 let uz = self.arg_num(&args, 5, 0.)? as f32;
8527 let vx = self.arg_num(&args, 6, 0.)? as f32;
8528 let vy = self.arg_num(&args, 7, 0.)? as f32;
8529 let vz = self.arg_num(&args, 8, 1.)? as f32;
8530 let ri = self.arg_num(&args, 9, 1.)? as f32;
8531 let ro = self.arg_num(&args, 10, 2.)? as f32;
8532 let np = self.arg_num(&args, 11, 12.)? as usize;
8533 let fr = self.arg_num(&args, 12, 0.)? as f32;
8534 let hue = self.arg_num(&args, 13, 0.)? as f32;
8535 let mut gfx = self.gfx.borrow_mut();
8536 let cam = gfx.camera.clone();
8537 crate::gfx::vtex::draw_lotus(
8538 &mut gfx.depth_queue,
8539 &cam,
8540 cx,
8541 cy,
8542 cz,
8543 ux,
8544 uy,
8545 uz,
8546 vx,
8547 vy,
8548 vz,
8549 ri,
8550 ro,
8551 np,
8552 fr,
8553 hue,
8554 );
8555 return Ok(Value::Unit);
8556 },
8557
8558 "vtex_chakra"
8560 | "ลายจักร"
8561 | "纹轮"
8562 | "輪模様"
8563 | "바퀴무늬"
8564 | "الگوی_چاکرا"
8565 | "نقش_تشاكرا"
8566 | "דוגמת_צ'אקרה"
8567 | "نقش_چکر"
8568 | "motif_chakra"
8569 | "muster_chakra"
8570 | "узор_чакра" => {
8571 let cx = self.arg_num(&args, 0, 0.)? as f32;
8572 let cy = self.arg_num(&args, 1, 0.)? as f32;
8573 let cz = self.arg_num(&args, 2, 0.)? as f32;
8574 let ux = self.arg_num(&args, 3, 1.)? as f32;
8575 let uy = self.arg_num(&args, 4, 0.)? as f32;
8576 let uz = self.arg_num(&args, 5, 0.)? as f32;
8577 let vx = self.arg_num(&args, 6, 0.)? as f32;
8578 let vy = self.arg_num(&args, 7, 0.)? as f32;
8579 let vz = self.arg_num(&args, 8, 1.)? as f32;
8580 let r = self.arg_num(&args, 9, 2.)? as f32;
8581 let ns = self.arg_num(&args, 10, 8.)? as usize;
8582 let fr = self.arg_num(&args, 11, 0.)? as f32;
8583 let hue = self.arg_num(&args, 12, 0.)? as f32;
8584 let mut gfx = self.gfx.borrow_mut();
8585 let cam = gfx.camera.clone();
8586 crate::gfx::vtex::draw_chakra(
8587 &mut gfx.depth_queue,
8588 &cam,
8589 cx,
8590 cy,
8591 cz,
8592 ux,
8593 uy,
8594 uz,
8595 vx,
8596 vy,
8597 vz,
8598 r,
8599 ns,
8600 fr,
8601 hue,
8602 );
8603 return Ok(Value::Unit);
8604 },
8605
8606 "vtex_yantra"
8608 | "ลายยันต์"
8609 | "纹咒"
8610 | "護符模様"
8611 | "부적무늬"
8612 | "الگوی_یانترا"
8613 | "نقش_يانترا"
8614 | "דוגמת_יאנטרה"
8615 | "نقش_ینترا"
8616 | "motif_yantra"
8617 | "muster_yantra"
8618 | "узор_янтра" => {
8619 let cx = self.arg_num(&args, 0, 0.)? as f32;
8620 let cy = self.arg_num(&args, 1, 0.)? as f32;
8621 let cz = self.arg_num(&args, 2, 0.)? as f32;
8622 let ux = self.arg_num(&args, 3, 1.)? as f32;
8623 let uy = self.arg_num(&args, 4, 0.)? as f32;
8624 let uz = self.arg_num(&args, 5, 0.)? as f32;
8625 let vx = self.arg_num(&args, 6, 0.)? as f32;
8626 let vy = self.arg_num(&args, 7, 0.)? as f32;
8627 let vz = self.arg_num(&args, 8, 1.)? as f32;
8628 let nl = self.arg_num(&args, 9, 4.)? as usize;
8629 let mr = self.arg_num(&args, 10, 3.)? as f32;
8630 let fr = self.arg_num(&args, 11, 0.)? as f32;
8631 let hue = self.arg_num(&args, 12, 0.)? as f32;
8632 let mut gfx = self.gfx.borrow_mut();
8633 let cam = gfx.camera.clone();
8634 crate::gfx::vtex::draw_yantra(
8635 &mut gfx.depth_queue,
8636 &cam,
8637 cx,
8638 cy,
8639 cz,
8640 ux,
8641 uy,
8642 uz,
8643 vx,
8644 vy,
8645 vz,
8646 nl,
8647 mr,
8648 fr,
8649 hue,
8650 );
8651 return Ok(Value::Unit);
8652 },
8653
8654 "vtex_spiked_cog"
8656 | "ฟันเฟืองหนาม"
8657 | "纹棘轮"
8658 | "歯車模様"
8659 | "톱니바퀴"
8660 | "الگوی_چرخدنده_خاردار"
8661 | "نقش_ترس_شائك"
8662 | "דוגמת_גלגל_קוצני"
8663 | "نقش_خاردار_گیئر"
8664 | "motif_engrenage_pointes"
8665 | "muster_stachelzahnrad"
8666 | "узор_шестерня_шипы" => {
8667 let cx = self.arg_num(&args, 0, 0.)? as f32;
8668 let cy = self.arg_num(&args, 1, 0.)? as f32;
8669 let cz = self.arg_num(&args, 2, 0.)? as f32;
8670 let ux = self.arg_num(&args, 3, 1.)? as f32;
8671 let uy = self.arg_num(&args, 4, 0.)? as f32;
8672 let uz = self.arg_num(&args, 5, 0.)? as f32;
8673 let vx = self.arg_num(&args, 6, 0.)? as f32;
8674 let vy = self.arg_num(&args, 7, 0.)? as f32;
8675 let vz = self.arg_num(&args, 8, 1.)? as f32;
8676 let nt = self.arg_num(&args, 9, 12.)? as usize;
8677 let rb = self.arg_num(&args, 10, 1.)? as f32;
8678 let rs = self.arg_num(&args, 11, 1.3)? as f32;
8679 let rh = self.arg_num(&args, 12, 0.2)? as f32;
8680 let ns = self.arg_num(&args, 13, 6.)? as usize;
8681 let fr = self.arg_num(&args, 14, 0.)? as f32;
8682 let hue = self.arg_num(&args, 15, 0.)? as f32;
8683 let mut gfx = self.gfx.borrow_mut();
8684 let cam = gfx.camera.clone();
8685 crate::gfx::vtex::draw_spiked_cog(
8686 &mut gfx.depth_queue,
8687 &cam,
8688 cx,
8689 cy,
8690 cz,
8691 ux,
8692 uy,
8693 uz,
8694 vx,
8695 vy,
8696 vz,
8697 nt,
8698 rb,
8699 rs,
8700 rh,
8701 ns,
8702 fr,
8703 hue,
8704 );
8705 return Ok(Value::Unit);
8706 },
8707
8708 "vtex_torii"
8710 | "ประตูโทริอิ"
8711 | "纹鸟居"
8712 | "鳥居"
8713 | "도리이"
8714 | "الگوی_توری_ژاپنی"
8715 | "نقش_توري"
8716 | "דוגמת_טוריי"
8717 | "نقش_توری_گیٹ"
8718 | "motif_torii"
8719 | "muster_torii"
8720 | "узор_тории" => {
8721 let cx = self.arg_num(&args, 0, 0.)? as f32;
8722 let cy = self.arg_num(&args, 1, 0.)? as f32;
8723 let cz = self.arg_num(&args, 2, 0.)? as f32;
8724 let ux = self.arg_num(&args, 3, 1.)? as f32;
8725 let uy = self.arg_num(&args, 4, 0.)? as f32;
8726 let uz = self.arg_num(&args, 5, 0.)? as f32;
8727 let vx = self.arg_num(&args, 6, 0.)? as f32;
8728 let vy = self.arg_num(&args, 7, 0.)? as f32;
8729 let vz = self.arg_num(&args, 8, 1.)? as f32;
8730 let w = self.arg_num(&args, 9, 4.)? as f32;
8731 let h = self.arg_num(&args, 10, 5.)? as f32;
8732 let fr = self.arg_num(&args, 11, 0.)? as f32;
8733 let hue = self.arg_num(&args, 12, 0.)? as f32;
8734 let mut gfx = self.gfx.borrow_mut();
8735 let cam = gfx.camera.clone();
8736 crate::gfx::vtex::draw_torii(
8737 &mut gfx.depth_queue,
8738 &cam,
8739 cx,
8740 cy,
8741 cz,
8742 ux,
8743 uy,
8744 uz,
8745 vx,
8746 vy,
8747 vz,
8748 w,
8749 h,
8750 fr,
8751 hue,
8752 );
8753 return Ok(Value::Unit);
8754 },
8755
8756 "vtex_pagoda"
8758 | "เจดีย์"
8759 | "纹塔"
8760 | "塔"
8761 | "탑"
8762 | "الگوی_پاگودا"
8763 | "نقش_باغودا"
8764 | "דוגמת_פגודה"
8765 | "نقش_پگوڈا"
8766 | "motif_pagode"
8767 | "muster_pagode"
8768 | "узор_пагода" => {
8769 let cx = self.arg_num(&args, 0, 0.)? as f32;
8770 let cy = self.arg_num(&args, 1, 0.)? as f32;
8771 let cz = self.arg_num(&args, 2, 0.)? as f32;
8772 let ux = self.arg_num(&args, 3, 1.)? as f32;
8773 let uy = self.arg_num(&args, 4, 0.)? as f32;
8774 let uz = self.arg_num(&args, 5, 0.)? as f32;
8775 let vx = self.arg_num(&args, 6, 0.)? as f32;
8776 let vy = self.arg_num(&args, 7, 0.)? as f32;
8777 let vz = self.arg_num(&args, 8, 1.)? as f32;
8778 let nt = self.arg_num(&args, 9, 5.)? as usize;
8779 let bw = self.arg_num(&args, 10, 2.)? as f32;
8780 let th = self.arg_num(&args, 11, 1.)? as f32;
8781 let tp = self.arg_num(&args, 12, 0.72)? as f32;
8782 let eo = self.arg_num(&args, 13, 0.28)? as f32;
8783 let fr = self.arg_num(&args, 14, 0.)? as f32;
8784 let hue = self.arg_num(&args, 15, 0.)? as f32;
8785 let mut gfx = self.gfx.borrow_mut();
8786 let cam = gfx.camera.clone();
8787 crate::gfx::vtex::draw_pagoda(
8788 &mut gfx.depth_queue,
8789 &cam,
8790 cx,
8791 cy,
8792 cz,
8793 ux,
8794 uy,
8795 uz,
8796 vx,
8797 vy,
8798 vz,
8799 nt,
8800 bw,
8801 th,
8802 tp,
8803 eo,
8804 fr,
8805 hue,
8806 );
8807 return Ok(Value::Unit);
8808 },
8809
8810 #[cfg(not(target_arch = "wasm32"))]
8816 "audio_tone"
8817 | "เสียงโทน"
8818 | "音调"
8819 | "音調"
8820 | "음조"
8821 | "空间音"
8822 | "空間音"
8823 | "공간음"
8824 | "تن_صدا"
8825 | "نغمة"
8826 | "צליל"
8827 | "آواز_کا_سر"
8828 | "tonalité_audio"
8829 | "audioton"
8830 | "звук_тон" => {
8831 let idx = self.arg_num(&args, 0, 0.0)? as usize;
8832 let x = self.arg_num(&args, 1, 0.0)? as f32;
8833 let y = self.arg_num(&args, 2, 0.0)? as f32;
8834 let z = self.arg_num(&args, 3, 0.0)? as f32;
8835 let w = self.arg_num(&args, 4, 1.0)? as f32;
8836 let freq = self.arg_num(&args, 5, 220.0)? as f32;
8837 let amp = self.arg_num(&args, 6, 0.15)? as f32;
8838 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
8839 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
8840 if let Some(audio) = &self.audio {
8841 audio.set_tone(
8842 idx,
8843 ToneParams { x, y, z, w, freq, amp, lfo_rate, lfo_depth },
8844 );
8845 }
8846 return Ok(Value::Unit);
8847 },
8848
8849 #[cfg(not(target_arch = "wasm32"))]
8850 "audio_listener"
8851 | "ผู้ฟัง"
8852 | "音频监听"
8853 | "音声リスナー"
8854 | "오디오리스너"
8855 | "شنونده_صدا"
8856 | "مستمع_الصوت"
8857 | "מאזין_קול"
8858 | "آواز_سننے_والا"
8859 | "auditeur_audio"
8860 | "audiohörer"
8861 | "звук_слушатель" => {
8862 let cry = self.arg_num(&args, 0, 1.0)? as f32;
8863 let sry = self.arg_num(&args, 1, 0.0)? as f32;
8864 let crx = self.arg_num(&args, 2, 1.0)? as f32;
8865 let srx = self.arg_num(&args, 3, 0.0)? as f32;
8866 if let Some(audio) = &self.audio {
8867 audio.set_listener(cry, sry, crx, srx);
8868 }
8869 return Ok(Value::Unit);
8870 },
8871
8872 #[cfg(not(target_arch = "wasm32"))]
8873 "audio_bgm"
8874 | "เพลงพื้นหลัง"
8875 | "เพลงประกอบ"
8876 | "背景乐"
8877 | "BGM"
8878 | "배경음악"
8879 | "موسیقی_پسزمینه"
8880 | "موسيقى_خلفية"
8881 | "מוזיקת_רקע"
8882 | "پس_منظر_موسیقی"
8883 | "musique_fond"
8884 | "hintergrundmusik"
8885 | "фоновая_музыка" => {
8886 let path = match args.first() {
8887 Some(Value::Str(s)) => s.clone(),
8888 _ => return Ok(Value::Unit),
8889 };
8890 let vol = self.arg_num(&args, 1, 0.5)? as f32;
8891 if let Some(audio) = &self.audio {
8892 audio.load_bgm(&path, vol);
8893 }
8894 return Ok(Value::Unit);
8895 },
8896
8897 #[cfg(not(target_arch = "wasm32"))]
8898 "audio_bgm_volume"
8899 | "ระดับเสียงพื้นหลัง"
8900 | "ระดับเพลงประกอบ"
8901 | "背景乐音量"
8902 | "BGM音量"
8903 | "배경음악음량"
8904 | "بلندی_موسیقی_پسزمینه"
8905 | "مستوى_موسيقى_الخلفية"
8906 | "עוצמת_מוזיקת_רקע"
8907 | "پس_منظر_موسیقی_شدت"
8908 | "volume_musique_fond"
8909 | "hintergrundmusiklautstärke"
8910 | "громкость_фоновой_музыки" => {
8911 let vol = self.arg_num(&args, 0, 0.5)? as f32;
8912 if let Some(audio) = &self.audio {
8913 audio.set_bgm_volume(vol);
8914 }
8915 return Ok(Value::Unit);
8916 },
8917
8918 #[cfg(not(target_arch = "wasm32"))]
8919 "audio_volume"
8920 | "ระดับเสียง"
8921 | "音量"
8922 | "음량"
8923 | "بلندی_صدا"
8924 | "مستوى_الصوت"
8925 | "עוצמת_קול"
8926 | "آواز_کی_شدت"
8927 | "volume_audio"
8928 | "audiolautstärke"
8929 | "звук_громкость" => {
8930 let vol = self.arg_num(&args, 0, 0.7)? as f32;
8931 if let Some(audio) = &self.audio {
8932 audio.set_master_volume(vol);
8933 }
8934 return Ok(Value::Unit);
8935 },
8936
8937 #[cfg(target_arch = "wasm32")]
8939 "audio_tone"
8940 | "เสียงโทน"
8941 | "音调"
8942 | "音調"
8943 | "음조"
8944 | "空间音"
8945 | "空間音"
8946 | "공간음"
8947 | "تن_صدا"
8948 | "نغمة"
8949 | "צליל"
8950 | "آواز_کا_سر"
8951 | "tonalité_audio"
8952 | "audioton"
8953 | "звук_тон" => {
8954 let idx = self.arg_num(&args, 0, 0.0)? as usize;
8955 let x = self.arg_num(&args, 1, 0.0)? as f32;
8956 let y = self.arg_num(&args, 2, 0.0)? as f32;
8957 let z = self.arg_num(&args, 3, 0.0)? as f32;
8958 let w = self.arg_num(&args, 4, 1.0)? as f32;
8959 let freq = self.arg_num(&args, 5, 220.0)? as f32;
8960 let amp = self.arg_num(&args, 6, 0.15)? as f32;
8961 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
8962 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
8963 crate::gfx::audio_web::set_tone(idx, x, y, z, w, freq, amp, lfo_rate, lfo_depth);
8964 return Ok(Value::Unit);
8965 },
8966
8967 #[cfg(target_arch = "wasm32")]
8968 "audio_listener"
8969 | "ผู้ฟัง"
8970 | "音频监听"
8971 | "音声リスナー"
8972 | "오디오리스너"
8973 | "شنونده_صدا"
8974 | "مستمع_الصوت"
8975 | "מאזין_קול"
8976 | "آواز_سننے_والا"
8977 | "auditeur_audio"
8978 | "audiohörer"
8979 | "звук_слушатель" => {
8980 let cry = self.arg_num(&args, 0, 1.0)? as f32;
8981 let sry = self.arg_num(&args, 1, 0.0)? as f32;
8982 let crx = self.arg_num(&args, 2, 1.0)? as f32;
8983 let srx = self.arg_num(&args, 3, 0.0)? as f32;
8984 crate::gfx::audio_web::set_listener(cry, sry, crx, srx);
8985 return Ok(Value::Unit);
8986 },
8987
8988 #[cfg(target_arch = "wasm32")]
8989 "audio_bgm"
8990 | "เพลงพื้นหลัง"
8991 | "เพลงประกอบ"
8992 | "背景乐"
8993 | "BGM"
8994 | "배경음악"
8995 | "موسیقی_پسزمینه"
8996 | "موسيقى_خلفية"
8997 | "מוזיקת_רקע"
8998 | "پس_منظر_موسیقی"
8999 | "musique_fond"
9000 | "hintergrundmusik"
9001 | "фоновая_музыка" => {
9002 let path = self.arg_str(&args, 0, "");
9003 let vol = self.arg_num(&args, 1, 0.5)? as f32;
9004 crate::gfx::audio_web::load_bgm(&path, vol);
9005 return Ok(Value::Unit);
9006 },
9007
9008 #[cfg(target_arch = "wasm32")]
9009 "audio_bgm_volume"
9010 | "ระดับเสียงพื้นหลัง"
9011 | "ระดับเพลงประกอบ"
9012 | "背景乐音量"
9013 | "BGM音量"
9014 | "배경음악음량"
9015 | "بلندی_موسیقی_پسزمینه"
9016 | "مستوى_موسيقى_الخلفية"
9017 | "עוצמת_מוזיקת_רקע"
9018 | "پس_منظر_موسیقی_شدت"
9019 | "volume_musique_fond"
9020 | "hintergrundmusiklautstärke"
9021 | "громкость_фоновой_музыки" => {
9022 let vol = self.arg_num(&args, 0, 0.5)? as f32;
9023 crate::gfx::audio_web::set_bgm_volume(vol);
9024 return Ok(Value::Unit);
9025 },
9026
9027 #[cfg(target_arch = "wasm32")]
9028 "audio_volume"
9029 | "ระดับเสียง"
9030 | "音量"
9031 | "음량"
9032 | "بلندی_صدا"
9033 | "مستوى_الصوت"
9034 | "עוצמת_קול"
9035 | "آواز_کی_شدت"
9036 | "volume_audio"
9037 | "audiolautstärke"
9038 | "звук_громкость" => {
9039 let vol = self.arg_num(&args, 0, 0.7)? as f32;
9040 crate::gfx::audio_web::set_master_volume(vol);
9041 return Ok(Value::Unit);
9042 },
9043
9044 #[cfg(target_arch = "wasm32")]
9046 "audio_sample_load"
9047 | "载入采样"
9048 | "サンプル読込"
9049 | "샘플로드"
9050 | "โหลดตัวอย่างเสียง"
9051 | "بارگذاری_نمونه_صدا"
9052 | "تحميل_عينة_صوتية"
9053 | "טעינת_דגימת_קול"
9054 | "آواز_نمونہ_لوڈ"
9055 | "charger_échantillon"
9056 | "sample_laden"
9057 | "загрузить_семпл" => {
9058 let path = self.arg_str(&args, 0, "");
9059 let resolved = self.wasm_resolve_source_path(&path);
9060 match wasm_fetch_bytes(&resolved)
9061 .and_then(|bytes| ling_music::from_bytes(&bytes).map_err(|e| e.to_string()))
9062 {
9063 Ok(t) => {
9064 let id = crate::gfx::audio_web::add_sample(&t.stereo, t.channels, t.rate);
9065 return Ok(Value::Number(id as f64));
9066 },
9067 Err(e) => {
9068 eprintln!("audio_sample_load failed ({path}): {e}");
9069 return Ok(Value::Number(-1.0));
9070 },
9071 }
9072 },
9073 #[cfg(target_arch = "wasm32")]
9074 "audio_sample_play"
9075 | "播放采样"
9076 | "サンプル再生"
9077 | "샘플재생"
9078 | "เล่นตัวอย่างเสียง"
9079 | "پخش_نمونه_صدا"
9080 | "تشغيل_عينة_صوتية"
9081 | "נגינת_דגימת_קול"
9082 | "آواز_نمونہ_چلاؤ"
9083 | "jouer_échantillon"
9084 | "sample_abspielen"
9085 | "играть_семпл" => {
9086 let id = self.arg_num(&args, 0, 0.0)? as usize;
9087 let x = self.arg_num(&args, 1, 0.0)? as f32;
9088 let y = self.arg_num(&args, 2, 0.0)? as f32;
9089 let z = self.arg_num(&args, 3, 0.0)? as f32;
9090 let vol = self.arg_num(&args, 5, 1.0)? as f32;
9092 let looping = self.arg_num(&args, 6, 0.0)? > 0.5;
9093 crate::gfx::audio_web::play_sample(id, x, y, z, vol, looping);
9094 return Ok(Value::Number(0.0));
9095 },
9096 #[cfg(target_arch = "wasm32")]
9097 "audio_sample_stop"
9098 | "停止采样"
9099 | "サンプル停止"
9100 | "샘플정지"
9101 | "หยุดตัวอย่างเสียง"
9102 | "audio_fx_reverb"
9103 | "混响"
9104 | "リバーブ"
9105 | "리버브"
9106 | "เสียงก้อง"
9107 | "audio_fx_delay"
9108 | "回声"
9109 | "ディレイ効果"
9110 | "딜레이"
9111 | "เสียงสะท้อน"
9112 | "audio_fx_lowpass"
9113 | "低通滤波"
9114 | "ローパス"
9115 | "저역통과"
9116 | "กรองความถี่ต่ำ"
9117 | "توقف_نمونه_صدا"
9118 | "إيقاف_عينة_صوتية"
9119 | "עצירת_דגימת_קול"
9120 | "آواز_نمونہ_روکو"
9121 | "arrêter_échantillon"
9122 | "sample_stoppen"
9123 | "остановить_семпл" => {
9124 return Ok(Value::Unit);
9125 },
9126
9127 "รอหน้าต่าง"
9129 | "wait_window"
9130 | "gfx_wait"
9131 | "انتظار_پنجره"
9132 | "انتظر_النافذة"
9133 | "המתן_לחלון"
9134 | "ونڈو_انتظار" => {
9135 #[cfg(not(target_arch = "wasm32"))]
9136 loop {
9137 let still_open = {
9138 let gfx = self.gfx.borrow();
9139 gfx.window
9140 .as_ref()
9141 .map(|w| w.is_open() && !w.is_key_down(minifb::Key::Escape))
9142 .unwrap_or(false)
9143 };
9144 if !still_open {
9145 break;
9146 }
9147 let (buf, w, h) = {
9148 let gfx = self.gfx.borrow();
9149 (gfx.buffer.clone(), gfx.width, gfx.height)
9150 };
9151 let mut gfx = self.gfx.borrow_mut();
9152 if let Some(win) = gfx.window.as_mut() {
9153 if win.update_with_buffer(&buf, w, h).is_err() {
9154 break;
9155 }
9156 }
9157 }
9158 return Ok(Value::Unit);
9159 },
9160
9161 "read_file" | "อ่านไฟล์" | "خواندن_فایل" | "اقرأ_الملف" | "קרא_קובץ" | "فائل_پڑھو" =>
9163 {
9164 #[cfg(target_arch = "wasm32")]
9165 return Ok(Value::Str(String::new()));
9166 #[cfg(not(target_arch = "wasm32"))]
9167 {
9168 let path = self.arg_str(&args, 0, "").replace('\\', "/");
9169 return std::fs::read_to_string(&path)
9170 .map(Value::Str)
9171 .map_err(|e| EvalErr::from(format!("read_file '{path}': {e}")));
9172 }
9173 },
9174 #[cfg(not(target_arch = "wasm32"))]
9176 "net_host" | "เน็ตโฮสต์" | "میزبانی_شبکه" | "استضف_الشبكة" | "ארח_רשת" | "نیٹ_ہوسٹ" =>
9177 {
9178 let port = self.arg_num(&args, 0, 7777.0)? as u16;
9179 net::host(port);
9180 return Ok(Value::Unit);
9181 },
9182 #[cfg(not(target_arch = "wasm32"))]
9183 "net_join" | "เน็ตจอย" | "پیوستن_شبکه" | "انضم_للشبكة" | "הצטרף_לרשת" | "نیٹ_شمولیت" =>
9184 {
9185 let ip = self.arg_str(&args, 0, "127.0.0.1");
9186 let port = self.arg_num(&args, 1, 7777.0)? as u16;
9187 net::join(&ip, port);
9188 return Ok(Value::Unit);
9189 },
9190 #[cfg(not(target_arch = "wasm32"))]
9191 "net_send" | "เน็ตส่ง" | "ارسال_شبکه" | "أرسل_عبر_الشبكة" | "שלח_ברשת" | "نیٹ_بھیجو" =>
9192 {
9193 let s = self.arg_str(&args, 0, "");
9194 net::send(&s);
9195 return Ok(Value::Unit);
9196 },
9197 #[cfg(not(target_arch = "wasm32"))]
9198 "net_recv" | "เน็ตรับ" | "دریافت_شبکه" | "استقبل_من_الشبكة" | "קבל_מרשת" | "نیٹ_وصول" =>
9199 {
9200 return Ok(Value::Str(net::recv()));
9201 },
9202 #[cfg(not(target_arch = "wasm32"))]
9203 "net_status" | "เน็ตสถานะ" | "وضعیت_شبکه" | "حالة_الشبكة" | "סטטוס_רשת" | "نیٹ_حالت" =>
9204 {
9205 return Ok(Value::Number(net::status() as f64));
9206 },
9207 #[cfg(not(target_arch = "wasm32"))]
9208 "net_recv_from" => {
9209 return Ok(Value::Str(net::recv_from()));
9210 },
9211 #[cfg(not(target_arch = "wasm32"))]
9212 "net_send_to" => {
9213 let id = self.arg_num(&args, 0, 0.0)? as u64;
9214 let s = self.arg_str(&args, 1, "");
9215 net::send_to(id, &s);
9216 return Ok(Value::Unit);
9217 },
9218 #[cfg(not(target_arch = "wasm32"))]
9219 "net_close" | "연결종료" => {
9220 net::close();
9221 return Ok(Value::Unit);
9222 },
9223 #[cfg(not(target_arch = "wasm32"))]
9225 "net_announce"
9226 | "เน็ตประกาศ"
9227 | "اعلام_شبکه"
9228 | "أعلن_في_الشبكة"
9229 | "הכרז_ברשת"
9230 | "نیٹ_اعلان" => {
9231 let port = self.arg_num(&args, 0, 7778.0)? as u16;
9232 let info = self.arg_str(&args, 1, "");
9233 net::announce(port, &info);
9234 return Ok(Value::Unit);
9235 },
9236 #[cfg(not(target_arch = "wasm32"))]
9237 "net_announce_stop"
9238 | "เน็ตหยุดประกาศ"
9239 | "توقف_اعلام"
9240 | "أوقف_الإعلان"
9241 | "עצור_הכרזה"
9242 | "اعلان_روکو" => {
9243 net::announce_stop();
9244 return Ok(Value::Unit);
9245 },
9246 #[cfg(not(target_arch = "wasm32"))]
9247 "net_discover" | "เน็ตค้นหา" | "کشف_شبکه" | "اكتشف_الشبكة" | "גלה_רשת" | "نیٹ_دریافت" =>
9248 {
9249 let port = self.arg_num(&args, 0, 7778.0)? as u16;
9250 return Ok(Value::Str(net::discover(port)));
9251 },
9252 #[cfg(not(target_arch = "wasm32"))]
9253 "net_test" | "เน็ตทดสอบ" | "آزمون_شبکه" | "اختبر_الشبكة" | "בדוק_רשת" | "نیٹ_ٹیسٹ" =>
9254 {
9255 let port = self.arg_num(&args, 0, 7777.0)? as u16;
9256 return Ok(Value::Str(net::test_bind(port)));
9257 },
9258 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9260 "http_route" | "เว็บเส้นทาง" | "مسیر_HTTP" | "مسار_HTTP" | "נתיב_HTTP" | "HTTP_روٹ" =>
9261 {
9262 let method = self.arg_str(&args, 0, "GET").to_uppercase();
9263 let path = self.arg_str(&args, 1, "/");
9264 let handler = args.get(2).cloned().unwrap_or(Value::Unit);
9265 self.http_routes.push((method, path, handler));
9266 return Ok(Value::Unit);
9267 },
9268 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9272 "http_static"
9273 | "เว็บสแตติก"
9274 | "فایل_ایستای_HTTP"
9275 | "ملفات_HTTP_ثابتة"
9276 | "קבצים_סטטיים_HTTP"
9277 | "HTTP_مستقل_فائل" => {
9278 let prefix = self.arg_str(&args, 0, "/static");
9279 let dir = self.arg_str(&args, 1, "static");
9280 self.http_static_dirs.push((prefix, dir));
9281 return Ok(Value::Unit);
9282 },
9283 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9284 "http_serve" | "เว็บเสิร์ฟ" | "سرویس_HTTP" | "قدّم_HTTP" | "הגש_HTTP" | "HTTP_سرو" =>
9285 {
9286 let host = self.arg_str(&args, 0, "127.0.0.1");
9287 let port = self.arg_num(&args, 1, 8080.0)? as u16;
9288 let routes = std::mem::take(&mut self.http_routes);
9289 let static_dirs = std::mem::take(&mut self.http_static_dirs);
9290 let rx = web::spawn_server(host.clone(), port, static_dirs);
9296 for pending in rx {
9297 let matched = routes
9298 .iter()
9299 .find(|(m, p, _)| m == &pending.method && p == &pending.path);
9300 let response = match matched {
9301 Some((_, _, handler)) => {
9302 let req_value = Value::Struct {
9303 name: "Request".to_string(),
9304 fields: vec![
9305 ("method".to_string(), Value::Str(pending.method.clone())),
9306 ("path".to_string(), Value::Str(pending.path.clone())),
9307 ("query".to_string(), Value::Str(pending.query.clone())),
9308 ("body".to_string(), Value::Str(pending.body.clone())),
9309 ("cookie".to_string(), Value::Str(pending.cookie.clone())),
9310 (
9311 "authorization".to_string(),
9312 Value::Str(pending.authorization.clone()),
9313 ),
9314 (
9315 "client_ip".to_string(),
9316 Value::Str(pending.client_ip.clone()),
9317 ),
9318 ],
9319 };
9320 match self.call_value(handler.clone(), vec![req_value]) {
9321 Ok(v) => web::value_to_response(&v),
9322 Err(e) => web::HttpResponse {
9323 status: 500,
9324 content_type: "text/plain; charset=utf-8".to_string(),
9325 body: format!("handler error: {e:?}"),
9326 set_cookie: None,
9327 location: None,
9328 },
9329 }
9330 },
9331 None => web::HttpResponse {
9332 status: 404,
9333 content_type: "text/plain; charset=utf-8".to_string(),
9334 body: "not found".to_string(),
9335 set_cookie: None,
9336 location: None,
9337 },
9338 };
9339 let _ = pending.respond_to.send(response);
9340 }
9341 return Ok(Value::Unit);
9342 },
9343 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9347 "http_post_async"
9348 | "เว็บโพสต์ไม่บล็อก"
9349 | "ارسال_ناهمگام_HTTP"
9350 | "أرسل_HTTP_غير_متزامن"
9351 | "שלח_HTTP_אסינכרוני"
9352 | "HTTP_غیر_ہمزمان_بھیجو" => {
9353 let url = self.arg_str(&args, 0, "");
9354 let body = self.arg_str(&args, 1, "");
9355 let content_type = self.arg_str(&args, 2, "application/json");
9356 let id = self.async_jobs.start_post(url, content_type, body);
9357 return Ok(Value::Str(id));
9358 },
9359 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9363 "http_job_poll"
9364 | "เว็บงานสำรวจ"
9365 | "بررسی_وظیفه_HTTP"
9366 | "استطلع_مهمة_HTTP"
9367 | "בדוק_משימת_HTTP"
9368 | "HTTP_کام_پول" => {
9369 let id = self.arg_str(&args, 0, "");
9370 return Ok(Value::Str(self.async_jobs.poll(&id).unwrap_or_default()));
9371 },
9372 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9379 "sdai_generate_start"
9380 | "เอสดีเอไอเริ่มสร้าง"
9381 | "شروع_تولید_هوش"
9382 | "ابدأ_توليد_الذكاء"
9383 | "התחל_יצירת_בינה"
9384 | "اے_آئی_تخلیق_شروع" => {
9385 let base_url = self.arg_str(&args, 0, "http://127.0.0.1:1342");
9386 let prompt = self.arg_str(&args, 1, "");
9387 let width = self.arg_num(&args, 2, 512.0)? as u32;
9388 let height = self.arg_num(&args, 3, 512.0)? as u32;
9389 let id = self
9390 .async_jobs
9391 .start_sdai_txt2img(base_url, prompt, width, height);
9392 return Ok(Value::Str(id));
9393 },
9394 "query_param"
9396 | "พารามิเตอร์"
9397 | "پارامتر_پرسوجو"
9398 | "معامل_الاستعلام"
9399 | "פרמטר_שאילתה"
9400 | "کوئری_پیرامیٹر" => {
9401 let qs = self.arg_str(&args, 0, "");
9402 let name = self.arg_str(&args, 1, "");
9403 let default = self.arg_str(&args, 2, "");
9404 let mut found = default;
9405 for pair in qs.split('&') {
9406 let mut it = pair.splitn(2, '=');
9407 if it.next().unwrap_or("") == name {
9408 found = url_decode(it.next().unwrap_or(""));
9409 break;
9410 }
9411 }
9412 return Ok(Value::Str(found));
9413 },
9414 "cookie_get"
9416 | "รับคุกกี้"
9417 | "دریافت_کوکی"
9418 | "اجلب_الكعكة"
9419 | "קבל_עוגייה"
9420 | "کوکی_حاصل_کرو" => {
9421 let header = self.arg_str(&args, 0, "");
9422 let name = self.arg_str(&args, 1, "");
9423 let default = self.arg_str(&args, 2, "");
9424 let mut found = default;
9425 for pair in header.split(';') {
9426 let p = pair.trim();
9427 let mut it = p.splitn(2, '=');
9428 if it.next().unwrap_or("") == name {
9429 found = it.next().unwrap_or("").to_string();
9430 break;
9431 }
9432 }
9433 return Ok(Value::Str(found));
9434 },
9435 "html_escape"
9437 | "กันเอชทีเอ็มแอล"
9438 | "فرار_HTML"
9439 | "أفلت_HTML"
9440 | "בריחת_HTML"
9441 | "HTML_ایسکیپ" => {
9442 let s = self.arg_str(&args, 0, "");
9443 return Ok(Value::Str(
9444 s.replace('&', "&")
9445 .replace('<', "<")
9446 .replace('>', ">")
9447 .replace('"', """)
9448 .replace('\'', "'"),
9449 ));
9450 },
9451 "json_escape" | "หนีเจสัน" | "فرار_JSON" | "أفلت_JSON" | "בריחת_JSON" | "JSON_ایسکیپ" =>
9456 {
9457 let s = self.arg_str(&args, 0, "");
9458 let mut out = String::with_capacity(s.len() + 8);
9459 for c in s.chars() {
9460 match c {
9461 '"' => out.push_str("\\\""),
9462 '\\' => out.push_str("\\\\"),
9463 '\n' => out.push_str("\\n"),
9464 '\r' => out.push_str("\\r"),
9465 '\t' => out.push_str("\\t"),
9466 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
9467 c => out.push(c),
9468 }
9469 }
9470 return Ok(Value::Str(out));
9471 },
9472 "cli_arg"
9474 | "อาร์กิวเมนต์"
9475 | "آرگومان_خطفرمان"
9476 | "معامل_سطر_الأوامر"
9477 | "ארגומנט_שורת_פקודה"
9478 | "سی_ایل_آئی_دلیل" => {
9479 let name = self.arg_str(&args, 0, "");
9480 let default = self.arg_str(&args, 1, "");
9481 let flag = format!("--{name}");
9482 let argv: Vec<String> = std::env::args().collect();
9483 let found = argv
9484 .iter()
9485 .position(|a| a == &flag)
9486 .and_then(|i| argv.get(i + 1))
9487 .cloned()
9488 .unwrap_or(default);
9489 return Ok(Value::Str(found));
9490 },
9491 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9493 "db_open"
9494 | "ฐานข้อมูลเปิด"
9495 | "باز_کردن_پایگاه_داده"
9496 | "افتح_قاعدة_البيانات"
9497 | "פתח_מסד_נתונים"
9498 | "ڈیٹا_بیس_کھولو" => {
9499 let path = self.arg_str(&args, 0, "app.db");
9500 let conn = ling_http::rusqlite::Connection::open(&path)
9501 .map_err(|e| EvalErr::from(format!("db_open '{path}': {e}")))?;
9502 let _ = conn.execute_batch("PRAGMA foreign_keys = ON; PRAGMA journal_mode = WAL;");
9503 self.db = Some(conn);
9504 return Ok(Value::Unit);
9505 },
9506 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9509 "db_exec"
9510 | "ฐานข้อมูลรัน"
9511 | "اجرای_پایگاه_داده"
9512 | "نفّذ_في_قاعدة_البيانات"
9513 | "בצע_במסד_נתונים"
9514 | "ڈیٹا_بیس_عمل" => {
9515 let sql = self.arg_str(&args, 0, "");
9516 let params = values_to_sql_params(&args[1.min(args.len())..]);
9517 let conn = self
9518 .db
9519 .as_ref()
9520 .ok_or_else(|| EvalErr::from("db_exec: call db_open first".to_string()))?;
9521 let n = conn
9522 .execute(&sql, ling_http::rusqlite::params_from_iter(params.iter()))
9523 .map_err(|e| EvalErr::from(format!("db_exec: {e}\n sql: {sql}")))?;
9524 return Ok(Value::Number(n as f64));
9525 },
9526 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9528 "db_query"
9529 | "ฐานข้อมูลถาม"
9530 | "پرسوجوی_پایگاه_داده"
9531 | "استعلم_قاعدة_البيانات"
9532 | "שאילתת_מסד_נתונים"
9533 | "ڈیٹا_بیس_سوال" => {
9534 let sql = self.arg_str(&args, 0, "");
9535 let params = values_to_sql_params(&args[1.min(args.len())..]);
9536 let conn = self
9537 .db
9538 .as_ref()
9539 .ok_or_else(|| EvalErr::from("db_query: call db_open first".to_string()))?;
9540 let mut stmt = conn
9541 .prepare(&sql)
9542 .map_err(|e| EvalErr::from(format!("db_query: {e}\n sql: {sql}")))?;
9543 let col_names: Vec<String> =
9544 stmt.column_names().iter().map(|s| s.to_string()).collect();
9545 let mut rows = stmt
9546 .query(ling_http::rusqlite::params_from_iter(params.iter()))
9547 .map_err(|e| EvalErr::from(format!("db_query: {e}")))?;
9548 let mut out = Vec::new();
9549 while let Some(row) = rows
9550 .next()
9551 .map_err(|e| EvalErr::from(format!("db_query row: {e}")))?
9552 {
9553 let mut fields = Vec::with_capacity(col_names.len());
9554 for (i, col) in col_names.iter().enumerate() {
9555 use ling_http::rusqlite::types::ValueRef;
9556 let v = match row.get_ref(i) {
9557 Ok(ValueRef::Null) => Value::Str(String::new()),
9558 Ok(ValueRef::Integer(n)) => Value::Number(n as f64),
9559 Ok(ValueRef::Real(n)) => Value::Number(n),
9560 Ok(ValueRef::Text(t)) => {
9561 Value::Str(String::from_utf8_lossy(t).into_owned())
9562 },
9563 Ok(ValueRef::Blob(b)) => {
9564 use base64::Engine as _;
9565 Value::Str(base64::engine::general_purpose::STANDARD.encode(b))
9566 },
9567 Err(_) => Value::Str(String::new()),
9568 };
9569 fields.push((col.clone(), v));
9570 }
9571 out.push(Value::Struct { name: "Row".to_string(), fields });
9572 }
9573 return Ok(Value::List(Rc::new(out)));
9574 },
9575 #[cfg(not(target_arch = "wasm32"))]
9577 "gamepad_poll"
9578 | "จอยโพล"
9579 | "بررسی_دسته_بازی"
9580 | "استطلع_يد_اللعب"
9581 | "בדוק_בקר_משחק"
9582 | "گیم_پیڈ_پول_کرو" => {
9583 gamepad::poll();
9584 return Ok(Value::Unit);
9585 },
9586 #[cfg(not(target_arch = "wasm32"))]
9587 "gamepad_button"
9588 | "จอยปุ่ม"
9589 | "دکمه_دسته_بازی"
9590 | "زر_يد_اللعب"
9591 | "כפתור_בקר_משחק"
9592 | "گیم_پیڈ_بٹن_دبایا" => {
9593 let name = self.arg_str(&args, 0, "");
9594 return Ok(Value::Number(if gamepad::button(&name) {
9595 1.0
9596 } else {
9597 0.0
9598 }));
9599 },
9600 #[cfg(not(target_arch = "wasm32"))]
9601 "gamepad_axis"
9602 | "จอยแกน"
9603 | "محور_دسته_بازی"
9604 | "محور_يد_اللعب"
9605 | "ציר_בקר_משחק"
9606 | "گیم_پیڈ_محور" => {
9607 let name = self.arg_str(&args, 0, "");
9608 return Ok(Value::Number(gamepad::axis(&name) as f64));
9609 },
9610 #[cfg(not(target_arch = "wasm32"))]
9611 "gamepad_rumble"
9612 | "จอยสั่น"
9613 | "لرزش_دسته_بازی"
9614 | "اهتزاز_يد_اللعب"
9615 | "רטט_בקר_משחק"
9616 | "گیم_پیڈ_لرزش" => {
9617 let low = self.arg_num(&args, 0, 0.0)? as f32;
9618 let high = self.arg_num(&args, 1, 0.0)? as f32;
9619 let ms = self.arg_num(&args, 2, 200.0)? as u32;
9620 gamepad::rumble(low, high, ms);
9621 return Ok(Value::Unit);
9622 },
9623 #[cfg(not(target_arch = "wasm32"))]
9624 "gamepad_list"
9625 | "จอยรายการ"
9626 | "فهرست_دستههای_بازی"
9627 | "قائمة_أيدي_اللعب"
9628 | "רשימת_בקרי_משחק"
9629 | "گیم_پیڈ_فہرست" => {
9630 return Ok(Value::Str(gamepad::list()));
9631 },
9632 #[cfg(not(target_arch = "wasm32"))]
9633 "gamepad_any"
9634 | "จอยใดๆ"
9635 | "هر_دسته_بازی"
9636 | "أي_يد_لعب"
9637 | "בקר_כלשהו"
9638 | "کوئی_بھی_گیم_پیڈ" => {
9639 return Ok(Value::Number(if gamepad::any_button() { 1.0 } else { 0.0 }));
9640 },
9641 #[cfg(target_arch = "wasm32")]
9643 "gamepad_poll"
9644 | "จอยโพล"
9645 | "gamepad_rumble"
9646 | "จอยสั่น"
9647 | "بررسی_دسته_بازی"
9648 | "استطلع_يد_اللعب"
9649 | "בדוק_בקר_משחק"
9650 | "گیم_پیڈ_پول_کرو" => {
9651 return Ok(Value::Unit);
9652 },
9653 #[cfg(target_arch = "wasm32")]
9654 "gamepad_button"
9655 | "จอยปุ่ม"
9656 | "gamepad_axis"
9657 | "จอยแกน"
9658 | "gamepad_any"
9659 | "จอยใดๆ"
9660 | "دکمه_دسته_بازی"
9661 | "زر_يد_اللعب"
9662 | "כפתור_בקר_משחק"
9663 | "گیم_پیڈ_بٹن" => {
9664 return Ok(Value::Number(0.0));
9665 },
9666 #[cfg(target_arch = "wasm32")]
9667 "gamepad_list"
9668 | "จอยรายการ"
9669 | "فهرست_دستههای_بازی"
9670 | "قائمة_أيدي_اللعب"
9671 | "רשימת_בקרי_משחק"
9672 | "گیم_پیڈ_فہرست" => {
9673 return Ok(Value::Str(String::new()));
9674 },
9675
9676 #[cfg(not(target_arch = "wasm32"))]
9679 "nn_new"
9680 | "建神经网"
9681 | "ニューラル作成"
9682 | "신경망생성"
9683 | "สร้างโครงข่าย"
9684 | "شبکه_جدید"
9685 | "شبكة_جديدة"
9686 | "רשת_חדשה"
9687 | "نئی_نیورل_نیٹ"
9688 | "nouveau_réseau"
9689 | "neues_netz"
9690 | "новая_сеть" => {
9691 let n_in = self.arg_num(&args, 0, 1.0)?.max(0.0) as usize;
9692 let seed = self.arg_num(&args, 1, 1.0)? as u64;
9693 return Ok(Value::Number(ai::nn_new(n_in, seed) as f64));
9694 },
9695 #[cfg(not(target_arch = "wasm32"))]
9697 "nn_dense"
9698 | "密集层"
9699 | "密層追加"
9700 | "밀집층"
9701 | "ชั้นหนาแน่น"
9702 | "لایه_متراکم"
9703 | "طبقة_كثيفة"
9704 | "שכבה_צפופה"
9705 | "ڈینس_لیئر"
9706 | "réseau_dense"
9707 | "netz_dicht"
9708 | "плотная_сеть" => {
9709 let id = self.arg_num(&args, 0, -1.0)? as i64;
9710 let units = self.arg_num(&args, 1, 1.0)?.max(1.0) as usize;
9711 let act = self.arg_str(&args, 2, "relu");
9712 ai::nn_dense(id, units, &act);
9713 return Ok(Value::Unit);
9714 },
9715 #[cfg(not(target_arch = "wasm32"))]
9717 "nn_forward"
9718 | "神经前向"
9719 | "順伝播"
9720 | "순전파"
9721 | "ส่งต่อโครงข่าย"
9722 | "پیشروی_شبکه"
9723 | "تمرير_أمامي"
9724 | "העברה_קדימה"
9725 | "فارورڈ_پاس"
9726 | "propager_réseau"
9727 | "netz_vorwärts"
9728 | "прямой_проход_сети" => {
9729 let id = self.arg_num(&args, 0, -1.0)? as i64;
9730 let input = self.arg_list_f32(&args, 1);
9731 let out = ai::nn_forward(id, &input);
9732 return Ok(Value::List(Rc::new(
9733 out.into_iter().map(|v| Value::Number(v as f64)).collect(),
9734 )));
9735 },
9736 #[cfg(not(target_arch = "wasm32"))]
9738 "nn_train"
9739 | "训练网"
9740 | "ニューラル学習"
9741 | "신경망학습"
9742 | "ฝึกโครงข่าย"
9743 | "آموزش_شبکه"
9744 | "درّب_الشبكة"
9745 | "אמן_רשת"
9746 | "نیٹ_ٹریننگ"
9747 | "entraîner_réseau"
9748 | "netz_trainieren"
9749 | "обучить_сеть" => {
9750 let id = self.arg_num(&args, 0, -1.0)? as i64;
9751 let input = self.arg_list_f32(&args, 1);
9752 let target = self.arg_list_f32(&args, 2);
9753 let lr = self.arg_num(&args, 3, 0.01)? as f32;
9754 return Ok(Value::Number(ai::nn_train(id, &input, &target, lr) as f64));
9755 },
9756 #[cfg(not(target_arch = "wasm32"))]
9758 "nn_save"
9759 | "保存网"
9760 | "網保存"
9761 | "신경망저장"
9762 | "บันทึกโครงข่าย"
9763 | "ذخیره_شبکه"
9764 | "احفظ_الشبكة"
9765 | "שמור_רשת"
9766 | "نیٹ_محفوظ_کرو"
9767 | "sauvegarder_réseau"
9768 | "netz_speichern"
9769 | "сохранить_сеть" => {
9770 let id = self.arg_num(&args, 0, -1.0)? as i64;
9771 let path = self.arg_str(&args, 1, "model.lnn");
9772 return Ok(Value::Bool(ai::nn_save(id, &path)));
9773 },
9774 #[cfg(not(target_arch = "wasm32"))]
9776 "nn_load"
9777 | "载入网"
9778 | "網読込"
9779 | "신경망불러오기"
9780 | "โหลดโครงข่าย"
9781 | "بارگذاری_شبکه"
9782 | "حمّل_الشبكة"
9783 | "טען_רשת"
9784 | "نیٹ_لوڈ"
9785 | "charger_réseau"
9786 | "netz_laden"
9787 | "загрузить_сеть" => {
9788 let path = self.arg_str(&args, 0, "model.lnn");
9789 return Ok(Value::Number(ai::nn_load(&path) as f64));
9790 },
9791
9792 #[cfg(not(target_arch = "wasm32"))]
9795 "bt_build"
9796 | "建行为树"
9797 | "行動木構築"
9798 | "행동트리구성"
9799 | "สร้างต้นไม้พฤติกรรม"
9800 | "ساخت_درخت_رفتار"
9801 | "ابنِ_شجرة_السلوك"
9802 | "בנה_עץ_התנהגות"
9803 | "بی_ٹی_تعمیر"
9804 | "construire_arbre_comportement"
9805 | "verhaltensbaum_bauen"
9806 | "построить_дерево_поведения" => {
9807 let spec = self.arg_str(&args, 0, "");
9808 return Ok(Value::Number(ai::bt_build(&spec) as f64));
9809 },
9810 #[cfg(not(target_arch = "wasm32"))]
9812 "bt_set"
9813 | "设事实"
9814 | "事実設定"
9815 | "사실설정"
9816 | "ตั้งข้อเท็จจริง"
9817 | "تنظیم_واقعیت"
9818 | "عيّن_حقيقة"
9819 | "קבע_עובדה"
9820 | "بی_ٹی_سیٹ"
9821 | "définir_arbre_comportement"
9822 | "verhaltensbaum_setzen"
9823 | "задать_дерево_поведения" => {
9824 let id = self.arg_num(&args, 0, -1.0)? as i64;
9825 let key = self.arg_str(&args, 1, "");
9826 let val = self.arg_num(&args, 2, 0.0)? as f32;
9827 ai::bt_set(id, &key, val);
9828 return Ok(Value::Unit);
9829 },
9830 #[cfg(not(target_arch = "wasm32"))]
9832 "bt_tick"
9833 | "行为树滴答"
9834 | "行動木更新"
9835 | "행동트리틱"
9836 | "เดินต้นไม้พฤติกรรม"
9837 | "تیک_درخت_رفتار"
9838 | "نبضة_شجرة_السلوك"
9839 | "טיק_עץ_התנהגות"
9840 | "بی_ٹی_ٹک"
9841 | "tick_arbre_comportement"
9842 | "verhaltensbaum_tick"
9843 | "тик_дерева_поведения" => {
9844 let id = self.arg_num(&args, 0, -1.0)? as i64;
9845 return Ok(Value::Str(ai::bt_tick(id)));
9846 },
9847 #[cfg(not(target_arch = "wasm32"))]
9849 "bt_status"
9850 | "行为树状态"
9851 | "行動木状態"
9852 | "행동트리상태"
9853 | "สถานะต้นไม้พฤติกรรม"
9854 | "وضعیت_درخت_رفتار"
9855 | "حالة_شجرة_السلوك"
9856 | "סטטוס_עץ_התנהגות"
9857 | "بی_ٹی_حالت"
9858 | "statut_arbre_comportement"
9859 | "verhaltensbaum_status"
9860 | "статус_дерева_поведения" => {
9861 let id = self.arg_num(&args, 0, -1.0)? as i64;
9862 return Ok(Value::Number(ai::bt_status(id) as f64));
9863 },
9864
9865 #[cfg(not(target_arch = "wasm32"))]
9868 "dialog_new"
9869 | "建对话模型"
9870 | "対話モデル作成"
9871 | "대화모델생성"
9872 | "สร้างโมเดลสนทนา"
9873 | "مدل_گفتگوی_جدید"
9874 | "نموذج_حوار_جديد"
9875 | "מודל_דיאלוג_חדש"
9876 | "نیا_مکالمہ_ماڈل"
9877 | "nouveau_dialogue"
9878 | "neuer_dialog"
9879 | "новый_диалог" => {
9880 let ctx = self.arg_num(&args, 0, 3.0)?.max(1.0) as usize;
9881 let embed = self.arg_num(&args, 1, 32.0)?.max(1.0) as usize;
9882 let hidden = self.arg_num(&args, 2, 64.0)?.max(1.0) as usize;
9883 let seed = self.arg_num(&args, 3, 1.0)? as u64;
9884 return Ok(Value::Number(
9885 ai::dialog_new(ctx, embed, hidden, seed) as f64
9886 ));
9887 },
9888 #[cfg(not(target_arch = "wasm32"))]
9890 "dialog_learn"
9891 | "对话学习"
9892 | "対話学習"
9893 | "대화학습"
9894 | "เรียนรู้สนทนา"
9895 | "یادگیری_گفتگو"
9896 | "تعلّم_الحوار"
9897 | "למד_דיאלוג"
9898 | "مکالمہ_سیکھو"
9899 | "apprendre_dialogue"
9900 | "dialog_lernen"
9901 | "обучить_диалог" => {
9902 let id = self.arg_num(&args, 0, -1.0)? as i64;
9903 let text = self.arg_str(&args, 1, "");
9904 ai::dialog_learn(id, &text);
9905 return Ok(Value::Unit);
9906 },
9907 #[cfg(not(target_arch = "wasm32"))]
9909 "dialog_load"
9910 | "对话载入"
9911 | "対話読込"
9912 | "대화불러오기"
9913 | "โหลดชุดสนทนา"
9914 | "بارگذاری_مجموعه_گفتگو"
9915 | "حمّل_مجموعة_الحوار"
9916 | "טען_מערך_דיאלוג"
9917 | "مکالمہ_مجموعہ_لوڈ"
9918 | "charger_dialogue"
9919 | "dialog_laden"
9920 | "загрузить_диалог" => {
9921 let id = self.arg_num(&args, 0, -1.0)? as i64;
9922 let path = self.arg_str(&args, 1, "");
9923 return Ok(Value::Number(ai::dialog_load(id, &path) as f64));
9924 },
9925 #[cfg(not(target_arch = "wasm32"))]
9927 "dialog_train"
9928 | "对话训练"
9929 | "対話訓練"
9930 | "대화훈련"
9931 | "ฝึกสนทนา"
9932 | "آموزش_گفتگو"
9933 | "درّب_الحوار"
9934 | "אמן_דיאלוג"
9935 | "مکالمہ_ٹریننگ"
9936 | "entraîner_dialogue"
9937 | "dialog_trainieren"
9938 | "тренировать_диалог" => {
9939 let id = self.arg_num(&args, 0, -1.0)? as i64;
9940 let epochs = self.arg_num(&args, 1, 20.0)?.max(1.0) as usize;
9941 let lr = self.arg_num(&args, 2, 0.1)? as f32;
9942 return Ok(Value::Number(ai::dialog_train(id, epochs, lr) as f64));
9943 },
9944 #[cfg(not(target_arch = "wasm32"))]
9946 "dialog_say"
9947 | "对话生成"
9948 | "対話生成"
9949 | "대화생성"
9950 | "พูดสนทนา"
9951 | "بگو"
9952 | "قل"
9953 | "אמור"
9954 | "کہو"
9955 | "dire_dialogue"
9956 | "dialog_sagen"
9957 | "сказать_диалог" => {
9958 let id = self.arg_num(&args, 0, -1.0)? as i64;
9959 let prompt = self.arg_str(&args, 1, "");
9960 let max = self.arg_num(&args, 2, 24.0)?.max(1.0) as usize;
9961 let temp = self.arg_num(&args, 3, 0.8)? as f32;
9962 return Ok(Value::Str(ai::dialog_say(id, &prompt, max, temp)));
9963 },
9964 #[cfg(not(target_arch = "wasm32"))]
9966 "dialog_save"
9967 | "对话存模"
9968 | "対話モデル保存"
9969 | "대화모델저장"
9970 | "บันทึกโมเดลสนทนา"
9971 | "ذخیره_مدل_گفتگو"
9972 | "احفظ_نموذج_الحوار"
9973 | "שמור_מודל_דיאלוג"
9974 | "مکالمہ_ماڈل_محفوظ"
9975 | "sauvegarder_dialogue"
9976 | "dialog_speichern"
9977 | "сохранить_диалог" => {
9978 let id = self.arg_num(&args, 0, -1.0)? as i64;
9979 let path = self.arg_str(&args, 1, "model.llm");
9980 return Ok(Value::Bool(ai::dialog_save(id, &path)));
9981 },
9982 #[cfg(not(target_arch = "wasm32"))]
9984 "dialog_load_model"
9985 | "对话载模"
9986 | "対話モデル読込"
9987 | "대화모델불러오기"
9988 | "โหลดโมเดลสนทนา"
9989 | "بارگذاری_مدل_گفتگو"
9990 | "حمّل_نموذج_الحوار"
9991 | "טען_מודל_דיאלוג"
9992 | "مکالمہ_ماڈل_لوڈ"
9993 | "charger_modèle_dialogue"
9994 | "dialog_modell_laden"
9995 | "загрузить_модель_диалога" => {
9996 let path = self.arg_str(&args, 0, "model.llm");
9997 return Ok(Value::Number(ai::dialog_load_model(&path) as f64));
9998 },
9999
10000 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10007 "llm_load" => {
10008 let gguf_path = self.arg_str(&args, 0, "");
10009 let tokenizer_path = self.arg_str(&args, 1, "");
10010 let device_index = self.arg_num(&args, 2, 0.0)? as i64;
10011 return Ok(Value::Number(
10012 llm::llm_load(&gguf_path, &tokenizer_path, device_index) as f64,
10013 ));
10014 },
10015 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10017 "llm_generate_start" => {
10018 let handle = self.arg_num(&args, 0, -1.0)? as i64;
10019 let system_prompt = self.arg_str(&args, 1, "");
10020 let history = self.arg_str(&args, 2, "");
10021 let max_tokens = self.arg_num(&args, 3, 512.0)? as i64;
10022 let temperature = self.arg_num(&args, 4, 0.7)?;
10023 let top_p = self.arg_num(&args, 5, 0.9)?;
10024 return Ok(Value::Str(llm::llm_generate_start(
10025 handle,
10026 &system_prompt,
10027 &history,
10028 max_tokens,
10029 temperature,
10030 top_p,
10031 )));
10032 },
10033 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10035 "llm_generate_poll" => {
10036 let job_id = self.arg_str(&args, 0, "");
10037 return Ok(Value::Str(llm::llm_generate_poll(&job_id)));
10038 },
10039 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10041 "llm_generate_done" => {
10042 let job_id = self.arg_str(&args, 0, "");
10043 return Ok(Value::Bool(llm::llm_generate_done(&job_id)));
10044 },
10045 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10047 "llm_job_status" => {
10048 let job_id = self.arg_str(&args, 0, "");
10049 return Ok(Value::Str(llm::llm_job_status(&job_id)));
10050 },
10051 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10053 "llm_job_tool_name" => {
10054 let job_id = self.arg_str(&args, 0, "");
10055 return Ok(Value::Str(llm::llm_job_tool_name(&job_id)));
10056 },
10057 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10059 "llm_job_provide_tool_result" => {
10060 let job_id = self.arg_str(&args, 0, "");
10061 let result_text = self.arg_str(&args, 1, "");
10062 return Ok(Value::Bool(llm::llm_job_provide_tool_result(
10063 &job_id,
10064 &result_text,
10065 )));
10066 },
10067 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10069 "llm_job_cancel" => {
10070 let job_id = self.arg_str(&args, 0, "");
10071 llm::llm_job_cancel(&job_id);
10072 return Ok(Value::Unit);
10073 },
10074
10075 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10081 "oauth_google_start" => {
10082 let code = self.arg_str(&args, 0, "");
10083 let client_id = self.arg_str(&args, 1, "");
10084 let client_secret = self.arg_str(&args, 2, "");
10085 let redirect_uri = self.arg_str(&args, 3, "");
10086 let id = self.oauth_jobs.start_google_login(
10087 code,
10088 client_id,
10089 client_secret,
10090 redirect_uri,
10091 );
10092 return Ok(Value::Str(id));
10093 },
10094 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10095 "oauth_google_done" => {
10096 let id = self.arg_str(&args, 0, "");
10097 return Ok(Value::Bool(self.oauth_jobs.done(&id)));
10098 },
10099 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10101 "oauth_google_error" => {
10102 let id = self.arg_str(&args, 0, "");
10103 return Ok(Value::Str(self.oauth_jobs.error(&id)));
10104 },
10105 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10106 "oauth_google_sub" => {
10107 let id = self.arg_str(&args, 0, "");
10108 return Ok(Value::Str(self.oauth_jobs.sub(&id)));
10109 },
10110 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10111 "oauth_google_email" => {
10112 let id = self.arg_str(&args, 0, "");
10113 return Ok(Value::Str(self.oauth_jobs.email(&id)));
10114 },
10115 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10116 "oauth_google_name" => {
10117 let id = self.arg_str(&args, 0, "");
10118 return Ok(Value::Str(self.oauth_jobs.name(&id)));
10119 },
10120
10121 #[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
10125 "vision_load" => {
10126 let gguf_path = self.arg_str(&args, 0, "");
10127 let tokenizer_path = self.arg_str(&args, 1, "");
10128 let device_index = self.arg_num(&args, 2, 0.0)? as i64;
10129 return Ok(Value::Number(vision::vision_load(
10130 &gguf_path,
10131 &tokenizer_path,
10132 device_index,
10133 ) as f64));
10134 },
10135 #[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
10137 "vision_analyze_start" => {
10138 let handle = self.arg_num(&args, 0, -1.0)? as i64;
10139 let image_base64 = self.arg_str(&args, 1, "");
10140 let question = self.arg_str(&args, 2, "Describe this image.");
10141 let max_tokens = self.arg_num(&args, 3, 256.0)? as i64;
10142 let temperature = self.arg_num(&args, 4, 0.7)?;
10143 let top_p = self.arg_num(&args, 5, 0.9)?;
10144 return Ok(Value::Str(vision::vision_analyze_start(
10145 handle,
10146 &image_base64,
10147 &question,
10148 max_tokens,
10149 temperature,
10150 top_p,
10151 )));
10152 },
10153 #[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
10154 "vision_analyze_poll" => {
10155 let job_id = self.arg_str(&args, 0, "");
10156 return Ok(Value::Str(vision::vision_analyze_poll(&job_id)));
10157 },
10158 #[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
10159 "vision_analyze_done" => {
10160 let job_id = self.arg_str(&args, 0, "");
10161 return Ok(Value::Bool(vision::vision_analyze_done(&job_id)));
10162 },
10163 #[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
10165 "vision_analyze_error" => {
10166 let job_id = self.arg_str(&args, 0, "");
10167 return Ok(Value::Str(vision::vision_analyze_error(&job_id)));
10168 },
10169
10170 "url_decode" | "网址解码" => {
10173 let s = self.arg_str(&args, 0, "");
10174 let bytes = s.as_bytes();
10175 let mut out = Vec::with_capacity(bytes.len());
10176 let mut i = 0;
10177 while i < bytes.len() {
10178 match bytes[i] {
10179 b'+' => {
10180 out.push(b' ');
10181 i += 1;
10182 },
10183 b'%' if i + 2 < bytes.len() => {
10184 let hex = std::str::from_utf8(&bytes[i + 1..i + 3]).unwrap_or("");
10185 match u8::from_str_radix(hex, 16) {
10186 Ok(b) => {
10187 out.push(b);
10188 i += 3;
10189 },
10190 Err(_) => {
10191 out.push(bytes[i]);
10192 i += 1;
10193 },
10194 }
10195 },
10196 b => {
10197 out.push(b);
10198 i += 1;
10199 },
10200 }
10201 }
10202 return Ok(Value::Str(String::from_utf8_lossy(&out).into_owned()));
10203 },
10204 "now_unix" | "现在时间" => {
10208 return Ok(Value::Number(now_secs()));
10209 },
10210 "file_exists" | "文件存在" => {
10211 #[cfg(target_arch = "wasm32")]
10212 return Ok(Value::Bool(false));
10213 #[cfg(not(target_arch = "wasm32"))]
10214 {
10215 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10216 return Ok(Value::Bool(std::path::Path::new(&path).exists()));
10217 }
10218 },
10219 "write_file" | "เขียนไฟล์" | "نوشتن_فایل" | "اكتب_الملف" | "כתוב_קובץ" | "فائل_لکھو" =>
10220 {
10221 #[cfg(target_arch = "wasm32")]
10222 return Ok(Value::Unit);
10223 #[cfg(not(target_arch = "wasm32"))]
10224 {
10225 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10226 let content = self.arg_str(&args, 1, "");
10227 if let Some(parent) = std::path::Path::new(&path).parent() {
10228 if !parent.as_os_str().is_empty() {
10229 let _ = std::fs::create_dir_all(parent);
10230 }
10231 }
10232 std::fs::write(&path, content.as_bytes())
10233 .map_err(|e| EvalErr::from(format!("write_file '{path}': {e}")))?;
10234 return Ok(Value::Unit);
10235 }
10236 },
10237 "print_file" | "พิมพ์ไฟล์" | "چاپ_فایل" | "اطبع_الملف" | "הדפס_קובץ" | "فائل_چھاپو" =>
10238 {
10239 let content = self.arg_str(&args, 0, "");
10240 print!("{content}");
10241 return Ok(Value::Unit);
10242 },
10243
10244 "get_args"
10246 | "รับอาร์กิวเมนต์"
10247 | "دریافت_آرگومانها"
10248 | "اجلب_المعاملات"
10249 | "קבל_ארגומנטים"
10250 | "دلائل_حاصل_کرو" => {
10251 let v: Vec<Value> = std::env::args().map(Value::Str).collect();
10252 return Ok(Value::List(Rc::new(v)));
10253 },
10254
10255 #[cfg(not(target_arch = "wasm32"))]
10260 "list_dir"
10261 | "รายการไดเรกทอรี"
10262 | "فهرست_پوشه"
10263 | "اسرد_المجلد"
10264 | "רשום_תיקייה"
10265 | "فولڈر_فہرست" => {
10266 let path = self.arg_str(&args, 0, ".").replace('\\', "/");
10267 let mut paths: Vec<String> = Vec::new();
10268 if let Ok(rd) = std::fs::read_dir(&path) {
10269 for e in rd.flatten() {
10270 paths.push(e.path().to_string_lossy().replace('\\', "/"));
10271 }
10272 }
10273 paths.sort();
10274 let out: Vec<Value> = paths.into_iter().map(Value::Str).collect();
10275 return Ok(Value::List(Rc::new(out)));
10276 },
10277 #[cfg(not(target_arch = "wasm32"))]
10278 "is_dir"
10279 | "เป็นไดเรกทอรี"
10280 | "آیا_پوشه_است"
10281 | "هل_مجلد"
10282 | "האם_תיקייה"
10283 | "کیا_فولڈر_ہے" => {
10284 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10285 return Ok(Value::Bool(std::path::Path::new(&path).is_dir()));
10286 },
10287 #[cfg(not(target_arch = "wasm32"))]
10288 "is_file" | "เป็นไฟล์" | "آیا_فایل_است" | "هل_ملف" | "האם_קובץ" | "کیا_فائل_ہے" =>
10289 {
10290 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10291 return Ok(Value::Bool(std::path::Path::new(&path).is_file()));
10292 },
10293 #[cfg(not(target_arch = "wasm32"))]
10294 "path_name" | "ชื่อไฟล์" | "نام_مسیر" | "اسم_المسار" | "שם_נתיב" | "پاتھ_نام" =>
10295 {
10296 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10297 let name = std::path::Path::new(&path)
10298 .file_name()
10299 .map(|s| s.to_string_lossy().into_owned())
10300 .unwrap_or_default();
10301 return Ok(Value::Str(name));
10302 },
10303 #[cfg(not(target_arch = "wasm32"))]
10304 "path_ext"
10305 | "นามสกุลไฟล์"
10306 | "پسوند_مسیر"
10307 | "امتداد_المسار"
10308 | "סיומת_נתיב"
10309 | "پاتھ_ایکسٹینشن" => {
10310 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10311 let ext = std::path::Path::new(&path)
10312 .extension()
10313 .map(|s| s.to_string_lossy().to_lowercase())
10314 .unwrap_or_default();
10315 return Ok(Value::Str(ext));
10316 },
10317 #[cfg(not(target_arch = "wasm32"))]
10318 "file_size" | "ขนาดไฟล์" | "اندازه_فایل" | "حجم_الملف" | "גודל_קובץ" | "فائل_سائز" =>
10319 {
10320 let path = self.arg_str(&args, 0, "");
10321 let sz = std::fs::metadata(&path).map(|m| m.len()).unwrap_or(0);
10322 return Ok(Value::Number(sz as f64));
10323 },
10324 #[cfg(not(target_arch = "wasm32"))]
10325 "file_modified"
10326 | "เวลาที่แก้ไข"
10327 | "زمان_تغییر_فایل"
10328 | "وقت_تعديل_الملف"
10329 | "זמן_עדכון_קובץ"
10330 | "فائل_ترمیم_وقت" => {
10331 let path = self.arg_str(&args, 0, "");
10332 let secs = std::fs::metadata(&path)
10333 .and_then(|m| m.modified())
10334 .ok()
10335 .and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
10336 .map(|d| d.as_secs_f64())
10337 .unwrap_or(0.0);
10338 return Ok(Value::Number(secs));
10339 },
10340 #[cfg(not(target_arch = "wasm32"))]
10341 "file_created"
10342 | "เวลาที่สร้าง"
10343 | "زمان_ایجاد_فایل"
10344 | "وقت_إنشاء_الملف"
10345 | "זמן_יצירת_קובץ"
10346 | "فائل_تخلیق_وقت" => {
10347 let path = self.arg_str(&args, 0, "");
10348 let secs = std::fs::metadata(&path)
10349 .ok()
10350 .and_then(|m| m.created().or_else(|_| m.modified()).ok())
10351 .and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
10352 .map(|d| d.as_secs_f64())
10353 .unwrap_or(0.0);
10354 return Ok(Value::Number(secs));
10355 },
10356 #[cfg(not(target_arch = "wasm32"))]
10357 "make_dir"
10358 | "สร้างไดเรกทอรี"
10359 | "ساخت_پوشه"
10360 | "أنشئ_مجلدا"
10361 | "צור_תיקייה"
10362 | "فولڈر_بناؤ" => {
10363 let path = self.arg_str(&args, 0, "");
10364 return Ok(Value::Bool(std::fs::create_dir_all(&path).is_ok()));
10365 },
10366 "str_strip_prefix"
10368 | "ตัดคำนำหน้า"
10369 | "حذف_پیشوند"
10370 | "أزل_البادئة"
10371 | "הסר_קידומת"
10372 | "سابقہ_ہٹاؤ" => {
10373 let s = self.arg_str(&args, 0, "");
10374 let prefix = self.arg_str(&args, 1, "");
10375 return Ok(Value::Str(
10376 s.strip_prefix(&prefix).unwrap_or(&s).to_string(),
10377 ));
10378 },
10379 #[cfg(not(target_arch = "wasm32"))]
10382 "file_magic"
10383 | "มายาไฟล์"
10384 | "امضای_فایل"
10385 | "توقيع_الملف"
10386 | "חתימת_קובץ"
10387 | "فائل_میجک" => {
10388 let path = self.arg_str(&args, 0, "");
10389 let kind = match std::fs::File::open(&path) {
10390 Ok(mut f) => {
10391 use std::io::Read;
10392 let mut buf = [0u8; 8];
10393 let n = f.read(&mut buf).unwrap_or(0);
10394 if n >= 2 && buf[0] == 0x1f && buf[1] == 0x8b {
10395 "gzip"
10396 } else if n >= 4 && &buf[0..2] == b"PK" {
10397 "zip"
10398 } else if n >= 8 && &buf[0..8] == b"\x89PNG\r\n\x1a\n" {
10399 "png"
10400 } else {
10401 "other"
10402 }
10403 },
10404 Err(_) => "missing",
10405 };
10406 return Ok(Value::Str(kind.to_string()));
10407 },
10408 #[cfg(not(target_arch = "wasm32"))]
10410 "copy_file" | "คัดลอกไฟล์" | "کپی_فایل" | "انسخ_الملف" | "העתק_קובץ" | "فائل_کاپی" =>
10411 {
10412 let src = self.arg_str(&args, 0, "");
10413 let dst = self.arg_str(&args, 1, "");
10414 if let Some(parent) = std::path::Path::new(&dst).parent() {
10415 let _ = std::fs::create_dir_all(parent);
10416 }
10417 return Ok(Value::Bool(std::fs::copy(&src, &dst).is_ok()));
10418 },
10419 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10423 "tar_gz_list"
10424 | "รายการทาร์"
10425 | "فهرست_TAR_GZ"
10426 | "اسرد_TAR_GZ"
10427 | "רשום_TAR_GZ"
10428 | "TAR_GZ_فہرست" => {
10429 let path = self.arg_str(&args, 0, "");
10430 let mut names: Vec<String> = Vec::new();
10431 if let Ok(file) = std::fs::File::open(&path) {
10432 let gz = flate2::read::GzDecoder::new(file);
10433 let mut ar = tar::Archive::new(gz);
10434 if let Ok(entries) = ar.entries() {
10435 for entry in entries.flatten() {
10436 if entry.header().entry_type().is_file() {
10437 if let Ok(p) = entry.path() {
10438 names.push(
10439 p.to_string_lossy()
10440 .trim_start_matches("./")
10441 .replace('\\', "/"),
10442 );
10443 }
10444 }
10445 }
10446 }
10447 }
10448 names.sort();
10449 names.dedup();
10450 let out: Vec<Value> = names.into_iter().map(Value::Str).collect();
10451 return Ok(Value::List(Rc::new(out)));
10452 },
10453 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10457 "tar_gz_read"
10458 | "อ่านทาร์"
10459 | "خواندن_TAR_GZ"
10460 | "اقرأ_TAR_GZ"
10461 | "קרא_TAR_GZ"
10462 | "TAR_GZ_پڑھو" => {
10463 let path = self.arg_str(&args, 0, "");
10464 let want = self.arg_str(&args, 1, "");
10465 let want = want.trim_start_matches("./").replace('\\', "/");
10466 let mut content = String::new();
10467 if let Ok(file) = std::fs::File::open(&path) {
10468 let gz = flate2::read::GzDecoder::new(file);
10469 let mut ar = tar::Archive::new(gz);
10470 if let Ok(entries) = ar.entries() {
10471 for entry in entries.flatten() {
10472 let mut entry = entry;
10473 let name = match entry.path() {
10474 Ok(p) => p
10475 .to_string_lossy()
10476 .trim_start_matches("./")
10477 .replace('\\', "/"),
10478 Err(_) => continue,
10479 };
10480 if name == want {
10481 use std::io::Read;
10482 let mut buf = Vec::new();
10483 let cap = 256 * 1024;
10484 if entry.take(cap as u64 + 1).read_to_end(&mut buf).is_ok() {
10485 let slice = if buf.len() > cap {
10486 &buf[..cap]
10487 } else {
10488 &buf[..]
10489 };
10490 content = String::from_utf8_lossy(slice).into_owned();
10491 }
10492 break;
10493 }
10494 }
10495 }
10496 }
10497 return Ok(Value::Str(content));
10498 },
10499 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10507 "run_process" | "รันโปรเซส" => {
10508 let cmd = self.arg_str(&args, 0, "");
10509 let argv = self.arg_list_str(&args, 1);
10510 let timeout_secs = self.arg_num(&args, 2, 60.0)?.max(1.0) as u64;
10511 let log_path = self.arg_str(&args, 3, "");
10512
10513 let make_stdio = || -> std::process::Stdio {
10514 if log_path.is_empty() {
10515 std::process::Stdio::null()
10516 } else {
10517 std::fs::OpenOptions::new()
10518 .create(true)
10519 .append(true)
10520 .open(&log_path)
10521 .map(std::process::Stdio::from)
10522 .unwrap_or_else(|_| std::process::Stdio::null())
10523 }
10524 };
10525
10526 let spawned = std::process::Command::new(&cmd)
10527 .args(&argv)
10528 .stdin(std::process::Stdio::null())
10529 .stdout(make_stdio())
10530 .stderr(make_stdio())
10531 .spawn();
10532
10533 let mut child = match spawned {
10534 Ok(c) => c,
10535 Err(_) => return Ok(Value::Number(-1.0)),
10536 };
10537
10538 let start = std::time::Instant::now();
10539 let timeout = std::time::Duration::from_secs(timeout_secs);
10540 loop {
10541 match child.try_wait() {
10542 Ok(Some(status)) => {
10543 return Ok(Value::Number(status.code().unwrap_or(-1) as f64));
10544 },
10545 Ok(None) => {
10546 if start.elapsed() > timeout {
10547 let _ = child.kill();
10548 let _ = child.wait();
10549 return Ok(Value::Number(-2.0));
10550 }
10551 std::thread::sleep(std::time::Duration::from_millis(300));
10552 },
10553 Err(_) => return Ok(Value::Number(-1.0)),
10554 }
10555 }
10556 },
10557 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10563 "tar_gz_extract_to" | "แตกไฟล์ทาร์" => {
10564 let path = self.arg_str(&args, 0, "");
10565 let want = self.arg_str(&args, 1, "");
10566 let want = want.trim_start_matches("./").replace('\\', "/");
10567 let out_path = self.arg_str(&args, 2, "");
10568 let mut found = false;
10569 if let Ok(file) = std::fs::File::open(&path) {
10570 let gz = flate2::read::GzDecoder::new(file);
10571 let mut ar = tar::Archive::new(gz);
10572 if let Ok(entries) = ar.entries() {
10573 for entry in entries.flatten() {
10574 let mut entry = entry;
10575 let name = match entry.path() {
10576 Ok(p) => p
10577 .to_string_lossy()
10578 .trim_start_matches("./")
10579 .replace('\\', "/"),
10580 Err(_) => continue,
10581 };
10582 if name == want {
10583 if let Some(parent) = std::path::Path::new(&out_path).parent() {
10584 let _ = std::fs::create_dir_all(parent);
10585 }
10586 if let Ok(mut out) = std::fs::File::create(&out_path) {
10587 use std::io::copy;
10588 found = copy(&mut entry, &mut out).is_ok();
10589 }
10590 break;
10591 }
10592 }
10593 }
10594 }
10595 return Ok(Value::Bool(found));
10596 },
10597 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10601 "icon_convert_to_png" | "แปลงไอคอนเป็นพีเอ็นจี" => {
10602 let src = self.arg_str(&args, 0, "");
10603 let out = self.arg_str(&args, 1, "");
10604 let size = self.arg_num(&args, 2, 300.0)? as u32;
10605 let ok = match ling_icon::png_bytes(std::path::Path::new(&src), size) {
10606 Ok(bytes) => std::fs::write(&out, bytes).is_ok(),
10607 Err(_) => false,
10608 };
10609 return Ok(Value::Bool(ok));
10610 },
10611 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10618 "totp_secret" | "โทเทนลับ" | "راز_TOTP" | "سر_TOTP" | "סוד_TOTP" | "TOTP_راز" =>
10619 {
10620 let mut bytes = [0u8; 20];
10621 rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut bytes);
10622 return Ok(Value::Str(base32_encode(&bytes)));
10623 },
10624 "totp_uri"
10628 | "โทเทนยูอาร์ไอ"
10629 | "آدرس_TOTP"
10630 | "رابط_TOTP"
10631 | "כתובת_TOTP"
10632 | "TOTP_یو_آر_آئی" => {
10633 let secret = self.arg_str(&args, 0, "");
10634 let account = self.arg_str(&args, 1, "user");
10635 let issuer = self.arg_str(&args, 2, "lingfu");
10636 return Ok(Value::Str(format!(
10637 "otpauth://totp/{issuer}:{account}?secret={secret}&issuer={issuer}&algorithm=SHA1&digits=6&period=30"
10638 )));
10639 },
10640 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10642 "totp_verify"
10643 | "โทเทนตรวจ"
10644 | "تایید_TOTP"
10645 | "تحقق_TOTP"
10646 | "אמת_TOTP"
10647 | "TOTP_تصدیق" => {
10648 let secret = self.arg_str(&args, 0, "");
10649 let code = self.arg_str(&args, 1, "");
10650 let ok = totp_check(&secret, code.trim());
10651 return Ok(Value::Bool(ok));
10652 },
10653 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10655 "totp_now" | "โทเทนตอนนี้" | "TOTP_اکنون" | "TOTP_الآن" | "TOTP_עכשיו" | "TOTP_ابھی" =>
10656 {
10657 let secret = self.arg_str(&args, 0, "");
10658 let step = (crate::runtime::now_secs() as u64) / 30;
10659 return Ok(Value::Str(totp_code(&secret, step).unwrap_or_default()));
10660 },
10661 #[cfg(not(target_arch = "wasm32"))]
10663 "file_hash" | "แฮชไฟล์" | "درهم_فایل" | "بصمة_الملف" | "גיבוב_קובץ" | "فائل_ہیش" =>
10664 {
10665 let path = self.arg_str(&args, 0, "");
10666 match std::fs::read(&path) {
10667 Ok(bytes) => {
10668 return Ok(Value::Str(hex_encode(&ling_crypto::Blake3::hash(&bytes))))
10669 },
10670 Err(_) => return Ok(Value::Str(String::new())),
10671 }
10672 },
10673 "hash_hex"
10675 | "แฮชสตริง"
10676 | "درهم_هگزادسیمال"
10677 | "بصمة_سداسية"
10678 | "גיבוב_הקסדצימלי"
10679 | "ہیکس_ہیش" => {
10680 let s = self.arg_str(&args, 0, "");
10681 return Ok(Value::Str(hex_encode(&ling_crypto::Blake3::hash(
10682 s.as_bytes(),
10683 ))));
10684 },
10685 #[cfg(not(target_arch = "wasm32"))]
10687 "env_get"
10688 | "รับตัวแปรแวดล้อม"
10689 | "دریافت_متغیر_محیطی"
10690 | "اجلب_متغير_البيئة"
10691 | "קבל_משתנה_סביבה"
10692 | "ماحولیاتی_متغیر_حاصل_کرو" => {
10693 let name = self.arg_str(&args, 0, "");
10694 let dflt = self.arg_str(&args, 1, "");
10695 return Ok(Value::Str(std::env::var(&name).unwrap_or(dflt)));
10696 },
10697
10698 "to_number" | "转数字" => {
10702 let s = self.arg_str(&args, 0, "");
10703 return Ok(Value::Number(s.trim().parse().unwrap_or(0.0)));
10704 },
10705 "sha256_hex" | "SHA256哈希" => {
10710 use sha2::Digest;
10711 let s = self.arg_str(&args, 0, "");
10712 let mut h = sha2::Sha256::new();
10713 h.update(s.as_bytes());
10714 return Ok(Value::Str(hex_encode(&h.finalize())));
10715 },
10716 "hex_to_number" | "十六进制转数字" => {
10719 let s = self.arg_str(&args, 0, "");
10720 let v = u64::from_str_radix(s.trim(), 16).unwrap_or(0);
10721 return Ok(Value::Number(v as f64));
10722 },
10723 "split" | "str_split" | "แยก" | "جداسازی" | "قسّم" | "פצל" | "تقسیم_کرو" =>
10724 {
10725 let s = self.arg_str(&args, 0, "");
10726 let sep = self.arg_str(&args, 1, "\n");
10727 let sep = if sep.is_empty() { "\n".into() } else { sep };
10728 let parts: Vec<Value> = s
10729 .split(sep.as_str())
10730 .map(|p| Value::Str(p.to_string()))
10731 .collect();
10732 return Ok(Value::List(Rc::new(parts)));
10733 },
10734 "trim"
10735 | "str_trim"
10736 | "ตัดช่องว่าง"
10737 | "حذف_فاصله"
10738 | "اقتطع_الفراغات"
10739 | "חתוך_רווחים"
10740 | "خالی_جگہ_کاٹو" => {
10741 let s = self.arg_str(&args, 0, "");
10742 return Ok(Value::Str(s.trim().to_string()));
10743 },
10744 "starts_with"
10745 | "str_starts_with"
10746 | "เริ่มด้วย"
10747 | "شروع_میشود_با"
10748 | "يبدأ_بـ"
10749 | "מתחיל_ב"
10750 | "شروع_ہوتا_ہے" => {
10751 let s = self.arg_str(&args, 0, "");
10752 let prefix = self.arg_str(&args, 1, "");
10753 return Ok(Value::Bool(s.starts_with(prefix.as_str())));
10754 },
10755 "ends_with"
10756 | "str_ends_with"
10757 | "ลงท้ายด้วย"
10758 | "پایان_مییابد_با"
10759 | "ينتهي_بـ"
10760 | "מסתיים_ב"
10761 | "ختم_ہوتا_ہے" => {
10762 let s = self.arg_str(&args, 0, "");
10763 let suffix = self.arg_str(&args, 1, "");
10764 return Ok(Value::Bool(s.ends_with(suffix.as_str())));
10765 },
10766 "str_replace"
10767 | "แทนสตริง"
10768 | "جایگزینی_رشته"
10769 | "استبدل_النص"
10770 | "החלף_מחרוזת"
10771 | "اسٹرنگ_تبدیل" => {
10772 let s = self.arg_str(&args, 0, "");
10773 let from = self.arg_str(&args, 1, "");
10774 let to = self.arg_str(&args, 2, "");
10775 return Ok(Value::Str(s.replace(from.as_str(), to.as_str())));
10776 },
10777 "str_find"
10778 | "หาในสตริง"
10779 | "جستجوی_رشته"
10780 | "ابحث_في_النص"
10781 | "חפש_מחרוזת"
10782 | "اسٹرنگ_تلاش" => {
10783 let s = self.arg_str(&args, 0, "");
10784 let needle = self.arg_str(&args, 1, "");
10785 let pos = s
10787 .find(needle.as_str())
10788 .map(|byte_i| s[..byte_i].chars().count() as f64)
10789 .unwrap_or(-1.0);
10790 return Ok(Value::Number(pos));
10791 },
10792 "substr"
10793 | "str_slice"
10794 | "ส่วนสตริง"
10795 | "زیررشته"
10796 | "جزء_النص"
10797 | "תת_מחרוזת"
10798 | "ذیلی_اسٹرنگ" => {
10799 let s = self.arg_str(&args, 0, "");
10800 let start = self.arg_num(&args, 1, 0.0)? as usize;
10801 let len = args
10802 .get(2)
10803 .map(|v| self.to_number(v).unwrap_or(999999.0) as usize)
10804 .unwrap_or_else(|| s.chars().count().saturating_sub(start));
10805 let chars: Vec<char> = s.chars().collect();
10806 let end = (start + len).min(chars.len());
10807 let slice: String = chars.get(start..end).unwrap_or(&[]).iter().collect();
10808 return Ok(Value::Str(slice));
10809 },
10810 "to_str"
10811 | "str"
10812 | "num_str"
10813 | "แปลงสตริง"
10814 | "تبدیل_به_رشته"
10815 | "حوّل_لنص"
10816 | "המר_למחרוזת"
10817 | "اسٹرنگ_میں_بدلو" => {
10818 let v = args.into_iter().next().unwrap_or(Value::Unit);
10819 return Ok(Value::Str(v.to_string()));
10820 },
10821 "str_repeat"
10822 | "ทำซ้ำสตริง"
10823 | "تکرار_رشته"
10824 | "كرّر_النص"
10825 | "חזור_על_מחרוזת"
10826 | "اسٹرنگ_دہراؤ" => {
10827 let s = self.arg_str(&args, 0, "");
10828 let n = self.arg_num(&args, 1, 1.0)? as usize;
10829 return Ok(Value::Str(s.repeat(n)));
10830 },
10831 "str_upper" => {
10832 let s = self.arg_str(&args, 0, "");
10833 return Ok(Value::Str(s.to_uppercase()));
10834 },
10835 "str_lower" => {
10836 let s = self.arg_str(&args, 0, "");
10837 return Ok(Value::Str(s.to_lowercase()));
10838 },
10839 "str_len"
10840 | "len"
10841 | "ความยาว"
10842 | "长度"
10843 | "長さ"
10844 | "길이"
10845 | "طول_رشته"
10846 | "طول_النص"
10847 | "אורך_מחרוזת"
10848 | "اسٹرنگ_لمبائی" => match args.first() {
10849 Some(Value::Str(s)) => return Ok(Value::Number(s.chars().count() as f64)),
10850 Some(Value::List(v)) => return Ok(Value::Number(v.len() as f64)),
10851 _ => return Ok(Value::Number(0.0)),
10852 },
10853
10854 "hash_str" | "แฮช" | "درهم_رشته" | "بصمة_نص" | "גיבוב_מחרוזת" | "اسٹرنگ_ہیش" =>
10856 {
10857 let s = self.arg_str(&args, 0, "");
10858 let mut h: u64 = 14695981039346656037_u64;
10859 for b in s.bytes() {
10860 h ^= b as u64;
10861 h = h.wrapping_mul(1099511628211);
10862 }
10863 return Ok(Value::Number((h & 0xFFFFFF) as f64 / 16777215.0));
10864 },
10865 "hash_int" | "แฮชจำนวน" | "درهم_عدد" | "بصمة_عدد" | "גיבוב_מספר" | "نمبر_ہیش" =>
10866 {
10867 let s = self.arg_str(&args, 0, "");
10868 let n = self.arg_num(&args, 1, 100.0)? as u64;
10869 let mut h: u64 = 14695981039346656037_u64;
10870 for b in s.bytes() {
10871 h ^= b as u64;
10872 h = h.wrapping_mul(1099511628211);
10873 }
10874 return Ok(Value::Number((h % n.max(1)) as f64));
10875 },
10876
10877 "list_new"
10879 | "รายการใหม่"
10880 | "新建列表"
10881 | "新規リスト"
10882 | "새목록"
10883 | "فهرست_جدید"
10884 | "قائمة_جديدة"
10885 | "רשימה_חדשה"
10886 | "نئی_فہرست"
10887 | "nouvelle_liste"
10888 | "neue_liste"
10889 | "новый_список" => {
10890 return Ok(Value::List(Rc::new(Vec::new())));
10891 },
10892 "list_push"
10893 | "เพิ่มรายการ"
10894 | "列表添加"
10895 | "リスト追加"
10896 | "목록추가"
10897 | "افزودن_به_فهرست"
10898 | "أضف_للقائمة"
10899 | "הוסף_לרשימה"
10900 | "فہرست_میں_شامل_کرو"
10901 | "ajouter_liste"
10902 | "liste_anhängen"
10903 | "добавить_в_список" => {
10904 let lst = args
10905 .first()
10906 .cloned()
10907 .unwrap_or(Value::List(Rc::new(vec![])));
10908 let val = args.get(1).cloned().unwrap_or(Value::Unit);
10909 if let Value::List(mut v) = lst {
10910 Rc::make_mut(&mut v).push(val);
10911 return Ok(Value::List(v));
10912 }
10913 return Ok(Value::List(Rc::new(vec![val])));
10914 },
10915 "list_get"
10916 | "รับรายการ"
10917 | "取元素"
10918 | "要素取得"
10919 | "요소가져오기"
10920 | "دریافت_از_فهرست"
10921 | "اجلب_من_القائمة"
10922 | "קבל_מרשימה"
10923 | "فہرست_سے_حاصل_کرو"
10924 | "obtenir_liste"
10925 | "liste_abrufen"
10926 | "получить_из_списка" => {
10927 let i = self.arg_num(&args, 1, 0.0)? as usize;
10929 if let Some(Value::List(v)) = args.first() {
10930 return Ok(v.get(i).cloned().unwrap_or(Value::Str(String::new())));
10931 }
10932 return Ok(Value::Str(String::new()));
10933 },
10934 "list_max" | "列表最大值" => {
10938 let lst = args
10939 .first()
10940 .cloned()
10941 .unwrap_or(Value::List(Rc::new(vec![])));
10942 let default = self.arg_num(&args, 1, 0.0)?;
10943 if let Value::List(v) = lst {
10944 let mut best = default;
10945 let mut any = false;
10946 for item in v.iter() {
10947 if let Value::Number(n) = item {
10948 if !any || *n > best {
10949 best = *n;
10950 any = true;
10951 }
10952 }
10953 }
10954 return Ok(Value::Number(best));
10955 }
10956 return Ok(Value::Number(default));
10957 },
10958 "list_min" | "列表最小值" => {
10959 let lst = args
10960 .first()
10961 .cloned()
10962 .unwrap_or(Value::List(Rc::new(vec![])));
10963 let default = self.arg_num(&args, 1, 0.0)?;
10964 if let Value::List(v) = lst {
10965 let mut best = default;
10966 let mut any = false;
10967 for item in v.iter() {
10968 if let Value::Number(n) = item {
10969 if !any || *n < best {
10970 best = *n;
10971 any = true;
10972 }
10973 }
10974 }
10975 return Ok(Value::Number(best));
10976 }
10977 return Ok(Value::Number(default));
10978 },
10979 "list_set"
10983 | "ตั้งรายการ"
10984 | "设元素"
10985 | "要素設定"
10986 | "요소설정"
10987 | "تنظیم_عنصر_فهرست"
10988 | "عيّن_عنصر_القائمة"
10989 | "קבע_איבר_רשימה"
10990 | "فہرست_سیٹ" => {
10991 let idx = self.arg_num(&args, 1, 0.0)? as usize;
10992 let mut ai = args.into_iter();
10993 let lst = ai.next().unwrap_or(Value::List(Rc::new(vec![])));
10994 ai.next(); let val = ai.next().unwrap_or(Value::Unit);
10996 if let Value::List(mut v) = lst {
10997 if idx < v.len() {
10998 Rc::make_mut(&mut v)[idx] = val;
10999 }
11000 return Ok(Value::List(v));
11001 }
11002 return Ok(Value::List(Rc::new(vec![])));
11003 },
11004 "list_join"
11005 | "join"
11006 | "รวมรายการ"
11007 | "连接"
11008 | "連結"
11009 | "연결"
11010 | "پیوستن_فهرست"
11011 | "اربط_القائمة"
11012 | "חבר_רשימה"
11013 | "فہرست_جوڑو" => {
11014 let lst = args
11015 .first()
11016 .cloned()
11017 .unwrap_or(Value::List(Rc::new(vec![])));
11018 let sep = args.get(1).map(|v| v.to_string()).unwrap_or_default();
11019 if let Value::List(v) = lst {
11020 return Ok(Value::Str(
11021 v.iter()
11022 .map(|x| x.to_string())
11023 .collect::<Vec<_>>()
11024 .join(&sep),
11025 ));
11026 }
11027 return Ok(Value::Str(String::new()));
11028 },
11029 "list_map" | "映射列表" => {
11038 let lst = args
11039 .first()
11040 .cloned()
11041 .unwrap_or(Value::List(Rc::new(vec![])));
11042 let f = args.get(1).cloned().unwrap_or(Value::Unit);
11043 if let Value::List(v) = lst {
11044 let mut out = Vec::with_capacity(v.len());
11045 for item in v.iter() {
11046 out.push(self.call_value(f.clone(), vec![item.clone()])?);
11047 }
11048 return Ok(Value::List(Rc::new(out)));
11049 }
11050 return Ok(Value::List(Rc::new(vec![])));
11051 },
11052 "list_filter" | "过滤列表" => {
11053 let lst = args
11054 .first()
11055 .cloned()
11056 .unwrap_or(Value::List(Rc::new(vec![])));
11057 let f = args.get(1).cloned().unwrap_or(Value::Unit);
11058 if let Value::List(v) = lst {
11059 let mut out = Vec::new();
11060 for item in v.iter() {
11061 if matches!(
11062 self.call_value(f.clone(), vec![item.clone()])?,
11063 Value::Bool(true)
11064 ) {
11065 out.push(item.clone());
11066 }
11067 }
11068 return Ok(Value::List(Rc::new(out)));
11069 }
11070 return Ok(Value::List(Rc::new(vec![])));
11071 },
11072 "list_find" | "查找列表" => {
11074 let lst = args
11075 .first()
11076 .cloned()
11077 .unwrap_or(Value::List(Rc::new(vec![])));
11078 let f = args.get(1).cloned().unwrap_or(Value::Unit);
11079 if let Value::List(v) = lst {
11080 for item in v.iter() {
11081 if matches!(
11082 self.call_value(f.clone(), vec![item.clone()])?,
11083 Value::Bool(true)
11084 ) {
11085 return Ok(item.clone());
11086 }
11087 }
11088 }
11089 return Ok(Value::Unit);
11090 },
11091 #[cfg(not(target_arch = "wasm32"))]
11095 "blob_f32" | "blob_i32" => {
11096 let s = self.arg_str(&args, 0, "");
11097 let is_i32 = name == "blob_i32";
11098 match decode_blob(&s) {
11099 Ok(bytes) => {
11100 let mut out = Vec::with_capacity(bytes.len() / 4);
11101 for ch in bytes.chunks_exact(4) {
11102 let arr = [ch[0], ch[1], ch[2], ch[3]];
11103 let n = if is_i32 {
11104 i32::from_le_bytes(arr) as f64
11105 } else {
11106 f32::from_le_bytes(arr) as f64
11107 };
11108 out.push(Value::Number(n));
11109 }
11110 return Ok(Value::List(Rc::new(out)));
11111 },
11112 Err(e) => {
11113 eprintln!("blob decode failed: {e}");
11114 return Ok(Value::List(Rc::new(vec![])));
11115 },
11116 }
11117 },
11118
11119 "svg_begin" | "开始SVG" | "เริ่มSVG" | "شروع_SVG" | "ابدأ_SVG" | "התחל_SVG"
11126 | "SVG_شروع" | "commencer_svg" | "svg_beginnen" | "начать_svg" => {
11127 let path = self.arg_str(&args, 0, "output.svg");
11128 let width = self.arg_num(&args, 1, 800.0)?;
11129 let height = self.arg_num(&args, 2, 600.0)?;
11130 *self.svg.borrow_mut() = Some(SvgWriter::new(path, width, height));
11131 return Ok(Value::Unit);
11132 },
11133
11134 "svg_rect"
11135 | "SVG矩形"
11136 | "SVGสี่เหลี่ยม"
11137 | "مستطیل_SVG"
11138 | "مستطيل_SVG"
11139 | "מלבן_SVG"
11140 | "SVG_مستطیل"
11141 | "rectangle_svg"
11142 | "svg_rechteck"
11143 | "прямоугольник_svg" => {
11144 let x = self.arg_num(&args, 0, 0.0)?;
11145 let y = self.arg_num(&args, 1, 0.0)?;
11146 let w = self.arg_num(&args, 2, 10.0)?;
11147 let h = self.arg_num(&args, 3, 10.0)?;
11148 let fill = self.arg_str(&args, 4, "#ffffff");
11149 if let Some(svg) = self.svg.borrow_mut().as_mut() {
11150 svg.elements.push(format!(
11151 "<rect x=\"{x:.1}\" y=\"{y:.1}\" width=\"{w:.1}\" \
11152 height=\"{h:.1}\" fill=\"{fill}\"/>"
11153 ));
11154 }
11155 return Ok(Value::Unit);
11156 },
11157
11158 "svg_circle" | "SVG圆形" | "SVGวงกลม" | "دایره_SVG" | "دائرة_SVG" | "עיגול_SVG"
11159 | "SVG_دائرہ" | "cercle_svg" | "svg_kreis" | "круг_svg" => {
11160 let cx = self.arg_num(&args, 0, 0.0)?;
11161 let cy = self.arg_num(&args, 1, 0.0)?;
11162 let r = self.arg_num(&args, 2, 5.0)?;
11163 let fill = self.arg_str(&args, 3, "#ffffff");
11164 if let Some(svg) = self.svg.borrow_mut().as_mut() {
11165 svg.elements.push(format!(
11166 "<circle cx=\"{cx:.1}\" cy=\"{cy:.1}\" r=\"{r:.1}\" fill=\"{fill}\"/>"
11167 ));
11168 }
11169 return Ok(Value::Unit);
11170 },
11171
11172 "svg_line" | "SVG线段" | "SVGเส้น" | "خط_SVG" | "קו_SVG" | "SVG_لکیر" | "ligne_svg"
11173 | "svg_linie" | "линия_svg" => {
11174 let x1 = self.arg_num(&args, 0, 0.0)?;
11175 let y1 = self.arg_num(&args, 1, 0.0)?;
11176 let x2 = self.arg_num(&args, 2, 0.0)?;
11177 let y2 = self.arg_num(&args, 3, 0.0)?;
11178 let stroke = self.arg_str(&args, 4, "#ffffff");
11179 let sw = self.arg_num(&args, 5, 1.0)?;
11180 if let Some(svg) = self.svg.borrow_mut().as_mut() {
11181 svg.elements.push(format!(
11182 "<line x1=\"{x1:.1}\" y1=\"{y1:.1}\" x2=\"{x2:.1}\" y2=\"{y2:.1}\" \
11183 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"
11184 ));
11185 }
11186 return Ok(Value::Unit);
11187 },
11188
11189 "svg_polyline"
11190 | "SVG折线"
11191 | "SVGเส้นหัก"
11192 | "چندخطی_SVG"
11193 | "خط_متعدد_SVG"
11194 | "קו_שבור_SVG"
11195 | "SVG_پولی_لائن"
11196 | "polyligne_svg"
11197 | "svg_polylinie"
11198 | "ломаная_svg" => {
11199 let pts = self.arg_str(&args, 0, "");
11200 let stroke = self.arg_str(&args, 1, "#ffffff");
11201 let sw = self.arg_num(&args, 2, 1.0)?;
11202 if let Some(svg) = self.svg.borrow_mut().as_mut() {
11203 svg.elements.push(format!(
11204 "<polyline points=\"{pts}\" fill=\"none\" \
11205 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"
11206 ));
11207 }
11208 return Ok(Value::Unit);
11209 },
11210
11211 "svg_text"
11212 | "SVG文本"
11213 | "SVGข้อความ"
11214 | "متن_SVG"
11215 | "نص_SVG"
11216 | "טקסט_SVG"
11217 | "SVG_متن"
11218 | "texte_svg"
11219 | "текст_svg" => {
11220 let x = self.arg_num(&args, 0, 0.0)?;
11221 let y = self.arg_num(&args, 1, 0.0)?;
11222 let text = self.arg_str(&args, 2, "");
11223 let fill = self.arg_str(&args, 3, "#ffffff");
11224 let size = self.arg_num(&args, 4, 12.0)?;
11225 if let Some(svg) = self.svg.borrow_mut().as_mut() {
11226 let safe = text
11227 .replace('&', "&")
11228 .replace('<', "<")
11229 .replace('>', ">");
11230 svg.elements.push(format!(
11231 "<text x=\"{x:.1}\" y=\"{y:.1}\" fill=\"{fill}\" \
11232 font-family=\"monospace\" font-size=\"{size:.0}\">{safe}</text>"
11233 ));
11234 }
11235 return Ok(Value::Unit);
11236 },
11237
11238 "svg_end"
11239 | "结束SVG"
11240 | "จบSVG"
11241 | "پایان_SVG"
11242 | "أنهِ_SVG"
11243 | "סיים_SVG"
11244 | "SVG_ختم"
11245 | "terminer_svg"
11246 | "svg_beenden"
11247 | "закончить_svg" => {
11248 {
11249 let borrow = self.svg.borrow();
11250 if let Some(svg) = borrow.as_ref() {
11251 svg.save()
11252 .map_err(|e| EvalErr::from(format!("svg_end: {e}")))?;
11253 }
11254 }
11255 *self.svg.borrow_mut() = None;
11256 return Ok(Value::Unit);
11257 },
11258
11259 "hsl_color" | "HSL颜色" | "สีHSL" | "رنگ_HSL" | "لون_HSL" | "צבע_HSL" | "HSL_رنگ"
11260 | "couleur_hsl" | "hsl_farbe" | "цвет_hsl" => {
11261 let h = self.arg_num(&args, 0, 0.0)?;
11262 let s = self.arg_num(&args, 1, 70.0)?;
11263 let l = self.arg_num(&args, 2, 50.0)?;
11264 return Ok(Value::Str(hsl_to_hex(h, s, l)));
11265 },
11266
11267 #[cfg(not(target_arch = "wasm32"))]
11273 "fft_push"
11274 | "วิเคราะห์เสียง"
11275 | "频谱输入"
11276 | "FFT入力"
11277 | "FFT입력"
11278 | "ورودی_FFT"
11279 | "أدخل_FFT"
11280 | "הזנת_FFT"
11281 | "FFT_ان_پٹ"
11282 | "fft_entrée"
11283 | "fft_eingabe"
11284 | "fft_вход" => {
11285 if let Some(Value::List(v)) = args.first() {
11286 let samples: Vec<f32> = v
11287 .iter()
11288 .filter_map(|x| {
11289 if let Value::Number(n) = x {
11290 Some(*n as f32)
11291 } else {
11292 None
11293 }
11294 })
11295 .collect();
11296 self.fft.borrow_mut().push_samples(&samples);
11297 }
11298 return Ok(Value::Unit);
11299 },
11300
11301 #[cfg(not(target_arch = "wasm32"))]
11303 "fft_bands"
11304 | "แถบความถี่"
11305 | "频段"
11306 | "周波数帯"
11307 | "주파수대"
11308 | "باندهای_FFT"
11309 | "نطاقات_FFT"
11310 | "פסי_FFT"
11311 | "FFT_بینڈز"
11312 | "fft_bandes"
11313 | "fft_bänder"
11314 | "fft_полосы" => {
11315 let n = self.arg_num(&args, 0, 32.0)? as usize;
11316 let bands = self.fft.borrow().freq_bands(n);
11317 *self.fft_bands_cache.borrow_mut() = bands.clone();
11318 return Ok(Value::List(Rc::new(
11319 bands.into_iter().map(|v| Value::Number(v as f64)).collect(),
11320 )));
11321 },
11322
11323 #[cfg(not(target_arch = "wasm32"))]
11325 "fft_beat"
11326 | "จังหวะเสียง"
11327 | "节拍检测"
11328 | "ビート検出"
11329 | "비트"
11330 | "ضرب_FFT"
11331 | "نبضة_FFT"
11332 | "פעימת_FFT"
11333 | "FFT_دھڑکن"
11334 | "fft_battement"
11335 | "fft_takt"
11336 | "fft_удар" => {
11337 return Ok(Value::Bool(self.fft.borrow().is_beat()));
11338 },
11339
11340 #[cfg(not(target_arch = "wasm32"))]
11342 "fft_beat_ratio"
11343 | "อัตราจังหวะ"
11344 | "节拍比"
11345 | "ビート比"
11346 | "비트비율"
11347 | "نسبت_ضرب_FFT"
11348 | "نسبة_نبضة_FFT"
11349 | "יחס_פעימת_FFT"
11350 | "FFT_بیٹ_تناسب" => {
11351 return Ok(Value::Number(self.fft.borrow().beat_ratio() as f64));
11352 },
11353
11354 #[cfg(not(target_arch = "wasm32"))]
11356 "fft_rms"
11357 | "ระดับRMS"
11358 | "均方根"
11359 | "二乗平均"
11360 | "RMS레벨"
11361 | "RMS_صدا"
11362 | "جذر_متوسط_مربع_FFT"
11363 | "RMS_של_FFT"
11364 | "FFT_RMS" => {
11365 return Ok(Value::Number(self.fft.borrow().rms() as f64));
11366 },
11367
11368 #[cfg(not(target_arch = "wasm32"))]
11370 "fft_dominant_freq"
11371 | "ความถี่หลัก"
11372 | "主频"
11373 | "主要周波数"
11374 | "주파수"
11375 | "فرکانس_غالب"
11376 | "التردد_السائد"
11377 | "תדר_דומיננטי"
11378 | "غالب_فریکوئنسی"
11379 | "fft_fréquence_dominante"
11380 | "fft_dominante_frequenz"
11381 | "fft_доминирующая_частота" => {
11382 return Ok(Value::Number(self.fft.borrow().dominant_freq() as f64));
11383 },
11384
11385 #[cfg(target_arch = "wasm32")]
11387 "fft_push"
11388 | "วิเคราะห์เสียง"
11389 | "频谱输入"
11390 | "FFT入力"
11391 | "FFT입력"
11392 | "ورودی_FFT"
11393 | "أدخل_FFT"
11394 | "הזנת_FFT"
11395 | "FFT_ان_پٹ"
11396 | "fft_entrée"
11397 | "fft_eingabe"
11398 | "fft_вход" => {
11399 return Ok(Value::Unit);
11400 },
11401 #[cfg(target_arch = "wasm32")]
11402 "fft_bands"
11403 | "แถบความถี่"
11404 | "频段"
11405 | "周波数帯"
11406 | "주파수대"
11407 | "باندهای_FFT"
11408 | "نطاقات_FFT"
11409 | "פסי_FFT"
11410 | "FFT_بینڈز"
11411 | "fft_bandes"
11412 | "fft_bänder"
11413 | "fft_полосы" => {
11414 let n = self.arg_num(&args, 0, 32.0)? as usize;
11415 return Ok(Value::List(vec![Value::Number(0.0); n].into()));
11416 },
11417 #[cfg(target_arch = "wasm32")]
11418 "fft_beat"
11419 | "จังหวะเสียง"
11420 | "节拍检测"
11421 | "ビート検出"
11422 | "비트"
11423 | "ضرب_FFT"
11424 | "نبضة_FFT"
11425 | "פעימת_FFT"
11426 | "FFT_دھڑکن"
11427 | "fft_battement"
11428 | "fft_takt"
11429 | "fft_удар" => {
11430 return Ok(Value::Bool(false));
11431 },
11432 #[cfg(target_arch = "wasm32")]
11433 "fft_beat_ratio"
11434 | "อัตราจังหวะ"
11435 | "节拍比"
11436 | "ビート比"
11437 | "비트비율"
11438 | "نسبت_ضرب_FFT"
11439 | "نسبة_نبضة_FFT"
11440 | "יחס_פעימת_FFT"
11441 | "FFT_بیٹ_تناسب" => {
11442 return Ok(Value::Number(1.0));
11443 },
11444 #[cfg(target_arch = "wasm32")]
11445 "fft_rms"
11446 | "ระดับRMS"
11447 | "均方根"
11448 | "二乗平均"
11449 | "RMS레벨"
11450 | "RMS_صدا"
11451 | "جذر_متوسط_مربع_FFT"
11452 | "RMS_של_FFT"
11453 | "FFT_RMS" => {
11454 return Ok(Value::Number(0.0));
11455 },
11456 #[cfg(target_arch = "wasm32")]
11457 "fft_dominant_freq"
11458 | "ความถี่หลัก"
11459 | "主频"
11460 | "主要周波数"
11461 | "주파수"
11462 | "فرکانس_غالب"
11463 | "التردد_السائد"
11464 | "תדר_דומיננטי"
11465 | "غالب_فریکوئنسی"
11466 | "fft_fréquence_dominante"
11467 | "fft_dominante_frequenz"
11468 | "fft_доминирующая_частота" => {
11469 return Ok(Value::Number(0.0));
11470 },
11471
11472 "tex_checkerboard"
11480 | "ลายตารางหมากรุก"
11481 | "بافت_شطرنجی"
11482 | "نسيج_رقعة_الشطرنج"
11483 | "מרקם_שחמט"
11484 | "شطرنج_ٹیکسچر" => {
11485 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11486 let tiles = self.arg_num(&args, 4, 8.0)? as u32;
11487 let (r1, g1, b1) = (
11488 self.arg_num(&args, 5, 255.)? as u32,
11489 self.arg_num(&args, 6, 255.)? as u32,
11490 self.arg_num(&args, 7, 255.)? as u32,
11491 );
11492 let (r2, g2, b2) = (
11493 self.arg_num(&args, 8, 0.)? as u32,
11494 self.arg_num(&args, 9, 0.)? as u32,
11495 self.arg_num(&args, 10, 0.)? as u32,
11496 );
11497 let c1 = (r1 << 16) | (g1 << 8) | b1;
11498 let c2 = (r2 << 16) | (g2 << 8) | b2;
11499 let mut gfx = self.gfx.borrow_mut();
11500 let (bw, bh) = (gfx.width, gfx.height);
11501 for row in 0..th {
11502 for col in 0..tw {
11503 let cx = col as u32 * tiles / tw as u32;
11504 let cy = row as u32 * tiles / th as u32;
11505 let (dx, dy) = (tx + col, ty + row);
11506 if dx < bw && dy < bh {
11507 gfx.buffer[dy * bw + dx] = if (cx + cy) % 2 == 0 { c1 } else { c2 };
11508 }
11509 }
11510 }
11511 return Ok(Value::Unit);
11512 },
11513
11514 "tex_gradient"
11516 | "ลายไล่สี"
11517 | "بافت_گرادیان"
11518 | "نسيج_متدرج"
11519 | "מרקם_גרדיאנט"
11520 | "گریڈینٹ_ٹیکسچر" => {
11521 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11522 let angle = self.arg_num(&args, 4, 0.0)? as f32;
11523 let (r1, g1, b1) = (
11524 self.arg_num(&args, 5, 0.)? as f32 / 255.,
11525 self.arg_num(&args, 6, 0.)? as f32 / 255.,
11526 self.arg_num(&args, 7, 0.)? as f32 / 255.,
11527 );
11528 let (r2, g2, b2) = (
11529 self.arg_num(&args, 8, 255.)? as f32 / 255.,
11530 self.arg_num(&args, 9, 255.)? as f32 / 255.,
11531 self.arg_num(&args, 10, 255.)? as f32 / 255.,
11532 );
11533 let (ca, sa) = (angle.to_radians().cos(), angle.to_radians().sin());
11534 let mut gfx = self.gfx.borrow_mut();
11535 let (bw, bh) = (gfx.width, gfx.height);
11536 for row in 0..th {
11537 for col in 0..tw {
11538 let nx = col as f32 / tw as f32 - 0.5;
11539 let ny = row as f32 / th as f32 - 0.5;
11540 let t = ((nx * ca + ny * sa + 0.707) / 1.414).clamp(0., 1.);
11541 let (dx, dy) = (tx + col, ty + row);
11542 if dx < bw && dy < bh {
11543 gfx.buffer[dy * bw + dx] =
11544 tex_rgb(r1 + (r2 - r1) * t, g1 + (g2 - g1) * t, b1 + (b2 - b1) * t);
11545 }
11546 }
11547 }
11548 return Ok(Value::Unit);
11549 },
11550
11551 "tex_noise" | "ลายนอยส์" | "بافت_نویز" | "نسيج_ضجيج" | "מרקם_רעש" | "نوائز_ٹیکسچر" =>
11553 {
11554 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11555 let scale = self.arg_num(&args, 4, 4.0)? as f32;
11556 let octaves = self.arg_num(&args, 5, 4.0)? as u32;
11557 let seed = self.arg_num(&args, 6, 0.0)? as u32;
11558 let palette = self.arg_str(&args, 7, "rainbow");
11559 let mut gfx = self.gfx.borrow_mut();
11560 let (bw, bh) = (gfx.width, gfx.height);
11561 for row in 0..th {
11562 for col in 0..tw {
11563 let v = tex_fbm(
11564 col as f32 * scale / tw as f32,
11565 row as f32 * scale / th as f32,
11566 octaves,
11567 seed,
11568 );
11569 let [r, g, b] = tex_palette(&palette, v);
11570 let (dx, dy) = (tx + col, ty + row);
11571 if dx < bw && dy < bh {
11572 gfx.buffer[dy * bw + dx] = tex_rgb(r, g, b);
11573 }
11574 }
11575 }
11576 return Ok(Value::Unit);
11577 },
11578
11579 "tex_freq_map"
11582 | "ลายความถี่"
11583 | "نقشه_فرکانس_بافت"
11584 | "خريطة_تردد_النسيج"
11585 | "מפת_תדר_מרקם"
11586 | "فریکوئنسی_میپ_ٹیکسچر" => {
11587 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11588 let time = self.arg_num(&args, 4, 0.0)? as f32;
11589 let speed = self.arg_num(&args, 5, 0.3)? as f32;
11590 let palette = self.arg_str(&args, 6, "rainbow");
11591 let bands: Vec<f32> = {
11592 let c = self.fft_bands_cache.borrow();
11593 if c.is_empty() {
11594 vec![0.0; 32]
11595 } else {
11596 c.clone()
11597 }
11598 };
11599 let n = bands.len().max(1);
11600 let mut gfx = self.gfx.borrow_mut();
11601 let (bw, bh) = (gfx.width, gfx.height);
11602 for row in 0..th {
11603 for col in 0..tw {
11604 let band_idx = (col * n / tw.max(1)).min(n - 1);
11605 let mag = bands[band_idx].clamp(0., 1.);
11606 let fill_y = (mag * th as f32) as usize;
11607 if row >= th.saturating_sub(fill_y) {
11608 let t = (col as f32 / tw as f32 + time * speed) % 1.0;
11609 let [r, g, b] = tex_palette(&palette, t);
11610 let bright = mag * (1.0 - row as f32 / th as f32 * 0.5);
11611 let (dx, dy) = (tx + col, ty + row);
11612 if dx < bw && dy < bh {
11613 gfx.buffer[dy * bw + dx] =
11614 tex_rgb(r * bright, g * bright, b * bright);
11615 }
11616 }
11617 }
11618 }
11619 return Ok(Value::Unit);
11620 },
11621
11622 "tex_spiral"
11624 | "ลายเกลียวหมุน"
11625 | "بافت_مارپیچ"
11626 | "نسيج_حلزوني"
11627 | "מרקם_ספירלה"
11628 | "سرپیچ_ٹیکسچر" => {
11629 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11630 let freq = self.arg_num(&args, 4, 5.0)? as f32;
11631 let n_bands = self.arg_num(&args, 5, 8.0)? as f32;
11632 let time = self.arg_num(&args, 6, 0.0)? as f32;
11633 let palette = self.arg_str(&args, 7, "rainbow");
11634 let mut gfx = self.gfx.borrow_mut();
11635 let (bw, bh) = (gfx.width, gfx.height);
11636 for row in 0..th {
11637 for col in 0..tw {
11638 let nx = col as f32 / tw as f32 - 0.5;
11639 let ny = row as f32 / th as f32 - 0.5;
11640 let r = (nx * nx + ny * ny).sqrt();
11641 let theta = ny.atan2(nx);
11642 let t = ((r * freq - theta / std::f32::consts::TAU + time * 0.5) * n_bands
11643 % 1.0)
11644 .abs();
11645 let [cr, cg, cb] = tex_palette(&palette, t);
11646 let (dx, dy) = (tx + col, ty + row);
11647 if dx < bw && dy < bh {
11648 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
11649 }
11650 }
11651 }
11652 return Ok(Value::Unit);
11653 },
11654
11655 "tex_ripple" | "ลายระลอก" | "بافت_موج" | "نسيج_تموج" | "מרקם_אדווה" | "ریپل_ٹیکسچر" =>
11657 {
11658 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11659 let freq = self.arg_num(&args, 4, 10.0)? as f32;
11660 let rcx = self.arg_num(&args, 5, 0.5)? as f32;
11661 let rcy = self.arg_num(&args, 6, 0.5)? as f32;
11662 let time = self.arg_num(&args, 7, 0.0)? as f32;
11663 let palette = self.arg_str(&args, 8, "ocean");
11664 let mut gfx = self.gfx.borrow_mut();
11665 let (bw, bh) = (gfx.width, gfx.height);
11666 for row in 0..th {
11667 for col in 0..tw {
11668 let nx = col as f32 / tw as f32 - rcx;
11669 let ny = row as f32 / th as f32 - rcy;
11670 let r = (nx * nx + ny * ny).sqrt();
11671 let t = ((r * freq - time) % 1.0).abs();
11672 let [cr, cg, cb] = tex_palette(&palette, t);
11673 let (dx, dy) = (tx + col, ty + row);
11674 if dx < bw && dy < bh {
11675 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
11676 }
11677 }
11678 }
11679 return Ok(Value::Unit);
11680 },
11681
11682 "tex_mandelbrot"
11684 | "ลายแมนเดลบรอต"
11685 | "بافت_ماندلبرو"
11686 | "نسيج_مانديلبروت"
11687 | "מרקם_מנדלברוט"
11688 | "مینڈل_بروٹ_ٹیکسچر" => {
11689 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11690 let zoom = self.arg_num(&args, 4, 1.0)?;
11691 let mcx = self.arg_num(&args, 5, -0.5)?;
11692 let mcy = self.arg_num(&args, 6, 0.0)?;
11693 let max_iter = self.arg_num(&args, 7, 64.0)? as u32;
11694 let palette = self.arg_str(&args, 8, "psychedelic");
11695 let mut gfx = self.gfx.borrow_mut();
11696 let (bw, bh) = (gfx.width, gfx.height);
11697 for row in 0..th {
11698 for col in 0..tw {
11699 let zx0 = (col as f64 / tw as f64 - 0.5) / zoom + mcx;
11700 let zy0 = (row as f64 / th as f64 - 0.5) / zoom + mcy;
11701 let mut x = 0.0f64;
11702 let mut y = 0.0f64;
11703 let mut i = 0u32;
11704 while i < max_iter && x * x + y * y < 4.0 {
11705 let t = x * x - y * y + zx0;
11706 y = 2.0 * x * y + zy0;
11707 x = t;
11708 i += 1;
11709 }
11710 let t = if i == max_iter {
11711 0.0f32
11712 } else {
11713 (i as f32
11714 - (x as f32 * x as f32 + y as f32 * y as f32).ln().ln()
11715 / 2.0f32.ln())
11716 / max_iter as f32
11717 };
11718 let [cr, cg, cb] = tex_palette(&palette, t.clamp(0., 1.));
11719 let (dx, dy) = (tx + col, ty + row);
11720 if dx < bw && dy < bh {
11721 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
11722 }
11723 }
11724 }
11725 return Ok(Value::Unit);
11726 },
11727
11728 "tex_julia"
11730 | "ลายจูเลีย"
11731 | "بافت_ژولیا"
11732 | "نسيج_جوليا"
11733 | "מרקם_ג'וליה"
11734 | "جولیا_ٹیکسچر" => {
11735 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11736 let c_re = self.arg_num(&args, 4, -0.7)?;
11737 let c_im = self.arg_num(&args, 5, 0.27)?;
11738 let max_iter = self.arg_num(&args, 6, 64.0)? as u32;
11739 let palette = self.arg_str(&args, 7, "neon");
11740 let mut gfx = self.gfx.borrow_mut();
11741 let (bw, bh) = (gfx.width, gfx.height);
11742 for row in 0..th {
11743 for col in 0..tw {
11744 let mut zx = (col as f64 / tw as f64 - 0.5) * 3.5;
11745 let mut zy = (row as f64 / th as f64 - 0.5) * 3.5;
11746 let mut i = 0u32;
11747 while i < max_iter && zx * zx + zy * zy < 4.0 {
11748 let t = zx * zx - zy * zy + c_re;
11749 zy = 2.0 * zx * zy + c_im;
11750 zx = t;
11751 i += 1;
11752 }
11753 let t = i as f32 / max_iter as f32;
11754 let [cr, cg, cb] = tex_palette(&palette, t);
11755 let (dx, dy) = (tx + col, ty + row);
11756 if dx < bw && dy < bh {
11757 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
11758 }
11759 }
11760 }
11761 return Ok(Value::Unit);
11762 },
11763
11764 "tex_voronoi"
11766 | "ลายโวโรนอย"
11767 | "بافت_ورونوی"
11768 | "نسيج_فورونوي"
11769 | "מרקם_וורונוי"
11770 | "ورونوئی_ٹیکسچر" => {
11771 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11772 let cells = self.arg_num(&args, 4, 16.0)? as u32;
11773 let seed = self.arg_num(&args, 5, 42.0)? as u32;
11774 let palette = self.arg_str(&args, 6, "rainbow");
11775 let pts: Vec<[f32; 2]> = (0..cells)
11776 .map(|i| {
11777 [
11778 tex_hash(i as i32, 0, seed),
11779 tex_hash(i as i32, 1, seed + 999),
11780 ]
11781 })
11782 .collect();
11783 let mut gfx = self.gfx.borrow_mut();
11784 let (bw, bh) = (gfx.width, gfx.height);
11785 for row in 0..th {
11786 for col in 0..tw {
11787 let (fx, fy) = (col as f32 / tw as f32, row as f32 / th as f32);
11788 let (min_d, nearest) = pts.iter().enumerate().fold(
11789 (f32::MAX, 0usize),
11790 |(d, idx), (i, &[cx, cy])| {
11791 let dd = (fx - cx).powi(2) + (fy - cy).powi(2);
11792 if dd < d {
11793 (dd, i)
11794 } else {
11795 (d, idx)
11796 }
11797 },
11798 );
11799 let t = (nearest as f32 / cells as f32 + min_d * 4.0) % 1.0;
11800 let [cr, cg, cb] = tex_palette(&palette, t);
11801 let (dx, dy) = (tx + col, ty + row);
11802 if dx < bw && dy < bh {
11803 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
11804 }
11805 }
11806 }
11807 return Ok(Value::Unit);
11808 },
11809
11810 "tex_halftone"
11812 | "ลายฮาล์ฟโทน"
11813 | "بافت_نیمتن"
11814 | "نسيج_نصفي"
11815 | "מרקם_חצי_גוון"
11816 | "ہاف_ٹون_ٹیکسچر" => {
11817 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11818 let dot_size = self.arg_num(&args, 4, 0.05)? as f32;
11819 let time = self.arg_num(&args, 5, 0.0)? as f32;
11820 let palette = self.arg_str(&args, 6, "rainbow");
11821 let mut gfx = self.gfx.borrow_mut();
11822 let (bw, bh) = (gfx.width, gfx.height);
11823 for row in 0..th {
11824 for col in 0..tw {
11825 let (fx, fy) = (col as f32 / tw as f32, row as f32 / th as f32);
11826 let gx = (fx / dot_size).floor();
11827 let gy = (fy / dot_size).floor();
11828 let lx = (fx / dot_size - gx - 0.5) * 2.0;
11829 let ly = (fy / dot_size - gy - 0.5) * 2.0;
11830 let r = (lx * lx + ly * ly).sqrt();
11831 let t = (gx / (1.0 / dot_size) + time * 0.1) % 1.0;
11832 let a = if r < 0.7 {
11833 ((0.7 - r) / 0.7).clamp(0., 1.)
11834 } else {
11835 0.0
11836 };
11837 if a > 0.0 {
11838 let [cr, cg, cb] = tex_palette(&palette, t);
11839 let (dx, dy) = (tx + col, ty + row);
11840 if dx < bw && dy < bh {
11841 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
11842 }
11843 }
11844 }
11845 }
11846 return Ok(Value::Unit);
11847 },
11848
11849 "set_shade_mode"
11854 | "设置着色"
11855 | "シェード設定"
11856 | "셰이드모드"
11857 | "ตั้งการแรเงา"
11858 | "تنظیم_حالت_سایهپردازی"
11859 | "عيّن_نمط_التظليل"
11860 | "קבע_מצב_הצללה"
11861 | "شیڈ_موڈ_مقرر_کرو"
11862 | "définir_mode_ombrage"
11863 | "schattierungsmodus_setzen"
11864 | "задать_режим_затенения" => {
11865 let m = self.arg_num(&args, 0, 2.0)? as u8;
11866 self.gfx.borrow_mut().shade_mode = m;
11867 return Ok(Value::Unit);
11868 },
11869 "set_cel_bands"
11871 | "设置色阶"
11872 | "セル段数"
11873 | "셀밴드"
11874 | "ตั้งระดับสี"
11875 | "تنظیم_باندهای_سل"
11876 | "عيّن_نطاقات_التظليل"
11877 | "קבע_רצועות_הצללה"
11878 | "سیل_بینڈز_مقرر_کرو"
11879 | "définir_bandes_cel"
11880 | "cel_bänder_setzen"
11881 | "задать_полосы_cel" => {
11882 let n = (self.arg_num(&args, 0, 4.0)? as u32).max(2);
11883 self.gfx.borrow_mut().shade.bands = n;
11884 return Ok(Value::Unit);
11885 },
11886 "set_shadow_color"
11888 | "设置阴影色"
11889 | "影の色"
11890 | "그림자색"
11891 | "ตั้งสีเงา"
11892 | "تنظیم_رنگ_سایه"
11893 | "عيّن_لون_الظل"
11894 | "קבע_צבע_צל"
11895 | "سایہ_رنگ_مقرر_کرو"
11896 | "définir_couleur_ombre"
11897 | "schattenfarbe_setzen"
11898 | "задать_цвет_тени" => {
11899 let r = self.arg_num(&args, 0, 26.)? as f32 / 255.0;
11900 let g = self.arg_num(&args, 1, 33.)? as f32 / 255.0;
11901 let b = self.arg_num(&args, 2, 77.)? as f32 / 255.0;
11902 self.gfx.borrow_mut().shade.shadow = [r, g, b];
11903 return Ok(Value::Unit);
11904 },
11905 #[cfg(not(target_arch = "wasm32"))]
11911 "crypto_hash"
11912 | "แฮชเข้ารหัส"
11913 | "几何哈希"
11914 | "幾何ハッシュ"
11915 | "기하해시"
11916 | "درهم_رمزنگاری"
11917 | "بصمة_تشفير"
11918 | "גיבוב_הצפנה"
11919 | "خفیہ_ہیش"
11920 | "hachage_crypto"
11921 | "krypto_hash"
11922 | "крипто_хеш" => {
11923 let s = self.arg_str(&args, 0, "");
11924 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(
11925 s.as_bytes(),
11926 ))));
11927 },
11928 #[cfg(target_arch = "wasm32")]
11929 "crypto_hash"
11930 | "แฮชเข้ารหัส"
11931 | "几何哈希"
11932 | "幾何ハッシュ"
11933 | "기하해시"
11934 | "درهم_رمزنگاری"
11935 | "بصمة_تشفير"
11936 | "גיבוב_הצפנה"
11937 | "خفیہ_ہیش"
11938 | "hachage_crypto"
11939 | "krypto_hash"
11940 | "крипто_хеш" => {
11941 let s = self.arg_str(&args, 0, "");
11942 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(
11943 s.as_bytes(),
11944 ))));
11945 },
11946 #[cfg(not(target_arch = "wasm32"))]
11948 "knot_points"
11949 | "จุดปม"
11950 | "结点坐标"
11951 | "結び目点"
11952 | "매듭점"
11953 | "نقاط_گره"
11954 | "نقاط_العقدة"
11955 | "נקודות_קשר"
11956 | "گرہ_پوائنٹس"
11957 | "points_nœud"
11958 | "knotenpunkte"
11959 | "точки_узла" => {
11960 let s = self.arg_str(&args, 0, "");
11961 let shape = ling_crypto::geo::KnotShape::from_bytes(s.as_bytes());
11962 let mut out = Vec::with_capacity(shape.points.len() * 3);
11963 for p in &shape.points {
11964 out.push(Value::Number(p[0] as f64));
11965 out.push(Value::Number(p[1] as f64));
11966 out.push(Value::Number(p[2] as f64));
11967 }
11968 return Ok(Value::List(Rc::new(out)));
11969 },
11970 #[cfg(target_arch = "wasm32")]
11971 "knot_points"
11972 | "จุดปม"
11973 | "结点坐标"
11974 | "結び目点"
11975 | "매듭점"
11976 | "نقاط_گره"
11977 | "نقاط_العقدة"
11978 | "נקודות_קשר"
11979 | "گرہ_پوائنٹس"
11980 | "points_nœud"
11981 | "knotenpunkte"
11982 | "точки_узла" => {
11983 let s = self.arg_str(&args, 0, "");
11984 let shape = ling_crypto::geo::KnotShape::from_bytes(s.as_bytes());
11985 let mut out = Vec::with_capacity(shape.points.len() * 3);
11986 for p in &shape.points {
11987 out.push(Value::Number(p[0] as f64));
11988 out.push(Value::Number(p[1] as f64));
11989 out.push(Value::Number(p[2] as f64));
11990 }
11991 return Ok(Value::List(out.into()));
11992 },
11993 #[cfg(not(target_arch = "wasm32"))]
11994 "knot_label"
11995 | "ป้ายปม"
11996 | "结点标签"
11997 | "結び目ラベル"
11998 | "매듭라벨"
11999 | "برچسب_گره"
12000 | "تسمية_العقدة"
12001 | "תווית_קשר"
12002 | "گرہ_لیبل"
12003 | "étiquette_nœud"
12004 | "knotenbezeichnung"
12005 | "метка_узла" => {
12006 let s = self.arg_str(&args, 0, "");
12007 return Ok(Value::Str(
12008 ling_crypto::geo::KnotShape::from_bytes(s.as_bytes()).label(),
12009 ));
12010 },
12011 #[cfg(target_arch = "wasm32")]
12012 "knot_label"
12013 | "ป้ายปม"
12014 | "结点标签"
12015 | "結び目ラベル"
12016 | "매듭라벨"
12017 | "برچسب_گره"
12018 | "تسمية_العقدة"
12019 | "תווית_קשר"
12020 | "گرہ_لیبل"
12021 | "étiquette_nœud"
12022 | "knotenbezeichnung"
12023 | "метка_узла" => {
12024 let s = self.arg_str(&args, 0, "");
12025 return Ok(Value::Str(
12026 ling_crypto::geo::KnotShape::from_bytes(s.as_bytes()).label(),
12027 ));
12028 },
12029 #[cfg(not(target_arch = "wasm32"))]
12031 "knot_keygen"
12032 | "hybrid_keygen"
12033 | "สร้างกุญแจปม"
12034 | "生成密钥"
12035 | "鍵生成"
12036 | "키생성"
12037 | "تولید_کلید_گره"
12038 | "توليد_مفتاح_العقدة"
12039 | "יצירת_מפתח_קשר"
12040 | "گرہ_کلید_تخلیق"
12041 | "génération_clé_nœud"
12042 | "knotenschlüsselerzeugung"
12043 | "генерация_ключа_узла" => {
12044 self.crypto_ids.push(ling_crypto::KnotIdentity::generate());
12045 return Ok(Value::Number((self.crypto_ids.len() - 1) as f64));
12046 },
12047 #[cfg(not(target_arch = "wasm32"))]
12048 "knot_public"
12049 | "hybrid_public"
12050 | "กุญแจสาธารณะปม"
12051 | "公钥"
12052 | "公開鍵"
12053 | "공개키"
12054 | "کلید_عمومی_گره"
12055 | "مفتاح_العقدة_العام"
12056 | "מפתח_ציבורי_קשר"
12057 | "گرہ_عوامی_کلید"
12058 | "clé_publique_nœud"
12059 | "knoten_öffentlicher_schlüssel"
12060 | "публичный_ключ_узла" => {
12061 let h = self.arg_num(&args, 0, 0.0)? as usize;
12062 let pk = self
12063 .crypto_ids
12064 .get(h)
12065 .map(|id| hex_encode(id.public_key()))
12066 .unwrap_or_default();
12067 return Ok(Value::Str(pk));
12068 },
12069 #[cfg(not(target_arch = "wasm32"))]
12071 "knot_encapsulate"
12072 | "hybrid_encapsulate"
12073 | "ห่อกุญแจปม"
12074 | "封装密钥"
12075 | "カプセル化"
12076 | "캡슐화"
12077 | "کپسولهسازی_گره"
12078 | "تغليف_مفتاح_العقدة"
12079 | "עטיפת_קשר"
12080 | "گرہ_احاطہ"
12081 | "encapsuler_nœud"
12082 | "knoten_kapseln"
12083 | "инкапсулировать_узел" => {
12084 let pk = hex_decode(&self.arg_str(&args, 0, ""));
12085 match ling_crypto::geo::knot_encapsulate(&pk) {
12086 Ok((ct, ss)) => {
12087 return Ok(Value::List(Rc::new(vec![
12088 Value::Str(hex_encode(&ct)),
12089 Value::Str(hex_encode(&ss)),
12090 ])))
12091 },
12092 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
12093 }
12094 },
12095 #[cfg(not(target_arch = "wasm32"))]
12097 "knot_decapsulate"
12098 | "hybrid_decapsulate"
12099 | "แกะกุญแจปม"
12100 | "解封装密钥"
12101 | "カプセル解除"
12102 | "캡슐해제"
12103 | "بازکردن_کپسوله_گره"
12104 | "فك_تغليف_مفتاح_العقدة"
12105 | "פתיחת_עטיפת_קשר"
12106 | "گرہ_احاطہ_کھولو"
12107 | "décapsuler_nœud"
12108 | "knoten_entkapseln"
12109 | "декапсулировать_узел" => {
12110 let h = self.arg_num(&args, 0, 0.0)? as usize;
12111 let ct = hex_decode(&self.arg_str(&args, 1, ""));
12112 let ss = self
12113 .crypto_ids
12114 .get(h)
12115 .and_then(|id| id.decapsulate(&ct).ok())
12116 .map(|s| hex_encode(&s))
12117 .unwrap_or_default();
12118 return Ok(Value::Str(ss));
12119 },
12120 #[cfg(not(target_arch = "wasm32"))]
12122 "crypto_seal"
12123 | "ผนึก"
12124 | "封印"
12125 | "封印する"
12126 | "봉인"
12127 | "مهر_رمزنگاری"
12128 | "ختم_تشفير"
12129 | "חתימת_הצפנה"
12130 | "خفیہ_مہر"
12131 | "sceller_crypto"
12132 | "krypto_versiegeln"
12133 | "запечатать_крипто" => {
12134 let key = hex_to_32(&self.arg_str(&args, 0, ""));
12135 let pt = self.arg_str(&args, 1, "");
12136 match ling_crypto::geo::holo_seal(key, pt.as_bytes()) {
12137 Ok(ct) => return Ok(Value::Str(hex_encode(&ct))),
12138 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
12139 }
12140 },
12141 #[cfg(not(target_arch = "wasm32"))]
12142 "crypto_open"
12143 | "เปิดผนึก"
12144 | "解封"
12145 | "封印解除"
12146 | "봉인해제"
12147 | "بازکردن_مهر"
12148 | "فتح_الختم"
12149 | "פתיחת_חתימה"
12150 | "مہر_کھولو"
12151 | "ouvrir_crypto"
12152 | "krypto_öffnen"
12153 | "открыть_крипто" => {
12154 let key = hex_to_32(&self.arg_str(&args, 0, ""));
12155 let ct = hex_decode(&self.arg_str(&args, 1, ""));
12156 match ling_crypto::geo::holo_open(key, &ct) {
12157 Ok(pt) => return Ok(Value::Str(String::from_utf8_lossy(&pt).into_owned())),
12158 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
12159 }
12160 },
12161 #[cfg(not(target_arch = "wasm32"))]
12163 "holo_points"
12164 | "จุดโฮโลแกรม"
12165 | "全息点"
12166 | "ホログラム点"
12167 | "홀로그램점"
12168 | "نقاط_هولوگرام"
12169 | "نقاط_الهولوغرام"
12170 | "נקודות_הולוגרמה"
12171 | "ہولوگرام_پوائنٹس"
12172 | "points_holo"
12173 | "holo_punkte"
12174 | "точки_голо" => {
12175 let s = self.arg_str(&args, 0, "");
12176 let frags = ling_crypto::geo::scatter(s.as_bytes());
12177 let mut out = Vec::with_capacity(frags.len() * 4);
12178 for f in &frags {
12179 for c in f.coord {
12180 out.push(Value::Number(c as f64));
12181 }
12182 }
12183 return Ok(Value::List(Rc::new(out)));
12184 },
12185 #[cfg(not(target_arch = "wasm32"))]
12186 "holo_fragment_count"
12187 | "จำนวนชิ้นโฮโลแกรม"
12188 | "全息碎片数"
12189 | "ホログラム断片数"
12190 | "홀로그램조각수"
12191 | "تعداد_قطعات_هولوگرام"
12192 | "عدد_شظايا_الهولوغرام"
12193 | "מספר_שברי_הולוגרמה"
12194 | "ہولوگرام_ٹکڑے_تعداد"
12195 | "nombre_fragments_holo"
12196 | "holo_fragmentanzahl"
12197 | "число_фрагментов_голо" => {
12198 let s = self.arg_str(&args, 0, "");
12199 return Ok(Value::Number(
12200 ling_crypto::geo::scatter(s.as_bytes()).len() as f64
12201 ));
12202 },
12203 #[cfg(not(target_arch = "wasm32"))]
12206 "shake_hex" | "SHAKE哈希" => {
12207 let s = self.arg_str(&args, 0, "");
12208 let len = self.arg_num(&args, 1, 32.0)?.max(0.0) as usize;
12209 return Ok(Value::Str(hex_encode(&ling_crypto::Shake256::hash(
12210 s.as_bytes(),
12211 len,
12212 ))));
12213 },
12214 #[cfg(not(target_arch = "wasm32"))]
12216 "ed25519_keygen"
12217 | "생성서명키"
12218 | "สร้างกุญแจลายเซ็น"
12219 | "生成签名密钥"
12220 | "署名鍵生成"
12221 | "تولید_کلید_امضا"
12222 | "توليد_مفتاح_التوقيع"
12223 | "יצירת_מפתח_חתימה"
12224 | "دستخط_کلید_تخلیق"
12225 | "génération_clé_signature"
12226 | "signaturschlüsselerzeugung"
12227 | "генерация_ключа_подписи" => {
12228 self.ed25519_ids
12229 .push(ling_crypto::Ed25519Keypair::generate());
12230 return Ok(Value::Number((self.ed25519_ids.len() - 1) as f64));
12231 },
12232 #[cfg(not(target_arch = "wasm32"))]
12237 "ed25519_keygen_from_seed"
12238 | "씨앗에서생성서명키"
12239 | "สร้างกุญแจลายเซ็นจากเมล็ด"
12240 | "从种子生成签名密钥"
12241 | "シードから署名鍵生成"
12242 | "تولید_کلید_امضا_از_دانه"
12243 | "توليد_مفتاح_التوقيع_من_البذرة"
12244 | "יצירת_מפתח_חתימה_מזרע"
12245 | "بیج_سے_دستخط_کلید_تخلیق"
12246 | "génération_clé_signature_depuis_graine"
12247 | "signaturschlüsselerzeugung_aus_saat"
12248 | "генерация_ключа_подписи_из_семени" => {
12249 let seed = hex_to_32(&self.arg_str(&args, 0, ""));
12250 self.ed25519_ids
12251 .push(ling_crypto::Ed25519Keypair::from_seed(seed));
12252 return Ok(Value::Number((self.ed25519_ids.len() - 1) as f64));
12253 },
12254 #[cfg(not(target_arch = "wasm32"))]
12255 "ed25519_public"
12256 | "서명공개키"
12257 | "กุญแจสาธารณะลายเซ็น"
12258 | "签名公钥"
12259 | "署名公開鍵"
12260 | "کلید_عمومی_امضا"
12261 | "مفتاح_التوقيع_العام"
12262 | "מפתח_חתימה_ציבורי"
12263 | "دستخط_عوامی_کلید"
12264 | "clé_publique_signature"
12265 | "signatur_öffentlicher_schlüssel"
12266 | "публичный_ключ_подписи" => {
12267 let h = self.arg_num(&args, 0, 0.0)? as usize;
12268 let pk = self
12269 .ed25519_ids
12270 .get(h)
12271 .map(|kp| hex_encode(&kp.public_key()))
12272 .unwrap_or_default();
12273 return Ok(Value::Str(pk));
12274 },
12275 #[cfg(not(target_arch = "wasm32"))]
12277 "ed25519_sign"
12278 | "서명하다"
12279 | "เซ็นชื่อ"
12280 | "签名"
12281 | "署名する"
12282 | "امضا_کردن"
12283 | "توقيع"
12284 | "לחתום"
12285 | "دستخط_کریں"
12286 | "signer"
12287 | "signieren"
12288 | "подписать" => {
12289 let h = self.arg_num(&args, 0, 0.0)? as usize;
12290 let msg = self.arg_str(&args, 1, "");
12291 let sig = self
12292 .ed25519_ids
12293 .get(h)
12294 .map(|kp| hex_encode(&kp.sign(msg.as_bytes())))
12295 .unwrap_or_default();
12296 return Ok(Value::Str(sig));
12297 },
12298 #[cfg(not(target_arch = "wasm32"))]
12300 "ed25519_verify"
12301 | "서명확인"
12302 | "ยืนยันลายเซ็น"
12303 | "验证签名"
12304 | "署名検証"
12305 | "تایید_امضا"
12306 | "التحقق_من_التوقيع"
12307 | "אימות_חתימה"
12308 | "دستخط_تصدیق"
12309 | "vérifier_signature"
12310 | "signatur_verifizieren"
12311 | "проверить_подпись" => {
12312 let pk_hex = self.arg_str(&args, 0, "");
12313 let msg = self.arg_str(&args, 1, "");
12314 let sig_hex = self.arg_str(&args, 2, "");
12315 let pk_bytes = hex_decode(&pk_hex);
12316 let sig_bytes = hex_decode(&sig_hex);
12317 let ok = (|| {
12318 let pk: [u8; 32] = pk_bytes.try_into().ok()?;
12319 let sig: [u8; 64] = sig_bytes.try_into().ok()?;
12320 Some(ling_crypto::Ed25519Keypair::verify(&pk, msg.as_bytes(), &sig).is_ok())
12321 })()
12322 .unwrap_or(false);
12323 return Ok(Value::Bool(ok));
12324 },
12325 #[cfg(not(target_arch = "wasm32"))]
12332 "mldsa_keygen"
12333 | "양자서명키생성"
12334 | "สร้างกุญแจลายเซ็นควอนตัม"
12335 | "生成量子签名密钥"
12336 | "量子署名鍵生成"
12337 | "تولید_کلید_امضای_کوانتومی"
12338 | "توليد_مفتاح_التوقيع_الكمي"
12339 | "יצירת_מפתח_חתימה_קוונטי"
12340 | "کوانٹم_دستخط_کلید_تخلیق"
12341 | "génération_clé_signature_quantique"
12342 | "quantensignaturschlüsselerzeugung"
12343 | "генерация_ключа_квантовой_подписи" => {
12344 self.mldsa_ids.push(ling_crypto::MlDsa65Keypair::generate());
12345 return Ok(Value::Number((self.mldsa_ids.len() - 1) as f64));
12346 },
12347 #[cfg(not(target_arch = "wasm32"))]
12350 "mldsa_keygen_from_seed"
12351 | "씨앗에서양자서명키생성"
12352 | "สร้างกุญแจลายเซ็นควอนตัมจากเมล็ด"
12353 | "从种子生成量子签名密钥"
12354 | "シードから量子署名鍵生成"
12355 | "تولید_کلید_امضای_کوانتومی_از_دانه"
12356 | "توليد_مفتاح_التوقيع_الكمي_من_البذرة"
12357 | "יצירת_מפתח_חתימה_קוונטי_מזרע"
12358 | "بیج_سے_کوانٹم_دستخط_کلید_تخلیق"
12359 | "génération_clé_signature_quantique_depuis_graine"
12360 | "quantensignaturschlüsselerzeugung_aus_saat"
12361 | "генерация_ключа_квантовой_подписи_из_семени" =>
12362 {
12363 let seed = hex_to_32(&self.arg_str(&args, 0, ""));
12364 self.mldsa_ids
12365 .push(ling_crypto::MlDsa65Keypair::from_seed(seed));
12366 return Ok(Value::Number((self.mldsa_ids.len() - 1) as f64));
12367 },
12368 #[cfg(not(target_arch = "wasm32"))]
12369 "mldsa_public"
12370 | "양자서명공개키"
12371 | "กุญแจสาธารณะลายเซ็นควอนตัม"
12372 | "量子签名公钥"
12373 | "量子署名公開鍵"
12374 | "کلید_عمومی_امضای_کوانتومی"
12375 | "مفتاح_التوقيع_الكمي_العام"
12376 | "מפתח_חתימה_קוונטי_ציבורי"
12377 | "کوانٹم_دستخط_عوامی_کلید"
12378 | "clé_publique_signature_quantique"
12379 | "quantensignatur_öffentlicher_schlüssel"
12380 | "публичный_ключ_квантовой_подписи" => {
12381 let h = self.arg_num(&args, 0, 0.0)? as usize;
12382 let pk = self
12383 .mldsa_ids
12384 .get(h)
12385 .map(|kp| hex_encode(&kp.public_key()))
12386 .unwrap_or_default();
12387 return Ok(Value::Str(pk));
12388 },
12389 #[cfg(not(target_arch = "wasm32"))]
12391 "mldsa_sign"
12392 | "양자서명하다"
12393 | "เซ็นชื่อควอนตัม"
12394 | "量子签名"
12395 | "量子署名する"
12396 | "امضای_کوانتومی_کردن"
12397 | "توقيع_كمي"
12398 | "לחתום_קוונטית"
12399 | "کوانٹم_دستخط_کریں"
12400 | "signer_quantique"
12401 | "quantensignieren"
12402 | "подписать_квантово" => {
12403 let h = self.arg_num(&args, 0, 0.0)? as usize;
12404 let msg = self.arg_str(&args, 1, "");
12405 let sig = self
12406 .mldsa_ids
12407 .get(h)
12408 .map(|kp| hex_encode(&kp.sign(msg.as_bytes())))
12409 .unwrap_or_default();
12410 return Ok(Value::Str(sig));
12411 },
12412 #[cfg(not(target_arch = "wasm32"))]
12414 "mldsa_verify"
12415 | "양자서명확인"
12416 | "ยืนยันลายเซ็นควอนตัม"
12417 | "验证量子签名"
12418 | "量子署名検証"
12419 | "تایید_امضای_کوانتومی"
12420 | "التحقق_من_التوقيع_الكمي"
12421 | "אימות_חתימה_קוונטית"
12422 | "کوانٹم_دستخط_تصدیق"
12423 | "vérifier_signature_quantique"
12424 | "quantensignatur_verifizieren"
12425 | "проверить_квантовую_подпись" => {
12426 let pk_hex = self.arg_str(&args, 0, "");
12427 let msg = self.arg_str(&args, 1, "");
12428 let sig_hex = self.arg_str(&args, 2, "");
12429 let pk_bytes = hex_decode(&pk_hex);
12430 let sig_bytes = hex_decode(&sig_hex);
12431 let ok = ling_crypto::MlDsa65Keypair::verify(&pk_bytes, msg.as_bytes(), &sig_bytes)
12432 .is_ok();
12433 return Ok(Value::Bool(ok));
12434 },
12435 #[cfg(not(target_arch = "wasm32"))]
12438 "password_hash" | "비밀번호해시" => {
12439 let pw = self.arg_str(&args, 0, "");
12440 let hash = ling_crypto::Argon2idParams::default()
12441 .hash_password(pw.as_bytes())
12442 .unwrap_or_default();
12443 return Ok(Value::Str(hash));
12444 },
12445 #[cfg(not(target_arch = "wasm32"))]
12446 "password_verify" | "비밀번호확인" => {
12447 let pw = self.arg_str(&args, 0, "");
12448 let hash = self.arg_str(&args, 1, "");
12449 let ok = ling_crypto::Argon2idParams::verify_password(pw.as_bytes(), &hash).is_ok();
12450 return Ok(Value::Bool(ok));
12451 },
12452 #[cfg(not(target_arch = "wasm32"))]
12455 "random_hex" | "무작위16진수" => {
12456 use rand::RngCore;
12457 let n = self.arg_num(&args, 0, 16.0)?.max(0.0) as usize;
12458 let mut buf = vec![0u8; n];
12459 rand::rngs::OsRng.fill_bytes(&mut buf);
12460 return Ok(Value::Str(hex_encode(&buf)));
12461 },
12462 #[cfg(not(target_arch = "wasm32"))]
12466 "base64_encode" | "base64인코딩" => {
12467 use base64::Engine as _;
12468 let s = self.arg_str(&args, 0, "");
12469 return Ok(Value::Str(
12470 base64::engine::general_purpose::STANDARD.encode(s.as_bytes()),
12471 ));
12472 },
12473 #[cfg(not(target_arch = "wasm32"))]
12477 "remove_file" | "ลบไฟล์" | "删除文件" | "ファイル削除" | "파일삭제" =>
12481 {
12482 let path = self.arg_str(&args, 0, "");
12483 return Ok(Value::Bool(std::fs::remove_file(&path).is_ok()));
12484 },
12485 "rename_file" | "เปลี่ยนชื่อไฟล์" | "重命名文件" | "ファイル改名" | "파일이름변경" =>
12490 {
12491 let from = self.arg_str(&args, 0, "");
12492 let to = self.arg_str(&args, 1, "");
12493 if std::fs::rename(&from, &to).is_ok() {
12494 return Ok(Value::Bool(true));
12495 }
12496 let ok = std::fs::copy(&from, &to).is_ok();
12497 if ok {
12498 let _ = std::fs::remove_file(&from);
12499 }
12500 return Ok(Value::Bool(ok));
12501 },
12502 "base64_decode_to_file" | "base64파일로저장" => {
12503 use base64::Engine as _;
12504 let b64 = self.arg_str(&args, 0, "");
12505 let path = self.arg_str(&args, 1, "");
12506 let b64 = b64.split(',').next_back().unwrap_or(&b64); let ok = base64::engine::general_purpose::STANDARD
12508 .decode(b64)
12509 .ok()
12510 .and_then(|bytes| std::fs::write(&path, bytes).ok())
12511 .is_some();
12512 return Ok(Value::Bool(ok));
12513 },
12514 #[cfg(not(target_arch = "wasm32"))]
12519 "qr_svg" | "QR코드" => {
12520 let text = self.arg_str(&args, 0, "");
12521 let svg = qrcode::QrCode::new(text.as_bytes())
12522 .map(|code| {
12523 code.render::<qrcode::render::svg::Color>()
12524 .min_dimensions(240, 240)
12525 .dark_color(qrcode::render::svg::Color("#1a0f3d"))
12526 .light_color(qrcode::render::svg::Color("#ffffff"))
12527 .build()
12528 })
12529 .unwrap_or_default();
12530 return Ok(Value::Str(svg));
12531 },
12532 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
12535 "zip_files" | "압축파일" => {
12536 let paths = match args.first() {
12537 Some(Value::List(l)) => l.iter().map(|v| v.to_string()).collect::<Vec<_>>(),
12538 _ => Vec::new(),
12539 };
12540 let out_path = self.arg_str(&args, 1, "out.zip");
12541 let ok = (|| -> std::io::Result<()> {
12542 let file = std::fs::File::create(&out_path)?;
12543 let mut writer = zip::ZipWriter::new(file);
12544 let options: zip::write::FileOptions<'_, ()> =
12545 zip::write::FileOptions::default()
12546 .compression_method(zip::CompressionMethod::Deflated);
12547 for p in &paths {
12548 let name = std::path::Path::new(p)
12549 .file_name()
12550 .map(|n| n.to_string_lossy().into_owned())
12551 .unwrap_or_else(|| p.clone());
12552 let bytes = std::fs::read(p)?;
12553 writer.start_file(name, options)?;
12554 std::io::Write::write_all(&mut writer, &bytes)?;
12555 }
12556 writer.finish()?;
12557 Ok(())
12558 })()
12559 .is_ok();
12560 return Ok(Value::Bool(ok));
12561 },
12562 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
12569 "pdf_from_images" | "PDF来自图片" => {
12570 let paths = match args.first() {
12571 Some(Value::List(l)) => l.iter().map(|v| v.to_string()).collect::<Vec<_>>(),
12572 _ => Vec::new(),
12573 };
12574 let out_path = self.arg_str(&args, 1, "out.pdf");
12575 let ok = build_pdf_from_images(&paths, &out_path).is_ok();
12576 return Ok(Value::Bool(ok));
12577 },
12578
12579 "ease" => {
12583 let name = self.arg_str(&args, 0, "ease");
12584 let t = self.arg_num(&args, 1, 0.0)? as f32;
12585 return Ok(Value::Number(
12586 ling_ui::Easing::from_name(&name).apply(t) as f64
12587 ));
12588 },
12589
12590 "tween"
12595 | "补间"
12596 | "補間"
12597 | "트윈"
12598 | "แทรกค่า"
12599 | "میانفریم"
12600 | "تدرج_حركي"
12601 | "טווין"
12602 | "ٹوئین"
12603 | "твин" => {
12604 let a = self.arg_num(&args, 0, 0.0)?;
12605 let b = self.arg_num(&args, 1, 0.0)?;
12606 let t = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 1.0);
12607 return Ok(Value::Number(a + (b - a) * t));
12608 },
12609 "tween_ease"
12610 | "缓动补间"
12611 | "緩和補間"
12612 | "이징트윈"
12613 | "แทรกนุ่ม"
12614 | "میانفریم_نرم"
12615 | "تدرج_ناعم_حركي"
12616 | "טווין_חלק"
12617 | "ٹوئین_ایز"
12618 | "tween_lisse"
12619 | "tween_glättung"
12620 | "твин_плавность" => {
12621 let a = self.arg_num(&args, 0, 0.0)? as f32;
12622 let b = self.arg_num(&args, 1, 0.0)? as f32;
12623 let t = self.arg_num(&args, 2, 0.0)? as f32;
12624 let kind = self.arg_str(&args, 3, "linear");
12625 let e = ling_animation::EaseFunction::from_name(&kind);
12626 return Ok(Value::Number(
12627 ling_animation::ease::tween_ease(&a, &b, t, e) as f64,
12628 ));
12629 },
12630 "breathe" | "呼吸" | "호흡" | "หายใจ" | "تنفس" | "נשימה" | "سانس" | "respirer"
12632 | "atmen" | "дышать" => {
12633 let t = self.arg_num(&args, 0, 0.0)? as f32;
12634 let rate = self.arg_num(&args, 1, 1.0)? as f32;
12635 let depth = self.arg_num(&args, 2, 0.1)? as f32;
12636 return Ok(Value::Number(
12637 ling_animation::scalar::breathe(t, rate, depth) as f64,
12638 ));
12639 },
12640 "wobble"
12641 | "摆动"
12642 | "揺れ"
12643 | "흔들림"
12644 | "โยก"
12645 | "نوسان"
12646 | "تذبذب"
12647 | "תנודה"
12648 | "لرزش"
12649 | "osciller"
12650 | "wackeln"
12651 | "покачивание" => {
12652 let t = self.arg_num(&args, 0, 0.0)? as f32;
12653 let freq = self.arg_num(&args, 1, 1.0)? as f32;
12654 let amp = self.arg_num(&args, 2, 1.0)? as f32;
12655 let phase = self.arg_num(&args, 3, 0.0)? as f32;
12656 return Ok(Value::Number(
12657 ling_animation::scalar::wobble(t, freq, amp, phase) as f64,
12658 ));
12659 },
12660 "gait_phase"
12661 | "步相"
12662 | "歩相"
12663 | "걸음위상"
12664 | "เฟสก้าว"
12665 | "فاز_گام"
12666 | "طور_المشية"
12667 | "שלב_הליכה"
12668 | "چال_مرحلہ"
12669 | "phase_démarche"
12670 | "gangphase"
12671 | "фаза_походки" => {
12672 let t = self.arg_num(&args, 0, 0.0)? as f32;
12673 let speed = self.arg_num(&args, 1, 1.0)? as f32;
12674 return Ok(Value::Number(
12675 ling_animation::scalar::gait_phase(t, speed) as f64
12676 ));
12677 },
12678 "gait_swing"
12679 | "步摆"
12680 | "歩振り"
12681 | "걸음흔들"
12682 | "ก้าวแกว่ง"
12683 | "نوسان_گام"
12684 | "أرجحة_المشية"
12685 | "נדנוד_הליכה"
12686 | "چال_جھولا"
12687 | "balancement_démarche"
12688 | "gangschwung"
12689 | "мах_походки" => {
12690 let t = self.arg_num(&args, 0, 0.0)? as f32;
12691 let speed = self.arg_num(&args, 1, 1.0)? as f32;
12692 let stride = self.arg_num(&args, 2, 1.0)? as f32;
12693 return Ok(Value::Number(
12694 ling_animation::scalar::gait_swing(t, speed, stride) as f64,
12695 ));
12696 },
12697 "gait_lift"
12698 | "抬脚"
12699 | "足上げ"
12700 | "발들기"
12701 | "ยกเท้า"
12702 | "بلندشدن_گام"
12703 | "رفع_المشية"
12704 | "הרמת_הליכה"
12705 | "چال_اٹھاؤ"
12706 | "levée_démarche"
12707 | "ganghub"
12708 | "подъём_походки" => {
12709 let t = self.arg_num(&args, 0, 0.0)? as f32;
12710 let speed = self.arg_num(&args, 1, 1.0)? as f32;
12711 let height = self.arg_num(&args, 2, 1.0)? as f32;
12712 return Ok(Value::Number(
12713 ling_animation::scalar::gait_lift(t, speed, height) as f64,
12714 ));
12715 },
12716 "spring_to"
12717 | "弹向"
12718 | "バネ寄せ"
12719 | "스프링이동"
12720 | "สปริงไป"
12721 | "فنر_بهسوی"
12722 | "نابض_إلى"
12723 | "קפיץ_אל"
12724 | "اسپرنگ_تک"
12725 | "ressort_vers"
12726 | "feder_zu"
12727 | "пружина_к" => {
12728 let pos = self.arg_num(&args, 0, 0.0)? as f32;
12729 let vel = self.arg_num(&args, 1, 0.0)? as f32;
12730 let target = self.arg_num(&args, 2, 0.0)? as f32;
12731 let stiffness = self.arg_num(&args, 3, 120.0)? as f32;
12732 let damping = self.arg_num(&args, 4, 14.0)? as f32;
12733 let dt = self.arg_num(&args, 5, 1.0 / 60.0)? as f32;
12734 let (np, nv) =
12735 ling_animation::scalar::spring_step(pos, vel, target, stiffness, damping, dt);
12736 return Ok(Value::List(Rc::new(vec![
12737 Value::Number(np as f64),
12738 Value::Number(nv as f64),
12739 ])));
12740 },
12741 "ik2"
12742 | "反解"
12743 | "逆運動"
12744 | "역운동"
12745 | "ไอเค2"
12746 | "سینماتیک_معکوس2"
12747 | "حركية_عكسية2"
12748 | "קינמטיקה_הפוכה2"
12749 | "آئی_کے2"
12750 | "cinématique_inverse2"
12751 | "inverse_kinematik2"
12752 | "обратная_кинематика2" => {
12753 let l1 = self.arg_num(&args, 0, 1.0)? as f32;
12754 let l2 = self.arg_num(&args, 1, 1.0)? as f32;
12755 let tx = self.arg_num(&args, 2, 0.0)? as f32;
12756 let ty = self.arg_num(&args, 3, 0.0)? as f32;
12757 let (sh, el) = ling_animation::scalar::two_bone_ik(l1, l2, tx, ty);
12758 return Ok(Value::List(Rc::new(vec![
12759 Value::Number(sh as f64),
12760 Value::Number(el as f64),
12761 ])));
12762 },
12763 "gear_couple"
12765 | "齿轮联动"
12766 | "歯車連動"
12767 | "기어연동"
12768 | "เฟืองทด"
12769 | "جفت_چرخدنده"
12770 | "اقتران_التروس"
12771 | "צימוד_גלגלי_שיניים"
12772 | "گیئر_جوڑا"
12773 | "accoupler_engrenage"
12774 | "zahnrad_koppeln"
12775 | "сцепить_шестерни" => {
12776 let angle = self.arg_num(&args, 0, 0.0)? as f32;
12777 let ti = self.arg_num(&args, 1, 1.0)? as f32;
12778 let to = self.arg_num(&args, 2, 1.0)? as f32;
12779 return Ok(Value::Number(
12780 ling_animation::scalar::gear(angle, ti, to) as f64
12781 ));
12782 },
12783 "gear_train"
12784 | "齿轮组"
12785 | "歯車列"
12786 | "기어열"
12787 | "ชุดเฟือง"
12788 | "مجموعه_چرخدنده"
12789 | "قطار_التروس"
12790 | "שרשרת_גלגלי_שיניים"
12791 | "گیئر_ٹرین"
12792 | "train_engrenages"
12793 | "zahnradgetriebe"
12794 | "передача_шестерён" => {
12795 let angle = self.arg_num(&args, 0, 0.0)? as f32;
12796 let teeth: Vec<f32> = match args.get(1) {
12797 Some(Value::List(items)) => items
12798 .iter()
12799 .filter_map(|v| {
12800 if let Value::Number(n) = v {
12801 Some(*n as f32)
12802 } else {
12803 None
12804 }
12805 })
12806 .collect(),
12807 _ => Vec::new(),
12808 };
12809 let out = ling_animation::mechanism::gear_train(angle, &teeth);
12810 return Ok(Value::List(Rc::new(
12811 out.into_iter().map(|a| Value::Number(a as f64)).collect(),
12812 )));
12813 },
12814 "cam_lift"
12815 | "凸轮升程"
12816 | "カム揚程"
12817 | "캠리프트"
12818 | "ยกลูกเบี้ยว"
12819 | "بلندشدن_بادامک"
12820 | "رفع_الكامة"
12821 | "הרמת_קאם"
12822 | "کیم_اٹھاؤ"
12823 | "levée_came"
12824 | "nockenhub"
12825 | "подъём_кулачка" => {
12826 let angle = self.arg_num(&args, 0, 0.0)? as f32;
12827 let lift = self.arg_num(&args, 1, 1.0)? as f32;
12828 return Ok(Value::Number(
12829 ling_animation::scalar::cam_lift(angle, lift) as f64
12830 ));
12831 },
12832 "piston" | "活塞" | "ピストン" | "피스톤" | "ลูกสูบ" | "پیستون" | "مكبس" | "בוכנה"
12833 | "پسٹن" | "kolben" | "поршень" => {
12834 let angle = self.arg_num(&args, 0, 0.0)? as f32;
12835 let crank = self.arg_num(&args, 1, 1.0)? as f32;
12836 let rod = self.arg_num(&args, 2, 2.0)? as f32;
12837 return Ok(Value::Number(
12838 ling_animation::scalar::piston(angle, crank, rod) as f64,
12839 ));
12840 },
12841 "rack"
12842 | "齿条"
12843 | "ラック"
12844 | "랙"
12845 | "แร็ค"
12846 | "زبانهدنده"
12847 | "سكة_مسننة"
12848 | "מוט_שיניים"
12849 | "ریک"
12850 | "crémaillère"
12851 | "zahnstange"
12852 | "рейка" => {
12853 let angle = self.arg_num(&args, 0, 0.0)? as f32;
12854 let radius = self.arg_num(&args, 1, 1.0)? as f32;
12855 return Ok(Value::Number(
12856 ling_animation::scalar::rack(angle, radius) as f64
12857 ));
12858 },
12859 #[cfg(not(target_arch = "wasm32"))]
12860 "mouse_x" => {
12861 let gfx = self.gfx.borrow();
12862 let v = gfx
12863 .window
12864 .as_ref()
12865 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
12866 .map(|p| p.0 as f64)
12867 .unwrap_or(0.0);
12868 return Ok(Value::Number(v));
12869 },
12870 #[cfg(target_arch = "wasm32")]
12871 "mouse_x" => {
12872 return Ok(Value::Number(crate::gfx::wasm_mouse_x() as f64));
12873 },
12874 #[cfg(not(target_arch = "wasm32"))]
12875 "mouse_y" => {
12876 let gfx = self.gfx.borrow();
12877 let v = gfx
12878 .window
12879 .as_ref()
12880 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
12881 .map(|p| p.1 as f64)
12882 .unwrap_or(0.0);
12883 return Ok(Value::Number(v));
12884 },
12885 #[cfg(target_arch = "wasm32")]
12886 "mouse_y" => {
12887 return Ok(Value::Number(crate::gfx::wasm_mouse_y() as f64));
12888 },
12889 #[cfg(not(target_arch = "wasm32"))]
12890 "mouse_down" => {
12891 let mut gfx = self.gfx.borrow_mut();
12892 let d = !gfx.input_suppressed()
12893 && gfx
12894 .window
12895 .as_ref()
12896 .map(|w| w.get_mouse_down(minifb::MouseButton::Left))
12897 .unwrap_or(false);
12898 return Ok(Value::Bool(d));
12899 },
12900 #[cfg(target_arch = "wasm32")]
12901 "mouse_down" => {
12902 return Ok(Value::Bool(crate::gfx::wasm_mouse_down()));
12903 },
12904 #[cfg(not(target_arch = "wasm32"))]
12905 "mouse_down_right"
12906 | "เมาส์ขวา"
12907 | "ماوس_راست_فشرده"
12908 | "الفأرة_اليمنى_مضغوطة"
12909 | "עכבר_ימני_לחוץ"
12910 | "دایاں_ماؤس_دبا_ہوا" => {
12911 let mut gfx = self.gfx.borrow_mut();
12912 let d = !gfx.input_suppressed()
12913 && gfx
12914 .window
12915 .as_ref()
12916 .map(|w| w.get_mouse_down(minifb::MouseButton::Right))
12917 .unwrap_or(false);
12918 return Ok(Value::Bool(d));
12919 },
12920 #[cfg(target_arch = "wasm32")]
12921 "mouse_down_right"
12922 | "เมาส์ขวา"
12923 | "ماوس_راست_فشرده"
12924 | "الفأرة_اليمنى_مضغوطة"
12925 | "עכבר_ימני_לחוץ"
12926 | "دایاں_ماؤس_دبا_ہوا" => {
12927 return Ok(Value::Bool(crate::gfx::wasm_mouse_down_right()));
12928 },
12929 #[cfg(not(target_arch = "wasm32"))]
12930 "mouse_down_middle"
12931 | "เมาส์กลาง"
12932 | "ماوس_وسط_فشرده"
12933 | "الفأرة_الوسطى_مضغوطة"
12934 | "עכבר_אמצעי_לחוץ"
12935 | "درمیانی_ماؤس_دبا_ہوا" => {
12936 let mut gfx = self.gfx.borrow_mut();
12937 let d = !gfx.input_suppressed()
12938 && gfx
12939 .window
12940 .as_ref()
12941 .map(|w| w.get_mouse_down(minifb::MouseButton::Middle))
12942 .unwrap_or(false);
12943 return Ok(Value::Bool(d));
12944 },
12945 #[cfg(target_arch = "wasm32")]
12946 "mouse_down_middle"
12947 | "เมาส์กลาง"
12948 | "ماوس_وسط_فشرده"
12949 | "الفأرة_الوسطى_مضغوطة"
12950 | "עכבר_אמצעי_לחוץ"
12951 | "درمیانی_ماؤس_دبا_ہوا" => {
12952 return Ok(Value::Bool(crate::gfx::wasm_mouse_down_middle()));
12953 },
12954 #[cfg(not(target_arch = "wasm32"))]
12955 "ui_hot"
12956 | "热区"
12957 | "ホットエリア"
12958 | "핫존"
12959 | "พื้นที่สัมผัส"
12960 | "ناحیه_فعال"
12961 | "منطقة_ساخنة"
12962 | "אזור_חם"
12963 | "ہاٹ_زون"
12964 | "survol_ui"
12965 | "ui_hover"
12966 | "ui_наведение" => {
12967 let x = self.arg_num(&args, 0, 0.0)? as f32;
12968 let y = self.arg_num(&args, 1, 0.0)? as f32;
12969 let w = self.arg_num(&args, 2, 0.0)? as f32;
12970 let h = self.arg_num(&args, 3, 0.0)? as f32;
12971 let gfx = self.gfx.borrow();
12972 let (mx, my) = gfx
12973 .window
12974 .as_ref()
12975 .and_then(|win| win.get_mouse_pos(minifb::MouseMode::Clamp))
12976 .unwrap_or((0.0, 0.0));
12977 return Ok(Value::Bool(ling_ui::holo::hit_rect(mx, my, x, y, w, h)));
12978 },
12979 #[cfg(target_arch = "wasm32")]
12980 "ui_hot"
12981 | "热区"
12982 | "ホットエリア"
12983 | "핫존"
12984 | "พื้นที่สัมผัส"
12985 | "ناحیه_فعال"
12986 | "منطقة_ساخنة"
12987 | "אזור_חם"
12988 | "ہاٹ_زون"
12989 | "survol_ui"
12990 | "ui_hover"
12991 | "ui_наведение" => {
12992 return Ok(Value::Bool(false));
12993 },
12994 "ui_text"
12996 | "界面文字"
12997 | "UI文字"
12998 | "UI텍스트"
12999 | "ข้อความหน้าจอ"
13000 | "متن_رابط"
13001 | "نص_الواجهة"
13002 | "טקסט_ממשק"
13003 | "یو_آئی_متن"
13004 | "texte_ui"
13005 | "ui_beschriftung"
13006 | "ui_текст" => {
13007 let x = self.arg_num(&args, 0, 0.0)? as f32;
13008 let y = self.arg_num(&args, 1, 0.0)? as f32;
13009 let scale = self.arg_num(&args, 2, 16.0)? as f32;
13010 let s = self.arg_str(&args, 3, "");
13011 let segs = ling_ui::holo::text_lines(&s, x, y, scale * 0.62, scale, scale * 0.24);
13012 let mut gfx = self.gfx.borrow_mut();
13013 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
13014 for sg in segs {
13015 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
13016 }
13017 return Ok(Value::Unit);
13018 },
13019 #[cfg(not(target_arch = "wasm32"))]
13022 "font_load"
13023 | "โหลดฟอนต์"
13024 | "加载字体"
13025 | "フォント読込"
13026 | "글꼴로드"
13027 | "بارگذاری_فونت"
13028 | "تحميل_الخط"
13029 | "טעינת_גופן"
13030 | "فونٹ_لوڈ"
13031 | "charger_police"
13032 | "schriftart_laden"
13033 | "загрузить_шрифт" => {
13034 let path = self.arg_str(&args, 0, "");
13035 let weight = match self.arg_num(&args, 1, 0.0)? {
13037 w if w > 0.0 => Some(w as f32),
13038 _ => None,
13039 };
13040 let mut loaded = ling_graphics::VectorFont::from_path_weight(&path, weight);
13042 if loaded.is_err() {
13043 if let Some(dir) = &self.source_dir {
13044 let joined = dir.join(&path);
13045 loaded = ling_graphics::VectorFont::from_path_weight(
13046 &joined.to_string_lossy(),
13047 weight,
13048 );
13049 }
13050 }
13051 match loaded {
13052 Ok(f) => {
13053 let id = self.fonts.len();
13054 self.fonts.push(f);
13055 return Ok(Value::Number(id as f64));
13056 },
13057 Err(e) => {
13058 eprintln!("font_load failed ({path}): {e}");
13059 return Ok(Value::Number(-1.0));
13060 },
13061 }
13062 },
13063 #[cfg(target_arch = "wasm32")]
13064 "font_load"
13065 | "โหลดฟอนต์"
13066 | "加载字体"
13067 | "フォント読込"
13068 | "글꼴로드"
13069 | "بارگذاری_فونت"
13070 | "تحميل_الخط"
13071 | "טעינת_גופן"
13072 | "فونٹ_لوڈ"
13073 | "charger_police"
13074 | "schriftart_laden"
13075 | "загрузить_шрифт" => {
13076 return Ok(Value::Number(-1.0));
13079 },
13080 #[cfg(not(target_arch = "wasm32"))]
13085 "image_load" => {
13086 let path = self.arg_str(&args, 0, "").replace('\\', "/");
13087 let mut loaded = image::open(&path);
13088 if loaded.is_err() {
13089 if let Some(dir) = &self.source_dir {
13090 let joined = dir.join(&path);
13091 loaded = image::open(&joined);
13092 }
13093 }
13094 match loaded {
13095 Ok(img) => {
13096 let id = self.images.len();
13097 self.images.push(img.to_rgba8());
13098 return Ok(Value::Number(id as f64));
13099 },
13100 Err(e) => {
13101 eprintln!("image_load failed ({path}): {e}");
13102 return Ok(Value::Number(-1.0));
13103 },
13104 }
13105 },
13106 #[cfg(target_arch = "wasm32")]
13107 "image_load" => {
13108 return Ok(Value::Number(-1.0));
13110 },
13111 "image_width" => {
13112 let id = self.arg_num(&args, 0, -1.0)? as i64;
13113 if id >= 0 && (id as usize) < self.images.len() {
13114 return Ok(Value::Number(self.images[id as usize].width() as f64));
13115 }
13116 return Ok(Value::Number(0.0));
13117 },
13118 "image_height" => {
13119 let id = self.arg_num(&args, 0, -1.0)? as i64;
13120 if id >= 0 && (id as usize) < self.images.len() {
13121 return Ok(Value::Number(self.images[id as usize].height() as f64));
13122 }
13123 return Ok(Value::Number(0.0));
13124 },
13125 "image_pixel_r" | "image_pixel_g" | "image_pixel_b" | "image_pixel_a" => {
13126 let id = self.arg_num(&args, 0, -1.0)? as i64;
13127 let px = self.arg_num(&args, 1, 0.0)? as i64;
13128 let py = self.arg_num(&args, 2, 0.0)? as i64;
13129 if id >= 0 && (id as usize) < self.images.len() {
13130 let img = &self.images[id as usize];
13131 if px >= 0 && py >= 0 && (px as u32) < img.width() && (py as u32) < img.height()
13132 {
13133 let p = img.get_pixel(px as u32, py as u32);
13134 let ch = match name {
13135 "image_pixel_r" => p[0],
13136 "image_pixel_g" => p[1],
13137 "image_pixel_b" => p[2],
13138 _ => p[3],
13139 };
13140 return Ok(Value::Number(ch as f64));
13141 }
13142 }
13143 return Ok(Value::Number(0.0));
13144 },
13145 "image_new" => {
13154 let w = self.arg_num(&args, 0, 1.0)?.max(1.0) as u32;
13155 let h = self.arg_num(&args, 1, 1.0)?.max(1.0) as u32;
13156 let id = self.images.len();
13157 self.images.push(image::RgbaImage::new(w, h));
13158 return Ok(Value::Number(id as f64));
13159 },
13160 "image_set_pixel" => {
13165 let id = self.arg_num(&args, 0, -1.0)? as i64;
13166 let px = self.arg_num(&args, 1, 0.0)? as i64;
13167 let py = self.arg_num(&args, 2, 0.0)? as i64;
13168 let r = self.arg_num(&args, 3, 0.0)?.clamp(0.0, 255.0) as u8;
13169 let g = self.arg_num(&args, 4, 0.0)?.clamp(0.0, 255.0) as u8;
13170 let b = self.arg_num(&args, 5, 0.0)?.clamp(0.0, 255.0) as u8;
13171 let a = self.arg_num(&args, 6, 255.0)?.clamp(0.0, 255.0) as u8;
13172 if id >= 0 && (id as usize) < self.images.len() {
13173 let img = &mut self.images[id as usize];
13174 if px >= 0 && py >= 0 && (px as u32) < img.width() && (py as u32) < img.height()
13175 {
13176 img.put_pixel(px as u32, py as u32, image::Rgba([r, g, b, a]));
13177 }
13178 }
13179 return Ok(Value::Unit);
13180 },
13181 #[cfg(not(target_arch = "wasm32"))]
13189 "image_save" => {
13190 let id = self.arg_num(&args, 0, -1.0)? as i64;
13191 let path = self.arg_str(&args, 1, "");
13192 if id >= 0 && (id as usize) < self.images.len() {
13193 if let Some(parent) = std::path::Path::new(&path).parent() {
13194 if !parent.as_os_str().is_empty() {
13195 let _ = std::fs::create_dir_all(parent);
13196 }
13197 }
13198 if self.images[id as usize].save(&path).is_ok() {
13199 return Ok(Value::Number(1.0));
13200 }
13201 }
13202 return Ok(Value::Number(-1.0));
13203 },
13204 #[cfg(target_arch = "wasm32")]
13205 "image_save" => {
13206 return Ok(Value::Number(-1.0));
13207 },
13208 #[cfg(not(target_arch = "wasm32"))]
13218 "image_draw" => {
13219 let id = self.arg_num(&args, 0, -1.0)? as i64;
13220 let dx = self.arg_num(&args, 1, 0.0)? as i32;
13221 let dy = self.arg_num(&args, 2, 0.0)? as i32;
13222 let dw = self.arg_num(&args, 3, 0.0)?.max(0.0) as i32;
13223 let dh = self.arg_num(&args, 4, 0.0)?.max(0.0) as i32;
13224 if id >= 0 && (id as usize) < self.images.len() && dw > 0 && dh > 0 {
13225 let img = &self.images[id as usize];
13226 let sw = img.width() as i32;
13227 let sh = img.height() as i32;
13228 if sw > 0 && sh > 0 {
13229 let mut gfx = self.gfx.borrow_mut();
13230 let (fw, fh) = (gfx.width as i32, gfx.height as i32);
13231 for py in 0..dh {
13232 let ty = dy + py;
13233 if ty < 0 || ty >= fh {
13234 continue;
13235 }
13236 let sy = (py * sh / dh).clamp(0, sh - 1) as u32;
13237 for px in 0..dw {
13238 let tx = dx + px;
13239 if tx < 0 || tx >= fw {
13240 continue;
13241 }
13242 let sx = (px * sw / dw).clamp(0, sw - 1) as u32;
13243 let p = img.get_pixel(sx, sy);
13244 let a = p[3] as u32;
13245 if a == 0 {
13246 continue;
13247 }
13248 let idx = ty as usize * gfx.width + tx as usize;
13249 if a >= 255 {
13250 gfx.buffer[idx] = ((p[0] as u32) << 16)
13251 | ((p[1] as u32) << 8)
13252 | (p[2] as u32);
13253 } else {
13254 let bg = gfx.buffer[idx];
13255 let br = (bg >> 16) & 0xff;
13256 let bg_g = (bg >> 8) & 0xff;
13257 let bb = bg & 0xff;
13258 let r = (p[0] as u32 * a + br * (255 - a)) / 255;
13259 let g = (p[1] as u32 * a + bg_g * (255 - a)) / 255;
13260 let b = (p[2] as u32 * a + bb * (255 - a)) / 255;
13261 gfx.buffer[idx] = (r << 16) | (g << 8) | b;
13262 }
13263 }
13264 }
13265 }
13266 }
13267 return Ok(Value::Unit);
13268 },
13269 #[cfg(target_arch = "wasm32")]
13270 "image_draw" => {
13271 return Ok(Value::Unit);
13272 },
13273 #[cfg(not(target_arch = "wasm32"))]
13276 "font_text"
13277 | "ข้อความฟอนต์"
13278 | "字体文本"
13279 | "フォント文字"
13280 | "글꼴텍스트"
13281 | "متن_فونت"
13282 | "نص_الخط"
13283 | "טקסט_גופן"
13284 | "فونٹ_متن"
13285 | "texte_police"
13286 | "schriftart_text"
13287 | "текст_шрифт" => {
13288 let id = self.arg_num(&args, 0, 0.0)? as i64;
13289 let x = self.arg_num(&args, 1, 0.0)? as f32;
13290 let y = self.arg_num(&args, 2, 0.0)? as f32;
13291 let px = self.arg_num(&args, 3, 16.0)? as f32;
13292 let s = self.arg_str(&args, 4, "");
13293 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
13294 let strokes = self.font_layout_2d(id as usize, x, y, px, &s);
13295 let mut gfx = self.gfx.borrow_mut();
13296 let (w, h, color, add, aa) = (
13297 gfx.width,
13298 gfx.height,
13299 gfx.color,
13300 gfx.blend == 1,
13301 gfx.font_antialias,
13302 );
13303 for pl in &strokes {
13304 for seg in pl.windows(2) {
13305 if aa {
13306 crate::gfx::raster::draw_line_aa(
13307 &mut gfx.buffer,
13308 w,
13309 h,
13310 color,
13311 add,
13312 seg[0][0],
13313 seg[0][1],
13314 seg[1][0],
13315 seg[1][1],
13316 );
13317 } else {
13318 crate::gfx::raster::draw_line(
13319 &mut gfx.buffer,
13320 w,
13321 h,
13322 color,
13323 seg[0][0],
13324 seg[0][1],
13325 seg[1][0],
13326 seg[1][1],
13327 );
13328 }
13329 }
13330 }
13331 }
13332 return Ok(Value::Unit);
13333 },
13334 #[cfg(target_arch = "wasm32")]
13335 "font_text"
13336 | "ข้อความฟอนต์"
13337 | "字体文本"
13338 | "フォント文字"
13339 | "글꼴텍스트"
13340 | "متن_فونت"
13341 | "نص_الخط"
13342 | "טקסט_גופן"
13343 | "فونٹ_متن"
13344 | "texte_police"
13345 | "schriftart_text"
13346 | "текст_шрифт" => {
13347 return Ok(Value::Unit);
13348 },
13349 #[cfg(not(target_arch = "wasm32"))]
13352 "font_text_fill"
13353 | "เติมฟอนต์"
13354 | "填充字体"
13355 | "フォント塗り"
13356 | "글꼴채움"
13357 | "پرکردن_متن_فونت"
13358 | "تعبئة_نص_الخط"
13359 | "מילוי_טקסט_גופן"
13360 | "فونٹ_متن_بھرو"
13361 | "remplir_texte_police"
13362 | "schriftart_text_füllen"
13363 | "заполнить_текст_шрифт" => {
13364 let id = self.arg_num(&args, 0, 0.0)? as i64;
13365 let x = self.arg_num(&args, 1, 0.0)? as f32;
13366 let y = self.arg_num(&args, 2, 0.0)? as f32;
13367 let px = self.arg_num(&args, 3, 16.0)? as f32;
13368 let s = self.arg_str(&args, 4, "");
13369 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
13370 let glyphs = self.font_layout_2d_glyphs(id as usize, x, y, px, &s);
13372 let mut gfx = self.gfx.borrow_mut();
13373 let (w, h, color, add, aa) = (
13374 gfx.width,
13375 gfx.height,
13376 gfx.color,
13377 gfx.blend == 1,
13378 gfx.font_antialias,
13379 );
13380 for contours in &glyphs {
13381 if aa {
13382 crate::gfx::raster::fill_contours_aa(
13383 &mut gfx.buffer,
13384 w,
13385 h,
13386 color,
13387 add,
13388 contours,
13389 );
13390 } else {
13391 crate::gfx::raster::fill_contours(
13392 &mut gfx.buffer,
13393 w,
13394 h,
13395 color,
13396 add,
13397 contours,
13398 );
13399 }
13400 }
13401 }
13402 return Ok(Value::Unit);
13403 },
13404 #[cfg(target_arch = "wasm32")]
13405 "font_text_fill"
13406 | "เติมฟอนต์"
13407 | "填充字体"
13408 | "フォント塗り"
13409 | "글꼴채움"
13410 | "پرکردن_متن_فونت"
13411 | "تعبئة_نص_الخط"
13412 | "מילוי_טקסט_גופן"
13413 | "فونٹ_متن_بھرو"
13414 | "remplir_texte_police"
13415 | "schriftart_text_füllen"
13416 | "заполнить_текст_шрифт" => {
13417 return Ok(Value::Unit);
13418 },
13419 #[cfg(not(target_arch = "wasm32"))]
13423 "font_text_3d"
13424 | "ข้อความฟอนต์3มิติ"
13425 | "字体3D"
13426 | "フォント3D"
13427 | "글꼴3D"
13428 | "متن_فونت_سهبعدی"
13429 | "نص_خط_ثلاثي_الأبعاد"
13430 | "טקסט_גופן_תלת_ממדי"
13431 | "تھری_ڈی_فونٹ_متن"
13432 | "texte_police_3d"
13433 | "schriftart_text_3d"
13434 | "текст_шрифт_3d" => {
13435 let id = self.arg_num(&args, 0, 0.0)? as i64;
13436 let cx = self.arg_num(&args, 1, 0.0)? as f32;
13437 let cy = self.arg_num(&args, 2, 0.0)? as f32;
13438 let cz = self.arg_num(&args, 3, 0.0)? as f32;
13439 let ux = self.arg_num(&args, 4, 1.0)? as f32;
13440 let uy = self.arg_num(&args, 5, 0.0)? as f32;
13441 let uz = self.arg_num(&args, 6, 0.0)? as f32;
13442 let vx = self.arg_num(&args, 7, 0.0)? as f32;
13443 let vy = self.arg_num(&args, 8, 1.0)? as f32;
13444 let vz = self.arg_num(&args, 9, 0.0)? as f32;
13445 let size = self.arg_num(&args, 10, 1.0)? as f32;
13446 let s = self.arg_str(&args, 11, "");
13447 let fill_rows = self.arg_num(&args, 12, 0.0)? as i32;
13451 if id >= 0 && (id as usize) < self.fonts.len() && size > 0.0 {
13452 let font = &mut self.fonts[id as usize];
13454 let asc = font.ascent();
13455 let mut pen = 0.0f32;
13456 let mut lines: Vec<[f32; 6]> = Vec::new();
13457 for ch in s.chars() {
13458 let go = font.glyph_outline(ch, 0.01);
13459 let map = |p: [f32; 2], pen: f32| {
13460 let a = pen + p[0];
13461 let b = p[1] - asc; [
13463 cx + a * size * ux + b * size * vx,
13464 cy + a * size * uy + b * size * vy,
13465 cz + a * size * uz + b * size * vz,
13466 ]
13467 };
13468 for pl in &go.polylines {
13469 for seg in pl.windows(2) {
13470 let p0 = map(seg[0], pen);
13471 let p1 = map(seg[1], pen);
13472 lines.push([p0[0], p0[1], p0[2], p1[0], p1[1], p1[2]]);
13473 }
13474 }
13475 if fill_rows > 0 {
13476 let (mut ymin, mut ymax) = (f32::MAX, f32::MIN);
13480 for pl in &go.polylines {
13481 for p in pl {
13482 ymin = ymin.min(p[1]);
13483 ymax = ymax.max(p[1]);
13484 }
13485 }
13486 if ymax > ymin {
13487 for r in 0..fill_rows {
13488 let y =
13489 ymin + (r as f32 + 0.5) * (ymax - ymin) / fill_rows as f32;
13490 let mut xs: Vec<f32> = Vec::new();
13491 for pl in &go.polylines {
13492 let n = pl.len();
13493 if n < 2 {
13494 continue;
13495 }
13496 for k in 0..n {
13497 let p0 = pl[k];
13498 let p1 = pl[(k + 1) % n];
13499 if k + 1 == n
13500 && (p1[0] - p0[0]).abs() < 1e-6
13501 && (p1[1] - p0[1]).abs() < 1e-6
13502 {
13503 continue; }
13505 let (y0, y1) = (p0[1], p1[1]);
13506 if (y0 <= y && y1 > y) || (y1 <= y && y0 > y) {
13507 let t = (y - y0) / (y1 - y0);
13508 xs.push(p0[0] + t * (p1[0] - p0[0]));
13509 }
13510 }
13511 }
13512 xs.sort_by(|a, b| {
13513 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
13514 });
13515 let mut k = 0;
13516 while k + 1 < xs.len() {
13517 let a = map([xs[k], y], pen);
13518 let b = map([xs[k + 1], y], pen);
13519 lines.push([a[0], a[1], a[2], b[0], b[1], b[2]]);
13520 k += 2;
13521 }
13522 }
13523 }
13524 }
13525 pen += go.advance;
13526 }
13527 let mut gfx = self.gfx.borrow_mut();
13528 let color = gfx.color;
13529 let near = -gfx.camera.zdist + 0.05;
13530 for l in &lines {
13531 let (mut ax, mut ay, mut az) = (l[0], l[1], l[2]);
13532 let (mut bx, mut by, mut bz) = (l[3], l[4], l[5]);
13533 let da = gfx.camera.depth(ax, ay, az);
13534 let db = gfx.camera.depth(bx, by, bz);
13535 if da <= near && db <= near {
13536 continue;
13537 }
13538 if da <= near {
13539 let t = (near - da) / (db - da);
13540 ax += t * (bx - ax);
13541 ay += t * (by - ay);
13542 az += t * (bz - az);
13543 } else if db <= near {
13544 let t = (near - da) / (db - da);
13545 bx = ax + t * (bx - ax);
13546 by = ay + t * (by - ay);
13547 bz = az + t * (bz - az);
13548 }
13549 let (sax, say, da2) = gfx.camera.project(ax, ay, az);
13550 let (sbx, sby, db2) = gfx.camera.project(bx, by, bz);
13551 let depth = (da2 + db2) / 2.0;
13552 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
13553 }
13554 }
13555 return Ok(Value::Unit);
13556 },
13557 #[cfg(target_arch = "wasm32")]
13558 "font_text_3d"
13559 | "ข้อความฟอนต์3มิติ"
13560 | "字体3D"
13561 | "フォント3D"
13562 | "글꼴3D"
13563 | "متن_فونت_سهبعدی"
13564 | "نص_خط_ثلاثي_الأبعاد"
13565 | "טקסט_גופן_תלת_ממדי"
13566 | "تھری_ڈی_فونٹ_متن"
13567 | "texte_police_3d"
13568 | "schriftart_text_3d"
13569 | "текст_шрифт_3d" => {
13570 return Ok(Value::Unit);
13571 },
13572 #[cfg(not(target_arch = "wasm32"))]
13574 "font_width"
13575 | "ความกว้างฟอนต์"
13576 | "字体宽度"
13577 | "フォント幅"
13578 | "글꼴너비"
13579 | "عرض_فونت"
13580 | "عرض_الخط"
13581 | "רוחב_גופן"
13582 | "فونٹ_چوڑائی"
13583 | "largeur_police"
13584 | "schriftart_breite"
13585 | "ширина_шрифта" => {
13586 let id = self.arg_num(&args, 0, 0.0)? as i64;
13587 let px = self.arg_num(&args, 1, 16.0)? as f32;
13588 let s = self.arg_str(&args, 2, "");
13589 if id >= 0 && (id as usize) < self.fonts.len() {
13590 return Ok(Value::Number(self.fonts[id as usize].measure(&s, px) as f64));
13591 }
13592 return Ok(Value::Number(0.0));
13593 },
13594 #[cfg(target_arch = "wasm32")]
13595 "font_width"
13596 | "ความกว้างฟอนต์"
13597 | "字体宽度"
13598 | "フォント幅"
13599 | "글꼴너비"
13600 | "عرض_فونت"
13601 | "عرض_الخط"
13602 | "רוחב_גופן"
13603 | "فونٹ_چوڑائی"
13604 | "largeur_police"
13605 | "schriftart_breite"
13606 | "ширина_шрифта" => {
13607 return Ok(Value::Number(0.0));
13608 },
13609 #[cfg(not(target_arch = "wasm32"))]
13614 "font_glyph_outline"
13615 | "font_outline"
13616 | "เส้นขอบฟอนต์"
13617 | "字体轮廓"
13618 | "フォント輪郭"
13619 | "글꼴윤곽"
13620 | "خطدور_نویسه_فونت"
13621 | "حدود_حرف_الخط"
13622 | "קו_מתאר_גליף"
13623 | "فونٹ_گلف_آؤٹ_لائن" => {
13624 let id = self.arg_num(&args, 0, 0.0)? as i64;
13625 let s = self.arg_str(&args, 1, "");
13626 let tol = self.arg_num(&args, 2, 0.01)? as f32;
13627 let ch = s.chars().next().unwrap_or(' ');
13628 if id >= 0 && (id as usize) < self.fonts.len() {
13629 let go = self.fonts[id as usize].glyph_outline(ch, tol.max(1e-4));
13630 let mut contours: Vec<Value> = Vec::with_capacity(go.polylines.len());
13631 for pl in &go.polylines {
13632 let mut flat: Vec<Value> = Vec::with_capacity(pl.len() * 2);
13633 for p in pl {
13634 flat.push(Value::Number(p[0] as f64));
13635 flat.push(Value::Number(p[1] as f64));
13636 }
13637 contours.push(Value::List(Rc::new(flat)));
13638 }
13639 return Ok(Value::List(Rc::new(contours)));
13640 }
13641 return Ok(Value::List(Rc::new(vec![])));
13642 },
13643 #[cfg(target_arch = "wasm32")]
13644 "font_glyph_outline"
13645 | "font_outline"
13646 | "เส้นขอบฟอนต์"
13647 | "字体轮廓"
13648 | "フォント輪郭"
13649 | "글꼴윤곽"
13650 | "خطدور_نویسه_فونت"
13651 | "حدود_حرف_الخط"
13652 | "קו_מתאר_גליף"
13653 | "فونٹ_گلف_آؤٹ_لائن" => {
13654 return Ok(Value::List(Rc::new(vec![])));
13655 },
13656 #[cfg(not(target_arch = "wasm32"))]
13659 "font_advance"
13660 | "ระยะฟอนต์"
13661 | "字体步进"
13662 | "フォント送り"
13663 | "글꼴전진"
13664 | "پیشروی_فونت"
13665 | "تقدم_الخط"
13666 | "קידום_גופן"
13667 | "فونٹ_ایڈوانس" => {
13668 let id = self.arg_num(&args, 0, 0.0)? as i64;
13669 let s = self.arg_str(&args, 1, "");
13670 let ch = s.chars().next().unwrap_or(' ');
13671 if id >= 0 && (id as usize) < self.fonts.len() {
13672 return Ok(Value::Number(self.fonts[id as usize].advance(ch) as f64));
13673 }
13674 return Ok(Value::Number(0.0));
13675 },
13676 #[cfg(target_arch = "wasm32")]
13677 "font_advance"
13678 | "ระยะฟอนต์"
13679 | "字体步进"
13680 | "フォント送り"
13681 | "글꼴전진"
13682 | "پیشروی_فونت"
13683 | "تقدم_الخط"
13684 | "קידום_גופן"
13685 | "فونٹ_ایڈوانس" => {
13686 return Ok(Value::Number(0.0));
13687 },
13688
13689 "ui_frame"
13691 | "边框"
13692 | "フレーム枠"
13693 | "프레임틀"
13694 | "กรอบ"
13695 | "قاب_رابط"
13696 | "إطار_الواجهة"
13697 | "מסגרת_ממשק"
13698 | "یو_آئی_فریم"
13699 | "cadre_ui"
13700 | "ui_rahmen"
13701 | "ui_рамка" => {
13702 let x = self.arg_num(&args, 0, 0.0)? as f32;
13703 let y = self.arg_num(&args, 1, 0.0)? as f32;
13704 let w0 = self.arg_num(&args, 2, 0.0)? as f32;
13705 let h0 = self.arg_num(&args, 3, 0.0)? as f32;
13706 let l = self.arg_num(&args, 4, 14.0)? as f32;
13707 let segs = ling_ui::holo::corner_brackets(x, y, w0, h0, l);
13708 let mut gfx = self.gfx.borrow_mut();
13709 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
13710 for sg in segs {
13711 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
13712 }
13713 return Ok(Value::Unit);
13714 },
13715 "ui_bevel"
13717 | "斜角框"
13718 | "ベベル枠"
13719 | "베벨틀"
13720 | "กรอบเฉียง"
13721 | "لبه_شیبدار"
13722 | "حافة_مشطوفة"
13723 | "מסגרת_משופעת"
13724 | "یو_آئی_بیول"
13725 | "biseau_ui"
13726 | "ui_fase"
13727 | "ui_фаска" => {
13728 let x = self.arg_num(&args, 0, 0.0)? as f32;
13729 let y = self.arg_num(&args, 1, 0.0)? as f32;
13730 let w0 = self.arg_num(&args, 2, 0.0)? as f32;
13731 let h0 = self.arg_num(&args, 3, 0.0)? as f32;
13732 let bv = self.arg_num(&args, 4, 10.0)? as f32;
13733 let segs = ling_ui::holo::beveled_rect(x, y, w0, h0, bv);
13734 let mut gfx = self.gfx.borrow_mut();
13735 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
13736 for sg in segs {
13737 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
13738 }
13739 return Ok(Value::Unit);
13740 },
13741
13742 #[cfg(not(target_arch = "wasm32"))]
13748 "ui_theme"
13749 | "界面主题"
13750 | "UIテーマ"
13751 | "인터페이스테마"
13752 | "ธีมส่วนติดต่อ"
13753 | "پوسته_رابط"
13754 | "سمة_الواجهة"
13755 | "ערכת_נושא"
13756 | "یو_آئی_تھیم"
13757 | "thème_ui"
13758 | "ui_thema"
13759 | "ui_тема" => {
13760 let cur = self.ui_theme;
13761 let primary = self.color_at(&args, 0, cur.primary);
13762 let accent = self.color_at(&args, 3, cur.accent);
13763 let track = self.color_at(&args, 6, cur.track);
13764 let warn = self.color_at(&args, 9, cur.warn);
13765 let text = self.color_at(&args, 12, cur.text);
13766 let bg = self.color_at(&args, 15, cur.bg);
13767 self.ui_theme = UiTheme { primary, accent, track, warn, text, bg };
13768 return Ok(Value::Unit);
13769 },
13770
13771 "ui_theme_colors" | "인터페이스테마색상" => {
13777 let th = self.ui_theme;
13778 let mut out = Vec::with_capacity(18);
13779 for c in [th.primary, th.accent, th.track, th.warn, th.text, th.bg] {
13780 out.push(Value::Number(((c >> 16) & 0xFF) as f64));
13781 out.push(Value::Number(((c >> 8) & 0xFF) as f64));
13782 out.push(Value::Number((c & 0xFF) as f64));
13783 }
13784 return Ok(Value::List(Rc::new(out)));
13785 },
13786
13787 #[cfg(not(target_arch = "wasm32"))]
13789 "ui_radar"
13790 | "雷达"
13791 | "レーダー"
13792 | "레이더"
13793 | "เรดาร์"
13794 | "رادار_رابط"
13795 | "رادار_الواجهة"
13796 | "מכ״ם_ממשק"
13797 | "یو_آئی_ریڈار"
13798 | "radar_ui"
13799 | "ui_радар" => {
13800 let cx = self.arg_num(&args, 0, 0.)? as f32;
13801 let cy = self.arg_num(&args, 1, 0.)? as f32;
13802 let r = self.arg_num(&args, 2, 60.)? as f32;
13803 let sweep = self.arg_num(&args, 3, 0.)? as f32;
13804 let th = self.ui_theme;
13805 let prim = self.color_at(&args, 4, th.primary);
13806 self.draw_ui(&ling_ui::widgets::radar(
13807 cx, cy, r, sweep, prim, th.accent, th.track,
13808 ));
13809 return Ok(Value::Unit);
13810 },
13811 #[cfg(not(target_arch = "wasm32"))]
13812 "ui_compass"
13813 | "罗盘"
13814 | "コンパス"
13815 | "나침반"
13816 | "เข็มทิศ"
13817 | "قطبنمای_رابط"
13818 | "بوصلة_الواجهة"
13819 | "מצפן_ממשק"
13820 | "یو_آئی_قطب_نما"
13821 | "boussole_ui"
13822 | "ui_kompass"
13823 | "ui_компас" => {
13824 let x = self.arg_num(&args, 0, 0.)? as f32;
13825 let y = self.arg_num(&args, 1, 0.)? as f32;
13826 let w0 = self.arg_num(&args, 2, 300.)? as f32;
13827 let h0 = self.arg_num(&args, 3, 24.)? as f32;
13828 let head = self.arg_num(&args, 4, 0.)? as f32;
13829 let th = self.ui_theme;
13830 let prim = self.color_at(&args, 5, th.primary);
13831 self.draw_ui(&ling_ui::widgets::compass(
13832 x, y, w0, h0, head, prim, th.track,
13833 ));
13834 return Ok(Value::Unit);
13835 },
13836 #[cfg(not(target_arch = "wasm32"))]
13837 "ui_reticle"
13838 | "准星"
13839 | "照準"
13840 | "조준선"
13841 | "เป้าเล็ง"
13842 | "نشانه_رابط"
13843 | "علامة_تصويب"
13844 | "כוונת"
13845 | "نشانہ"
13846 | "réticule_ui"
13847 | "ui_fadenkreuz"
13848 | "ui_прицел" => {
13849 let cx = self.arg_num(&args, 0, 0.)? as f32;
13850 let cy = self.arg_num(&args, 1, 0.)? as f32;
13851 let r = self.arg_num(&args, 2, 30.)? as f32;
13852 let spread = self.arg_num(&args, 3, 0.)? as f32;
13853 let th = self.ui_theme;
13854 let prim = self.color_at(&args, 4, th.primary);
13855 self.draw_ui(&ling_ui::widgets::reticle(cx, cy, r, spread, prim));
13856 return Ok(Value::Unit);
13857 },
13858 #[cfg(not(target_arch = "wasm32"))]
13859 "ui_target"
13860 | "锁定框"
13861 | "ターゲット"
13862 | "표적"
13863 | "กรอบเป้า"
13864 | "قاب_هدف"
13865 | "إطار_الهدف"
13866 | "מסגרת_מטרה"
13867 | "ہدف_فریم"
13868 | "cible_ui"
13869 | "ui_ziel"
13870 | "ui_цель" => {
13871 let x = self.arg_num(&args, 0, 0.)? as f32;
13872 let y = self.arg_num(&args, 1, 0.)? as f32;
13873 let w0 = self.arg_num(&args, 2, 80.)? as f32;
13874 let h0 = self.arg_num(&args, 3, 80.)? as f32;
13875 let lock = self.arg_num(&args, 4, 0.)? as f32;
13876 let th = self.ui_theme;
13877 let prim = self.color_at(&args, 5, th.primary);
13878 self.draw_ui(&ling_ui::widgets::target(
13879 x, y, w0, h0, lock, prim, th.accent,
13880 ));
13881 return Ok(Value::Unit);
13882 },
13883 #[cfg(not(target_arch = "wasm32"))]
13884 "ui_panel"
13885 | "面板"
13886 | "パネル"
13887 | "패널"
13888 | "แผง"
13889 | "پنل_رابط"
13890 | "لوحة_الواجهة"
13891 | "לוח_ממשק"
13892 | "یو_آئی_پینل"
13893 | "panneau_ui"
13894 | "ui_feld"
13895 | "ui_панель" => {
13896 let x = self.arg_num(&args, 0, 0.)? as f32;
13897 let y = self.arg_num(&args, 1, 0.)? as f32;
13898 let w0 = self.arg_num(&args, 2, 200.)? as f32;
13899 let h0 = self.arg_num(&args, 3, 120.)? as f32;
13900 let bv = self.arg_num(&args, 4, 12.)? as f32;
13901 let th = self.ui_theme;
13902 let prim = self.color_at(&args, 5, th.primary);
13903 self.draw_ui(&ling_ui::widgets::panel(x, y, w0, h0, bv, prim, th.bg));
13904 return Ok(Value::Unit);
13905 },
13906 #[cfg(not(target_arch = "wasm32"))]
13907 "ui_scanlines"
13908 | "扫描线"
13909 | "走査線"
13910 | "스캔라인"
13911 | "เส้นสแกน"
13912 | "خطوط_اسکن"
13913 | "خطوط_المسح"
13914 | "קווי_סריקה"
13915 | "اسکین_لائنز"
13916 | "lignes_balayage_ui"
13917 | "ui_abtastzeilen"
13918 | "ui_линии_развёртки" => {
13919 let x = self.arg_num(&args, 0, 0.)? as f32;
13920 let y = self.arg_num(&args, 1, 0.)? as f32;
13921 let w0 = self.arg_num(&args, 2, 200.)? as f32;
13922 let h0 = self.arg_num(&args, 3, 120.)? as f32;
13923 let dens = self.arg_num(&args, 4, 24.)? as usize;
13924 let th = self.ui_theme;
13925 let line = self.color_at(&args, 5, th.track);
13926 self.draw_ui(&ling_ui::widgets::scanlines(x, y, w0, h0, dens, line));
13927 return Ok(Value::Unit);
13928 },
13929
13930 #[cfg(not(target_arch = "wasm32"))]
13932 "ui_bar"
13933 | "进度条"
13934 | "バー"
13935 | "막대"
13936 | "แถบ"
13937 | "نوار_رابط"
13938 | "شريط_الواجهة"
13939 | "סרגל_ממשק"
13940 | "یو_آئی_بار"
13941 | "barre_ui"
13942 | "ui_leiste"
13943 | "ui_полоса" => {
13944 let x = self.arg_num(&args, 0, 0.)? as f32;
13945 let y = self.arg_num(&args, 1, 0.)? as f32;
13946 let w0 = self.arg_num(&args, 2, 160.)? as f32;
13947 let h0 = self.arg_num(&args, 3, 16.)? as f32;
13948 let val = self.arg_num(&args, 4, 0.)? as f32;
13949 let max = self.arg_num(&args, 5, 1.)? as f32;
13950 let th = self.ui_theme;
13951 let fill = self.color_at(&args, 6, th.primary);
13952 self.draw_ui(&ling_ui::widgets::bar(
13953 x,
13954 y,
13955 w0,
13956 h0,
13957 val / max.max(1e-6),
13958 fill,
13959 th.track,
13960 ));
13961 return Ok(Value::Unit);
13962 },
13963 #[cfg(not(target_arch = "wasm32"))]
13964 "ui_segbar"
13965 | "分段条"
13966 | "分割バー"
13967 | "분할막대"
13968 | "แถบแบ่ง"
13969 | "نوار_قطعهای"
13970 | "شريط_مقسم"
13971 | "סרגל_מקוטע"
13972 | "سیگمنٹ_بار"
13973 | "barre_segmentée_ui"
13974 | "ui_segmentleiste"
13975 | "ui_сегментная_полоса" => {
13976 let x = self.arg_num(&args, 0, 0.)? as f32;
13977 let y = self.arg_num(&args, 1, 0.)? as f32;
13978 let w0 = self.arg_num(&args, 2, 160.)? as f32;
13979 let h0 = self.arg_num(&args, 3, 16.)? as f32;
13980 let val = self.arg_num(&args, 4, 0.)? as f32;
13981 let max = self.arg_num(&args, 5, 1.)? as f32;
13982 let segs = self.arg_num(&args, 6, 10.)? as usize;
13983 let th = self.ui_theme;
13984 let fill = self.color_at(&args, 7, th.primary);
13985 self.draw_ui(&ling_ui::widgets::segbar(
13986 x,
13987 y,
13988 w0,
13989 h0,
13990 val / max.max(1e-6),
13991 segs,
13992 fill,
13993 th.track,
13994 ));
13995 return Ok(Value::Unit);
13996 },
13997 #[cfg(not(target_arch = "wasm32"))]
13998 "ui_gauge"
13999 | "仪表"
14000 | "ゲージ"
14001 | "게이지"
14002 | "มาตรวัด"
14003 | "گیج_رابط"
14004 | "مقياس_الواجهة"
14005 | "מד_ממשק"
14006 | "یو_آئی_گیج"
14007 | "jauge_ui"
14008 | "ui_anzeige"
14009 | "ui_индикатор" => {
14010 let cx = self.arg_num(&args, 0, 0.)? as f32;
14011 let cy = self.arg_num(&args, 1, 0.)? as f32;
14012 let r = self.arg_num(&args, 2, 50.)? as f32;
14013 let val = self.arg_num(&args, 3, 0.)? as f32;
14014 let max = self.arg_num(&args, 4, 1.)? as f32;
14015 let th = self.ui_theme;
14016 let needle = self.color_at(&args, 5, th.warn);
14017 self.draw_ui(&ling_ui::widgets::gauge(
14018 cx,
14019 cy,
14020 r,
14021 val / max.max(1e-6),
14022 needle,
14023 th.accent,
14024 th.track,
14025 ));
14026 return Ok(Value::Unit);
14027 },
14028 #[cfg(not(target_arch = "wasm32"))]
14029 "ui_ring"
14030 | "环表"
14031 | "リングメーター"
14032 | "링미터"
14033 | "วงแหวนวัด"
14034 | "حلقه_گیج"
14035 | "حلقة_قياس"
14036 | "טבעת_מד"
14037 | "رنگ_گیج"
14038 | "anneau_ui"
14039 | "ui_кольцо" => {
14040 let cx = self.arg_num(&args, 0, 0.)? as f32;
14041 let cy = self.arg_num(&args, 1, 0.)? as f32;
14042 let r = self.arg_num(&args, 2, 40.)? as f32;
14043 let val = self.arg_num(&args, 3, 0.)? as f32;
14044 let max = self.arg_num(&args, 4, 1.)? as f32;
14045 let th = self.ui_theme;
14046 let fill = self.color_at(&args, 5, th.primary);
14047 self.draw_ui(&ling_ui::widgets::ring(
14048 cx,
14049 cy,
14050 r,
14051 val / max.max(1e-6),
14052 fill,
14053 th.track,
14054 ));
14055 return Ok(Value::Unit);
14056 },
14057 #[cfg(not(target_arch = "wasm32"))]
14058 "ui_vu"
14059 | "音量条"
14060 | "VUメーター"
14061 | "음량막대"
14062 | "มาตรเสียง"
14063 | "گیج_صدا"
14064 | "مقياس_مستوى_الصوت"
14065 | "מד_עוצמה"
14066 | "وی_یو_میٹر"
14067 | "vumètre_ui"
14068 | "ui_vumeter"
14069 | "ui_вю_метр" => {
14070 let x = self.arg_num(&args, 0, 0.)? as f32;
14071 let y = self.arg_num(&args, 1, 0.)? as f32;
14072 let w0 = self.arg_num(&args, 2, 160.)? as f32;
14073 let h0 = self.arg_num(&args, 3, 60.)? as f32;
14074 let levels = self.arg_list_f32(&args, 4);
14075 let th = self.ui_theme;
14076 let fill = self.color_at(&args, 5, th.primary);
14077 self.draw_ui(&ling_ui::widgets::vu(x, y, w0, h0, &levels, fill, th.warn));
14078 return Ok(Value::Unit);
14079 },
14080 #[cfg(not(target_arch = "wasm32"))]
14081 "ui_spark"
14082 | "迷你图"
14083 | "スパークライン"
14084 | "스파크라인"
14085 | "กราฟจิ๋ว"
14086 | "نمودار_ریز"
14087 | "رسم_مصغر"
14088 | "גרף_זעיר"
14089 | "اسپارک_لائن"
14090 | "mini_graphe_ui"
14091 | "ui_sparkline"
14092 | "ui_мини_график" => {
14093 let x = self.arg_num(&args, 0, 0.)? as f32;
14094 let y = self.arg_num(&args, 1, 0.)? as f32;
14095 let w0 = self.arg_num(&args, 2, 160.)? as f32;
14096 let h0 = self.arg_num(&args, 3, 40.)? as f32;
14097 let vals = self.arg_list_f32(&args, 4);
14098 let th = self.ui_theme;
14099 let line = self.color_at(&args, 5, th.accent);
14100 self.draw_ui(&ling_ui::widgets::spark(x, y, w0, h0, &vals, line));
14101 return Ok(Value::Unit);
14102 },
14103 #[cfg(not(target_arch = "wasm32"))]
14104 "ui_battery"
14105 | "电池"
14106 | "バッテリー"
14107 | "배터리"
14108 | "แบตเตอรี่"
14109 | "نشانگر_باتری"
14110 | "مؤشر_البطارية"
14111 | "מחוון_סוללה"
14112 | "بیٹری_انڈیکیٹر"
14113 | "batterie_ui"
14114 | "ui_batterie"
14115 | "ui_батарея" => {
14116 let x = self.arg_num(&args, 0, 0.)? as f32;
14117 let y = self.arg_num(&args, 1, 0.)? as f32;
14118 let w0 = self.arg_num(&args, 2, 50.)? as f32;
14119 let h0 = self.arg_num(&args, 3, 22.)? as f32;
14120 let val = self.arg_num(&args, 4, 1.)? as f32;
14121 let max = self.arg_num(&args, 5, 1.)? as f32;
14122 let th = self.ui_theme;
14123 let fill = self.color_at(&args, 6, th.accent);
14124 self.draw_ui(&ling_ui::widgets::battery(
14125 x,
14126 y,
14127 w0,
14128 h0,
14129 val / max.max(1e-6),
14130 fill,
14131 th.track,
14132 th.warn,
14133 ));
14134 return Ok(Value::Unit);
14135 },
14136
14137 #[cfg(not(target_arch = "wasm32"))]
14139 "ui_button"
14140 | "按钮"
14141 | "ボタン"
14142 | "버튼"
14143 | "ปุ่ม"
14144 | "دکمه_رابط"
14145 | "زر_الواجهة"
14146 | "כפתור_ממשק"
14147 | "یو_آئی_بٹن"
14148 | "bouton_ui"
14149 | "ui_knopf"
14150 | "ui_кнопка" => {
14151 let x = self.arg_num(&args, 0, 0.)? as f32;
14152 let y = self.arg_num(&args, 1, 0.)? as f32;
14153 let w0 = self.arg_num(&args, 2, 120.)? as f32;
14154 let h0 = self.arg_num(&args, 3, 40.)? as f32;
14155 let (mx, my, down) = self.mouse_now();
14156 let hover = ling_ui::holo::hit_rect(mx, my, x, y, w0, h0);
14157 let clicked = hover && down && !self.mouse_was_down;
14158 let th = self.ui_theme;
14159 let prim = self.color_at(&args, 4, th.primary);
14160 self.draw_ui(&ling_ui::widgets::button(
14161 x,
14162 y,
14163 w0,
14164 h0,
14165 hover,
14166 down && hover,
14167 prim,
14168 th.bg,
14169 ));
14170 return Ok(Value::Number(if clicked { 1.0 } else { 0.0 }));
14171 },
14172 #[cfg(not(target_arch = "wasm32"))]
14173 "ui_toggle"
14174 | "开关"
14175 | "トグル"
14176 | "토글"
14177 | "สวิตช์"
14178 | "کلید_ضامن"
14179 | "مفتاح_تبديل"
14180 | "מתג"
14181 | "ٹوگل"
14182 | "bascule_ui"
14183 | "ui_schalter"
14184 | "ui_переключатель" => {
14185 let x = self.arg_num(&args, 0, 0.)? as f32;
14186 let y = self.arg_num(&args, 1, 0.)? as f32;
14187 let w0 = self.arg_num(&args, 2, 52.)? as f32;
14188 let h0 = self.arg_num(&args, 3, 24.)? as f32;
14189 let mut state = self.arg_num(&args, 4, 0.)? > 0.5;
14190 let (mx, my, down) = self.mouse_now();
14191 let hover = ling_ui::holo::hit_rect(mx, my, x, y, w0, h0);
14192 if hover && down && !self.mouse_was_down {
14193 state = !state;
14194 }
14195 let th = self.ui_theme;
14196 let on = self.color_at(&args, 5, th.accent);
14197 self.draw_ui(&ling_ui::widgets::toggle(x, y, w0, h0, state, on, th.track));
14198 return Ok(Value::Number(if state { 1.0 } else { 0.0 }));
14199 },
14200 #[cfg(not(target_arch = "wasm32"))]
14201 "ui_slider"
14202 | "滑块"
14203 | "スライダー"
14204 | "슬라이더"
14205 | "แถบเลื่อน"
14206 | "لغزنده"
14207 | "شريط_انزلاق"
14208 | "מחוון_החלקה"
14209 | "سلائیڈر"
14210 | "curseur_ui"
14211 | "ui_schieberegler"
14212 | "ui_ползунок" => {
14213 let x = self.arg_num(&args, 0, 0.)? as f32;
14214 let y = self.arg_num(&args, 1, 0.)? as f32;
14215 let w0 = self.arg_num(&args, 2, 160.)? as f32;
14216 let mut val = self.arg_num(&args, 3, 0.)? as f32;
14217 let mn = self.arg_num(&args, 4, 0.)? as f32;
14218 let mx_ = self.arg_num(&args, 5, 1.)? as f32;
14219 let (mx, my, down) = self.mouse_now();
14220 let hover = ling_ui::holo::hit_rect(mx, my, x - 8.0, y - 10.0, w0 + 16.0, 20.0);
14221 if hover && down {
14222 let frac = ((mx - x) / w0).clamp(0.0, 1.0);
14223 val = mn + (mx_ - mn) * frac;
14224 }
14225 let frac = ((val - mn) / (mx_ - mn).abs().max(1e-6)).clamp(0.0, 1.0);
14226 let th = self.ui_theme;
14227 let fill = self.color_at(&args, 6, th.primary);
14228 self.draw_ui(&ling_ui::widgets::slider(
14229 x, y, w0, frac, hover, fill, th.track,
14230 ));
14231 return Ok(Value::Number(val as f64));
14232 },
14233 #[cfg(not(target_arch = "wasm32"))]
14234 "ui_checkbox"
14235 | "复选框"
14236 | "チェックボックス"
14237 | "체크박스"
14238 | "ช่องเลือก"
14239 | "جعبه_علامت"
14240 | "مربع_اختيار"
14241 | "תיבת_סימון"
14242 | "چیک_باکس"
14243 | "case_cocher_ui"
14244 | "ui_kontrollkästchen"
14245 | "ui_флажок" => {
14246 let x = self.arg_num(&args, 0, 0.)? as f32;
14247 let y = self.arg_num(&args, 1, 0.)? as f32;
14248 let s = self.arg_num(&args, 2, 20.)? as f32;
14249 let mut checked = self.arg_num(&args, 3, 0.)? > 0.5;
14250 let (mx, my, down) = self.mouse_now();
14251 let hover = ling_ui::holo::hit_rect(mx, my, x, y, s, s);
14252 if hover && down && !self.mouse_was_down {
14253 checked = !checked;
14254 }
14255 let th = self.ui_theme;
14256 let prim = self.color_at(&args, 4, th.primary);
14257 self.draw_ui(&ling_ui::widgets::checkbox(
14258 x, y, s, checked, hover, prim, th.track,
14259 ));
14260 return Ok(Value::Number(if checked { 1.0 } else { 0.0 }));
14261 },
14262 #[cfg(not(target_arch = "wasm32"))]
14263 "ui_tabs"
14264 | "标签页"
14265 | "タブ"
14266 | "탭"
14267 | "แท็บ"
14268 | "برگهها"
14269 | "ألسنة_الواجهة"
14270 | "לשוניות"
14271 | "ٹیبز"
14272 | "onglets_ui"
14273 | "ui_reiter"
14274 | "ui_вкладки" => {
14275 let x = self.arg_num(&args, 0, 0.)? as f32;
14276 let y = self.arg_num(&args, 1, 0.)? as f32;
14277 let w0 = self.arg_num(&args, 2, 240.)? as f32;
14278 let h0 = self.arg_num(&args, 3, 28.)? as f32;
14279 let count = self.arg_num(&args, 4, 3.)? as usize;
14280 let mut active = self.arg_num(&args, 5, 0.)? as i32;
14281 let (mx, my, down) = self.mouse_now();
14282 let mut hover = -1;
14283 if my >= y && my <= y + h0 && mx >= x && mx <= x + w0 && count > 0 {
14284 hover = (((mx - x) / (w0 / count as f32)) as i32)
14285 .max(0)
14286 .min(count as i32 - 1);
14287 if down && !self.mouse_was_down {
14288 active = hover;
14289 }
14290 }
14291 let th = self.ui_theme;
14292 let prim = self.color_at(&args, 6, th.primary);
14293 self.draw_ui(&ling_ui::widgets::tabs(
14294 x,
14295 y,
14296 w0,
14297 h0,
14298 count,
14299 active as usize,
14300 hover,
14301 prim,
14302 th.track,
14303 ));
14304 return Ok(Value::Number(active as f64));
14305 },
14306 #[cfg(not(target_arch = "wasm32"))]
14307 "ui_progress"
14308 | "进度"
14309 | "プログレス"
14310 | "진행바"
14311 | "ความคืบหน้า"
14312 | "نوار_پیشرفت"
14313 | "شريط_التقدم"
14314 | "פס_התקדמות"
14315 | "پیش_رفت_بار"
14316 | "progression_ui"
14317 | "ui_fortschritt"
14318 | "ui_прогресс" => {
14319 let x = self.arg_num(&args, 0, 0.)? as f32;
14320 let y = self.arg_num(&args, 1, 0.)? as f32;
14321 let w0 = self.arg_num(&args, 2, 200.)? as f32;
14322 let h0 = self.arg_num(&args, 3, 12.)? as f32;
14323 let frac = self.arg_num(&args, 4, 0.)? as f32;
14324 let th = self.ui_theme;
14325 let fill = self.color_at(&args, 5, th.accent);
14326 self.draw_ui(&ling_ui::widgets::progress(
14327 x, y, w0, h0, frac, fill, th.track,
14328 ));
14329 return Ok(Value::Unit);
14330 },
14331 #[cfg(not(target_arch = "wasm32"))]
14332 "ui_tooltip"
14333 | "提示框"
14334 | "ツールチップ"
14335 | "툴팁"
14336 | "คำแนะนำ"
14337 | "راهنمای_شناور"
14338 | "تلميح_الواجهة"
14339 | "חלונית_עזרה"
14340 | "ٹول_ٹپ"
14341 | "infobulle_ui"
14342 | "ui_подсказка" => {
14343 let x = self.arg_num(&args, 0, 0.)? as f32;
14344 let y = self.arg_num(&args, 1, 0.)? as f32;
14345 let w0 = self.arg_num(&args, 2, 120.)? as f32;
14346 let h0 = self.arg_num(&args, 3, 28.)? as f32;
14347 let th = self.ui_theme;
14348 let prim = self.color_at(&args, 4, th.primary);
14349 self.draw_ui(&ling_ui::widgets::tooltip(x, y, w0, h0, prim, th.bg));
14350 return Ok(Value::Unit);
14351 },
14352 #[cfg(not(target_arch = "wasm32"))]
14353 "ui_stepper"
14354 | "步进器"
14355 | "ステッパー"
14356 | "스테퍼"
14357 | "ตัวปรับค่า"
14358 | "پلهگر"
14359 | "زر_خطوات"
14360 | "בורר_מדורג"
14361 | "اسٹیپر"
14362 | "pas_à_pas_ui"
14363 | "ui_schrittsteuerung"
14364 | "ui_степпер" => {
14365 let x = self.arg_num(&args, 0, 0.)? as f32;
14366 let y = self.arg_num(&args, 1, 0.)? as f32;
14367 let w0 = self.arg_num(&args, 2, 120.)? as f32;
14368 let h0 = self.arg_num(&args, 3, 28.)? as f32;
14369 let mut val = self.arg_num(&args, 4, 0.)? as f32;
14370 let step = self.arg_num(&args, 5, 1.)? as f32;
14371 let (mx, my, down) = self.mouse_now();
14372 let hm = ling_ui::holo::hit_rect(mx, my, x, y, h0, h0);
14373 let hp = ling_ui::holo::hit_rect(mx, my, x + w0 - h0, y, h0, h0);
14374 if down && !self.mouse_was_down {
14375 if hm {
14376 val -= step;
14377 }
14378 if hp {
14379 val += step;
14380 }
14381 }
14382 let th = self.ui_theme;
14383 let prim = self.color_at(&args, 6, th.primary);
14384 self.draw_ui(&ling_ui::widgets::stepper(
14385 x, y, w0, h0, hm, hp, prim, th.track,
14386 ));
14387 return Ok(Value::Number(val as f64));
14388 },
14389
14390 #[cfg(not(target_arch = "wasm32"))]
14392 "ui_healthbar"
14393 | "血条"
14394 | "体力バー"
14395 | "체력바"
14396 | "แถบพลังชีวิต"
14397 | "نوار_سلامتی"
14398 | "شريط_الصحة"
14399 | "פס_בריאות"
14400 | "ہیلتھ_بار"
14401 | "barre_vie_ui"
14402 | "ui_lebensbalken"
14403 | "ui_полоса_здоровья" => {
14404 let x = self.arg_num(&args, 0, 0.)? as f32;
14405 let y = self.arg_num(&args, 1, 0.)? as f32;
14406 let w0 = self.arg_num(&args, 2, 180.)? as f32;
14407 let h0 = self.arg_num(&args, 3, 16.)? as f32;
14408 let val = self.arg_num(&args, 4, 1.)? as f32;
14409 let max = self.arg_num(&args, 5, 1.)? as f32;
14410 let pulse = self.arg_num(&args, 6, 0.)? as f32;
14411 let th = self.ui_theme;
14412 let full = self.color_at(&args, 7, th.accent);
14413 self.draw_ui(&ling_ui::widgets::healthbar(
14414 x,
14415 y,
14416 w0,
14417 h0,
14418 val / max.max(1e-6),
14419 pulse,
14420 full,
14421 th.warn,
14422 th.track,
14423 ));
14424 return Ok(Value::Unit);
14425 },
14426 #[cfg(not(target_arch = "wasm32"))]
14427 "ui_cooldown"
14428 | "冷却"
14429 | "クールダウン"
14430 | "쿨다운"
14431 | "คูลดาวน์"
14432 | "زمان_خنکسازی"
14433 | "مؤقت_التهدئة"
14434 | "זמן_קירור"
14435 | "کول_ڈاؤن"
14436 | "recharge_ui"
14437 | "ui_abklingzeit"
14438 | "ui_перезарядка" => {
14439 let cx = self.arg_num(&args, 0, 0.)? as f32;
14440 let cy = self.arg_num(&args, 1, 0.)? as f32;
14441 let r = self.arg_num(&args, 2, 28.)? as f32;
14442 let frac = self.arg_num(&args, 3, 0.)? as f32;
14443 let th = self.ui_theme;
14444 let fill = self.color_at(&args, 4, th.primary);
14445 self.draw_ui(&ling_ui::widgets::cooldown(cx, cy, r, frac, fill, th.track));
14446 return Ok(Value::Unit);
14447 },
14448 #[cfg(not(target_arch = "wasm32"))]
14449 "ui_counter"
14450 | "计数器"
14451 | "カウンター"
14452 | "카운터"
14453 | "ตัวนับ"
14454 | "شمارشگر"
14455 | "عداد_الواجهة"
14456 | "מונה_ממשק"
14457 | "کاؤنٹر"
14458 | "compteur_ui"
14459 | "ui_zähler"
14460 | "ui_счётчик" => {
14461 let x = self.arg_num(&args, 0, 0.)? as f32;
14462 let y = self.arg_num(&args, 1, 0.)? as f32;
14463 let dw = self.arg_num(&args, 2, 14.)? as f32;
14464 let dh = self.arg_num(&args, 3, 24.)? as f32;
14465 let val = self.arg_num(&args, 4, 0.)? as i64;
14466 let digits = self.arg_num(&args, 5, 4.)? as usize;
14467 let th = self.ui_theme;
14468 let on = self.color_at(&args, 6, th.primary);
14469 let off = ling_ui::widgets::shade(th.track, 0.5);
14470 self.draw_ui(&ling_ui::widgets::counter(
14471 x, y, dw, dh, val, digits, on, off,
14472 ));
14473 return Ok(Value::Unit);
14474 },
14475 #[cfg(not(target_arch = "wasm32"))]
14476 "ui_minimap"
14477 | "小地图"
14478 | "ミニマップ"
14479 | "미니맵"
14480 | "แผนที่ย่อ"
14481 | "نقشه_کوچک"
14482 | "خريطة_مصغرة"
14483 | "מפה_מוקטנת"
14484 | "منی_میپ"
14485 | "minicarte_ui"
14486 | "ui_minikarte"
14487 | "ui_миникарта" => {
14488 let x = self.arg_num(&args, 0, 0.)? as f32;
14489 let y = self.arg_num(&args, 1, 0.)? as f32;
14490 let w0 = self.arg_num(&args, 2, 140.)? as f32;
14491 let h0 = self.arg_num(&args, 3, 140.)? as f32;
14492 let th = self.ui_theme;
14493 let prim = self.color_at(&args, 4, th.primary);
14494 self.draw_ui(&ling_ui::widgets::minimap(x, y, w0, h0, prim, th.bg));
14495 return Ok(Value::Unit);
14496 },
14497 #[cfg(not(target_arch = "wasm32"))]
14498 "ui_dpad"
14499 | "方向键"
14500 | "方向パッド"
14501 | "방향패드"
14502 | "ปุ่มทิศทาง"
14503 | "دسته_جهتدار"
14504 | "لوحة_الاتجاهات"
14505 | "לוח_כיוונים"
14506 | "ڈی_پیڈ"
14507 | "croix_direction_ui"
14508 | "ui_steuerkreuz"
14509 | "ui_крестовина" => {
14510 let cx = self.arg_num(&args, 0, 0.)? as f32;
14511 let cy = self.arg_num(&args, 1, 0.)? as f32;
14512 let r = self.arg_num(&args, 2, 50.)? as f32;
14513 let (mx, my, down) = self.mouse_now();
14514 let mut dir = 0;
14515 if down {
14516 let (dx, dy) = (mx - cx, my - cy);
14517 if dx * dx + dy * dy <= r * r {
14518 if dx.abs() > dy.abs() {
14519 dir = if dx > 0.0 { 2 } else { 4 };
14520 } else {
14521 dir = if dy > 0.0 { 3 } else { 1 };
14522 }
14523 }
14524 }
14525 let th = self.ui_theme;
14526 let prim = self.color_at(&args, 3, th.primary);
14527 self.draw_ui(&ling_ui::widgets::dpad(cx, cy, r, dir, prim, th.track));
14528 return Ok(Value::Number(dir as f64));
14529 },
14530 #[cfg(not(target_arch = "wasm32"))]
14531 "ui_slotgrid"
14532 | "物品格"
14533 | "スロットグリッド"
14534 | "슬롯격자"
14535 | "ช่องไอเทม"
14536 | "شبکه_شیار"
14537 | "شبكة_الفتحات"
14538 | "רשת_חריצים"
14539 | "سلاٹ_گرڈ"
14540 | "grille_emplacements_ui"
14541 | "ui_slotraster"
14542 | "ui_сетка_слотов" => {
14543 let x = self.arg_num(&args, 0, 0.)? as f32;
14544 let y = self.arg_num(&args, 1, 0.)? as f32;
14545 let cols = self.arg_num(&args, 2, 4.)? as usize;
14546 let rows = self.arg_num(&args, 3, 1.)? as usize;
14547 let cell = self.arg_num(&args, 4, 36.)? as f32;
14548 let sel = self.arg_num(&args, 5, -1.)? as i32;
14549 let th = self.ui_theme;
14550 let prim = self.color_at(&args, 6, th.primary);
14551 self.draw_ui(&ling_ui::widgets::slotgrid(
14552 x, y, cols, rows, cell, sel, prim, th.track,
14553 ));
14554 return Ok(Value::Unit);
14555 },
14556 #[cfg(not(target_arch = "wasm32"))]
14557 "ui_vignette"
14558 | "暗角"
14559 | "ビネット"
14560 | "비네트"
14561 | "ขอบมืด"
14562 | "سایهگرد_کادر"
14563 | "تظليل_الحواف"
14564 | "הצללת_מסגרת"
14565 | "ویگنیٹ"
14566 | "vignette_ui"
14567 | "ui_виньетка" => {
14568 let intensity = self.arg_num(&args, 0, 0.5)? as f32;
14569 let (w, h) = {
14570 let g = self.gfx.borrow();
14571 (g.width as f32, g.height as f32)
14572 };
14573 let th = self.ui_theme;
14574 let col = self.color_at(&args, 1, th.warn);
14575 self.draw_ui(&ling_ui::widgets::vignette(w, h, intensity, col));
14576 return Ok(Value::Unit);
14577 },
14578
14579 #[cfg(not(target_arch = "wasm32"))]
14581 "ui_gauge3d"
14582 | "立体仪表"
14583 | "立体ゲージ"
14584 | "입체게이지"
14585 | "มาตรวัด3มิติ"
14586 | "گیج_سهبعدی"
14587 | "مقياس_ثلاثي_الأبعاد"
14588 | "מד_תלת_ממדי"
14589 | "تھری_ڈی_گیج"
14590 | "jauge_3d_ui"
14591 | "ui_anzeige_3d"
14592 | "ui_индикатор_3d" => {
14593 let cx = self.arg_num(&args, 0, 0.)? as f32;
14594 let cy = self.arg_num(&args, 1, 0.)? as f32;
14595 let r = self.arg_num(&args, 2, 50.)? as f32;
14596 let val = self.arg_num(&args, 3, 0.)? as f32;
14597 let max = self.arg_num(&args, 4, 1.)? as f32;
14598 let spin = self.arg_num(&args, 5, 0.)? as f32;
14599 let th = self.ui_theme;
14600 let fill = self.color_at(&args, 6, th.primary);
14601 self.draw_ui(&ling_ui::widgets::gauge3d(
14602 cx,
14603 cy,
14604 r,
14605 val / max.max(1e-6),
14606 spin,
14607 fill,
14608 th.track,
14609 ));
14610 return Ok(Value::Unit);
14611 },
14612 #[cfg(not(target_arch = "wasm32"))]
14613 "ui_panel3d"
14614 | "立体面板"
14615 | "立体パネル"
14616 | "입체패널"
14617 | "แผง3มิติ"
14618 | "پنل_سهبعدی"
14619 | "لوحة_ثلاثية_الأبعاد"
14620 | "לוח_תלת_ממדי"
14621 | "تھری_ڈی_پینل"
14622 | "panneau_3d_ui"
14623 | "ui_feld_3d"
14624 | "ui_панель_3d" => {
14625 let x = self.arg_num(&args, 0, 0.)? as f32;
14626 let y = self.arg_num(&args, 1, 0.)? as f32;
14627 let w0 = self.arg_num(&args, 2, 200.)? as f32;
14628 let h0 = self.arg_num(&args, 3, 120.)? as f32;
14629 let depth = self.arg_num(&args, 4, 14.)? as f32;
14630 let th = self.ui_theme;
14631 let prim = self.color_at(&args, 5, th.primary);
14632 self.draw_ui(&ling_ui::widgets::panel3d(x, y, w0, h0, depth, prim, th.bg));
14633 return Ok(Value::Unit);
14634 },
14635 #[cfg(not(target_arch = "wasm32"))]
14636 "ui_radar3d"
14637 | "立体雷达"
14638 | "立体レーダー"
14639 | "입체레이더"
14640 | "เรดาร์3มิติ"
14641 | "رادار_سهبعدی"
14642 | "رادار_ثلاثي_الأبعاد"
14643 | "מכ״ם_תלת_ממדי"
14644 | "تھری_ڈی_ریڈار"
14645 | "radar_3d_ui"
14646 | "ui_radar_3d"
14647 | "ui_радар_3d" => {
14648 let cx = self.arg_num(&args, 0, 0.)? as f32;
14649 let cy = self.arg_num(&args, 1, 0.)? as f32;
14650 let r = self.arg_num(&args, 2, 60.)? as f32;
14651 let tilt = self.arg_num(&args, 3, 0.9)? as f32;
14652 let sweep = self.arg_num(&args, 4, 0.)? as f32;
14653 let th = self.ui_theme;
14654 let prim = self.color_at(&args, 5, th.primary);
14655 self.draw_ui(&ling_ui::widgets::radar3d(
14656 cx, cy, r, tilt, sweep, prim, th.track,
14657 ));
14658 return Ok(Value::Unit);
14659 },
14660
14661 #[cfg(not(target_arch = "wasm32"))]
14663 "audio_blip"
14664 | "提示音"
14665 | "ビープ音"
14666 | "효과음"
14667 | "เสียงบี๊บ"
14668 | "بوق_کوتاه"
14669 | "نغمة_قصيرة"
14670 | "ביפ"
14671 | "بلپ_آواز"
14672 | "bip_audio"
14673 | "звук_бип" => {
14674 let freq = self.arg_num(&args, 0, 660.)? as f32;
14675 let dur = self.arg_num(&args, 1, 0.08)? as f32;
14676 let wave = Wave::from_name(&self.arg_str(&args, 2, "sine"));
14677 let amp = self.arg_num(&args, 3, 0.25)? as f32;
14678 if let Some(audio) = &self.audio {
14679 audio.blip(freq, amp, dur, wave);
14680 }
14681 return Ok(Value::Unit);
14682 },
14683 #[cfg(not(target_arch = "wasm32"))]
14684 "ui_sound"
14685 | "界面音"
14686 | "UI音"
14687 | "인터페이스음"
14688 | "เสียงปุ่ม"
14689 | "صدای_رابط"
14690 | "صوت_الواجهة"
14691 | "צליל_ממשק"
14692 | "یو_آئی_آواز"
14693 | "son_ui"
14694 | "ui_klang"
14695 | "ui_звук" => {
14696 let name = self.arg_str(&args, 0, "click");
14697 if let Some(audio) = &self.audio {
14698 match name.as_str() {
14699 "hover" => audio.blip(880.0, 0.10, 0.04, Wave::Sine),
14700 "confirm" => {
14701 audio.blip(660.0, 0.22, 0.07, Wave::Square);
14702 audio.blip(990.0, 0.18, 0.10, Wave::Square);
14703 },
14704 "error" => {
14705 audio.blip(180.0, 0.30, 0.16, Wave::Saw);
14706 audio.blip(140.0, 0.30, 0.18, Wave::Saw);
14707 },
14708 "toggle" => audio.blip(520.0, 0.22, 0.05, Wave::Triangle),
14709 "tick" => audio.blip(1500.0, 0.12, 0.02, Wave::Square),
14710 _ => audio.blip(720.0, 0.26, 0.05, Wave::Square), }
14712 }
14713 return Ok(Value::Unit);
14714 },
14715
14716 #[cfg(not(target_arch = "wasm32"))]
14724 "music_load"
14725 | "载入音乐"
14726 | "音楽読込"
14727 | "음악로드"
14728 | "โหลดเพลง"
14729 | "بارگذاری_موسیقی"
14730 | "تحميل_الموسيقى"
14731 | "טעינת_מוזיקה"
14732 | "موسیقی_لوڈ"
14733 | "charger_musique"
14734 | "musik_laden"
14735 | "загрузить_музыку" => {
14736 let path = self.arg_str(&args, 0, "");
14737 let resolved = if std::path::Path::new(&path).exists() {
14738 path.clone()
14739 } else if let Some(d) = &self.source_dir {
14740 d.join(&path).to_string_lossy().into_owned()
14741 } else {
14742 path.clone()
14743 };
14744 match ling_music::load(&resolved) {
14745 Ok(t) => {
14746 let id = self.tracks.len();
14747 self.tracks.push(t);
14748 return Ok(Value::Number(id as f64));
14749 },
14750 Err(e) => {
14751 eprintln!("music_load failed ({path}): {e}");
14752 return Ok(Value::Number(-1.0));
14753 },
14754 }
14755 },
14756 #[cfg(not(target_arch = "wasm32"))]
14757 "music_duration"
14758 | "音乐时长"
14759 | "音楽長さ"
14760 | "음악길이"
14761 | "ความยาวเพลง"
14762 | "مدت_موسیقی"
14763 | "مدة_الموسيقى"
14764 | "משך_מוזיקה"
14765 | "موسیقی_دورانیہ"
14766 | "durée_musique"
14767 | "musik_dauer"
14768 | "длительность_музыки" => {
14769 let id = self.arg_num(&args, 0, 0.0)? as i64;
14770 let d = self
14771 .tracks
14772 .get(id as usize)
14773 .map(|t| t.duration)
14774 .unwrap_or(0.0);
14775 return Ok(Value::Number(d as f64));
14776 },
14777 #[cfg(not(target_arch = "wasm32"))]
14778 "music_bpm"
14779 | "节拍速度"
14780 | "テンポ"
14781 | "템포"
14782 | "จังหวะต่อนาที"
14783 | "ضربان_در_دقیقه"
14784 | "نبضات_بالدقيقة"
14785 | "פעימות_לדקה"
14786 | "بی_پی_ایم"
14787 | "bpm_musique"
14788 | "musik_bpm"
14789 | "музыка_bpm" => {
14790 let id = self.arg_num(&args, 0, 0.0)? as i64;
14791 let b = self
14792 .tracks
14793 .get(id as usize)
14794 .map(|t| ling_music::analysis::bpm(&t.mono, t.rate))
14795 .unwrap_or(0.0);
14796 return Ok(Value::Number(b as f64));
14797 },
14798 #[cfg(not(target_arch = "wasm32"))]
14799 "music_key"
14800 | "调性"
14801 | "調性"
14802 | "조성"
14803 | "คีย์เพลง"
14804 | "گام_موسیقی"
14805 | "مقام_الموسيقى"
14806 | "סולם_מוזיקלי"
14807 | "موسیقی_کلید"
14808 | "tonalité_musique"
14809 | "musik_tonart"
14810 | "тональность_музыки" => {
14811 let id = self.arg_num(&args, 0, 0.0)? as i64;
14812 let k = self
14813 .tracks
14814 .get(id as usize)
14815 .map(|t| ling_music::analysis::key_name(&t.mono, t.rate))
14816 .unwrap_or_default();
14817 return Ok(Value::Str(k));
14818 },
14819 #[cfg(not(target_arch = "wasm32"))]
14820 "music_onsets"
14821 | "音符起点"
14822 | "オンセット"
14823 | "온셋"
14824 | "จุดเริ่มเสียง"
14825 | "آغازهای_نت"
14826 | "بدايات_النغمات"
14827 | "התחלות_תווים"
14828 | "نوٹ_شروعات"
14829 | "attaques_musique"
14830 | "musik_einsätze"
14831 | "атаки_музыки" => {
14832 let id = self.arg_num(&args, 0, 0.0)? as i64;
14833 let v = self
14834 .tracks
14835 .get(id as usize)
14836 .map(|t| ling_music::analysis::onsets(&t.mono, t.rate))
14837 .unwrap_or_default();
14838 return Ok(Value::List(Rc::new(
14839 v.into_iter().map(|x| Value::Number(x as f64)).collect(),
14840 )));
14841 },
14842 #[cfg(not(target_arch = "wasm32"))]
14843 "music_beat_grid"
14844 | "节拍网格"
14845 | "ビートグリッド"
14846 | "비트그리드"
14847 | "กริดจังหวะ"
14848 | "شبکه_ضرب"
14849 | "شبكة_الإيقاع"
14850 | "רשת_פעימות"
14851 | "بیٹ_گرڈ"
14852 | "grille_temps_musique"
14853 | "musik_taktraster"
14854 | "сетка_ритма_музыки" => {
14855 let id = self.arg_num(&args, 0, 0.0)? as i64;
14856 let beats = self
14857 .tracks
14858 .get(id as usize)
14859 .map(|t| {
14860 let b = ling_music::analysis::bpm(&t.mono, t.rate);
14861 ling_music::analysis::beat_grid(&t.mono, t.rate, b)
14862 })
14863 .unwrap_or_default();
14864 return Ok(Value::List(Rc::new(
14865 beats.into_iter().map(|x| Value::Number(x as f64)).collect(),
14866 )));
14867 },
14868
14869 #[cfg(not(target_arch = "wasm32"))]
14871 "music_play"
14872 | "播放音乐"
14873 | "音楽再生"
14874 | "음악재생"
14875 | "เล่นเพลง"
14876 | "پخش_موسیقی"
14877 | "شغّل_الموسيقى"
14878 | "נגן_מוזיקה"
14879 | "موسیقی_چلاؤ"
14880 | "jouer_musique"
14881 | "musik_abspielen"
14882 | "играть_музыку" => {
14883 let id = self.arg_num(&args, 0, 0.0)? as i64;
14884 if self.ensure_music() {
14885 let track = self
14886 .tracks
14887 .get(id as usize)
14888 .map(|t| (t.stereo.clone(), t.rate));
14889 if let (Some((st, rate)), Some(m)) = (track, &self.music) {
14890 m.set_track(st, rate);
14891 m.play();
14892 } else if let Some(m) = &self.music {
14893 m.play();
14894 }
14895 }
14896 return Ok(Value::Unit);
14897 },
14898 #[cfg(not(target_arch = "wasm32"))]
14899 "music_pause"
14900 | "暂停音乐"
14901 | "音楽一時停止"
14902 | "음악일시정지"
14903 | "หยุดเพลงชั่วคราว"
14904 | "مکث_موسیقی"
14905 | "ألبث_الموسيقى"
14906 | "השהה_מוזיקה"
14907 | "موسیقی_روکو_مؤقت"
14908 | "pause_musique"
14909 | "musik_pausieren"
14910 | "пауза_музыки" => {
14911 if let Some(m) = &self.music {
14912 m.pause();
14913 }
14914 return Ok(Value::Unit);
14915 },
14916 #[cfg(not(target_arch = "wasm32"))]
14917 "music_stop"
14918 | "停止音乐"
14919 | "音楽停止"
14920 | "음악정지"
14921 | "หยุดเพลง"
14922 | "توقف_موسیقی"
14923 | "أوقف_الموسيقى"
14924 | "עצור_מוזיקה"
14925 | "موسیقی_روکو"
14926 | "arrêter_musique"
14927 | "musik_stoppen"
14928 | "остановить_музыку" => {
14929 if let Some(m) = &self.music {
14930 m.stop();
14931 }
14932 return Ok(Value::Unit);
14933 },
14934 #[cfg(not(target_arch = "wasm32"))]
14935 "music_seek"
14936 | "定位音乐"
14937 | "音楽シーク"
14938 | "음악탐색"
14939 | "ค้นหาเพลง"
14940 | "جستجوی_موسیقی"
14941 | "ابحث_في_الموسيقى"
14942 | "חפש_במוזיקה"
14943 | "موسیقی_تلاش"
14944 | "chercher_musique"
14945 | "musik_suchen"
14946 | "перемотать_музыку" => {
14947 let sec = self.arg_num(&args, 0, 0.0)? as f32;
14948 if let Some(m) = &self.music {
14949 m.seek(sec);
14950 }
14951 return Ok(Value::Unit);
14952 },
14953 #[cfg(not(target_arch = "wasm32"))]
14954 "music_pos"
14955 | "音乐位置"
14956 | "音楽位置"
14957 | "음악위치"
14958 | "ตำแหน่งเพลง"
14959 | "موقعیت_موسیقی"
14960 | "موضع_الموسيقى"
14961 | "מיקום_מוזיקה"
14962 | "موسیقی_مقام"
14963 | "position_musique"
14964 | "musik_position"
14965 | "позиция_музыки" => {
14966 let p = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
14967 return Ok(Value::Number(p as f64));
14968 },
14969 #[cfg(not(target_arch = "wasm32"))]
14970 "music_volume"
14971 | "音乐音量"
14972 | "音楽音量"
14973 | "음악음량"
14974 | "ระดับเพลง"
14975 | "بلندی_موسیقی"
14976 | "مستوى_الموسيقى"
14977 | "עוצמת_מוזיקה"
14978 | "موسیقی_شدت"
14979 | "volume_musique"
14980 | "musik_lautstärke"
14981 | "громкость_музыки" => {
14982 let v = self.arg_num(&args, 0, 0.8)? as f32;
14983 if self.ensure_music() {
14984 if let Some(m) = &self.music {
14985 m.set_volume(v);
14986 }
14987 }
14988 return Ok(Value::Unit);
14989 },
14990
14991 #[cfg(not(target_arch = "wasm32"))]
14993 "music_patch"
14994 | "乐器音色"
14995 | "音色読込"
14996 | "악기패치"
14997 | "แพตช์เครื่องดนตรี"
14998 | "پچ_موسیقی"
14999 | "آلة_الموسيقى"
15000 | "תיקון_כלי_נגינה"
15001 | "میوزک_پیچ"
15002 | "patch_musique"
15003 | "musik_patch"
15004 | "патч_музыки" => {
15005 let path = self.arg_str(&args, 0, "");
15006 let resolved = if std::path::Path::new(&path).exists() {
15007 path.clone()
15008 } else if let Some(d) = &self.source_dir {
15009 d.join(&path).to_string_lossy().into_owned()
15010 } else {
15011 path.clone()
15012 };
15013 if !self.ensure_music() {
15014 return Ok(Value::Number(-1.0));
15015 }
15016 match ling_music::patch::from_path(&resolved) {
15017 Ok(p) => {
15018 let id = self.music.as_ref().unwrap().add_patch(p);
15019 return Ok(Value::Number(id as f64));
15020 },
15021 Err(e) => {
15022 eprintln!("music_patch failed ({path}): {e}");
15023 return Ok(Value::Number(-1.0));
15024 },
15025 }
15026 },
15027 #[cfg(not(target_arch = "wasm32"))]
15028 "music_note"
15029 | "弹音符"
15030 | "音符演奏"
15031 | "음표연주"
15032 | "เล่นโน้ต"
15033 | "نواختن_نت"
15034 | "عزف_نغمة"
15035 | "נגן_תו"
15036 | "نوٹ_بجاؤ"
15037 | "note_musique"
15038 | "musik_note"
15039 | "нота_музыки" => {
15040 let inst = self.arg_num(&args, 0, 0.0)? as usize;
15041 let midi = self.pitch_arg(&args, 1, 60);
15042 let dur = self.arg_num(&args, 2, 0.5)? as f32;
15043 let vel = self.arg_num(&args, 3, 0.9)? as f32;
15044 if self.ensure_music() {
15045 if let Some(m) = &self.music {
15046 m.note(inst, midi, vel, dur);
15047 }
15048 }
15049 return Ok(Value::Unit);
15050 },
15051 #[cfg(not(target_arch = "wasm32"))]
15052 "music_note_on"
15053 | "音符开始"
15054 | "音符オン"
15055 | "음표켜기"
15056 | "โน้ตเริ่ม"
15057 | "شروع_نت"
15058 | "بدء_النغمة"
15059 | "התחלת_תו"
15060 | "نوٹ_شروع"
15061 | "note_musique_on"
15062 | "musik_note_an"
15063 | "нота_музыки_вкл" => {
15064 let inst = self.arg_num(&args, 0, 0.0)? as usize;
15065 let midi = self.pitch_arg(&args, 1, 60);
15066 let vel = self.arg_num(&args, 2, 0.9)? as f32;
15067 if self.ensure_music() {
15068 if let Some(m) = &self.music {
15069 m.note_on(inst, midi, vel);
15070 }
15071 }
15072 return Ok(Value::Unit);
15073 },
15074 #[cfg(not(target_arch = "wasm32"))]
15075 "music_note_off"
15076 | "音符结束"
15077 | "音符オフ"
15078 | "음표끄기"
15079 | "โน้ตจบ"
15080 | "پایان_نت"
15081 | "إيقاف_النغمة"
15082 | "סיום_תו"
15083 | "نوٹ_ختم"
15084 | "note_musique_off"
15085 | "musik_note_aus"
15086 | "нота_музыки_выкл" => {
15087 let inst = self.arg_num(&args, 0, 0.0)? as usize;
15088 let midi = self.pitch_arg(&args, 1, 60);
15089 if let Some(m) = &self.music {
15090 m.note_off(inst, midi);
15091 }
15092 return Ok(Value::Unit);
15093 },
15094
15095 #[cfg(not(target_arch = "wasm32"))]
15097 "music_judge"
15098 | "判定"
15099 | "判定する"
15100 | "판정"
15101 | "ตัดสินจังหวะ"
15102 | "داوری_ضرب"
15103 | "حكم_الإيقاع"
15104 | "שיפוט_קצב"
15105 | "بیٹ_فیصلہ"
15106 | "juger_musique"
15107 | "musik_bewerten"
15108 | "оценить_музыку" => {
15109 let delta_ms = self.arg_num(&args, 0, 9999.0)? as f32;
15110 return Ok(Value::Number(
15111 ling_music::Grade::judge(delta_ms).index() as f64
15112 ));
15113 },
15114 #[cfg(not(target_arch = "wasm32"))]
15115 "music_grade_name"
15116 | "判定名"
15117 | "判定名称"
15118 | "판정이름"
15119 | "ชื่อการตัดสิน"
15120 | "نام_رتبه"
15121 | "اسم_التقييم"
15122 | "שם_דירוג"
15123 | "گریڈ_نام"
15124 | "nom_grade_musique"
15125 | "musik_bewertungsname"
15126 | "имя_оценки_музыки" => {
15127 let idx = self.arg_num(&args, 0, 4.0)? as i32;
15128 return Ok(Value::Str(
15129 ling_music::Grade::from_index(idx).name().to_string(),
15130 ));
15131 },
15132
15133 #[cfg(not(target_arch = "wasm32"))]
15135 "music_lrc"
15136 | "载入歌词"
15137 | "歌詞読込"
15138 | "가사로드"
15139 | "โหลดเนื้อเพลง"
15140 | "بارگذاری_متن_ترانه"
15141 | "تحميل_كلمات_الأغنية"
15142 | "טעינת_מילות_שיר"
15143 | "گیت_متن_لوڈ"
15144 | "lrc_musique"
15145 | "musik_lrc"
15146 | "lrc_музыки" => {
15147 let path = self.arg_str(&args, 0, "");
15148 let resolved = if std::path::Path::new(&path).exists() {
15149 path.clone()
15150 } else if let Some(d) = &self.source_dir {
15151 d.join(&path).to_string_lossy().into_owned()
15152 } else {
15153 path.clone()
15154 };
15155 match std::fs::read_to_string(&resolved) {
15156 Ok(text) => {
15157 let id = self.lyrics.len();
15158 self.lyrics.push(ling_music::Lyrics::parse(&text));
15159 return Ok(Value::Number(id as f64));
15160 },
15161 Err(e) => {
15162 eprintln!("music_lrc failed ({path}): {e}");
15163 return Ok(Value::Number(-1.0));
15164 },
15165 }
15166 },
15167 #[cfg(not(target_arch = "wasm32"))]
15168 "music_lyric"
15169 | "当前歌词"
15170 | "現在歌詞"
15171 | "현재가사"
15172 | "เนื้อเพลงปัจจุบัน"
15173 | "متن_ترانه_فعلی"
15174 | "كلمات_الأغنية_الحالية"
15175 | "מילות_שיר_נוכחיות"
15176 | "موجودہ_گیت_متن"
15177 | "paroles_musique"
15178 | "musik_liedtext"
15179 | "текст_песни" => {
15180 let id = self.arg_num(&args, 0, 0.0)? as i64;
15181 let t = self.arg_num(&args, 1, 0.0)? as f32;
15182 let line = self
15183 .lyrics
15184 .get(id as usize)
15185 .map(|l| l.line_at(t).to_string())
15186 .unwrap_or_default();
15187 return Ok(Value::Str(line));
15188 },
15189 #[cfg(not(target_arch = "wasm32"))]
15190 "music_mic_pitch"
15191 | "麦克风音高"
15192 | "マイク音程"
15193 | "마이크음정"
15194 | "ระดับเสียงไมค์"
15195 | "زیروبمی_میکروفون"
15196 | "طبقة_صوت_الميكروفون"
15197 | "גובה_צליל_מיקרופון"
15198 | "مائیکروفون_پچ"
15199 | "hauteur_micro_musique"
15200 | "musik_mikrofon_tonhöhe"
15201 | "высота_тона_микрофона" => {
15202 let hz = if let Some(mic) = self.mic.as_ref() {
15203 let s = mic.latest_samples();
15204 let rate = mic.sample_rate();
15205 ling_music::pitch::detect(&s, rate).unwrap_or(0.0)
15206 } else {
15207 0.0
15208 };
15209 return Ok(Value::Number(hz as f64));
15210 },
15211 #[cfg(not(target_arch = "wasm32"))]
15212 "music_note_name"
15213 | "音名"
15214 | "音名称"
15215 | "음이름"
15216 | "ชื่อโน้ต"
15217 | "نام_نت"
15218 | "اسم_النغمة"
15219 | "שם_תו"
15220 | "نوٹ_نام"
15221 | "nom_note_musique"
15222 | "musik_notenname"
15223 | "имя_ноты_музыки" => {
15224 let hz = self.arg_num(&args, 0, 0.0)? as f32;
15225 return Ok(Value::Str(ling_music::note::hz_to_name(hz)));
15226 },
15227 #[cfg(not(target_arch = "wasm32"))]
15228 "music_hz"
15229 | "音符频率"
15230 | "音符周波数"
15231 | "음표주파수"
15232 | "ความถี่โน้ต"
15233 | "فرکانس_نت"
15234 | "تردد_النغمة"
15235 | "תדר_תו"
15236 | "نوٹ_ہرٹز"
15237 | "hz_musique"
15238 | "musik_hz"
15239 | "музыка_гц" => {
15240 let midi = self.pitch_arg(&args, 0, 69);
15241 return Ok(Value::Number(
15242 ling_music::note::midi_to_hz(midi as f32) as f64
15243 ));
15244 },
15245 #[cfg(not(target_arch = "wasm32"))]
15246 "music_pitch_score"
15247 | "音准评分"
15248 | "音程スコア"
15249 | "음정점수"
15250 | "คะแนนเสียง"
15251 | "امتیاز_زیروبمی"
15252 | "درجة_طبقة_الصوت"
15253 | "ציון_גובה_צליל"
15254 | "پچ_اسکور"
15255 | "score_hauteur_musique"
15256 | "musik_tonhöhen_punktzahl"
15257 | "счёт_высоты_тона" => {
15258 let hz = self.arg_num(&args, 0, 0.0)? as f32;
15259 let target = self.arg_num(&args, 1, 0.0)? as f32;
15260 return Ok(Value::Number(
15261 ling_music::karaoke::pitch_score(hz, target) as f64
15262 ));
15263 },
15264
15265 #[cfg(not(target_arch = "wasm32"))]
15267 "music_midi_load"
15268 | "载入MIDI"
15269 | "MIDI読込"
15270 | "미디로드"
15271 | "โหลดมิดี"
15272 | "بارگذاری_MIDI"
15273 | "تحميل_MIDI"
15274 | "טעינת_MIDI"
15275 | "MIDI_لوڈ"
15276 | "charger_midi_musique"
15277 | "musik_midi_laden"
15278 | "загрузить_midi_музыки" => {
15279 let path = self.arg_str(&args, 0, "");
15280 let resolved = if std::path::Path::new(&path).exists() {
15281 path.clone()
15282 } else if let Some(d) = &self.source_dir {
15283 d.join(&path).to_string_lossy().into_owned()
15284 } else {
15285 path.clone()
15286 };
15287 match ling_music::midi::load(&resolved) {
15288 Ok(m) => {
15289 let id = self.midis.len();
15290 self.midis.push(m);
15291 return Ok(Value::Number(id as f64));
15292 },
15293 Err(e) => {
15294 eprintln!("music_midi_load failed ({path}): {e}");
15295 return Ok(Value::Number(-1.0));
15296 },
15297 }
15298 },
15299 #[cfg(not(target_arch = "wasm32"))]
15300 "music_midi_count"
15301 | "MIDI数量"
15302 | "MIDI数"
15303 | "미디수"
15304 | "จำนวนมิดี"
15305 | "تعداد_MIDI"
15306 | "عدد_MIDI"
15307 | "מספר_MIDI"
15308 | "MIDI_تعداد"
15309 | "nombre_midi_musique"
15310 | "musik_midi_anzahl"
15311 | "число_midi_музыки" => {
15312 let id = self.arg_num(&args, 0, 0.0)? as i64;
15313 let n = self
15314 .midis
15315 .get(id as usize)
15316 .map(|m| m.notes.len())
15317 .unwrap_or(0);
15318 return Ok(Value::Number(n as f64));
15319 },
15320 #[cfg(not(target_arch = "wasm32"))]
15322 "music_midi_notes"
15323 | "MIDI音符"
15324 | "MIDIノート"
15325 | "미디음표"
15326 | "โน้ตมิดี"
15327 | "نتهای_MIDI"
15328 | "نغمات_MIDI"
15329 | "תווי_MIDI"
15330 | "MIDI_نوٹس"
15331 | "notes_midi_musique"
15332 | "musik_midi_noten"
15333 | "ноты_midi_музыки" => {
15334 let id = self.arg_num(&args, 0, 0.0)? as i64;
15335 let mut out = Vec::new();
15336 if let Some(m) = self.midis.get(id as usize) {
15337 for n in &m.notes {
15338 out.push(Value::Number(n.time as f64));
15339 out.push(Value::Number(n.midi as f64));
15340 }
15341 }
15342 return Ok(Value::List(Rc::new(out)));
15343 },
15344 #[cfg(not(target_arch = "wasm32"))]
15346 "music_midi_bars"
15347 | "MIDI音条"
15348 | "MIDIバー"
15349 | "미디바"
15350 | "แท่งมิดี"
15351 | "میلههای_MIDI"
15352 | "أعمدة_MIDI"
15353 | "עמודות_MIDI"
15354 | "MIDI_بارز"
15355 | "mesures_midi_musique"
15356 | "musik_midi_takte"
15357 | "такты_midi_музыки" => {
15358 let id = self.arg_num(&args, 0, 0.0)? as i64;
15359 let mut out = Vec::new();
15360 if let Some(m) = self.midis.get(id as usize) {
15361 for n in &m.notes {
15362 out.push(Value::Number(n.time as f64));
15363 out.push(Value::Number(n.midi as f64));
15364 out.push(Value::Number(n.dur as f64));
15365 }
15366 }
15367 return Ok(Value::List(Rc::new(out)));
15368 },
15369
15370 #[cfg(not(target_arch = "wasm32"))]
15372 "music_fft"
15373 | "音乐频谱"
15374 | "音楽スペクトル"
15375 | "음악스펙트럼"
15376 | "สเปกตรัมเพลง"
15377 | "طیف_موسیقی"
15378 | "طيف_الموسيقى"
15379 | "ספקטרום_מוזיקה"
15380 | "میوزک_اسپیکٹرم"
15381 | "fft_musique"
15382 | "musik_fft"
15383 | "fft_музыки" => {
15384 let id = self.arg_num(&args, 0, 0.0)? as i64;
15385 let nbands = self.arg_num(&args, 1, 16.0)? as usize;
15386 let pos = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
15387 if let Some(t) = self.tracks.get(id as usize) {
15388 let idx = (pos * t.rate as f32) as usize;
15389 let end = (idx + 2048).min(t.mono.len());
15390 if end > idx + 64 {
15391 self.fft.borrow_mut().push_samples(&t.mono[idx..end]);
15392 }
15393 }
15394 let bands = self.fft.borrow().freq_bands(nbands);
15395 return Ok(Value::List(Rc::new(
15396 bands.into_iter().map(|x| Value::Number(x as f64)).collect(),
15397 )));
15398 },
15399
15400 #[cfg(not(target_arch = "wasm32"))]
15402 "audio_stop_sfx"
15403 | "停止音效"
15404 | "効果音停止"
15405 | "효과음정지"
15406 | "หยุดเอฟเฟกต์ทั้งหมด"
15407 | "توقف_همه_جلوهها"
15408 | "أوقف_كل_المؤثرات"
15409 | "עצור_כל_האפקטים"
15410 | "تمام_ایفیکٹ_روکو" => {
15411 if let Some(a) = &self.audio {
15412 a.stop_all_sfx();
15413 }
15414 return Ok(Value::Unit);
15415 },
15416 #[cfg(not(target_arch = "wasm32"))]
15418 "audio_sfx"
15419 | "音效"
15420 | "空間効果音"
15421 | "공간효과음"
15422 | "เสียงเอฟเฟกต์"
15423 | "جلوه_صوتی"
15424 | "مؤثرات_صوتية"
15425 | "אפקט_קול"
15426 | "آواز_ایفیکٹ"
15427 | "effet_sonore"
15428 | "klangeffekt"
15429 | "звуковой_эффект" => {
15430 let x = self.arg_num(&args, 0, 0.0)? as f32;
15431 let y = self.arg_num(&args, 1, 0.0)? as f32;
15432 let z = self.arg_num(&args, 2, 0.0)? as f32;
15433 let w = self.arg_num(&args, 3, 1.0)? as f32;
15434 let freq = self.arg_num(&args, 4, 440.0)? as f32;
15435 let amp = self.arg_num(&args, 5, 0.3)? as f32;
15436 let dur = self.arg_num(&args, 6, 0.15)? as f32;
15437 let wave = Wave::from_name(&self.arg_str(&args, 7, "sine"));
15438 if let Some(a) = &self.audio {
15439 a.sfx(x, y, z, w, freq, amp, dur, wave);
15440 }
15441 return Ok(Value::Unit);
15442 },
15443 #[cfg(not(target_arch = "wasm32"))]
15448 "morph_note"
15449 | "โน้ตมอร์ฟ"
15450 | "变形音"
15451 | "モーフ音"
15452 | "모프음"
15453 | "نت_مورف"
15454 | "نغمة_متحولة"
15455 | "תו_מורף"
15456 | "مورف_نوٹ" => {
15457 let x = self.arg_num(&args, 0, 0.0)? as f32;
15458 let y = self.arg_num(&args, 1, 0.0)? as f32;
15459 let z = self.arg_num(&args, 2, 0.0)? as f32;
15460 let w = self.arg_num(&args, 3, 1.0)? as f32;
15461 let freq = self.arg_num(&args, 4, 220.0)? as f32;
15462 let amp = self.arg_num(&args, 5, 0.3)? as f32;
15463 let dur = self.arg_num(&args, 6, 0.6)? as f32;
15464 let material = self.arg_num(&args, 7, 0.0)?.clamp(0.0, 3.0) as u8;
15465 let morph = self.arg_num(&args, 8, 0.0)? as f32;
15466 if let Some(a) = &self.audio {
15467 a.morph_note(x, y, z, w, freq, amp, dur, material, morph);
15468 }
15469 return Ok(Value::Unit);
15470 },
15471 #[cfg(not(target_arch = "wasm32"))]
15473 "audio_sample_load"
15474 | "载入采样"
15475 | "サンプル読込"
15476 | "샘플로드"
15477 | "โหลดตัวอย่างเสียง"
15478 | "بارگذاری_نمونه_صدا"
15479 | "تحميل_عينة_صوتية"
15480 | "טעינת_דגימת_קול"
15481 | "آواز_نمونہ_لوڈ"
15482 | "charger_échantillon"
15483 | "sample_laden"
15484 | "загрузить_семпл" => {
15485 let path = self.arg_str(&args, 0, "");
15486 let resolved = if std::path::Path::new(&path).exists() {
15487 path.clone()
15488 } else if let Some(d) = &self.source_dir {
15489 d.join(&path).to_string_lossy().into_owned()
15490 } else {
15491 path.clone()
15492 };
15493 match ling_music::load(&resolved) {
15494 Ok(t) => {
15495 if let Some(a) = &self.audio {
15496 return Ok(Value::Number(a.add_sample(t.mono, t.rate) as f64));
15497 }
15498 return Ok(Value::Number(-1.0));
15499 },
15500 Err(e) => {
15501 eprintln!("audio_sample_load failed ({path}): {e}");
15502 return Ok(Value::Number(-1.0));
15503 },
15504 }
15505 },
15506 #[cfg(not(target_arch = "wasm32"))]
15507 "audio_sample_play"
15508 | "播放采样"
15509 | "サンプル再生"
15510 | "샘플재생"
15511 | "เล่นตัวอย่างเสียง"
15512 | "پخش_نمونه_صدا"
15513 | "تشغيل_عينة_صوتية"
15514 | "נגינת_דגימת_קול"
15515 | "آواز_نمونہ_چلاؤ"
15516 | "jouer_échantillon"
15517 | "sample_abspielen"
15518 | "играть_семпл" => {
15519 let id = self.arg_num(&args, 0, 0.0)? as usize;
15520 let x = self.arg_num(&args, 1, 0.0)? as f32;
15521 let y = self.arg_num(&args, 2, 0.0)? as f32;
15522 let z = self.arg_num(&args, 3, 0.0)? as f32;
15523 let w = self.arg_num(&args, 4, 1.0)? as f32;
15524 let vol = self.arg_num(&args, 5, 1.0)? as f32;
15525 let looping = self.arg_num(&args, 6, 0.0)? > 0.5;
15526 let v = self
15527 .audio
15528 .as_ref()
15529 .map(|a| a.play_sample(id, x, y, z, w, vol, looping))
15530 .unwrap_or(0);
15531 return Ok(Value::Number(v as f64));
15532 },
15533 #[cfg(not(target_arch = "wasm32"))]
15538 "audio_sample_play_flat"
15539 | "播放采样平"
15540 | "サンプル再生平"
15541 | "샘플재생평"
15542 | "เล่นตัวอย่างเสียงเรียบ" => {
15543 let id = self.arg_num(&args, 0, 0.0)? as usize;
15544 let vol = self.arg_num(&args, 1, 1.0)? as f32;
15545 let looping = self.arg_num(&args, 2, 0.0)? > 0.5;
15546 let v = self
15547 .audio
15548 .as_ref()
15549 .map(|a| a.play_sample_flat(id, vol, looping))
15550 .unwrap_or(0);
15551 return Ok(Value::Number(v as f64));
15552 },
15553 #[cfg(not(target_arch = "wasm32"))]
15554 "audio_sample_stop"
15555 | "停止采样"
15556 | "サンプル停止"
15557 | "샘플정지"
15558 | "หยุดตัวอย่างเสียง"
15559 | "توقف_نمونه_صدا"
15560 | "إيقاف_عينة_صوتية"
15561 | "עצירת_דגימת_קול"
15562 | "آواز_نمونہ_روکو"
15563 | "arrêter_échantillon"
15564 | "sample_stoppen"
15565 | "остановить_семпл" => {
15566 let v = self.arg_num(&args, 0, 0.0)? as u32;
15567 if let Some(a) = &self.audio {
15568 a.stop_sample(v);
15569 }
15570 return Ok(Value::Unit);
15571 },
15572 #[cfg(not(target_arch = "wasm32"))]
15577 "audio_sample_duration"
15578 | "采样时长"
15579 | "サンプル長さ"
15580 | "샘플길이"
15581 | "ความยาวตัวอย่างเสียง" => {
15582 let id = self.arg_num(&args, 0, 0.0)? as usize;
15583 let dur = self
15584 .audio
15585 .as_ref()
15586 .map(|a| a.sample_duration(id))
15587 .unwrap_or(0.0);
15588 return Ok(Value::Number(dur as f64));
15589 },
15590 #[cfg(not(target_arch = "wasm32"))]
15592 "audio_fx_delay"
15593 | "回声"
15594 | "ディレイ効果"
15595 | "딜레이"
15596 | "เสียงสะท้อน"
15597 | "افکت_تاخیر"
15598 | "صدى_تأخير"
15599 | "אפקט_עיכוב"
15600 | "تاخیر_ایفیکٹ"
15601 | "délai_audio"
15602 | "audio_verzögerung"
15603 | "звук_задержка" => {
15604 let time = self.arg_num(&args, 0, 0.3)? as f32;
15605 let fb = self.arg_num(&args, 1, 0.3)? as f32;
15606 let mix = self.arg_num(&args, 2, 0.3)? as f32;
15607 if let Some(a) = &self.audio {
15608 a.fx_delay(time, fb, mix);
15609 }
15610 return Ok(Value::Unit);
15611 },
15612 #[cfg(not(target_arch = "wasm32"))]
15613 "audio_fx_reverb"
15614 | "混响"
15615 | "リバーブ"
15616 | "리버브"
15617 | "เสียงก้อง"
15618 | "افکت_پژواک"
15619 | "صدى_ارتداد"
15620 | "אפקט_הדהוד"
15621 | "بازگشت_آواز_ایفیکٹ"
15622 | "réverbération_audio"
15623 | "audio_nachhall"
15624 | "звук_реверберация" => {
15625 let mix = self.arg_num(&args, 0, 0.3)? as f32;
15626 if let Some(a) = &self.audio {
15627 a.fx_reverb(mix);
15628 }
15629 return Ok(Value::Unit);
15630 },
15631 #[cfg(not(target_arch = "wasm32"))]
15632 "audio_fx_lowpass"
15633 | "低通滤波"
15634 | "ローパス"
15635 | "저역통과"
15636 | "กรองความถี่ต่ำ"
15637 | "فیلتر_پایینگذر"
15638 | "مرشح_تمرير_منخفض"
15639 | "מסנן_תדר_נמוך"
15640 | "لو_پاس_فلٹر"
15641 | "passe_bas_audio"
15642 | "audio_tiefpass"
15643 | "звук_фнч" => {
15644 let cutoff = self.arg_num(&args, 0, 1.0)? as f32;
15645 if let Some(a) = &self.audio {
15646 a.fx_lowpass(cutoff);
15647 }
15648 return Ok(Value::Unit);
15649 },
15650
15651 #[cfg(not(target_arch = "wasm32"))]
15658 "soft_ball"
15659 | "软球"
15660 | "ソフトボール"
15661 | "소프트볼"
15662 | "ลูกบอลนุ่ม"
15663 | "توپ_نرم"
15664 | "كرة_ناعمة"
15665 | "כדור_רך"
15666 | "نرم_گیند"
15667 | "balle_molle"
15668 | "weicher_ball"
15669 | "мягкий_шар" => {
15670 let x = self.arg_num(&args, 0, 0.)? as f32;
15671 let y = self.arg_num(&args, 1, 0.)? as f32;
15672 let z = self.arg_num(&args, 2, 0.)? as f32;
15673 let r = self.arg_num(&args, 3, 1.0)? as f32;
15674 let b = ling_physics::soft::SoftBody::sphere(
15675 ling_physics::Vec3::new(x, y, z),
15676 r,
15677 8,
15678 12,
15679 1.0,
15680 );
15681 let id = self.soft_bodies.len();
15682 self.soft_bodies.push(b);
15683 return Ok(Value::Number(id as f64));
15684 },
15685 #[cfg(not(target_arch = "wasm32"))]
15686 "soft_step"
15687 | "软体步进"
15688 | "ソフト更新"
15689 | "소프트스텝"
15690 | "ก้าวนุ่ม"
15691 | "گام_نرم_جسم"
15692 | "خطوة_ناعمة"
15693 | "צעד_רך"
15694 | "نرم_قدم"
15695 | "pas_mou"
15696 | "weicher_schritt"
15697 | "мягкий_шаг" => {
15698 let id = self.arg_num(&args, 0, 0.)? as usize;
15699 let dt = self.arg_num(&args, 1, 0.016)? as f32;
15700 let gy = self.arg_num(&args, 2, 15.0)? as f32;
15701 if let Some(b) = self.soft_bodies.get_mut(id) {
15702 b.integrate(dt, ling_physics::Vec3::new(0.0, gy, 0.0), 4);
15703 }
15704 return Ok(Value::Unit);
15705 },
15706 #[cfg(not(target_arch = "wasm32"))]
15707 "soft_bounce"
15708 | "软体落地"
15709 | "ソフト着地"
15710 | "소프트바운스"
15711 | "เด้งนุ่ม"
15712 | "جهش_نرم"
15713 | "ارتداد_ناعم"
15714 | "קפיצה_רכה"
15715 | "نرم_اچھال"
15716 | "rebond_mou"
15717 | "weicher_abprall"
15718 | "мягкий_отскок" => {
15719 let id = self.arg_num(&args, 0, 0.)? as usize;
15720 let fy = self.arg_num(&args, 1, 0.)? as f32;
15721 let rest = self.arg_num(&args, 2, 0.5)? as f32;
15722 if let Some(b) = self.soft_bodies.get_mut(id) {
15723 b.floor_collision(fy, rest);
15724 }
15725 return Ok(Value::Unit);
15726 },
15727 #[cfg(not(target_arch = "wasm32"))]
15728 "soft_contain"
15729 | "软体边界"
15730 | "ソフト箱"
15731 | "소프트경계"
15732 | "กล่องนุ่ม"
15733 | "محفظه_نرم"
15734 | "احتواء_ناعم"
15735 | "הכלה_רכה"
15736 | "نرم_احاطہ"
15737 | "contenir_mou"
15738 | "weiche_eindämmung"
15739 | "мягкое_сдерживание" => {
15740 let id = self.arg_num(&args, 0, 0.)? as usize;
15741 let nx = self.arg_num(&args, 1, -5.)? as f32;
15742 let ny = self.arg_num(&args, 2, -5.)? as f32;
15743 let nz = self.arg_num(&args, 3, -5.)? as f32;
15744 let mx = self.arg_num(&args, 4, 5.)? as f32;
15745 let my = self.arg_num(&args, 5, 5.)? as f32;
15746 let mz = self.arg_num(&args, 6, 5.)? as f32;
15747 let rest = self.arg_num(&args, 7, 0.6)? as f32;
15748 if let Some(b) = self.soft_bodies.get_mut(id) {
15749 b.contain(
15750 ling_physics::Vec3::new(nx, ny, nz),
15751 ling_physics::Vec3::new(mx, my, mz),
15752 rest,
15753 );
15754 }
15755 return Ok(Value::Unit);
15756 },
15757 #[cfg(not(target_arch = "wasm32"))]
15758 "soft_kick"
15759 | "软体踢"
15760 | "ソフト衝撃"
15761 | "소프트킥"
15762 | "เตะนุ่ม"
15763 | "ضربه_نرم"
15764 | "ركلة_ناعمة"
15765 | "בעיטה_רכה"
15766 | "نرم_ٹھوکر"
15767 | "coup_mou"
15768 | "weicher_stoß"
15769 | "мягкий_удар" => {
15770 let id = self.arg_num(&args, 0, 0.)? as usize;
15771 let dx = self.arg_num(&args, 1, 0.)? as f32;
15772 let dy = self.arg_num(&args, 2, 0.)? as f32;
15773 let dz = self.arg_num(&args, 3, 0.)? as f32;
15774 let s = self.arg_num(&args, 4, 0.1)? as f32;
15775 if let Some(b) = self.soft_bodies.get_mut(id) {
15776 b.kick(ling_physics::Vec3::new(dx, dy, dz), s);
15777 }
15778 return Ok(Value::Unit);
15779 },
15780 #[cfg(not(target_arch = "wasm32"))]
15783 "soft_spin"
15784 | "软体自旋"
15785 | "ソフト回転"
15786 | "소프트회전"
15787 | "หมุนนุ่ม"
15788 | "چرخش_نرم"
15789 | "دوران_ناعم"
15790 | "סיבוב_רך"
15791 | "نرم_گھماؤ"
15792 | "rotation_molle"
15793 | "weicher_spin"
15794 | "мягкое_вращение" => {
15795 let id = self.arg_num(&args, 0, 0.)? as usize;
15796 let ax = self.arg_num(&args, 1, 0.)? as f32;
15797 let ay = self.arg_num(&args, 2, 0.)? as f32;
15798 let az = self.arg_num(&args, 3, 0.)? as f32;
15799 let rate = self.arg_num(&args, 4, 0.1)? as f32;
15800 if let Some(b) = self.soft_bodies.get_mut(id) {
15801 b.spin(ling_physics::Vec3::new(ax, ay, az), rate);
15802 }
15803 return Ok(Value::Unit);
15804 },
15805 #[cfg(not(target_arch = "wasm32"))]
15806 "soft_deform"
15807 | "形变量"
15808 | "変形量"
15809 | "변형량"
15810 | "ความบิดเบี้ยว"
15811 | "تغییرشکل_نرم"
15812 | "تشوه_ناعم"
15813 | "עיוות_רך"
15814 | "نرم_بگاڑ"
15815 | "déformer_mou"
15816 | "weiches_verformen"
15817 | "мягкая_деформация" => {
15818 let id = self.arg_num(&args, 0, 0.)? as usize;
15819 let d = self
15820 .soft_bodies
15821 .get(id)
15822 .map(|b| b.deformation())
15823 .unwrap_or(0.0);
15824 return Ok(Value::Number(d as f64));
15825 },
15826 #[cfg(not(target_arch = "wasm32"))]
15829 "soft_angular_speed"
15830 | "软体角速"
15831 | "ソフト角速度"
15832 | "소프트각속도"
15833 | "ความเร็วเชิงมุมนุ่ม"
15834 | "سرعت_زاویهای_نرم"
15835 | "سرعة_زاوية_ناعمة"
15836 | "מהירות_זוויתית_רכה"
15837 | "نرم_زاویائی_رفتار"
15838 | "vitesse_angulaire_molle"
15839 | "weiche_winkelgeschwindigkeit"
15840 | "мягкая_угловая_скорость" => {
15841 let id = self.arg_num(&args, 0, 0.)? as usize;
15842 let w = self
15843 .soft_bodies
15844 .get(id)
15845 .map(|b| b.angular_speed())
15846 .unwrap_or(0.0);
15847 return Ok(Value::Number(w as f64));
15848 },
15849 #[cfg(not(target_arch = "wasm32"))]
15850 "soft_centroid"
15851 | "软体质心"
15852 | "ソフト重心"
15853 | "소프트중심"
15854 | "จุดศูนย์กลางนุ่ม"
15855 | "مرکز_جرم_نرم"
15856 | "مركز_ثقل_ناعم"
15857 | "מרכז_כובד_רך"
15858 | "نرم_مرکز_ثقل"
15859 | "centroïde_mou"
15860 | "weicher_schwerpunkt"
15861 | "мягкий_центроид" => {
15862 let id = self.arg_num(&args, 0, 0.)? as usize;
15863 let c = self
15864 .soft_bodies
15865 .get(id)
15866 .map(|b| b.centroid())
15867 .unwrap_or(ling_physics::Vec3::ZERO);
15868 return Ok(Value::List(Rc::new(vec![
15869 Value::Number(c.x as f64),
15870 Value::Number(c.y as f64),
15871 Value::Number(c.z as f64),
15872 ])));
15873 },
15874 #[cfg(not(target_arch = "wasm32"))]
15876 "soft_nodes"
15877 | "软体节点"
15878 | "ソフト節点"
15879 | "소프트노드"
15880 | "จุดนุ่ม"
15881 | "گرههای_نرم"
15882 | "عقد_ناعمة"
15883 | "צמתי_רך"
15884 | "نرم_نوڈز"
15885 | "nœuds_mous"
15886 | "weiche_knoten"
15887 | "мягкие_узлы" => {
15888 let id = self.arg_num(&args, 0, 0.)? as usize;
15889 let mut out = Vec::new();
15890 if let Some(b) = self.soft_bodies.get(id) {
15891 for n in &b.nodes {
15892 out.push(Value::Number(n.pos.x as f64));
15893 out.push(Value::Number(n.pos.y as f64));
15894 out.push(Value::Number(n.pos.z as f64));
15895 }
15896 }
15897 return Ok(Value::List(Rc::new(out)));
15898 },
15899
15900 #[cfg(not(target_arch = "wasm32"))]
15902 "rb_add"
15903 | "刚体添加"
15904 | "剛体追加"
15905 | "강체추가"
15906 | "เพิ่มวัตถุแข็ง"
15907 | "افزودن_جسم_صلب"
15908 | "أضف_جسما_صلبا"
15909 | "הוסף_גוף_קשיח"
15910 | "سخت_جسم_شامل_کرو"
15911 | "ajouter_corps_rigide"
15912 | "starrkörper_hinzufügen"
15913 | "добавить_твёрдое_тело" => {
15914 let x = self.arg_num(&args, 0, 0.)? as f32;
15915 let y = self.arg_num(&args, 1, 0.)? as f32;
15916 let z = self.arg_num(&args, 2, 0.)? as f32;
15917 let mass = self.arg_num(&args, 3, 1.0)? as f32;
15918 let mut b =
15919 ling_physics::rigid::RigidBody::new(ling_physics::Vec3::new(x, y, z), mass);
15920 b.restitution = 0.6;
15921 return Ok(Value::Number(self.rigid_world.add(b) as f64));
15922 },
15923 #[cfg(not(target_arch = "wasm32"))]
15924 "rb_torque"
15925 | "扭矩"
15926 | "トルク"
15927 | "토크"
15928 | "แรงบิด"
15929 | "گشتاور"
15930 | "عزم_دوران"
15931 | "מומנט"
15932 | "ٹارک"
15933 | "couple_corps_rigide"
15934 | "starrkörper_drehmoment"
15935 | "крутящий_момент_твёрдого_тела" => {
15936 let i = self.arg_num(&args, 0, 0.)? as usize;
15937 let tx = self.arg_num(&args, 1, 0.)? as f32;
15938 let ty = self.arg_num(&args, 2, 0.)? as f32;
15939 let tz = self.arg_num(&args, 3, 0.)? as f32;
15940 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
15941 b.apply_torque(ling_physics::Vec3::new(tx, ty, tz));
15942 }
15943 return Ok(Value::Unit);
15944 },
15945 #[cfg(not(target_arch = "wasm32"))]
15946 "rb_spin"
15947 | "自旋"
15948 | "スピン"
15949 | "스핀"
15950 | "หมุน"
15951 | "چرخش_جسم_صلب"
15952 | "دوران_جسم_صلب"
15953 | "סיבוב_גוף_קשיח"
15954 | "سخت_جسم_گھماؤ"
15955 | "spin_corps_rigide"
15956 | "starrkörper_spin"
15957 | "вращение_твёрдого_тела" => {
15958 let i = self.arg_num(&args, 0, 0.)? as usize;
15959 let wx = self.arg_num(&args, 1, 0.)? as f32;
15960 let wy = self.arg_num(&args, 2, 0.)? as f32;
15961 let wz = self.arg_num(&args, 3, 0.)? as f32;
15962 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
15963 b.apply_spin(ling_physics::Vec3::new(wx, wy, wz));
15964 }
15965 return Ok(Value::Unit);
15966 },
15967 #[cfg(not(target_arch = "wasm32"))]
15968 "rb_impulse"
15969 | "刚体冲量"
15970 | "剛体インパルス"
15971 | "강체충격"
15972 | "แรงดลแข็ง"
15973 | "ضربه_جسم_صلب"
15974 | "دفعة_جسم_صلب"
15975 | "דחף_גוף_קשיח"
15976 | "سخت_جسم_دھکا"
15977 | "impulsion_corps_rigide"
15978 | "starrkörper_impuls"
15979 | "импульс_твёрдого_тела" => {
15980 let i = self.arg_num(&args, 0, 0.)? as usize;
15981 let ix = self.arg_num(&args, 1, 0.)? as f32;
15982 let iy = self.arg_num(&args, 2, 0.)? as f32;
15983 let iz = self.arg_num(&args, 3, 0.)? as f32;
15984 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
15985 b.apply_impulse(ling_physics::Vec3::new(ix, iy, iz));
15986 }
15987 return Ok(Value::Unit);
15988 },
15989 #[cfg(not(target_arch = "wasm32"))]
15990 "rb_floor"
15991 | "刚体落地"
15992 | "剛体着地"
15993 | "강체바닥"
15994 | "พื้นแข็ง"
15995 | "کف_جسم_صلب"
15996 | "أرضية_جسم_صلب"
15997 | "רצפת_גוף_קשיח"
15998 | "سخت_جسم_فرش"
15999 | "sol_corps_rigide"
16000 | "starrkörper_boden"
16001 | "пол_твёрдого_тела" => {
16002 let i = self.arg_num(&args, 0, 0.)? as usize;
16003 let fy = self.arg_num(&args, 1, 0.)? as f32;
16004 let rest = self.arg_num(&args, 2, 0.6)? as f32;
16005 let fric = self.arg_num(&args, 3, 0.6)? as f32;
16006 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
16007 b.bounce_floor(fy, rest, fric);
16008 }
16009 return Ok(Value::Unit);
16010 },
16011 #[cfg(not(target_arch = "wasm32"))]
16012 "rb_gravity"
16013 | "刚体重力"
16014 | "剛体重力"
16015 | "강체중력"
16016 | "แรงโน้มถ่วงแข็ง"
16017 | "گرانش_جسم_صلب"
16018 | "جاذبية_جسم_صلب"
16019 | "כבידת_גוף_קשיח"
16020 | "سخت_جسم_کشش_ثقل"
16021 | "gravité_corps_rigide"
16022 | "starrkörper_schwerkraft"
16023 | "гравитация_твёрдого_тела" => {
16024 let gx = self.arg_num(&args, 0, 0.)? as f32;
16025 let gy = self.arg_num(&args, 1, 9.81)? as f32;
16026 let gz = self.arg_num(&args, 2, 0.)? as f32;
16027 self.rigid_world.gravity = ling_physics::Vec3::new(gx, gy, gz);
16028 return Ok(Value::Unit);
16029 },
16030 #[cfg(not(target_arch = "wasm32"))]
16031 "rb_step"
16032 | "刚体步进"
16033 | "剛体更新"
16034 | "강체스텝"
16035 | "ก้าวแข็ง"
16036 | "گام_جسم_صلب"
16037 | "خطوة_جسم_صلب"
16038 | "צעד_גוף_קשיח"
16039 | "سخت_جسم_قدم"
16040 | "pas_corps_rigide"
16041 | "starrkörper_schritt"
16042 | "шаг_твёрдого_тела" => {
16043 let dt = self.arg_num(&args, 0, 0.016)? as f32;
16044 self.rigid_world.step(dt);
16045 return Ok(Value::Unit);
16046 },
16047 #[cfg(not(target_arch = "wasm32"))]
16048 "rb_pos"
16049 | "刚体位置"
16050 | "剛体位置"
16051 | "강체위치"
16052 | "ตำแหน่งแข็ง"
16053 | "موقعیت_جسم_صلب"
16054 | "موضع_جسم_صلب"
16055 | "מיקום_גוף_קשיח"
16056 | "سخت_جسم_مقام"
16057 | "position_corps_rigide"
16058 | "starrkörper_position"
16059 | "позиция_твёрдого_тела" => {
16060 let i = self.arg_num(&args, 0, 0.)? as usize;
16061 let p = self
16062 .rigid_world
16063 .bodies
16064 .get(i)
16065 .map(|b| b.pos)
16066 .unwrap_or(ling_physics::Vec3::ZERO);
16067 return Ok(Value::List(Rc::new(vec![
16068 Value::Number(p.x as f64),
16069 Value::Number(p.y as f64),
16070 Value::Number(p.z as f64),
16071 ])));
16072 },
16073 #[cfg(not(target_arch = "wasm32"))]
16074 "rb_rot"
16075 | "刚体旋转"
16076 | "剛体回転"
16077 | "강체회전"
16078 | "การหมุนแข็ง"
16079 | "چرخش_وضعية_جسم_صلب"
16080 | "دوران_وضعية_جسم_صلب"
16081 | "סיבוב_זווית_גוף_קשיח"
16082 | "سخت_جسم_گردش"
16083 | "rotation_corps_rigide"
16084 | "starrkörper_rotation"
16085 | "поворот_твёрдого_тела" => {
16086 let i = self.arg_num(&args, 0, 0.)? as usize;
16087 let q = self
16088 .rigid_world
16089 .bodies
16090 .get(i)
16091 .map(|b| b.orientation)
16092 .unwrap_or(ling_physics::Quat::IDENTITY);
16093 return Ok(Value::List(Rc::new(vec![
16094 Value::Number(q.x as f64),
16095 Value::Number(q.y as f64),
16096 Value::Number(q.z as f64),
16097 Value::Number(q.w as f64),
16098 ])));
16099 },
16100
16101 #[cfg(not(target_arch = "wasm32"))]
16103 "mesh_load"
16104 | "โหลดเมช"
16105 | "载入网格"
16106 | "メッシュ読込"
16107 | "메시로드"
16108 | "بارگذاری_مش"
16109 | "حمّل_شبكة"
16110 | "טען_מש"
16111 | "میش_لوڈ" => {
16112 let path = self.arg_str(&args, 0, "");
16113 let resolved = if std::path::Path::new(&path).exists() {
16114 path.clone()
16115 } else if let Some(d) = &self.source_dir {
16116 d.join(&path).to_string_lossy().into_owned()
16117 } else {
16118 path.clone()
16119 };
16120 let bytes = match std::fs::read(&resolved) {
16121 Ok(b) => b,
16122 Err(e) => {
16123 eprintln!("mesh_load failed ({path}): {e}");
16124 return Ok(Value::Number(-1.0));
16125 },
16126 };
16127 if bytes.len() < 16 || &bytes[0..4] != b"LMSH" {
16128 eprintln!("mesh_load: bad header ({path})");
16129 return Ok(Value::Number(-1.0));
16130 }
16131 let rd4 =
16132 |o: usize| -> [u8; 4] { [bytes[o], bytes[o + 1], bytes[o + 2], bytes[o + 3]] };
16133 let height = f32::from_le_bytes(rd4(8));
16134 let ntri = u32::from_le_bytes(rd4(12)) as usize;
16135 let need = 16usize.saturating_add(ntri.saturating_mul(9 * 4 + 3));
16136 if bytes.len() < need {
16137 eprintln!("mesh_load: truncated ({path})");
16138 return Ok(Value::Number(-1.0));
16139 }
16140 let mut pos = Vec::with_capacity(ntri * 3);
16141 let mut col = Vec::with_capacity(ntri);
16142 let mut off = 16usize;
16143 for _ in 0..ntri {
16144 for _k in 0..3 {
16145 let x = f32::from_le_bytes(rd4(off));
16146 let y = f32::from_le_bytes(rd4(off + 4));
16147 let z = f32::from_le_bytes(rd4(off + 8));
16148 off += 12;
16149 pos.push([x, y, z]);
16150 }
16151 col.push([bytes[off], bytes[off + 1], bytes[off + 2]]);
16152 off += 3;
16153 }
16154 eprintln!("mesh_load: {} ({} tris, h={:.2})", path, ntri, height);
16155 let id = self.meshes.len();
16156 self.meshes
16157 .push(crate::gfx::shapes::ColorMesh { pos, col, height });
16158 return Ok(Value::Number(id as f64));
16159 },
16160 #[cfg(target_arch = "wasm32")]
16161 "mesh_load"
16162 | "โหลดเมช"
16163 | "载入网格"
16164 | "メッシュ読込"
16165 | "메시로드"
16166 | "بارگذاری_مش"
16167 | "حمّل_شبكة"
16168 | "טען_רשת"
16169 | "میش_لوڈ" => {
16170 return Ok(Value::Number(-1.0));
16173 },
16174 #[cfg(not(target_arch = "wasm32"))]
16175 "mesh_draw"
16176 | "วาดเมชสี"
16177 | "绘制网格"
16178 | "メッシュ描画"
16179 | "메시그리기"
16180 | "رسم_مش_رنگی"
16181 | "ارسم_شبكة_ملونة"
16182 | "צייר_רשת_צבעונית"
16183 | "رنگین_میش_کھینچو" => {
16184 let id = self.arg_num(&args, 0, 0.)? as usize;
16186 let cx = self.arg_num(&args, 1, 0.)? as f32;
16187 let cy = self.arg_num(&args, 2, 0.)? as f32;
16188 let cz = self.arg_num(&args, 3, 0.)? as f32;
16189 let sc = self.arg_num(&args, 4, 1.)? as f32;
16190 let yaw = self.arg_num(&args, 5, 0.)? as f32;
16191 let sway = self.arg_num(&args, 6, 0.)? as f32;
16192 let arm = self.arg_num(&args, 7, 0.)? as f32;
16193 let lean = self.arg_num(&args, 8, 0.)? as f32;
16194 let leg = self.arg_num(&args, 9, 0.)? as f32;
16195 let tuck = self.arg_num(&args, 10, 0.)? as f32;
16196 if id < self.meshes.len() {
16197 let m = &self.meshes[id];
16198 let mut gfx = self.gfx.borrow_mut();
16199 gfx.draw_color_mesh(m, cx, cy, cz, sc, yaw, sway, arm, lean, leg, tuck);
16200 }
16201 return Ok(Value::Unit);
16202 },
16203 #[cfg(target_arch = "wasm32")]
16204 "mesh_draw"
16205 | "วาดเมชสี"
16206 | "绘制网格"
16207 | "メッシュ描画"
16208 | "메시그리기"
16209 | "رسم_مش_رنگی"
16210 | "ارسم_شبكة_ملونة"
16211 | "צייר_רשת_צבעונית"
16212 | "رنگین_میش_کھینچو" => {
16213 return Ok(Value::Unit);
16214 },
16215
16216 "liquid_new"
16218 | "新建液体"
16219 | "液体新規"
16220 | "액체생성"
16221 | "สร้างของเหลว"
16222 | "مایع_جدید"
16223 | "سائل_جديد"
16224 | "נוזל_חדש"
16225 | "نیا_مائع"
16226 | "nouveau_liquide"
16227 | "neue_flüssigkeit"
16228 | "новая_жидкость" => {
16229 let w = self.arg_num(&args, 0, 64.)? as usize;
16230 let h = self.arg_num(&args, 1, 64.)? as usize;
16231 let id = self.liquids.len();
16232 self.liquids
16233 .push(ling_physics::liquid::LiquidGrid::new(w, h));
16234 return Ok(Value::Number(id as f64));
16235 },
16236 "liquid_set_colors"
16237 | "液体颜色"
16238 | "液体配色"
16239 | "액체색상"
16240 | "สีของเหลว"
16241 | "تنظیم_رنگ_مایع"
16242 | "عيّن_ألوان_السائل"
16243 | "קבע_צבעי_נוזל"
16244 | "مائع_رنگ_مقرر_کرو" => {
16245 let id = self.arg_num(&args, 0, 0.)? as usize;
16246 let wr = self.arg_num(&args, 1, 40.)? as f32;
16247 let wg = self.arg_num(&args, 2, 110.)? as f32;
16248 let wb = self.arg_num(&args, 3, 235.)? as f32;
16249 let or_ = self.arg_num(&args, 4, 240.)? as f32;
16250 let og = self.arg_num(&args, 5, 175.)? as f32;
16251 let ob = self.arg_num(&args, 6, 45.)? as f32;
16252 if let Some(g) = self.liquids.get_mut(id) {
16253 g.set_colors(wr, wg, wb, or_, og, ob);
16254 }
16255 return Ok(Value::Unit);
16256 },
16257 "liquid_splat"
16258 | "液体注入"
16259 | "液体追加"
16260 | "액체분사"
16261 | "หยดของเหลว"
16262 | "پاشش_مایع"
16263 | "بقعة_سائل"
16264 | "התזת_נוזל"
16265 | "مائع_چھینٹا"
16266 | "éclaboussure_liquide"
16267 | "flüssigkeit_spritzer"
16268 | "брызги_жидкости" => {
16269 let id = self.arg_num(&args, 0, 0.)? as usize;
16270 let x = self.arg_num(&args, 1, 0.)? as f32;
16271 let y = self.arg_num(&args, 2, 0.)? as f32;
16272 let kind = self.arg_num(&args, 3, 0.)? as i32;
16273 let amt = self.arg_num(&args, 4, 1.0)? as f32;
16274 let rad = self.arg_num(&args, 5, 4.0)? as f32;
16275 if let Some(g) = self.liquids.get_mut(id) {
16276 g.splat(x, y, kind, amt, rad);
16277 }
16278 return Ok(Value::Unit);
16279 },
16280 "liquid_gravity"
16281 | "液体重力"
16282 | "液体重力ベクトル"
16283 | "액체중력"
16284 | "แรงโน้มถ่วงเหลว"
16285 | "گرانش_مایع"
16286 | "جاذبية_السائل"
16287 | "כבידת_נוזל"
16288 | "مائع_کشش_ثقل"
16289 | "gravité_liquide"
16290 | "flüssigkeit_schwerkraft"
16291 | "гравитация_жидкости" => {
16292 let id = self.arg_num(&args, 0, 0.)? as usize;
16293 let gx = self.arg_num(&args, 1, 0.)? as f32;
16294 let gy = self.arg_num(&args, 2, 60.)? as f32;
16295 if let Some(g) = self.liquids.get_mut(id) {
16296 g.set_gravity(gx, gy);
16297 }
16298 return Ok(Value::Unit);
16299 },
16300 "liquid_step"
16301 | "液体步进"
16302 | "液体更新"
16303 | "액체스텝"
16304 | "ก้าวของเหลว"
16305 | "گام_مایع"
16306 | "خطوة_السائل"
16307 | "צעד_נוזל"
16308 | "مائع_قدم"
16309 | "pas_liquide"
16310 | "flüssigkeit_schritt"
16311 | "шаг_жидкости" => {
16312 let id = self.arg_num(&args, 0, 0.)? as usize;
16313 let dt = self.arg_num(&args, 1, 0.016)? as f32;
16314 if let Some(g) = self.liquids.get_mut(id) {
16315 g.step(dt);
16316 }
16317 return Ok(Value::Unit);
16318 },
16319 "liquid_step_all"
16325 | "液体全步进"
16326 | "液体全更新"
16327 | "전체액체스텝"
16328 | "ก้าวของเหลวทั้งหมด"
16329 | "گام_همه_مایعات"
16330 | "خطوة_كل_السوائل"
16331 | "צעד_כל_הנוזלים"
16332 | "تمام_مائع_قدم" => {
16333 let dt = self.arg_num(&args, 0, 0.016)? as f32;
16334 ling_physics::liquid::step_all(&mut self.liquids, dt);
16335 return Ok(Value::Unit);
16336 },
16337 "liquid_rainbow"
16339 | "液体彩虹"
16340 | "液体虹"
16341 | "액체무지개"
16342 | "ของเหลวสายรุ้ง"
16343 | "مایع_رنگینکمان"
16344 | "سائل_قوس_قزح"
16345 | "נוזל_קשת"
16346 | "قوس_قزح_مائع"
16347 | "arc_en_ciel_liquide"
16348 | "flüssigkeit_regenbogen"
16349 | "радуга_жидкости" => {
16350 let id = self.arg_num(&args, 0, 0.)? as usize;
16351 let on = self.arg_num(&args, 1, 1.0)? > 0.5;
16352 if let Some(g) = self.liquids.get_mut(id) {
16353 g.rainbow = on;
16354 }
16355 return Ok(Value::Unit);
16356 },
16357 "liquid_mix"
16359 | "液体混合"
16360 | "液体混合度"
16361 | "액체혼합"
16362 | "การผสมของเหลว"
16363 | "ترکیب_مایع"
16364 | "مزج_سائل"
16365 | "ערבוב_נוזל"
16366 | "مائع_ملاؤ"
16367 | "mélanger_liquide"
16368 | "flüssigkeit_mischen"
16369 | "смешать_жидкость" => {
16370 let id = self.arg_num(&args, 0, 0.)? as usize;
16371 let m = self.liquids.get(id).map(|g| g.mix_amount()).unwrap_or(0.0);
16372 return Ok(Value::Number(m as f64));
16373 },
16374 #[cfg(not(target_arch = "wasm32"))]
16376 "liquid_draw"
16377 | "绘制液体"
16378 | "液体描画"
16379 | "액체그리기"
16380 | "วาดของเหลว"
16381 | "رسم_مایع"
16382 | "ارسم_سائلا"
16383 | "צייר_נוזל"
16384 | "مائع_کھینچو"
16385 | "dessiner_liquide"
16386 | "flüssigkeit_zeichnen"
16387 | "рисовать_жидкость" => {
16388 let id = self.arg_num(&args, 0, 0.)? as usize;
16389 let sx = self.arg_num(&args, 1, 0.)? as i32;
16390 let sy = self.arg_num(&args, 2, 0.)? as i32;
16391 let scale = (self.arg_num(&args, 3, 4.)? as i32).max(1);
16392 if id < self.liquids.len() {
16393 let (gw, gh) = {
16394 let g = &self.liquids[id];
16395 (g.w, g.h)
16396 };
16397 let mut gfx = self.gfx.borrow_mut();
16398 let (w, h) = (gfx.width as i32, gfx.height as i32);
16399 let g = &self.liquids[id];
16400 for cy in 0..gh {
16401 for cx in 0..gw {
16402 let col = g.sample_rgb(cx, cy);
16403 let bx = sx + cx as i32 * scale;
16404 let by = sy + cy as i32 * scale;
16405 for dy in 0..scale {
16406 for dx in 0..scale {
16407 let px = bx + dx;
16408 let py = by + dy;
16409 if px >= 0 && py >= 0 && px < w && py < h {
16410 gfx.buffer[(py * w + px) as usize] = col;
16411 }
16412 }
16413 }
16414 }
16415 }
16416 }
16417 return Ok(Value::Unit);
16418 },
16419 "liquid_draw_surface"
16422 | "液体贴面"
16423 | "液体曲面"
16424 | "액체곡면"
16425 | "ของเหลวบนพื้นผิว"
16426 | "رسم_سطح_مایع"
16427 | "ارسم_سطح_السائل"
16428 | "צייר_משטח_נוזל"
16429 | "مائع_سطح_کھینچو"
16430 | "dessiner_surface_liquide"
16431 | "flüssigkeit_oberfläche_zeichnen"
16432 | "рисовать_поверхность_жидкости" => {
16433 #[cfg(not(target_arch = "wasm32"))]
16434 {
16435 let id = self.arg_num(&args, 0, 0.)? as usize;
16436 let kind = self.arg_num(&args, 1, 1.)? as i32;
16437 let cx = self.arg_num(&args, 2, 0.)? as f32;
16438 let cy = self.arg_num(&args, 3, 0.)? as f32;
16439 let cz = self.arg_num(&args, 4, 0.)? as f32;
16440 let radius = self.arg_num(&args, 5, 2.0)? as f32;
16441 let height = self.arg_num(&args, 6, 3.0)? as f32;
16442 if id < self.liquids.len() {
16443 let (gw, gh) = {
16444 let g = &self.liquids[id];
16445 (g.w, g.h)
16446 };
16447 let mut gfx = self.gfx.borrow_mut();
16448 let (w, h, add) = (gfx.width, gfx.height, gfx.blend == 1);
16449 let cam = gfx.camera.clone();
16450 let near = -cam.zdist + 0.05;
16451 let g = &self.liquids[id];
16452 let tau = std::f32::consts::TAU;
16453 let pi = std::f32::consts::PI;
16454 let sp = |u: f32, v: f32| -> [f32; 3] {
16456 if kind == 0 {
16457 [
16458 cx + (u - 0.5) * 2.0 * radius,
16459 cy,
16460 cz + (v - 0.5) * 2.0 * radius,
16461 ]
16462 } else if kind == 2 {
16463 let th = u * tau;
16464 [
16465 cx + th.cos() * radius,
16466 cy + (v - 0.5) * height,
16467 cz + th.sin() * radius,
16468 ]
16469 } else if kind == 3 {
16470 let th = u * tau;
16471 let rr = radius * (1.0 - v);
16472 [
16473 cx + th.cos() * rr,
16474 cy + (v - 0.5) * height,
16475 cz + th.sin() * rr,
16476 ]
16477 } else if kind == 4 {
16478 let th = u * tau;
16479 let ph = v * pi * 0.5;
16480 [
16481 cx + ph.sin() * th.cos() * radius,
16482 cy - ph.cos() * radius,
16483 cz + ph.sin() * th.sin() * radius,
16484 ]
16485 } else {
16486 let th = u * tau;
16487 let ph = v * pi;
16488 [
16489 cx + ph.sin() * th.cos() * radius,
16490 cy + ph.cos() * radius,
16491 cz + ph.sin() * th.sin() * radius,
16492 ]
16493 }
16494 };
16495 let nrm = |u: f32, v: f32| -> [f32; 3] {
16496 if kind == 0 {
16497 [0.0, -1.0, 0.0]
16498 } else if kind == 2 {
16499 let th = u * tau;
16500 [th.cos(), 0.0, th.sin()]
16501 } else if kind == 3 {
16502 let th = u * tau;
16503 let s = (radius / height.max(0.01)).atan();
16504 [th.cos() * s.cos(), s.sin(), th.sin() * s.cos()]
16505 } else if kind == 4 {
16506 let th = u * tau;
16507 let ph = v * pi * 0.5;
16508 [ph.sin() * th.cos(), -ph.cos(), ph.sin() * th.sin()]
16509 } else {
16510 let th = u * tau;
16511 let ph = v * pi;
16512 [ph.sin() * th.cos(), ph.cos(), ph.sin() * th.sin()]
16513 }
16514 };
16515 let gwf = gw as f32;
16516 let ghf = gh as f32;
16517 let mut cyc = 0usize;
16518 while cyc < gh {
16519 let mut cxc = 0usize;
16520 while cxc < gw {
16521 let uc = (cxc as f32 + 0.5) / gwf;
16523 let vc = (cyc as f32 + 0.5) / ghf;
16524 let c = sp(uc, vc);
16525 let n = nrm(uc, vc);
16526 let dc = cam.depth(c[0], c[1], c[2]);
16527 if dc > near {
16528 let cull = kind != 0
16529 && cam.depth(
16530 c[0] + n[0] * 0.06,
16531 c[1] + n[1] * 0.06,
16532 c[2] + n[2] * 0.06,
16533 ) > dc;
16534 if !cull {
16535 let u0 = cxc as f32 / gwf;
16537 let u1 = (cxc + 1) as f32 / gwf;
16538 let v0 = cyc as f32 / ghf;
16539 let v1 = (cyc + 1) as f32 / ghf;
16540 let q = [sp(u0, v0), sp(u1, v0), sp(u1, v1), sp(u0, v1)];
16541 let mut poly: Vec<[f32; 2]> = Vec::with_capacity(5);
16542 let mut ok = true;
16543 for p in &q {
16544 if cam.depth(p[0], p[1], p[2]) <= near {
16545 ok = false;
16546 break;
16547 }
16548 let (sx, sy, _) = cam.project(p[0], p[1], p[2]);
16549 poly.push([sx, sy]);
16550 }
16551 if ok {
16552 let p0 = poly[0];
16553 poly.push(p0);
16554 let col = g.sample_rgb(cxc, cyc);
16555 crate::gfx::raster::fill_contours_aa(
16556 &mut gfx.buffer,
16557 w,
16558 h,
16559 col,
16560 add,
16561 std::slice::from_ref(&poly),
16562 );
16563 }
16564 }
16565 }
16566 cxc += 1;
16567 }
16568 cyc += 1;
16569 }
16570 }
16571 }
16572 #[cfg(target_arch = "wasm32")]
16573 {
16574 }
16577 return Ok(Value::Unit);
16578 },
16579 #[cfg(not(target_arch = "wasm32"))]
16582 "sparkle" | "闪光" | "きらめき" | "반짝임" | "ประกาย" | "درخشش" | "بريق" | "נצנוץ"
16583 | "چمک" | "scintillement" | "funkeln" | "искриться" => {
16584 let x = self.arg_num(&args, 0, 0.)? as f32;
16585 let y = self.arg_num(&args, 1, 0.)? as f32;
16586 let ww = self.arg_num(&args, 2, 200.)? as f32;
16587 let hh = self.arg_num(&args, 3, 200.)? as f32;
16588 let count = self.arg_num(&args, 4, 40.)? as i32;
16589 let t = self.arg_num(&args, 5, 0.)? as f32;
16590 let mut gfx = self.gfx.borrow_mut();
16591 let (w, h, add, color) = (gfx.width, gfx.height, gfx.blend == 1, gfx.color);
16592 let (cr, cg, cb) = (
16593 (color >> 16 & 0xFF) as f32,
16594 (color >> 8 & 0xFF) as f32,
16595 (color & 0xFF) as f32,
16596 );
16597 let mut n = 0i32;
16598 while n < count {
16599 let hsh = (n as u32).wrapping_mul(2654435761).wrapping_add(0x9E3779B9);
16600 let u = ((hsh >> 8) & 1023) as f32 / 1023.0;
16601 let v = ((hsh >> 18) & 1023) as f32 / 1023.0;
16602 let phase = (hsh & 255) as f32 / 255.0;
16603 let tw = (t * 3.0 + phase * std::f32::consts::TAU + n as f32).sin() * 0.5 + 0.5;
16604 let sz = 1.5 + tw * 5.0;
16605 let px = x + u * ww;
16606 let py = y + v * hh;
16607 let b = tw * tw; let col =
16609 (((cr * b) as u32) << 16) | (((cg * b) as u32) << 8) | ((cb * b) as u32);
16610 crate::gfx::raster::draw_line_aa(
16611 &mut gfx.buffer,
16612 w,
16613 h,
16614 col,
16615 add,
16616 px - sz,
16617 py,
16618 px + sz,
16619 py,
16620 );
16621 crate::gfx::raster::draw_line_aa(
16622 &mut gfx.buffer,
16623 w,
16624 h,
16625 col,
16626 add,
16627 px,
16628 py - sz,
16629 px,
16630 py + sz,
16631 );
16632 let d = sz * 0.55;
16633 crate::gfx::raster::draw_line_aa(
16634 &mut gfx.buffer,
16635 w,
16636 h,
16637 col,
16638 add,
16639 px - d,
16640 py - d,
16641 px + d,
16642 py + d,
16643 );
16644 crate::gfx::raster::draw_line_aa(
16645 &mut gfx.buffer,
16646 w,
16647 h,
16648 col,
16649 add,
16650 px - d,
16651 py + d,
16652 px + d,
16653 py - d,
16654 );
16655 n += 1;
16656 }
16657 return Ok(Value::Unit);
16658 },
16659
16660 #[cfg(not(target_arch = "wasm32"))]
16666 "dialog_show"
16667 | "对话显示"
16668 | "会話表示"
16669 | "대화표시"
16670 | "แสดงบทสนทนา"
16671 | "نمایش_گفتگو"
16672 | "اعرض_الحوار"
16673 | "הצג_דיאלוג"
16674 | "مکالمہ_دکھاؤ"
16675 | "afficher_dialogue"
16676 | "dialog_anzeigen"
16677 | "показать_диалог" => {
16678 let text = self.arg_str(&args, 0, "");
16679 let cps = self.arg_num(&args, 1, 32.0)? as f32;
16680 self.dialog = Some(ling_game::dialog::Dialog::new(&text, cps));
16681 return Ok(Value::Unit);
16682 },
16683 #[cfg(not(target_arch = "wasm32"))]
16684 "dialog_step"
16685 | "对话步进"
16686 | "会話更新"
16687 | "대화스텝"
16688 | "ก้าวบทสนทนา"
16689 | "گام_گفتگو"
16690 | "خطوة_الحوار"
16691 | "צעד_דיאלוג"
16692 | "مکالمہ_قدم"
16693 | "pas_dialogue"
16694 | "dialog_schritt"
16695 | "шаг_диалога" => {
16696 let dt = self.arg_num(&args, 0, 0.016)? as f32;
16697 if let Some(d) = self.dialog.as_mut() {
16698 d.update(dt);
16699 }
16700 return Ok(Value::Unit);
16701 },
16702 #[cfg(not(target_arch = "wasm32"))]
16703 "dialog_advance"
16704 | "对话推进"
16705 | "会話送り"
16706 | "대화진행"
16707 | "เลื่อนบทสนทนา"
16708 | "پیشروی_گفتگو"
16709 | "تقدّم_الحوار"
16710 | "קדם_דיאלוג"
16711 | "مکالمہ_آگے_بڑھاؤ"
16712 | "avancer_dialogue"
16713 | "dialog_weiter"
16714 | "продолжить_диалог" => {
16715 if let Some(d) = self.dialog.as_mut() {
16716 d.advance();
16717 }
16718 return Ok(Value::Unit);
16719 },
16720 #[cfg(not(target_arch = "wasm32"))]
16721 "dialog_active"
16722 | "对话激活"
16723 | "会話中"
16724 | "대화중"
16725 | "บทสนทนาทำงาน"
16726 | "گفتگو_فعال"
16727 | "الحوار_نشط"
16728 | "דיאלוג_פעיל"
16729 | "مکالمہ_فعال"
16730 | "dialogue_actif"
16731 | "dialog_aktiv"
16732 | "диалог_активен" => {
16733 let a = self
16734 .dialog
16735 .as_ref()
16736 .map(|d| !d.is_closed())
16737 .unwrap_or(false);
16738 return Ok(Value::Bool(a));
16739 },
16740 #[cfg(not(target_arch = "wasm32"))]
16741 "dialog_typing"
16742 | "对话打字"
16743 | "会話タイプ中"
16744 | "대화타이핑"
16745 | "กำลังพิมพ์บทสนทนา"
16746 | "گفتگو_در_حال_تایپ"
16747 | "الحوار_يكتب"
16748 | "דיאלוג_מקליד"
16749 | "مکالمہ_ٹائپنگ"
16750 | "dialogue_frappe"
16751 | "dialog_tippen"
16752 | "диалог_печатает" => {
16753 use ling_game::dialog::Dialog;
16754
16755 let a = self
16756 .dialog
16757 .as_ref()
16758 .map(|d: &Dialog| !d.is_closed() && d.is_typing())
16759 .unwrap_or(false);
16760 return Ok(Value::Bool(a));
16761 },
16762 #[cfg(not(target_arch = "wasm32"))]
16763 "dialog_close"
16764 | "对话关闭"
16765 | "会話閉じる"
16766 | "대화닫기"
16767 | "ปิดบทสนทนา"
16768 | "بستن_گفتگو"
16769 | "أغلق_الحوار"
16770 | "סגור_דיאלוג"
16771 | "مکالمہ_بند"
16772 | "fermer_dialogue"
16773 | "dialog_schließen"
16774 | "закрыть_диалог" => {
16775 self.dialog = None;
16776 return Ok(Value::Unit);
16777 },
16778 #[cfg(not(target_arch = "wasm32"))]
16780 "dialog_color"
16781 | "对话颜色"
16782 | "会話色"
16783 | "대화색"
16784 | "สีบทสนทนา"
16785 | "رنگ_گفتگو"
16786 | "لون_الحوار"
16787 | "צבע_דיאלוג"
16788 | "مکالمہ_رنگ"
16789 | "couleur_dialogue"
16790 | "dialog_farbe"
16791 | "цвет_диалога" => {
16792 let role = (self.arg_num(&args, 0, 0.0)? as usize).min(3);
16793 let r = self.arg_num(&args, 1, 255.0)? as u32 & 0xFF;
16794 let g = self.arg_num(&args, 2, 255.0)? as u32 & 0xFF;
16795 let b = self.arg_num(&args, 3, 255.0)? as u32 & 0xFF;
16796 self.dialog_colors[role] = (r << 16) | (g << 8) | b;
16797 return Ok(Value::Unit);
16798 },
16799 #[cfg(not(target_arch = "wasm32"))]
16801 "dialog_draw"
16802 | "对话绘制"
16803 | "会話描画"
16804 | "대화그리기"
16805 | "วาดบทสนทนา"
16806 | "رسم_گفتگو"
16807 | "ارسم_الحوار"
16808 | "צייר_דיאלוג"
16809 | "مکالمہ_کھینچو"
16810 | "dessiner_dialogue"
16811 | "dialog_zeichnen"
16812 | "рисовать_диалог" => {
16813 let x = self.arg_num(&args, 0, 40.0)? as f32;
16814 let y = self.arg_num(&args, 1, 0.0)? as f32;
16815 let ww = self.arg_num(&args, 2, 720.0)? as f32;
16816 let hh = self.arg_num(&args, 3, 150.0)? as f32;
16817 let font = self.arg_num(&args, 4, -1.0)? as i64;
16818 let t = (crate::runtime::now_secs() - self.start_time_secs) as f32;
16819 self.render_dialog(x, y, ww, hh, font, t);
16820 return Ok(Value::Unit);
16821 },
16822
16823 #[cfg(not(target_arch = "wasm32"))]
16828 "text_poll" => {
16829 const TEXT_BUFFER_MAX: usize = 240;
16830 #[cfg(windows)]
16839 {
16840 let topmost = self.gfx.borrow().topmost_window;
16841 if topmost {
16842 if !window_is_foreground(self.gfx.borrow().hwnd) {
16843 return Ok(Value::Str(self.text_buffer.clone()));
16844 }
16845 let shift = os_key_down(VK_SHIFT);
16846 let now = crate::runtime::now_secs();
16847 let mut gfx = self.gfx.borrow_mut();
16848 let back_idx = VK_BACK as usize;
16849 let back_down = os_key_down(VK_BACK);
16850 let back_was = gfx.raw_keys_prev[back_idx];
16851 let (mut back_since, mut back_fire) = (
16852 gfx.raw_keys_down_since[back_idx],
16853 gfx.raw_keys_last_fire[back_idx],
16854 );
16855 if key_repeat_fire(
16856 now,
16857 back_down,
16858 back_was,
16859 &mut back_since,
16860 &mut back_fire,
16861 ) {
16862 self.text_buffer.pop();
16863 }
16864 gfx.raw_keys_down_since[back_idx] = back_since;
16865 gfx.raw_keys_last_fire[back_idx] = back_fire;
16866 gfx.raw_keys_prev[back_idx] = back_down;
16867 for &vk in TEXT_POLL_VKS {
16868 let idx = (vk as usize) & 0xFF;
16869 let down = os_key_down(vk);
16870 let was = gfx.raw_keys_prev[idx];
16871 let (mut since, mut fire) =
16872 (gfx.raw_keys_down_since[idx], gfx.raw_keys_last_fire[idx]);
16873 if key_repeat_fire(now, down, was, &mut since, &mut fire) {
16874 if let Some(c) = vk_char(vk, shift) {
16875 if self.text_buffer.chars().count() < TEXT_BUFFER_MAX {
16876 self.text_buffer.push(c);
16877 }
16878 }
16879 }
16880 gfx.raw_keys_down_since[idx] = since;
16881 gfx.raw_keys_last_fire[idx] = fire;
16882 gfx.raw_keys_prev[idx] = down;
16883 }
16884 return Ok(Value::Str(self.text_buffer.clone()));
16885 }
16886 }
16887 let (keys, shift) = {
16888 let gfx = self.gfx.borrow();
16889 match gfx.window.as_ref() {
16890 Some(w) => (
16891 w.get_keys_pressed(minifb::KeyRepeat::Yes),
16892 w.is_key_down(minifb::Key::LeftShift)
16893 || w.is_key_down(minifb::Key::RightShift),
16894 ),
16895 None => (Vec::new(), false),
16896 }
16897 };
16898 for k in keys {
16899 if k == minifb::Key::Backspace {
16900 self.text_buffer.pop();
16901 } else if let Some(c) = key_char(k, shift) {
16902 if self.text_buffer.chars().count() < TEXT_BUFFER_MAX {
16903 self.text_buffer.push(c);
16904 }
16905 }
16906 }
16907 return Ok(Value::Str(self.text_buffer.clone()));
16908 },
16909 #[cfg(target_arch = "wasm32")]
16910 "text_poll" => {
16911 return Ok(Value::Str(self.text_buffer.clone()));
16912 },
16913 "text_get" => return Ok(Value::Str(self.text_buffer.clone())),
16914 "text_set" => {
16915 self.text_buffer = self.arg_str(&args, 0, "");
16916 return Ok(Value::Unit);
16917 },
16918 "text_clear" => {
16919 self.text_buffer.clear();
16920 return Ok(Value::Unit);
16921 },
16922 #[cfg(not(target_arch = "wasm32"))]
16924 "record_frame" => {
16925 let n = self.record_n;
16926 let (buf, w, h) = {
16927 let gfx = self.gfx.borrow();
16928 (gfx.buffer.clone(), gfx.width, gfx.height)
16929 };
16930 let _ = std::fs::create_dir_all("recordings");
16931 let mut out = Vec::with_capacity(w * h * 3 + 32);
16932 out.extend_from_slice(format!("P6\n{w} {h}\n255\n").as_bytes());
16933 for px in &buf {
16934 let p = *px;
16935 out.push((p >> 16) as u8);
16936 out.push((p >> 8) as u8);
16937 out.push(p as u8);
16938 }
16939 let _ = std::fs::write(format!("recordings/frame_{n:05}.ppm"), out);
16940 self.record_n += 1;
16941 return Ok(Value::Number(n as f64));
16942 },
16943 "record_count" => return Ok(Value::Number(self.record_n as f64)),
16944 #[cfg(not(target_arch = "wasm32"))]
16946 "screenshot" | "บันทึกภาพ" | "عکسصفحه" | "لقطة_شاشة" | "צילום_מסך" | "اسکرین_شاٹ" =>
16947 {
16948 let mode = self.arg_str(&args, 0, "game");
16949 let (buf, w, h) = {
16950 let gfx = self.gfx.borrow();
16951 (gfx.buffer.clone(), gfx.width, gfx.height)
16952 };
16953 let _ = std::fs::create_dir_all("screenshots");
16954 let ts = std::time::SystemTime::now()
16955 .duration_since(std::time::UNIX_EPOCH)
16956 .map(|d| d.as_secs())
16957 .unwrap_or(0);
16958 let safe: String = mode
16959 .chars()
16960 .map(|c| if c.is_alphanumeric() { c } else { '_' })
16961 .collect();
16962 let path = format!("screenshots/ss_{ts}_{safe}_{w}x{h}.png");
16963 let mut rgb = Vec::with_capacity(w * h * 3);
16964 for px in &buf {
16965 let p = *px;
16966 rgb.push((p >> 16) as u8);
16967 rgb.push((p >> 8) as u8);
16968 rgb.push(p as u8);
16969 }
16970 if let Some(img) = image::RgbImage::from_raw(w as u32, h as u32, rgb) {
16971 let _ = img.save(&path);
16972 }
16973 return Ok(Value::Str(path));
16974 },
16975 #[cfg(not(target_arch = "wasm32"))]
16979 "mic_capture" => {
16980 if let Some(mic) = self.mic.as_ref() {
16981 let s = mic.latest_samples();
16982 self.mic_buffer.extend_from_slice(&s);
16983 let cap = 96_000usize; if self.mic_buffer.len() > cap {
16985 let drop = self.mic_buffer.len() - cap;
16986 self.mic_buffer.drain(0..drop);
16987 }
16988 }
16989 return Ok(Value::Number(self.mic_buffer.len() as f64));
16990 },
16991 #[cfg(not(target_arch = "wasm32"))]
16993 "mic_seed" => {
16994 let mut bytes = Vec::with_capacity(self.mic_buffer.len() * 4);
16995 for f in &self.mic_buffer {
16996 bytes.extend_from_slice(&f.to_le_bytes());
16997 }
16998 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(&bytes))));
16999 },
17000 #[cfg(not(target_arch = "wasm32"))]
17001 "mic_clear" => {
17002 self.mic_buffer.clear();
17003 return Ok(Value::Number(0.0));
17004 },
17005 #[cfg(not(target_arch = "wasm32"))]
17008 "flush_3d" | "render_3d" => {
17009 let mut gfx = self.gfx.borrow_mut();
17010 if !gfx.depth_queue.is_empty() {
17011 let w = gfx.width;
17012 let h = gfx.height;
17013 let dt = gfx.depth_test;
17014 let reset_z = gfx.zbuf_needs_clear;
17015 let (bm, ba) = (gfx.blend, gfx.alpha);
17016 let aa = gfx.antialias;
17017 let queue = std::mem::take(&mut gfx.depth_queue);
17018 {
17019 let g = &mut *gfx;
17020 let z = if dt { Some(&mut g.depth_buf) } else { None };
17021 queue.flush(&mut g.buffer, z, reset_z, w, h, aa);
17022 }
17023 gfx.zbuf_needs_clear = false;
17024 gfx.depth_queue.set_state(bm, ba); }
17026 return Ok(Value::Unit);
17027 },
17028 #[cfg(target_arch = "wasm32")]
17029 "flush_3d" | "render_3d" => {
17030 let mut gfx = self.gfx.borrow_mut();
17031 if !gfx.depth_queue.is_empty() {
17032 let w = gfx.width;
17033 let h = gfx.height;
17034 let dt = gfx.depth_test;
17035 let reset_z = gfx.zbuf_needs_clear;
17036 let (bm, ba) = (gfx.blend, gfx.alpha);
17037 let aa = gfx.antialias;
17038 let queue = std::mem::take(&mut gfx.depth_queue);
17039 {
17040 let g = &mut *gfx;
17041 let z = if dt { Some(&mut g.depth_buf) } else { None };
17042 queue.flush(&mut g.buffer, z, reset_z, w, h, aa);
17043 }
17044 gfx.zbuf_needs_clear = false;
17045 gfx.depth_queue.set_state(bm, ba);
17046 }
17047 return Ok(Value::Unit);
17048 },
17049
17050 "flush_post" | "post_now" | "포스트플러시" | "后期冲刷" => {
17055 let mut gfx = self.gfx.borrow_mut();
17056 if !gfx.depth_queue.is_empty() {
17057 let w = gfx.width;
17058 let h = gfx.height;
17059 let dt = gfx.depth_test;
17060 let reset_z = gfx.zbuf_needs_clear;
17061 let (bm, ba) = (gfx.blend, gfx.alpha);
17062 let aa = gfx.antialias;
17063 let queue = std::mem::take(&mut gfx.depth_queue);
17064 {
17065 let g = &mut *gfx;
17066 let z = if dt { Some(&mut g.depth_buf) } else { None };
17067 queue.flush(&mut g.buffer, z, reset_z, w, h, aa);
17068 }
17069 gfx.zbuf_needs_clear = false;
17070 gfx.depth_queue.set_state(bm, ba);
17071 }
17072 gfx.toon_post_process();
17073 gfx.post_done = true;
17074 return Ok(Value::Unit);
17075 },
17076
17077 #[cfg(not(target_arch = "wasm32"))]
17080 "screen_distort"
17081 | "บิดจอ"
17082 | "屏幕扭曲"
17083 | "画面歪み"
17084 | "화면왜곡"
17085 | "اعوجاج_صفحه"
17086 | "شوّه_الشاشة"
17087 | "עוות_מסך"
17088 | "اسکرین_ڈسٹورٹ" => {
17089 let amount = self.arg_num(&args, 0, 8.0)? as f32;
17090 let t = self.arg_num(&args, 1, 0.0)? as f32;
17091 let step = self.arg_num(&args, 2, 1.0)?.max(1.0) as usize;
17094 let _d = std::time::Instant::now();
17095 self.gfx.borrow_mut().distort(amount, t, step);
17096 ling_phase_add(phase::DISTORT, _d.elapsed().as_nanos());
17097 return Ok(Value::Unit);
17098 },
17099
17100 "set_rim"
17101 | "设置边缘光"
17102 | "リム設定"
17103 | "림라이트"
17104 | "ตั้งขอบเรือง"
17105 | "تنظیم_نور_لبه"
17106 | "عيّن_إضاءة_الحافة"
17107 | "קבע_תאורת_קצה"
17108 | "رم_لائٹ_مقرر_کرو"
17109 | "définir_contour_lumineux"
17110 | "rimlicht_setzen"
17111 | "задать_контурный_свет" => {
17112 let s = self.arg_num(&args, 0, 0.6)? as f32;
17113 let r = self.arg_num(&args, 1, 115.)? as f32 / 255.0;
17114 let g = self.arg_num(&args, 2, 217.)? as f32 / 255.0;
17115 let b = self.arg_num(&args, 3, 255.)? as f32 / 255.0;
17116 let mut gfx = self.gfx.borrow_mut();
17117 gfx.shade.rim = s;
17118 gfx.shade.rim_color = [r, g, b];
17119 return Ok(Value::Unit);
17120 },
17121
17122 n if crate::gfx::shapes::canon(n).is_some() => {
17131 let kind = crate::gfx::shapes::canon(n).unwrap();
17132 let cx = self.arg_num(&args, 0, 0.)? as f32;
17133 let cy = self.arg_num(&args, 1, 0.)? as f32;
17134 let cz = self.arg_num(&args, 2, 0.)? as f32;
17135 let sx = self.arg_num(&args, 3, 1.)? as f32;
17136 let sy = self.arg_num(&args, 4, 1.)? as f32;
17137 let sz = self.arg_num(&args, 5, 1.)? as f32;
17138 let rx = self.arg_num(&args, 6, 0.)? as f32;
17139 let ry = self.arg_num(&args, 7, 0.)? as f32;
17140 let rz = self.arg_num(&args, 8, 0.)? as f32;
17141 let mode = self.arg_num(&args, 9, 0.)? as i32;
17142 let e0 = self.arg_num(&args, 10, 0.)? as f32;
17143 let e1 = self.arg_num(&args, 11, 0.)? as f32;
17144 let e2 = self.arg_num(&args, 12, 0.)? as f32;
17145 if let Some(mesh) = crate::gfx::shapes::build(
17146 kind,
17147 [cx, cy, cz, sx, sy, sz, rx, ry, rz],
17148 e0,
17149 e1,
17150 e2,
17151 ) {
17152 let mut gfx = self.gfx.borrow_mut();
17153 gfx.emit_mesh(&mesh, mode);
17154 }
17155 return Ok(Value::Unit);
17156 },
17157
17158 _ => {},
17159 }
17160
17161 if let Some(field_names) = self.structs.get(name).cloned() {
17163 if args.len() != field_names.len() {
17164 return Err(EvalErr::from(format!(
17165 "{name} expects {} field(s), got {}",
17166 field_names.len(),
17167 args.len()
17168 )));
17169 }
17170 let fields = field_names.into_iter().zip(args).collect();
17171 return Ok(Value::Struct { name: name.to_string(), fields });
17172 }
17173
17174 if let Some((enum_name, arity)) = self.enum_variants.get(name).cloned() {
17176 if args.len() != arity {
17177 return Err(EvalErr::from(format!(
17178 "{name} expects {arity} value(s), got {}",
17179 args.len()
17180 )));
17181 }
17182 let variant = name.rsplit("::").next().unwrap_or(name).to_string();
17183 return Ok(Value::Variant { enum_name, variant, payload: args });
17184 }
17185
17186 #[cfg(target_arch = "wasm32")]
17187 if let Some(v) = wasm_unsupported_builtin(name) {
17188 return Ok(v);
17189 }
17190
17191 Err(EvalErr::from(format!("unknown function '{name}'")))
17192 }
17193
17194 fn call_value(&mut self, v: Value, args: Vec<Value>) -> EvalResult {
17195 match v {
17196 Value::Fn(params, body, mut captured) => {
17197 for (p, a) in params.iter().zip(args) {
17198 captured.insert(p.clone(), a);
17199 }
17200 match self.framed("<closure>", |me| me.exec_block(&body, &mut captured)) {
17201 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
17202 Err(EvalErr::Return(v)) => Ok(v),
17203 Err(e) => Err(e),
17204 }
17205 },
17206 other => Err(EvalErr::from(format!("cannot call {:?}", other))),
17207 }
17208 }
17209
17210 fn call_method(&self, recv: Value, method: &str, args: Vec<Value>) -> EvalResult {
17211 match (&recv, method) {
17212 (Value::Str(s), "is_empty" | "是空") => Ok(Value::Bool(s.is_empty())),
17213 (Value::Str(s), "len" | "长" | "长度" | "長さ" | "길이" | "ความยาว") => {
17220 Ok(Value::Number(s.len() as f64))
17221 },
17222 (Value::Str(s), "to_string" | "转文") => Ok(Value::Str(s.clone())),
17223 (Value::Str(s), "contains" | "包含") => {
17224 if let Some(Value::Str(sub)) = args.first() {
17225 Ok(Value::Bool(s.contains(sub.as_str())))
17226 } else {
17227 Ok(Value::Bool(false))
17228 }
17229 },
17230 (Value::Str(s), "push_str" | "推_文") => {
17231 let mut s2 = s.clone();
17232 if let Some(Value::Str(a)) = args.first() {
17233 s2.push_str(a);
17234 }
17235 Ok(Value::Str(s2))
17236 },
17237 (Value::List(v), "len" | "长" | "长度" | "長さ" | "길이" | "ความยาว") => {
17238 Ok(Value::Number(v.len() as f64))
17239 },
17240 (Value::List(v), "push" | "推" | "添加" | "追加" | "추가" | "เพิ่ม") =>
17241 {
17242 let mut v2: Vec<Value> = (**v).clone();
17243 if let Some(a) = args.first() {
17244 v2.push(a.clone());
17245 }
17246 Ok(Value::List(Rc::new(v2)))
17247 },
17248 (Value::Struct { fields, .. }, _) if args.is_empty() => fields
17250 .iter()
17251 .find(|(k, _)| k == method)
17252 .map(|(_, v)| v.clone())
17253 .ok_or_else(|| EvalErr::from(format!("no field '{method}' on {recv}"))),
17254 (Value::Variant { variant, .. }, "tag" | "标签" | "タグ" | "태그" | "ป้าย")
17256 if args.is_empty() =>
17257 {
17258 Ok(Value::Str(variant.clone()))
17259 },
17260 (Value::Ok(inner), _) | (Value::Err(inner), _) => Ok(*inner.clone()),
17261 _ => Err(EvalErr::from(format!("no method '{method}' on {recv}"))),
17262 }
17263 }
17264
17265 fn match_pattern(&self, pat: &Pattern, val: &Value) -> Option<Env> {
17268 match (pat, val) {
17269 (Pattern::Wildcard, _) => Some(new_env()),
17270 (Pattern::Str(s), Value::Str(v)) if s == v => Some(new_env()),
17271 (Pattern::Number(n), Value::Number(v)) if (n - v).abs() < 1e-12 => Some(new_env()),
17272 (Pattern::Bool(b), Value::Bool(v)) if b == v => Some(new_env()),
17273 (Pattern::Ident(name), _) => {
17274 let mut e = new_env();
17275 e.insert(name.clone(), val.clone());
17276 Some(e)
17277 },
17278 (Pattern::Constructor(ctor, inner_pat), _) => {
17279 let (matches, inner_val) = match (ctor.as_str(), val) {
17280 ("ok" | "好", Value::Ok(v)) => (true, Some(v.as_ref().clone())),
17281 ("bad" | "坏", Value::Err(v)) => (true, Some(v.as_ref().clone())),
17282 ("ok" | "好", v) if !matches!(v, Value::Err(_)) => (true, Some(v.clone())),
17283 _ => (false, None),
17284 };
17285 if !matches {
17286 return None;
17287 }
17288 match (inner_pat, inner_val) {
17289 (Some(p), Some(v)) => self.match_pattern(p, &v),
17290 (None, _) => Some(new_env()),
17291 (Some(p), None) => self.match_pattern(p, &Value::Unit),
17292 }
17293 },
17294 (Pattern::Variant(vname, sub_pats), Value::Variant { variant, payload, .. }) => {
17296 if vname != variant || sub_pats.len() != payload.len() {
17297 return None;
17298 }
17299 let mut bindings = new_env();
17300 for (p, v) in sub_pats.iter().zip(payload.iter()) {
17301 bindings.extend(self.match_pattern(p, v)?);
17302 }
17303 Some(bindings)
17304 },
17305 (Pattern::Variant(vname, sub), Value::Ok(v)) if (vname == "ok" || vname == "好") => {
17308 match sub.as_slice() {
17309 [] => Some(new_env()),
17310 [p] => self.match_pattern(p, v),
17311 _ => None,
17312 }
17313 },
17314 (Pattern::Variant(vname, sub), Value::Err(v))
17315 if (vname == "bad" || vname == "坏" || vname == "err") =>
17316 {
17317 match sub.as_slice() {
17318 [] => Some(new_env()),
17319 [p] => self.match_pattern(p, v),
17320 _ => None,
17321 }
17322 },
17323 _ => None,
17324 }
17325 }
17326
17327 fn value_to_iter(&self, val: Value) -> Result<Vec<Value>, EvalErr> {
17330 match val {
17331 Value::List(v) => Ok(Rc::try_unwrap(v).unwrap_or_else(|rc| (*rc).clone())),
17332 Value::Str(s) => Ok(s.chars().map(|c| Value::Str(c.to_string())).collect()),
17333 Value::Number(n) => Ok((0..n as i64).map(|i| Value::Number(i as f64)).collect()),
17334 other => Err(EvalErr::from(format!("cannot iterate over {:?}", other))),
17335 }
17336 }
17337
17338 pub(crate) fn is_truthy(&self, val: &Value) -> bool {
17339 match val {
17340 Value::Bool(b) => *b,
17341 Value::Unit => false,
17342 Value::Number(n) => *n != 0.0,
17343 Value::Str(s) => !s.is_empty(),
17344 Value::List(v) => !v.is_empty(),
17345 Value::Ok(_) => true,
17346 Value::Err(_) => false,
17347 Value::Fn(_, _, _) => true,
17348 Value::Struct { .. } => true,
17349 Value::Variant { .. } => true,
17350 }
17351 }
17352
17353 fn to_number(&self, val: &Value) -> Result<f64, EvalErr> {
17354 match val {
17355 Value::Number(n) => Ok(*n),
17356 Value::Str(s) => s
17357 .parse()
17358 .map_err(|_| EvalErr::from(format!("cannot convert '{s}' to number"))),
17359 other => Err(EvalErr::from(format!("expected number, got {:?}", other))),
17360 }
17361 }
17362
17363 fn arg_num(&self, args: &[Value], n: usize, default: f64) -> Result<f64, EvalErr> {
17365 match args.get(n) {
17366 Some(v) => self.to_number(v),
17367 None => Ok(default),
17368 }
17369 }
17370
17371 fn arg_str(&self, args: &[Value], n: usize, default: &str) -> String {
17372 args.get(n)
17373 .map(|v| v.to_string())
17374 .unwrap_or_else(|| default.to_string())
17375 }
17376
17377 fn arg_list_str(&self, args: &[Value], n: usize) -> Vec<String> {
17380 match args.get(n) {
17381 Some(Value::List(v)) => v.iter().map(|x| x.to_string()).collect(),
17382 _ => Vec::new(),
17383 }
17384 }
17385
17386 #[allow(dead_code)]
17388 fn arg_list_f32(&self, args: &[Value], n: usize) -> Vec<f32> {
17389 match args.get(n) {
17390 Some(Value::List(v)) => v
17391 .iter()
17392 .filter_map(|x| match x {
17393 Value::Number(n) => Some(*n as f32),
17394 _ => None,
17395 })
17396 .collect(),
17397 _ => Vec::new(),
17398 }
17399 }
17400
17401 #[cfg(not(target_arch = "wasm32"))]
17404 fn color_at(&self, args: &[Value], i: usize, default: u32) -> u32 {
17405 match (args.get(i), args.get(i + 1), args.get(i + 2)) {
17406 (Some(a), Some(b), Some(c)) => {
17407 match (self.to_number(a), self.to_number(b), self.to_number(c)) {
17408 (Ok(r), Ok(g), Ok(bl)) => {
17409 ((r as u32 & 0xFF) << 16) | ((g as u32 & 0xFF) << 8) | (bl as u32 & 0xFF)
17410 },
17411 _ => default,
17412 }
17413 },
17414 _ => default,
17415 }
17416 }
17417
17418 #[cfg(not(target_arch = "wasm32"))]
17420 fn pitch_arg(&self, args: &[Value], i: usize, default: i32) -> i32 {
17421 match args.get(i) {
17422 Some(Value::Str(s)) => ling_music::note::parse_pitch(s).unwrap_or(default),
17423 Some(Value::Number(n)) => *n as i32,
17424 _ => default,
17425 }
17426 }
17427
17428 #[cfg(not(target_arch = "wasm32"))]
17430 fn mouse_now(&self) -> (f32, f32, bool) {
17431 let gfx = self.gfx.borrow();
17432 let (mx, my) = gfx
17433 .window
17434 .as_ref()
17435 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
17436 .unwrap_or((0.0, 0.0));
17437 let down = gfx
17438 .window
17439 .as_ref()
17440 .map(|w| w.get_mouse_down(minifb::MouseButton::Left))
17441 .unwrap_or(false);
17442 (mx, my, down)
17443 }
17444
17445 #[cfg(not(target_arch = "wasm32"))]
17449 fn draw_ui(&self, d: &ling_ui::widgets::Draw) {
17450 let mut gfx = self.gfx.borrow_mut();
17451 let (w, h, add) = (gfx.width, gfx.height, gfx.blend == 1);
17452 for (c, poly) in &d.fills {
17453 crate::gfx::raster::fill_contours_aa(
17454 &mut gfx.buffer,
17455 w,
17456 h,
17457 *c,
17458 add,
17459 std::slice::from_ref(poly),
17460 );
17461 }
17462 for (c, pl) in &d.strokes {
17463 for s in pl.windows(2) {
17464 crate::gfx::raster::draw_line_aa(
17465 &mut gfx.buffer,
17466 w,
17467 h,
17468 *c,
17469 add,
17470 s[0][0],
17471 s[0][1],
17472 s[1][0],
17473 s[1][1],
17474 );
17475 }
17476 }
17477 }
17478
17479 fn tex_rect(&self, args: &[Value]) -> Result<(usize, usize, usize, usize), EvalErr> {
17481 let tx = self.arg_num(args, 0, 0.0)? as usize;
17482 let ty = self.arg_num(args, 1, 0.0)? as usize;
17483 let tw = self.arg_num(args, 2, 256.0)? as usize;
17484 let th = self.arg_num(args, 3, 256.0)? as usize;
17485 Ok((tx, ty, tw.max(1), th.max(1)))
17486 }
17487
17488 pub(crate) fn apply_binop(&self, op: &BinOp, l: Value, r: Value) -> EvalResult {
17489 match op {
17490 BinOp::Add => match (l, r) {
17491 (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
17492 (Value::Str(a), Value::Str(b)) => Ok(Value::Str(a + &b)),
17493 (Value::Str(a), b) => Ok(Value::Str(a + &b.to_string())),
17494 (a, Value::Str(b)) => Ok(Value::Str(a.to_string() + &b)),
17495 (a, b) => Err(EvalErr::from(format!("cannot add {:?} and {:?}", a, b))),
17496 },
17497 BinOp::Sub => Ok(Value::Number(self.to_number(&l)? - self.to_number(&r)?)),
17498 BinOp::Mul => Ok(Value::Number(self.to_number(&l)? * self.to_number(&r)?)),
17499 BinOp::Div => Ok(Value::Number(self.to_number(&l)? / self.to_number(&r)?)),
17500 BinOp::Rem => Ok(Value::Number(self.to_number(&l)? % self.to_number(&r)?)),
17501 BinOp::Eq => Ok(Value::Bool(values_equal(&l, &r))),
17502 BinOp::Ne => Ok(Value::Bool(!values_equal(&l, &r))),
17503 BinOp::Lt => Ok(Value::Bool(self.to_number(&l)? < self.to_number(&r)?)),
17504 BinOp::Gt => Ok(Value::Bool(self.to_number(&l)? > self.to_number(&r)?)),
17505 BinOp::Le => Ok(Value::Bool(self.to_number(&l)? <= self.to_number(&r)?)),
17506 BinOp::Ge => Ok(Value::Bool(self.to_number(&l)? >= self.to_number(&r)?)),
17507 BinOp::And => Ok(Value::Bool(self.is_truthy(&l) && self.is_truthy(&r))),
17508 BinOp::Or => Ok(Value::Bool(self.is_truthy(&l) || self.is_truthy(&r))),
17509 }
17510 }
17511
17512 fn builtin_format(&self, args: &[Value]) -> Result<String, EvalErr> {
17513 if args.is_empty() {
17514 return Ok(String::new());
17515 }
17516 let fmt = match &args[0] {
17517 Value::Str(s) => s.clone(),
17518 other => return Ok(other.to_string()),
17519 };
17520
17521 let mut result = String::new();
17522 let mut arg_idx = 1usize;
17523 let mut chars = fmt.chars().peekable();
17524 while let Some(c) = chars.next() {
17525 if c == '{' {
17526 if chars.peek() == Some(&'}') {
17527 chars.next();
17528 if arg_idx < args.len() {
17529 result.push_str(&args[arg_idx].to_string());
17530 arg_idx += 1;
17531 }
17532 } else {
17533 let mut spec = String::new();
17534 for ch in chars.by_ref() {
17535 if ch == '}' {
17536 break;
17537 }
17538 spec.push(ch);
17539 }
17540 if arg_idx < args.len() {
17541 if let Some(suffix) = spec.strip_prefix(":.") {
17542 if let Value::Number(n) = &args[arg_idx] {
17543 let prec: usize = suffix.trim_end_matches('f').parse().unwrap_or(2);
17544 result.push_str(&format!("{:.prec$}", n));
17545 arg_idx += 1;
17546 continue;
17547 }
17548 }
17549 result.push_str(&args[arg_idx].to_string());
17550 arg_idx += 1;
17551 }
17552 }
17553 } else {
17554 result.push(c);
17555 }
17556 }
17557 Ok(result)
17558 }
17559}
17560
17561#[cfg(not(target_arch = "wasm32"))]
17562#[cfg(not(target_arch = "wasm32"))]
17564fn parse_pad_button(name: &str) -> Option<ling_input::GamepadButton> {
17565 use ling_input::GamepadButton as B;
17566 Some(match name.to_ascii_lowercase().as_str() {
17567 "a" | "south" | "cross" => B::South,
17568 "b" | "east" | "circle" => B::East,
17569 "x" | "west" | "square" => B::West,
17570 "y" | "north" | "triangle" => B::North,
17571 "lb" | "l1" | "left_shoulder" => B::LeftShoulder,
17572 "rb" | "r1" | "right_shoulder" => B::RightShoulder,
17573 "lt" | "l2" | "left_trigger" => B::LeftTrigger,
17574 "rt" | "r2" | "right_trigger" => B::RightTrigger,
17575 "start" | "menu" | "options" | "démarrer" | "начать" => B::Start,
17576 "select" | "back" | "share" | "view" => B::Select,
17577 "guide" | "home" => B::Guide,
17578 "l3" | "left_stick" => B::LeftStick,
17579 "r3" | "right_stick" => B::RightStick,
17580 "up" | "dpad_up" => B::DpadUp,
17581 "down" | "dpad_down" => B::DpadDown,
17582 "left" | "dpad_left" => B::DpadLeft,
17583 "right" | "dpad_right" => B::DpadRight,
17584 _ => return None,
17585 })
17586}
17587
17588#[cfg(not(target_arch = "wasm32"))]
17589fn str_to_minifb_key(name: &str) -> Option<minifb::Key> {
17590 use minifb::Key;
17591 Some(match name {
17592 "numpad0" | "kp0" => Key::NumPad0,
17593 "numpad1" | "kp1" => Key::NumPad1,
17594 "numpad2" | "kp2" => Key::NumPad2,
17595 "numpad3" | "kp3" => Key::NumPad3,
17596 "numpad4" | "kp4" => Key::NumPad4,
17597 "numpad5" | "kp5" => Key::NumPad5,
17598 "numpad6" | "kp6" => Key::NumPad6,
17599 "numpad7" | "kp7" => Key::NumPad7,
17600 "numpad8" | "kp8" => Key::NumPad8,
17601 "numpad9" | "kp9" => Key::NumPad9,
17602 "numpad+" | "kp+" => Key::NumPadPlus,
17603 "numpad-" | "kp-" => Key::NumPadMinus,
17604 "numpad*" | "kp*" => Key::NumPadAsterisk,
17605 "numpad/" | "kp/" => Key::NumPadSlash,
17606 "left" => Key::Left,
17607 "right" => Key::Right,
17608 "up" => Key::Up,
17609 "down" => Key::Down,
17610 "space" => Key::Space,
17611 "enter" => Key::Enter,
17612 "escape" => Key::Escape,
17613 "pageup" => Key::PageUp,
17614 "pagedown" => Key::PageDown,
17615 "lshift" | "leftshift" => Key::LeftShift,
17616 "rshift" | "rightshift" => Key::RightShift,
17617 "lctrl" | "leftctrl" => Key::LeftCtrl,
17618 "rctrl" | "rightctrl" => Key::RightCtrl,
17619 "lalt" | "leftalt" => Key::LeftAlt,
17620 "ralt" | "rightalt" => Key::RightAlt,
17621 "tab" => Key::Tab,
17622 "backspace" => Key::Backspace,
17623 "delete" => Key::Delete,
17624 "insert" => Key::Insert,
17625 "home" => Key::Home,
17626 "end" => Key::End,
17627 "a" => Key::A,
17628 "b" => Key::B,
17629 "c" => Key::C,
17630 "d" => Key::D,
17631 "e" => Key::E,
17632 "f" => Key::F,
17633 "g" => Key::G,
17634 "h" => Key::H,
17635 "i" => Key::I,
17636 "j" => Key::J,
17637 "k" => Key::K,
17638 "l" => Key::L,
17639 "m" => Key::M,
17640 "n" => Key::N,
17641 "o" => Key::O,
17642 "p" => Key::P,
17643 "q" => Key::Q,
17644 "r" => Key::R,
17645 "s" => Key::S,
17646 "t" => Key::T,
17647 "u" => Key::U,
17648 "v" => Key::V,
17649 "w" => Key::W,
17650 "x" => Key::X,
17651 "y" => Key::Y,
17652 "z" => Key::Z,
17653 "0" => Key::Key0,
17654 "1" => Key::Key1,
17655 "2" => Key::Key2,
17656 "3" => Key::Key3,
17657 "4" => Key::Key4,
17658 "5" => Key::Key5,
17659 "6" => Key::Key6,
17660 "7" => Key::Key7,
17661 "8" => Key::Key8,
17662 "9" => Key::Key9,
17663 _ => return None,
17664 })
17665}
17666
17667pub(crate) fn values_equal(a: &Value, b: &Value) -> bool {
17668 match (a, b) {
17669 (Value::Number(x), Value::Number(y)) => (x - y).abs() < 1e-12,
17670 (Value::Str(x), Value::Str(y)) => x == y,
17671 (Value::Bool(x), Value::Bool(y)) => x == y,
17672 (Value::Unit, Value::Unit) => true,
17673 _ => false,
17674 }
17675}
17676
17677#[cfg(all(not(target_arch = "wasm32"), windows))]
17686fn make_borderless_fullscreen(hwnd: isize, screen_w: i32, screen_h: i32) {
17687 if hwnd == 0 {
17688 return;
17689 }
17690 unsafe {
17691 extern "system" {
17692 fn SetWindowLongPtrW(hwnd: isize, index: i32, new: isize) -> isize;
17693 fn SetWindowPos(
17694 hwnd: isize,
17695 insert_after: isize,
17696 x: i32,
17697 y: i32,
17698 cx: i32,
17699 cy: i32,
17700 flags: u32,
17701 ) -> i32;
17702 fn ShowWindow(hwnd: isize, cmd: i32) -> i32;
17703 }
17704 const GWL_STYLE: i32 = -16;
17705 const GWL_EXSTYLE: i32 = -20;
17706 SetWindowLongPtrW(hwnd, GWL_STYLE, 0x9000_0000isize);
17709 SetWindowLongPtrW(hwnd, GWL_EXSTYLE, 0);
17711 SetWindowPos(hwnd, -1isize, 0, 0, screen_w, screen_h, 0x0020 | 0x0040);
17713 ShowWindow(hwnd, 3); }
17715}
17716
17717#[cfg(all(not(target_arch = "wasm32"), windows))]
17731fn force_window_focus(hwnd: isize) {
17732 if hwnd == 0 {
17733 return;
17734 }
17735 unsafe {
17736 extern "system" {
17737 fn GetForegroundWindow() -> isize;
17738 fn GetWindowThreadProcessId(hwnd: isize, pid: *mut u32) -> u32;
17739 fn GetCurrentThreadId() -> u32;
17740 fn AttachThreadInput(id_attach: u32, id_attach_to: u32, attach: i32) -> i32;
17741 fn SetForegroundWindow(hwnd: isize) -> i32;
17742 fn BringWindowToTop(hwnd: isize) -> i32;
17743 fn SetFocus(hwnd: isize) -> isize;
17744 fn SetActiveWindow(hwnd: isize) -> isize;
17745 }
17746 let fg = GetForegroundWindow();
17747 if fg != 0 && fg != hwnd {
17748 let mut fg_pid: u32 = 0;
17749 let fg_tid = GetWindowThreadProcessId(fg, &mut fg_pid);
17750 let my_tid = GetCurrentThreadId();
17751 if fg_tid != 0 && fg_tid != my_tid {
17752 AttachThreadInput(my_tid, fg_tid, 1);
17753 SetForegroundWindow(hwnd);
17754 BringWindowToTop(hwnd);
17755 SetFocus(hwnd);
17756 SetActiveWindow(hwnd);
17757 AttachThreadInput(my_tid, fg_tid, 0);
17758 return;
17759 }
17760 }
17761 SetForegroundWindow(hwnd);
17762 BringWindowToTop(hwnd);
17763 SetFocus(hwnd);
17764 SetActiveWindow(hwnd);
17765 }
17766}
17767
17768#[cfg(all(not(target_arch = "wasm32"), windows))]
17773fn set_window_topmost(hwnd: isize, topmost: bool) {
17774 if hwnd == 0 {
17775 return;
17776 }
17777 unsafe {
17778 extern "system" {
17779 fn SetWindowPos(
17780 hwnd: isize,
17781 insert_after: isize,
17782 x: i32,
17783 y: i32,
17784 cx: i32,
17785 cy: i32,
17786 flags: u32,
17787 ) -> i32;
17788 }
17789 let insert_after: isize = if topmost { -1 } else { -2 }; SetWindowPos(hwnd, insert_after, 0, 0, 0, 0, 0x0002 | 0x0001 | 0x0010);
17793 }
17794}
17795
17796#[cfg(not(target_arch = "wasm32"))]
17802fn apply_frame_pacing(win: &mut minifb::Window, vsync: bool) {
17803 match std::env::var("LING_FPS_CAP")
17804 .ok()
17805 .and_then(|v| v.parse::<usize>().ok())
17806 {
17807 Some(0) => win.set_target_fps(100_000),
17808 Some(cap) => win.set_target_fps(cap),
17809 None if vsync => win.set_target_fps(monitor_info().2.max(30) as usize),
17810 None => win.set_target_fps(100_000),
17811 }
17812}
17813
17814#[cfg(all(not(target_arch = "wasm32"), windows))]
17817fn monitor_info() -> (i32, i32, i32) {
17818 unsafe {
17819 extern "system" {
17820 fn GetSystemMetrics(index: i32) -> i32;
17821 fn GetDC(hwnd: isize) -> isize;
17822 fn ReleaseDC(hwnd: isize, hdc: isize) -> i32;
17823 fn GetDeviceCaps(hdc: isize, index: i32) -> i32;
17824 }
17825 let w = GetSystemMetrics(0).max(1); let h = GetSystemMetrics(1).max(1); let hdc = GetDC(0);
17828 let mut hz = if hdc != 0 { GetDeviceCaps(hdc, 116) } else { 0 }; if hdc != 0 {
17830 ReleaseDC(0, hdc);
17831 }
17832 if hz <= 1 {
17833 hz = 60; }
17835 (w, h, hz)
17836 }
17837}
17838
17839#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
17843fn monitor_info() -> (i32, i32, i32) {
17844 let (w, h) = native_screen_size();
17845 (w as i32, h as i32, linux_refresh_hz().unwrap_or(60))
17846}
17847
17848#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
17853fn linux_refresh_hz() -> Option<i32> {
17854 let out = std::process::Command::new("xrandr")
17855 .arg("--current")
17856 .output()
17857 .ok()?;
17858 if !out.status.success() {
17859 return None;
17860 }
17861 parse_xrandr_max_hz(&String::from_utf8_lossy(&out.stdout))
17862}
17863
17864#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
17867fn parse_xrandr_max_hz(text: &str) -> Option<i32> {
17868 text.split_whitespace()
17869 .filter(|tok| tok.contains('*'))
17870 .filter_map(|tok| {
17871 tok.trim_matches(|c: char| !c.is_ascii_digit() && c != '.')
17872 .parse::<f64>()
17873 .ok()
17874 })
17875 .map(|hz| hz.round() as i32)
17876 .filter(|&hz| (24..=1000).contains(&hz))
17877 .max()
17878}
17879
17880#[cfg(target_arch = "wasm32")]
17882fn monitor_info() -> (i32, i32, i32) {
17883 let (w, h) = crate::gfx::webgl::canvas_size();
17884 (w as i32, h as i32, 60)
17885}
17886
17887#[cfg(all(test, not(target_arch = "wasm32"), not(windows)))]
17888mod xrandr_tests {
17889 use super::parse_xrandr_max_hz;
17890
17891 #[test]
17892 fn picks_highest_active_output() {
17893 let text = "\
17894eDP-1 connected primary 1920x1080+0+0
17895 1920x1080 60.00*+ 59.94
17896DP-1 connected 2560x1440+1920+0
17897 2560x1440 144.00*+ 120.00 60.00
17898";
17899 assert_eq!(parse_xrandr_max_hz(text), Some(144));
17900 }
17901
17902 #[test]
17903 fn single_output() {
17904 let text = " 1920x1080 75.00*+ 60.00\n";
17905 assert_eq!(parse_xrandr_max_hz(text), Some(75));
17906 }
17907
17908 #[test]
17909 fn no_active_mode_returns_none() {
17910 let text = "eDP-1 disconnected\n";
17911 assert_eq!(parse_xrandr_max_hz(text), None);
17912 }
17913
17914 #[test]
17915 fn out_of_range_hz_filtered() {
17916 let text = " 1x1 5000.00*+\n";
17917 assert_eq!(parse_xrandr_max_hz(text), None);
17918 }
17919}
17920
17921#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
17924fn native_screen_size() -> (f64, f64) {
17925 (1920.0, 1080.0)
17928}
17929
17930struct LingProfileState {
17942 enabled: bool,
17943 every: u64,
17944 frames: u64,
17945 calls: std::collections::HashMap<String, (u64, u128)>, }
17947
17948thread_local! {
17949 static LING_PROFILE: std::cell::RefCell<LingProfileState> = std::cell::RefCell::new({
17950 let enabled = std::env::var("LING_PROFILE").map(|v| v != "0" && !v.is_empty()).unwrap_or(false);
17951 let every = std::env::var("LING_PROFILE_EVERY").ok()
17952 .and_then(|v| v.parse::<u64>().ok()).filter(|&n| n > 0).unwrap_or(240);
17953 if enabled {
17954 eprintln!("[ling-profile] ON — report every {every} frames (set LING_PROFILE_EVERY to change)");
17955 }
17956 LingProfileState { enabled, every, frames: 0, calls: std::collections::HashMap::new() }
17957 });
17958}
17959
17960#[inline]
17961fn ling_profile_enabled() -> bool {
17962 LING_PROFILE.with(|p| p.borrow().enabled)
17963}
17964
17965thread_local! {
17966 static LING_FPS: std::cell::RefCell<(bool, f64, u32, f64)> = std::cell::RefCell::new(
17967 (std::env::var("LING_FPS").map(|v| v != "0" && !v.is_empty()).unwrap_or(false), 0.0, 0, 0.0)
17968 );
17969}
17970
17971#[cfg(not(target_arch = "wasm32"))]
17972fn ling_fps_tick() {
17973 LING_FPS.with(|s| {
17974 let mut s = s.borrow_mut();
17975 if !s.0 {
17976 return;
17977 }
17978 let now = crate::runtime::now_secs();
17979 if s.1 > 0.0 {
17980 s.3 += now - s.1;
17981 s.2 += 1;
17982 if s.2 >= 120 {
17983 let avg = s.3 / s.2 as f64;
17984 eprintln!(
17985 "[fps] {:.1} fps ({:.2} ms/frame, wall, {} frames)",
17986 1.0 / avg,
17987 avg * 1000.0,
17988 s.2
17989 );
17990 s.2 = 0;
17991 s.3 = 0.0;
17992 }
17993 }
17994 s.1 = now;
17995 });
17996}
17997
17998thread_local! {
18003 static LING_PHASE: std::cell::RefCell<(bool, u64, [u128; 5])> = std::cell::RefCell::new(
18004 (std::env::var_os("LING_PHASE").is_some(), 0, [0; 5])
18005 );
18006}
18007
18008pub mod phase {
18010 pub const FLUSH: usize = 0;
18011 pub const TOON: usize = 1;
18012 pub const BLIT: usize = 2;
18013 pub const DISTORT: usize = 3;
18014 pub const SORT: usize = 4;
18015}
18016
18017#[cfg(not(target_arch = "wasm32"))]
18018#[inline]
18019pub fn ling_phase_add(idx: usize, nanos: u128) {
18020 LING_PHASE.with(|p| {
18021 let mut p = p.borrow_mut();
18022 if p.0 {
18023 p.2[idx] += nanos;
18024 }
18025 });
18026}
18027
18028#[cfg(not(target_arch = "wasm32"))]
18029fn ling_phase_frame() {
18030 LING_PHASE.with(|p| {
18031 let mut p = p.borrow_mut();
18032 if !p.0 {
18033 return;
18034 }
18035 p.1 += 1;
18036 if p.1 >= 120 {
18037 let f = p.1 as f64;
18038 let ms = |i: usize| p.2[i] as f64 / 1e6 / f;
18039 eprintln!(
18040 "[phase] sort={:.2} flush={:.2} toon={:.2} blit={:.2} distort={:.2} ms/frame",
18041 ms(phase::SORT),
18042 ms(phase::FLUSH),
18043 ms(phase::TOON),
18044 ms(phase::BLIT),
18045 ms(phase::DISTORT)
18046 );
18047 p.1 = 0;
18048 p.2 = [0; 5];
18049 }
18050 });
18051}
18052
18053fn ling_profile_record(name: &str, nanos: u128) {
18054 let is_frame = matches!(
18056 name,
18057 "present" | "แสดงผล" | "gfx_present" | "show" | "显" | "呈现" | "表示" | "표시"
18058 );
18059 LING_PROFILE.with(|p| {
18060 let mut p = p.borrow_mut();
18061 let e = p.calls.entry(name.to_string()).or_insert((0, 0));
18062 e.0 += 1;
18063 e.1 += nanos;
18064 if is_frame {
18065 p.frames += 1;
18066 if p.frames % p.every == 0 {
18067 ling_profile_print(&p);
18068 }
18069 }
18070 });
18071}
18072
18073fn ling_profile_print(p: &LingProfileState) {
18074 let mut rows: Vec<(&String, u64, u128)> =
18075 p.calls.iter().map(|(n, (c, ns))| (n, *c, *ns)).collect();
18076 use std::cmp::Reverse;
18077 rows.sort_by_key(|x| Reverse(x.2)); let total_ns: u128 = p.calls.values().map(|(_, ns)| *ns).sum();
18079 let total_calls: u64 = p.calls.values().map(|(c, _)| *c).sum();
18080 let fr = p.frames.max(1) as f64;
18081 eprintln!(
18082 "\n┌─ LING PROFILE ── frames={} ─ builtin calls by total inclusive time ─────────────",
18083 p.frames
18084 );
18085 eprintln!(
18086 "│ {:<24} {:>9} {:>9} {:>10} {:>9} {:>6}",
18087 "builtin", "calls", "calls/fr", "total_ms", "ms/frame", "%time"
18088 );
18089 eprintln!("├──────────────────────────────────────────────────────────────────────────────");
18090 for (name, count, ns) in rows.iter().take(30) {
18091 let ms = *ns as f64 / 1e6;
18092 let pct = if total_ns > 0 {
18093 *ns as f64 / total_ns as f64 * 100.0
18094 } else {
18095 0.0
18096 };
18097 eprintln!(
18098 "│ {:<24} {:>9} {:>9.1} {:>10.1} {:>9.3} {:>5.1}%",
18099 truncate_name(name),
18100 count,
18101 *count as f64 / fr,
18102 ms,
18103 ms / fr,
18104 pct
18105 );
18106 }
18107 eprintln!("├──────────────────────────────────────────────────────────────────────────────");
18108 eprintln!(
18109 "│ TOTAL {} builtin calls, {:.1} ms over {} frames → {:.0} calls/frame, {:.2} ms/frame in builtins",
18110 total_calls,
18111 total_ns as f64 / 1e6,
18112 p.frames,
18113 total_calls as f64 / fr,
18114 total_ns as f64 / 1e6 / fr
18115 );
18116 eprintln!("└──────────────────────────────────────────────────────────────────────────────");
18117}
18118
18119fn truncate_name(s: &str) -> String {
18122 let max = 24;
18123 if s.chars().count() <= max {
18124 s.to_string()
18125 } else {
18126 let mut t: String = s.chars().take(max - 1).collect();
18127 t.push('…');
18128 t
18129 }
18130}