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(not(target_arch = "wasm32"))]
156mod ssh;
157#[cfg(not(target_arch = "wasm32"))]
158mod lingtp;
159#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
160pub mod web;
161use crate::gfx::{GfxState, Light};
162use crate::parser::ast::*;
163#[cfg(target_arch = "wasm32")]
164use js_sys;
165use std::cell::RefCell;
166use std::collections::HashMap;
167use std::rc::Rc;
168use crate::gfx::raster::draw_line;
171#[cfg(not(target_arch = "wasm32"))]
172use crate::gfx::raster::fill_triangle;
173#[cfg(not(target_arch = "wasm32"))]
174use ling_audio::{AudioEngine, ToneParams, Wave};
175
176#[cfg(not(target_arch = "wasm32"))]
177use ling_audio::FftAnalyzer;
178
179#[cfg(not(target_arch = "wasm32"))]
180use ling_mic;
181
182#[derive(Debug, Clone)]
185pub enum Value {
186 Str(String),
187 Number(f64),
188 Bool(bool),
189 Unit,
190 List(Rc<Vec<Value>>),
191 Ok(Box<Value>),
192 Err(Box<Value>),
193 Fn(Vec<String>, Vec<Stmt>, Env),
194 Struct {
196 name: String,
197 fields: Vec<(String, Value)>,
198 },
199 Variant {
201 enum_name: String,
202 variant: String,
203 payload: Vec<Value>,
204 },
205}
206
207use rustc_hash::FxHashMap;
210type Env = FxHashMap<String, Value>;
211
212#[inline]
213fn new_env() -> Env {
214 FxHashMap::default()
215}
216
217impl std::fmt::Display for Value {
218 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219 match self {
220 Value::Str(s) => write!(f, "{s}"),
221 Value::Number(n) => {
222 if n.fract() == 0.0 && n.abs() < 1e15 {
223 write!(f, "{}", *n as i64)
224 } else {
225 write!(f, "{n}")
226 }
227 },
228 Value::Bool(b) => write!(f, "{b}"),
229 Value::Unit => write!(f, "()"),
230 Value::List(v) => {
231 write!(f, "[")?;
232 for (i, x) in v.iter().enumerate() {
233 if i > 0 {
234 write!(f, ", ")?;
235 }
236 write!(f, "{x}")?;
237 }
238 write!(f, "]")
239 },
240 Value::Ok(v) => write!(f, "Ok({v})"),
241 Value::Err(v) => write!(f, "Err({v})"),
242 Value::Fn(_, _, _) => write!(f, "<fn>"),
243 Value::Struct { name, fields } => {
244 write!(f, "{name} {{ ")?;
245 for (i, (k, v)) in fields.iter().enumerate() {
246 if i > 0 {
247 write!(f, ", ")?;
248 }
249 write!(f, "{k}: {v}")?;
250 }
251 write!(f, " }}")
252 },
253 Value::Variant { variant, payload, .. } => {
254 write!(f, "{variant}")?;
255 if !payload.is_empty() {
256 write!(f, "(")?;
257 for (i, v) in payload.iter().enumerate() {
258 if i > 0 {
259 write!(f, ", ")?;
260 }
261 write!(f, "{v}")?;
262 }
263 write!(f, ")")?;
264 }
265 Ok(())
266 },
267 }
268 }
269}
270
271#[cfg(target_arch = "wasm32")]
272fn wasm_unsupported_builtin(name: &str) -> Option<Value> {
273 Some(match name {
274 "audio_blip"
276 | "提示音"
277 | "ビープ音"
278 | "효과음"
279 | "เสียงบี๊บ"
280 | "ui_sound"
281 | "界面音"
282 | "UI音"
283 | "인터페이스음"
284 | "เสียงปุ่ม"
285 | "audio_stop_sfx"
286 | "停止音效"
287 | "効果音停止"
288 | "효과음정지"
289 | "หยุดเอฟเฟกต์ทั้งหมด"
290 | "بوق_کوتاه"
291 | "نغمة_قصيرة"
292 | "ביפ"
293 | "بلپ_آواز" => Value::Unit,
294
295 "music_load"
297 | "载入音乐"
298 | "音楽読込"
299 | "음악로드"
300 | "โหลดเพลง"
301 | "music_patch"
302 | "乐器音色"
303 | "音色読込"
304 | "악기패치"
305 | "แพตช์เครื่องดนตรี"
306 | "music_lrc"
307 | "载入歌词"
308 | "歌詞読込"
309 | "가사로드"
310 | "โหลดเนื้อเพลง"
311 | "music_midi_load"
312 | "载入MIDI"
313 | "MIDI読込"
314 | "미디로드"
315 | "โหลดมิดี"
316 | "بارگذاری_موسیقی"
317 | "تحميل_الموسيقى"
318 | "טעינת_מוזיקה"
319 | "موسیقی_لوڈ" => Value::Number(-1.0),
320
321 "music_duration"
322 | "音乐时长"
323 | "音楽長さ"
324 | "음악길이"
325 | "ความยาวเพลง"
326 | "music_bpm"
327 | "节拍速度"
328 | "テンポ"
329 | "템포"
330 | "จังหวะต่อนาที"
331 | "music_pos"
332 | "音乐位置"
333 | "音楽位置"
334 | "음악위치"
335 | "ตำแหน่งเพลง"
336 | "music_mic_pitch"
337 | "麦克风音高"
338 | "マイク音程"
339 | "마이크음정"
340 | "ระดับเสียงไมค์"
341 | "music_hz"
342 | "音符频率"
343 | "音符周波数"
344 | "음표주파수"
345 | "ความถี่โน้ต"
346 | "music_pitch_score"
347 | "音准评分"
348 | "音程スコア"
349 | "음정점수"
350 | "คะแนนเสียง"
351 | "music_judge"
352 | "判定"
353 | "判定する"
354 | "판정"
355 | "ตัดสินจังหวะ"
356 | "music_midi_count"
357 | "MIDI数量"
358 | "MIDI数"
359 | "미디수"
360 | "จำนวนมิดี"
361 | "مدت_موسیقی"
362 | "مدة_الموسيقى"
363 | "משך_מוזיקה"
364 | "موسیقی_دورانیہ" => Value::Number(0.0),
365
366 "music_key"
367 | "调性"
368 | "調性"
369 | "조성"
370 | "คีย์เพลง"
371 | "music_lyric"
372 | "当前歌词"
373 | "現在歌詞"
374 | "현재가사"
375 | "เนื้อเพลงปัจจุบัน"
376 | "music_note_name"
377 | "音名"
378 | "音名称"
379 | "음이름"
380 | "ชื่อโน้ต"
381 | "music_grade_name"
382 | "判定名"
383 | "判定名称"
384 | "판정이름"
385 | "ชื่อการตัดสิน"
386 | "گام_موسیقی"
387 | "مقام_الموسيقى"
388 | "סולם_מוזיקלי"
389 | "موسیقی_کلید" => Value::Str(String::new()),
390
391 "music_onsets"
392 | "音符起点"
393 | "オンセット"
394 | "온셋"
395 | "จุดเริ่มเสียง"
396 | "music_beat_grid"
397 | "节拍网格"
398 | "ビートグリッド"
399 | "비트그리드"
400 | "กริดจังหวะ"
401 | "music_midi_notes"
402 | "MIDI音符"
403 | "MIDIノート"
404 | "미디음표"
405 | "โน้ตมิดี"
406 | "music_midi_bars"
407 | "MIDI音条"
408 | "MIDIバー"
409 | "미디바"
410 | "แท่งมิดี"
411 | "music_fft"
412 | "音乐频谱"
413 | "音楽スペクトル"
414 | "음악스펙트럼"
415 | "สเปกตรัมเพลง"
416 | "آغازهای_نت"
417 | "بدايات_النغمات"
418 | "התחלות_תווים"
419 | "نوٹ_شروعات" => Value::List(Vec::new().into()),
420
421 "music_play"
422 | "播放音乐"
423 | "音楽再生"
424 | "음악재생"
425 | "เล่นเพลง"
426 | "music_pause"
427 | "暂停音乐"
428 | "音楽一時停止"
429 | "음악일시정지"
430 | "หยุดเพลงชั่วคราว"
431 | "music_stop"
432 | "停止音乐"
433 | "音楽停止"
434 | "음악정지"
435 | "หยุดเพลง"
436 | "music_seek"
437 | "定位音乐"
438 | "音楽シーク"
439 | "음악탐색"
440 | "ค้นหาเพลง"
441 | "music_volume"
442 | "音乐音量"
443 | "音楽音量"
444 | "음악음량"
445 | "ระดับเพลง"
446 | "music_note"
447 | "弹音符"
448 | "音符演奏"
449 | "음표연주"
450 | "เล่นโน้ต"
451 | "music_note_on"
452 | "音符开始"
453 | "音符オン"
454 | "음표켜기"
455 | "โน้ตเริ่ม"
456 | "music_note_off"
457 | "音符结束"
458 | "音符オフ"
459 | "음표끄기"
460 | "โน้ตจบ"
461 | "پخش_موسیقی"
462 | "شغّل_الموسيقى"
463 | "נגן_מוזיקה"
464 | "موسیقی_چلاؤ" => Value::Unit,
465
466 "liquid_new"
468 | "新建液体"
469 | "液体新規"
470 | "액체생성"
471 | "สร้างของเหลว"
472 | "مایع_جدید"
473 | "سائل_جديد"
474 | "נוזל_חדש"
475 | "نیا_مائع" => Value::Number(0.0),
476 "liquid_mix"
477 | "液体混合"
478 | "液体混合度"
479 | "액체혼합"
480 | "การผสมของเหลว"
481 | "ترکیب_مایع"
482 | "مزج_سائل"
483 | "ערבוב_נוזל"
484 | "مائع_ملاؤ" => Value::Number(0.0),
485 "liquid_set_colors"
486 | "液体颜色"
487 | "液体配色"
488 | "액체색상"
489 | "สีของเหลว"
490 | "liquid_splat"
491 | "液体注入"
492 | "液体追加"
493 | "액체분사"
494 | "หยดของเหลว"
495 | "liquid_gravity"
496 | "液体重力"
497 | "液体重力ベクトル"
498 | "액체중력"
499 | "แรงโน้มถ่วงเหลว"
500 | "liquid_step"
501 | "液体步进"
502 | "液体更新"
503 | "액체스텝"
504 | "ก้าวของเหลว"
505 | "liquid_step_all"
506 | "液体全步进"
507 | "液体全更新"
508 | "전체액체스텝"
509 | "ก้าวของเหลวทั้งหมด"
510 | "liquid_rainbow"
511 | "液体彩虹"
512 | "液体虹"
513 | "액체무지개"
514 | "ของเหลวสายรุ้ง"
515 | "liquid_draw"
516 | "绘制液体"
517 | "液体描画"
518 | "액체그리기"
519 | "วาดของเหลว"
520 | "liquid_draw_surface"
521 | "液体贴面"
522 | "液体曲面"
523 | "액체곡면"
524 | "ของเหลวบนพื้นผิว"
525 | "تنظیم_رنگ_مایع"
526 | "عيّن_ألوان_السائل"
527 | "קבע_צבעי_נוזל"
528 | "مائع_رنگ_مقرر_کرو" => Value::Unit,
529
530 "nn_new"
532 | "建神经网"
533 | "ニューラル作成"
534 | "신경망생성"
535 | "สร้างโครงข่าย"
536 | "nn_load"
537 | "载入网"
538 | "網読込"
539 | "신경망불러오기"
540 | "โหลดโครงข่าย"
541 | "شبکه_جدید"
542 | "شبكة_جديدة"
543 | "רשת_חדשה"
544 | "نئی_نیورل_نیٹ" => Value::Number(-1.0),
545 "nn_forward"
546 | "神经前向"
547 | "順伝播"
548 | "순전파"
549 | "ส่งต่อโครงข่าย"
550 | "پیشروی_شبکه"
551 | "تمرير_أمامي"
552 | "העברה_קדימה"
553 | "فارورڈ_پاس" => Value::List(Vec::new().into()),
554 "nn_train"
555 | "训练网"
556 | "ニューラル学習"
557 | "신경망학습"
558 | "ฝึกโครงข่าย"
559 | "nn_dense"
560 | "密集层"
561 | "密層追加"
562 | "밀집층"
563 | "ชั้นหนาแน่น"
564 | "آموزش_شبکه"
565 | "درّب_الشبكة"
566 | "אמן_רשת"
567 | "نیٹ_ٹریننگ" => Value::Number(0.0),
568 "nn_save"
569 | "保存网"
570 | "網保存"
571 | "신경망저장"
572 | "บันทึกโครงข่าย"
573 | "ذخیره_شبکه"
574 | "احفظ_الشبكة"
575 | "שמור_רשת"
576 | "نیٹ_محفوظ_کرو" => Value::Bool(false),
577
578 "bt_build"
580 | "建行为树"
581 | "行動木構築"
582 | "행동트리구성"
583 | "สร้างต้นไม้พฤติกรรม"
584 | "ساخت_درخت_رفتار"
585 | "ابنِ_شجرة_السلوك"
586 | "בנה_עץ_התנהגות"
587 | "بی_ٹی_تعمیر" => Value::Number(-1.0),
588 "bt_tick"
589 | "行为树滴答"
590 | "行動木更新"
591 | "행동트리틱"
592 | "เดินต้นไม้พฤติกรรม"
593 | "تیک_درخت_رفتار"
594 | "نبضة_شجرة_السلوك"
595 | "טיק_עץ_התנהגות"
596 | "بی_ٹی_ٹک" => Value::Str(String::new()),
597 "bt_status"
598 | "行为树状态"
599 | "行動木状態"
600 | "행동트리상태"
601 | "สถานะต้นไม้พฤติกรรม"
602 | "وضعیت_درخت_رفتار"
603 | "حالة_شجرة_السلوك"
604 | "סטטוס_עץ_התנהגות"
605 | "بی_ٹی_حالت" => Value::Number(0.0),
606 "bt_set"
607 | "设事实"
608 | "事実設定"
609 | "사실설정"
610 | "ตั้งข้อเท็จจริง"
611 | "تنظیم_واقعیت"
612 | "عيّن_حقيقة"
613 | "קבע_עובדה"
614 | "بی_ٹی_سیٹ" => Value::Unit,
615
616 "dialog_new"
618 | "建对话模型"
619 | "対話モデル作成"
620 | "대화모델생성"
621 | "สร้างโมเดลสนทนา"
622 | "dialog_load_model"
623 | "对话载模"
624 | "対話モデル読込"
625 | "대화모델불러오기"
626 | "โหลดโมเดลสนทนา"
627 | "dialog_train"
628 | "对话训练"
629 | "対話訓練"
630 | "대화훈련"
631 | "ฝึกสนทนา"
632 | "dialog_load"
633 | "对话载入"
634 | "対話読込"
635 | "대화불러오기"
636 | "โหลดชุดสนทนา"
637 | "مدل_گفتگوی_جدید"
638 | "نموذج_حوار_جديد"
639 | "מודל_דיאלוג_חדש"
640 | "نیا_مکالمہ_ماڈل" => Value::Number(-1.0),
641 "dialog_say"
642 | "对话生成"
643 | "対話生成"
644 | "대화생성"
645 | "พูดสนทนา"
646 | "بگو"
647 | "قل"
648 | "אמור"
649 | "کہو" => Value::Str(String::new()),
650 "dialog_save"
651 | "对话存模"
652 | "対話モデル保存"
653 | "대화모델저장"
654 | "บันทึกโมเดลสนทนา"
655 | "ذخیره_مدل_گفتگو"
656 | "احفظ_نموذج_الحوار"
657 | "שמור_מודל_דיאלוג"
658 | "مکالمہ_ماڈل_محفوظ" => Value::Bool(false),
659 "dialog_learn"
660 | "对话学习"
661 | "対話学習"
662 | "대화학습"
663 | "เรียนรู้สนทนา"
664 | "یادگیری_گفتگو"
665 | "تعلّم_الحوار"
666 | "למד_דיאלוג"
667 | "مکالمہ_سیکھو" => Value::Unit,
668
669 "net_connect"
671 | "联网"
672 | "ネット接続"
673 | "네트연결"
674 | "เชื่อมเน็ต"
675 | "net_listen"
676 | "监听"
677 | "待機"
678 | "리슨"
679 | "รอรับ"
680 | "net_send"
681 | "发送"
682 | "送信"
683 | "전송"
684 | "ส่ง"
685 | "اتصال_شبکه"
686 | "اتصل_بالشبكة"
687 | "התחבר_לרשת"
688 | "نیٹ_کنیکٹ" => Value::Number(-1.0),
689 "net_recv"
690 | "接收"
691 | "受信"
692 | "수신"
693 | "รับ"
694 | "net_status"
695 | "连接状态"
696 | "接続状態"
697 | "연결상태"
698 | "สถานะการเชื่อม"
699 | "دریافت_شبکه"
700 | "استقبل_من_الشبكة"
701 | "קבל_מרשת"
702 | "نیٹ_وصول" => Value::Str(String::new()),
703 "net_discover"
704 | "发现"
705 | "探索"
706 | "검색"
707 | "ค้นหาเครือข่าย"
708 | "کشف_شبکه"
709 | "اكتشف_الشبكة"
710 | "גלה_רשת"
711 | "نیٹ_دریافت" => Value::List(Vec::new().into()),
712 "net_close"
713 | "断开"
714 | "切断"
715 | "연결종료"
716 | "ตัดเชื่อม"
717 | "net_test"
718 | "测连接"
719 | "接続テスト"
720 | "연결테스트"
721 | "ทดสอบเน็ต"
722 | "بستن_شبکه"
723 | "أغلق_الشبكة"
724 | "סגור_רשת"
725 | "نیٹ_بند" => Value::Number(0.0),
726
727 _ => Value::Unit,
729 })
730}
731
732#[derive(Debug)]
735pub(crate) enum EvalErr {
736 Runtime(String),
737 Return(Value),
738 #[allow(dead_code)] Break,
740}
741
742impl From<String> for EvalErr {
743 fn from(s: String) -> Self {
744 EvalErr::Runtime(s)
745 }
746}
747
748type EvalResult = Result<Value, EvalErr>;
749
750#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
752fn base32_encode(data: &[u8]) -> String {
753 const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
754 let mut out = String::new();
755 let mut buffer: u32 = 0;
756 let mut bits = 0u32;
757 for &b in data {
758 buffer = (buffer << 8) | b as u32;
759 bits += 8;
760 while bits >= 5 {
761 bits -= 5;
762 out.push(ALPHABET[((buffer >> bits) & 0x1f) as usize] as char);
763 }
764 }
765 if bits > 0 {
766 out.push(ALPHABET[((buffer << (5 - bits)) & 0x1f) as usize] as char);
767 }
768 out
769}
770
771#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
773fn base32_decode(s: &str) -> Option<Vec<u8>> {
774 let mut buffer: u32 = 0;
775 let mut bits = 0u32;
776 let mut out = Vec::new();
777 for c in s.chars() {
778 if c == '=' || c.is_whitespace() {
779 continue;
780 }
781 let v = match c.to_ascii_uppercase() {
782 'A'..='Z' => c.to_ascii_uppercase() as u32 - 'A' as u32,
783 '2'..='7' => c as u32 - '2' as u32 + 26,
784 _ => return None,
785 };
786 buffer = (buffer << 5) | v;
787 bits += 5;
788 if bits >= 8 {
789 bits -= 8;
790 out.push((buffer >> bits) as u8);
791 }
792 }
793 Some(out)
794}
795
796#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
798fn totp_code(secret_b32: &str, step: u64) -> Option<String> {
799 let key = base32_decode(secret_b32)?;
800 let msg = step.to_be_bytes();
801 let mac = hmac_sha1(&key, &msg);
802 let offset = (mac[19] & 0x0f) as usize;
803 let bin = ((mac[offset] as u32 & 0x7f) << 24)
804 | ((mac[offset + 1] as u32) << 16)
805 | ((mac[offset + 2] as u32) << 8)
806 | (mac[offset + 3] as u32);
807 Some(format!("{:06}", bin % 1_000_000))
808}
809
810#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
812fn totp_check(secret_b32: &str, code: &str) -> bool {
813 if code.len() != 6 || !code.bytes().all(|b| b.is_ascii_digit()) {
814 return false;
815 }
816 let now_step = (crate::runtime::now_secs() as u64) / 30;
817 for delta in [-1i64, 0, 1] {
818 let step = (now_step as i64 + delta) as u64;
819 if let Some(expected) = totp_code(secret_b32, step) {
820 if expected.as_bytes().ct_eq_str(code.as_bytes()) {
822 return true;
823 }
824 }
825 }
826 false
827}
828
829#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
831fn hmac_sha1(key: &[u8], msg: &[u8]) -> [u8; 20] {
832 use hmac::{Mac, SimpleHmac};
833 let mut mac = SimpleHmac::<sha1::Sha1>::new_from_slice(key).expect("hmac key");
834 mac.update(msg);
835 let out = mac.finalize().into_bytes();
836 let mut arr = [0u8; 20];
837 arr.copy_from_slice(&out);
838 arr
839}
840
841#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
843trait CtEqStr {
844 fn ct_eq_str(&self, other: &[u8]) -> bool;
845}
846#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
847impl CtEqStr for [u8] {
848 fn ct_eq_str(&self, other: &[u8]) -> bool {
849 if self.len() != other.len() {
850 return false;
851 }
852 let mut diff = 0u8;
853 for (a, b) in self.iter().zip(other.iter()) {
854 diff |= a ^ b;
855 }
856 diff == 0
857 }
858}
859
860fn url_decode(s: &str) -> String {
862 let bytes = s.as_bytes();
863 let mut out = Vec::with_capacity(bytes.len());
864 let mut i = 0;
865 while i < bytes.len() {
866 match bytes[i] {
867 b'+' => {
868 out.push(b' ');
869 i += 1;
870 },
871 b'%' if i + 2 < bytes.len() => {
872 let hi = (bytes[i + 1] as char).to_digit(16);
873 let lo = (bytes[i + 2] as char).to_digit(16);
874 if let (Some(h), Some(l)) = (hi, lo) {
875 out.push((h * 16 + l) as u8);
876 i += 3;
877 } else {
878 out.push(bytes[i]);
879 i += 1;
880 }
881 },
882 b => {
883 out.push(b);
884 i += 1;
885 },
886 }
887 }
888 String::from_utf8_lossy(&out).into_owned()
889}
890
891#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
893fn values_to_sql_params(args: &[Value]) -> Vec<ling_http::rusqlite::types::Value> {
894 use ling_http::rusqlite::types::Value as Sql;
895 args.iter()
896 .map(|v| match v {
897 Value::Number(n) if n.fract() == 0.0 && n.abs() < 9e15 => Sql::Integer(*n as i64),
898 Value::Number(n) => Sql::Real(*n),
899 Value::Bool(b) => Sql::Integer(*b as i64),
900 other => Sql::Text(other.to_string()),
901 })
902 .collect()
903}
904
905struct SvgWriter {
910 path: String,
911 width: f64,
912 height: f64,
913 elements: Vec<String>,
914}
915
916impl SvgWriter {
917 fn new(path: String, width: f64, height: f64) -> Self {
918 let bg = format!("<rect width=\"{width}\" height=\"{height}\" fill=\"#0a0a0a\"/>");
919 Self { path, width, height, elements: vec![bg] }
920 }
921
922 fn save(&self) -> std::io::Result<()> {
923 let w = self.width;
924 let h = self.height;
925 let mut out = format!(
926 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
927 <svg xmlns=\"http://www.w3.org/2000/svg\" \
928 width=\"{w}\" height=\"{h}\" viewBox=\"0 0 {w} {h}\">\n"
929 );
930 for elem in &self.elements {
931 out.push_str(" ");
932 out.push_str(elem);
933 out.push('\n');
934 }
935 out.push_str("</svg>\n");
936 if let Some(parent) = std::path::Path::new(&self.path).parent() {
938 if !parent.as_os_str().is_empty() {
939 let _ = std::fs::create_dir_all(parent);
940 }
941 }
942 std::fs::write(&self.path, out.as_bytes())
943 }
944}
945
946fn hsl_to_hex(h: f64, s: f64, l: f64) -> String {
947 let s = s / 100.0;
948 let l = l / 100.0;
949 let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
950 let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
951 let m = l - c / 2.0;
952 let (r1, g1, b1) = if h < 60.0 {
953 (c, x, 0.0)
954 } else if h < 120.0 {
955 (x, c, 0.0)
956 } else if h < 180.0 {
957 (0.0, c, x)
958 } else if h < 240.0 {
959 (0.0, x, c)
960 } else if h < 300.0 {
961 (x, 0.0, c)
962 } else {
963 (c, 0.0, x)
964 };
965 let r = ((r1 + m) * 255.0).round() as u8;
966 let g = ((g1 + m) * 255.0).round() as u8;
967 let b = ((b1 + m) * 255.0).round() as u8;
968 format!("#{r:02x}{g:02x}{b:02x}")
969}
970
971fn tex_hash(x: i32, y: i32, seed: u32) -> f32 {
974 let mut h = (x as u32)
975 .wrapping_add((y as u32).wrapping_mul(2654435769))
976 .wrapping_add(seed.wrapping_mul(1234567891));
977 h ^= h >> 16;
978 h = h.wrapping_mul(0x45d9f3b);
979 h ^= h >> 16;
980 h as f32 / u32::MAX as f32
981}
982
983fn tex_vnoise(x: f32, y: f32, seed: u32) -> f32 {
984 let xi = x.floor() as i32;
985 let yi = y.floor() as i32;
986 let sm = |t: f32| t * t * (3.0 - 2.0 * t);
987 let xf = sm(x - xi as f32);
988 let yf = sm(y - yi as f32);
989 let a = tex_hash(xi, yi, seed);
990 let b = tex_hash(xi + 1, yi, seed);
991 let c = tex_hash(xi, yi + 1, seed);
992 let d = tex_hash(xi + 1, yi + 1, seed);
993 a + (b - a) * xf + (c - a) * yf + (a - b - c + d) * xf * yf
994}
995
996fn tex_fbm(x: f32, y: f32, octaves: u32, seed: u32) -> f32 {
997 let mut v = 0.0f32;
998 let mut amp = 0.5f32;
999 let mut f = 1.0f32;
1000 for i in 0..octaves {
1001 v += tex_vnoise(x * f, y * f, seed.wrapping_add(i * 7919)) * amp;
1002 amp *= 0.5;
1003 f *= 2.0;
1004 }
1005 v
1006}
1007
1008fn tex_palette(name: &str, t: f32) -> [f32; 3] {
1009 let (a, b, c, d): ([f32; 3], [f32; 3], [f32; 3], [f32; 3]) = match name {
1010 "fire" => (
1011 [0.8, 0.4, 0.1],
1012 [0.7, 0.3, 0.1],
1013 [1.0, 0.5, 0.3],
1014 [0.0, 0.5, 0.8],
1015 ),
1016 "ocean" => (
1017 [0.1, 0.4, 0.7],
1018 [0.3, 0.3, 0.4],
1019 [0.8, 1.0, 0.5],
1020 [0.3, 0.0, 0.6],
1021 ),
1022 "psychedelic" => (
1023 [0.5, 0.5, 0.5],
1024 [0.8, 0.8, 0.8],
1025 [1.0, 1.3, 0.7],
1026 [0.0, 0.15, 0.3],
1027 ),
1028 "neon" => (
1029 [0.5, 0.5, 0.5],
1030 [0.5, 0.5, 0.5],
1031 [2.0, 1.0, 0.0],
1032 [0.5, 0.2, 0.25],
1033 ),
1034 "forest" => (
1035 [0.3, 0.5, 0.2],
1036 [0.2, 0.3, 0.1],
1037 [1.0, 0.5, 0.8],
1038 [0.1, 0.3, 0.6],
1039 ),
1040 _ => (
1041 [0.5, 0.5, 0.5],
1042 [0.5, 0.5, 0.5],
1043 [1.0, 1.0, 1.0],
1044 [0.0, 0.333, 0.667],
1045 ),
1046 };
1047 [0, 1, 2]
1048 .map(|i| (a[i] + b[i] * (std::f32::consts::TAU * (c[i] * t + d[i])).cos()).clamp(0.0, 1.0))
1049}
1050
1051#[cfg(not(target_arch = "wasm32"))]
1053fn key_char(k: minifb::Key, shift: bool) -> Option<char> {
1059 use minifb::Key::*;
1060 let base = match k {
1061 A => 'a',
1062 B => 'b',
1063 C => 'c',
1064 D => 'd',
1065 E => 'e',
1066 F => 'f',
1067 G => 'g',
1068 H => 'h',
1069 I => 'i',
1070 J => 'j',
1071 K => 'k',
1072 L => 'l',
1073 M => 'm',
1074 N => 'n',
1075 O => 'o',
1076 P => 'p',
1077 Q => 'q',
1078 R => 'r',
1079 S => 's',
1080 T => 't',
1081 U => 'u',
1082 V => 'v',
1083 W => 'w',
1084 X => 'x',
1085 Y => 'y',
1086 Z => 'z',
1087 Key0 => '0',
1088 Key1 => '1',
1089 Key2 => '2',
1090 Key3 => '3',
1091 Key4 => '4',
1092 Key5 => '5',
1093 Key6 => '6',
1094 Key7 => '7',
1095 Key8 => '8',
1096 Key9 => '9',
1097 Space => ' ',
1098 Minus => '-',
1099 Equal => '=',
1100 Period => '.',
1101 Comma => ',',
1102 Slash => '/',
1103 Backslash => '\\',
1104 Semicolon => ';',
1105 Apostrophe => '\'',
1106 LeftBracket => '[',
1107 RightBracket => ']',
1108 Backquote => '`',
1109 _ => return None,
1110 };
1111 if !shift {
1112 return Some(base);
1113 }
1114 Some(match base {
1115 'a'..='z' => base.to_ascii_uppercase(),
1116 '0' => ')',
1117 '1' => '!',
1118 '2' => '@',
1119 '3' => '#',
1120 '4' => '$',
1121 '5' => '%',
1122 '6' => '^',
1123 '7' => '&',
1124 '8' => '*',
1125 '9' => '(',
1126 '-' => '_',
1127 '=' => '+',
1128 '.' => '>',
1129 ',' => '<',
1130 '/' => '?',
1131 '\\' => '|',
1132 ';' => ':',
1133 '\'' => '"',
1134 '[' => '{',
1135 ']' => '}',
1136 '`' => '~',
1137 other => other,
1138 })
1139}
1140
1141#[cfg(all(not(target_arch = "wasm32"), windows))]
1145fn vk_char(vk: i32, shift: bool) -> Option<char> {
1146 let base = match vk {
1147 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,
1162 };
1163 if !shift {
1164 return Some(base);
1165 }
1166 Some(match base {
1167 'a'..='z' => base.to_ascii_uppercase(),
1168 '0' => ')',
1169 '1' => '!',
1170 '2' => '@',
1171 '3' => '#',
1172 '4' => '$',
1173 '5' => '%',
1174 '6' => '^',
1175 '7' => '&',
1176 '8' => '*',
1177 '9' => '(',
1178 '-' => '_',
1179 '=' => '+',
1180 '.' => '>',
1181 ',' => '<',
1182 '/' => '?',
1183 '\\' => '|',
1184 ';' => ':',
1185 '\'' => '"',
1186 '[' => '{',
1187 ']' => '}',
1188 '`' => '~',
1189 other => other,
1190 })
1191}
1192
1193#[cfg(all(not(target_arch = "wasm32"), windows))]
1196const TEXT_POLL_VKS: &[i32] = &[
1197 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
1198 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
1199 0x36, 0x37, 0x38, 0x39, 0x20, 0xBD, 0xBB, 0xBE, 0xBC, 0xBF, 0xDC, 0xBA, 0xDE, 0xDB, 0xDD, 0xC0,
1200];
1201#[cfg(all(not(target_arch = "wasm32"), windows))]
1204const VK_BACK: i32 = 0x08;
1205#[cfg(all(not(target_arch = "wasm32"), windows))]
1206const VK_SHIFT: i32 = 0x10;
1207
1208#[cfg(all(not(target_arch = "wasm32"), windows))]
1215fn os_key_down(vk: i32) -> bool {
1216 unsafe {
1217 extern "system" {
1218 fn GetAsyncKeyState(vkey: i32) -> i16;
1219 }
1220 (GetAsyncKeyState(vk) as u16 & 0x8000) != 0
1221 }
1222}
1223
1224#[cfg(all(not(target_arch = "wasm32"), windows))]
1232fn window_is_foreground(hwnd: isize) -> bool {
1233 unsafe {
1234 extern "system" {
1235 fn GetForegroundWindow() -> isize;
1236 }
1237 hwnd != 0 && GetForegroundWindow() == hwnd
1238 }
1239}
1240
1241#[cfg(all(not(target_arch = "wasm32"), windows))]
1244const KEY_REPEAT_DELAY: f64 = 0.45;
1245#[cfg(all(not(target_arch = "wasm32"), windows))]
1247const KEY_REPEAT_RATE: f64 = 0.045;
1248
1249#[cfg(all(not(target_arch = "wasm32"), windows))]
1256fn key_repeat_fire(
1257 now: f64,
1258 down: bool,
1259 was_down: bool,
1260 down_since: &mut f64,
1261 last_fire: &mut f64,
1262) -> bool {
1263 if !down {
1264 return false;
1265 }
1266 if !was_down {
1267 *down_since = now;
1268 *last_fire = now;
1269 return true;
1270 }
1271 if now - *down_since >= KEY_REPEAT_DELAY && now - *last_fire >= KEY_REPEAT_RATE {
1272 *last_fire = now;
1273 return true;
1274 }
1275 false
1276}
1277
1278#[cfg(all(not(target_arch = "wasm32"), windows))]
1282fn str_to_vk(name: &str) -> Option<i32> {
1283 Some(match name {
1284 "numpad0" | "kp0" => 0x60,
1285 "numpad1" | "kp1" => 0x61,
1286 "numpad2" | "kp2" => 0x62,
1287 "numpad3" | "kp3" => 0x63,
1288 "numpad4" | "kp4" => 0x64,
1289 "numpad5" | "kp5" => 0x65,
1290 "numpad6" | "kp6" => 0x66,
1291 "numpad7" | "kp7" => 0x67,
1292 "numpad8" | "kp8" => 0x68,
1293 "numpad9" | "kp9" => 0x69,
1294 "numpad+" | "kp+" => 0x6B,
1295 "numpad-" | "kp-" => 0x6D,
1296 "numpad*" | "kp*" => 0x6A,
1297 "numpad/" | "kp/" => 0x6F,
1298 "left" => 0x25,
1299 "up" => 0x26,
1300 "right" => 0x27,
1301 "down" => 0x28,
1302 "space" => 0x20,
1303 "enter" => 0x0D,
1304 "escape" => 0x1B,
1305 "pageup" => 0x21,
1306 "pagedown" => 0x22,
1307 "lshift" | "leftshift" => 0xA0,
1308 "rshift" | "rightshift" => 0xA1,
1309 "lctrl" | "leftctrl" => 0xA2,
1310 "rctrl" | "rightctrl" => 0xA3,
1311 "lalt" | "leftalt" => 0xA4,
1312 "ralt" | "rightalt" => 0xA5,
1313 "tab" => 0x09,
1314 "backspace" => VK_BACK,
1315 "delete" => 0x2E,
1316 "insert" => 0x2D,
1317 "home" => 0x24,
1318 "end" => 0x23,
1319 "a" => 0x41,
1320 "b" => 0x42,
1321 "c" => 0x43,
1322 "d" => 0x44,
1323 "e" => 0x45,
1324 "f" => 0x46,
1325 "g" => 0x47,
1326 "h" => 0x48,
1327 "i" => 0x49,
1328 "j" => 0x4A,
1329 "k" => 0x4B,
1330 "l" => 0x4C,
1331 "m" => 0x4D,
1332 "n" => 0x4E,
1333 "o" => 0x4F,
1334 "p" => 0x50,
1335 "q" => 0x51,
1336 "r" => 0x52,
1337 "s" => 0x53,
1338 "t" => 0x54,
1339 "u" => 0x55,
1340 "v" => 0x56,
1341 "w" => 0x57,
1342 "x" => 0x58,
1343 "y" => 0x59,
1344 "z" => 0x5A,
1345 "0" => 0x30,
1346 "1" => 0x31,
1347 "2" => 0x32,
1348 "3" => 0x33,
1349 "4" => 0x34,
1350 "5" => 0x35,
1351 "6" => 0x36,
1352 "7" => 0x37,
1353 "8" => 0x38,
1354 "9" => 0x39,
1355 _ => return None,
1356 })
1357}
1358
1359fn hex_encode(bytes: &[u8]) -> String {
1361 let mut s = String::with_capacity(bytes.len() * 2);
1362 for b in bytes {
1363 s.push_str(&format!("{b:02x}"));
1364 }
1365 s
1366}
1367
1368#[cfg(not(target_arch = "wasm32"))]
1370fn decode_blob(s: &str) -> Result<Vec<u8>, String> {
1371 use base64::Engine as _;
1372 use std::io::Read as _;
1373 let comp = base64::engine::general_purpose::STANDARD
1374 .decode(s.trim())
1375 .map_err(|e| format!("base64: {e}"))?;
1376 let mut out = Vec::new();
1377 flate2::read::ZlibDecoder::new(&comp[..])
1378 .read_to_end(&mut out)
1379 .map_err(|e| format!("inflate: {e}"))?;
1380 Ok(out)
1381}
1382
1383#[cfg(not(target_arch = "wasm32"))]
1385fn hex_decode(s: &str) -> Vec<u8> {
1386 let s = s.trim();
1387 (0..s.len() / 2)
1388 .filter_map(|i| u8::from_str_radix(s.get(i * 2..i * 2 + 2)?, 16).ok())
1389 .collect()
1390}
1391
1392#[cfg(not(target_arch = "wasm32"))]
1394fn hex_to_32(s: &str) -> [u8; 32] {
1395 let v = hex_decode(s);
1396 let mut out = [0u8; 32];
1397 let n = v.len().min(32);
1398 out[..n].copy_from_slice(&v[..n]);
1399 out
1400}
1401
1402#[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1410fn build_pdf_from_images(
1411 paths: &[String],
1412 out_path: &str,
1413) -> Result<(), Box<dyn std::error::Error>> {
1414 use std::io::Write as _;
1415
1416 struct PageImg {
1417 w: u32,
1418 h: u32,
1419 compressed: Vec<u8>,
1420 }
1421
1422 let mut pages = Vec::with_capacity(paths.len());
1423 for p in paths {
1424 let img = image::open(p)?.to_rgb8();
1425 let (w, h) = (img.width(), img.height());
1426 let mut enc = flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
1427 enc.write_all(img.as_raw())?;
1428 pages.push(PageImg { w, h, compressed: enc.finish()? });
1429 }
1430
1431 let n = pages.len();
1432 let page_nums: Vec<u32> = (0..n).map(|i| 3 + (i as u32) * 3).collect();
1435 let total_objs = 2 + n * 3;
1436 let mut off = vec![0usize; total_objs + 1]; let mut buf: Vec<u8> = Vec::new();
1439 buf.extend_from_slice(b"%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
1440
1441 off[1] = buf.len();
1442 buf.extend_from_slice(b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n");
1443
1444 off[2] = buf.len();
1445 let kids = page_nums
1446 .iter()
1447 .map(|n| format!("{n} 0 R"))
1448 .collect::<Vec<_>>()
1449 .join(" ");
1450 buf.extend_from_slice(
1451 format!("2 0 obj\n<< /Type /Pages /Kids [{kids}] /Count {n} >>\nendobj\n").as_bytes(),
1452 );
1453
1454 for (i, page) in pages.iter().enumerate() {
1455 let page_num = page_nums[i];
1456 let content_num = page_num + 1;
1457 let image_num = page_num + 2;
1458 let (w, h) = (page.w, page.h);
1459
1460 off[page_num as usize] = buf.len();
1461 buf.extend_from_slice(format!(
1462 "{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"
1463 ).as_bytes());
1464
1465 let content = format!("q {w} 0 0 {h} 0 0 cm /Im0 Do Q");
1466 off[content_num as usize] = buf.len();
1467 buf.extend_from_slice(
1468 format!(
1469 "{content_num} 0 obj\n<< /Length {} >>\nstream\n{content}\nendstream\nendobj\n",
1470 content.len()
1471 )
1472 .as_bytes(),
1473 );
1474
1475 off[image_num as usize] = buf.len();
1476 buf.extend_from_slice(format!(
1477 "{image_num} 0 obj\n<< /Type /XObject /Subtype /Image /Width {w} /Height {h} /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /FlateDecode /Length {} >>\nstream\n",
1478 page.compressed.len()
1479 ).as_bytes());
1480 buf.extend_from_slice(&page.compressed);
1481 buf.extend_from_slice(b"\nendstream\nendobj\n");
1482 }
1483
1484 let xref_offset = buf.len();
1485 buf.extend_from_slice(format!("xref\n0 {}\n", total_objs + 1).as_bytes());
1486 buf.extend_from_slice(b"0000000000 65535 f \n");
1487 for entry in off.iter().skip(1) {
1488 buf.extend_from_slice(format!("{entry:010} 00000 n \n").as_bytes());
1489 }
1490 buf.extend_from_slice(
1491 format!(
1492 "trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref_offset}\n%%EOF",
1493 total_objs + 1
1494 )
1495 .as_bytes(),
1496 );
1497
1498 std::fs::write(out_path, buf)?;
1499 Ok(())
1500}
1501
1502fn tex_rgb(r: f32, g: f32, b: f32) -> u32 {
1503 ((r * 255.0) as u32) << 16 | ((g * 255.0) as u32) << 8 | (b * 255.0) as u32
1504}
1505
1506const PERM: [u8; 512] = [
1509 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
1510 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
1511 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 35, 63, 189, 114, 56, 42, 123,
1512 165, 38, 72, 93, 69, 139, 138, 78, 149, 159, 56, 89, 152, 78, 61, 140, 63, 26, 142, 76, 124,
1513 132, 72, 11, 90, 44, 82, 59, 96, 41, 148, 126, 157, 13, 49, 27, 176, 33, 47, 14, 97, 78, 71,
1514 40, 87, 183, 4, 122, 92, 7, 72, 3, 246, 17, 225, 87, 91, 106, 203, 190, 57, 74, 76, 88, 207,
1515 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146,
1516 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196,
1517 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94,
1518 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55,
1519 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180,
1520 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185,
1521 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,
1522 140, 161, 137, 13, 191, 230, 66, 104, 153, 199, 167, 147, 99, 179, 92,
1523 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
1525 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
1526 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 35, 63, 189, 114, 56, 42, 123,
1527 165, 38, 72, 93, 69, 139, 138, 78, 149, 159, 56, 89, 152, 78, 61, 140, 63, 26, 142, 76, 124,
1528 132, 72, 11, 90, 44, 82, 59, 96, 41, 148, 126, 157, 13, 49, 27, 176, 33, 47, 14, 97, 78, 71,
1529 40, 87, 183, 4, 122, 92, 7, 72, 3, 246, 17, 225, 87, 91, 106, 203, 190, 57, 74, 76, 88, 207,
1530 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146,
1531 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196,
1532 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94,
1533 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55,
1534 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174,
1535];
1536
1537fn fade(t: f32) -> f32 {
1538 t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
1539}
1540
1541fn grad(hash: u8, x: f32, y: f32, z: f32) -> f32 {
1542 let h = hash & 15;
1543 let u = if h < 8 { x } else { y };
1544 let v = if h < 8 { y } else { z };
1545 (if (h & 1) == 0 { u } else { -u }) + (if (h & 2) == 0 { v } else { -v })
1546}
1547
1548fn perlin3(x: f32, y: f32, z: f32) -> f32 {
1549 let xi = (x.floor() as i32) & 255;
1550 let yi = (y.floor() as i32) & 255;
1551 let zi = (z.floor() as i32) & 255;
1552
1553 let xf = x - x.floor();
1554 let yf = y - y.floor();
1555 let zf = z - z.floor();
1556
1557 let u = fade(xf);
1558 let v = fade(yf);
1559 let w = fade(zf);
1560
1561 let p0 = PERM[xi as usize] as usize;
1562 let p1 = PERM[((xi + 1) & 255) as usize] as usize;
1563 let pa = PERM[(p0 + yi as usize) & 255] as usize;
1564 let pb = PERM[(p0 + ((yi + 1) & 255) as usize) & 255] as usize;
1565 let pc = PERM[(p1 + yi as usize) & 255] as usize;
1566 let pd = PERM[(p1 + ((yi + 1) & 255) as usize) & 255] as usize;
1567
1568 let g000 = grad(PERM[(pa + zi as usize) & 255], xf, yf, zf);
1569 let g001 = grad(
1570 PERM[(pa + ((zi + 1) & 255) as usize) & 255],
1571 xf,
1572 yf,
1573 zf - 1.0,
1574 );
1575 let g010 = grad(PERM[(pb + zi as usize) & 255], xf, yf - 1.0, zf);
1576 let g011 = grad(
1577 PERM[(pb + ((zi + 1) & 255) as usize) & 255],
1578 xf,
1579 yf - 1.0,
1580 zf - 1.0,
1581 );
1582 let g100 = grad(PERM[(pc + zi as usize) & 255], xf - 1.0, yf, zf);
1583 let g101 = grad(
1584 PERM[(pc + ((zi + 1) & 255) as usize) & 255],
1585 xf - 1.0,
1586 yf,
1587 zf - 1.0,
1588 );
1589 let g110 = grad(PERM[(pd + zi as usize) & 255], xf - 1.0, yf - 1.0, zf);
1590 let g111 = grad(
1591 PERM[(pd + ((zi + 1) & 255) as usize) & 255],
1592 xf - 1.0,
1593 yf - 1.0,
1594 zf - 1.0,
1595 );
1596
1597 let l00 = g000 + u * (g100 - g000);
1598 let l01 = g001 + u * (g101 - g001);
1599 let l10 = g010 + u * (g110 - g010);
1600 let l11 = g011 + u * (g111 - g011);
1601
1602 let l0 = l00 + v * (l10 - l00);
1603 let l1 = l01 + v * (l11 - l01);
1604
1605 l0 + w * (l1 - l0)
1606}
1607
1608fn fast_rand_f64(state: &mut u64) -> f64 {
1609 *state = state
1610 .wrapping_mul(6364136223846793005)
1611 .wrapping_add(1442695040888963407);
1612 ((*state >> 32) as u32) as f64 / 4294967296.0
1613}
1614
1615#[inline]
1619fn put_px(buf: &mut [u32], idx: usize, color: u32, blend: u8) {
1620 if idx >= buf.len() {
1621 return;
1622 }
1623 if blend == 0 {
1624 buf[idx] = color;
1625 } else {
1626 let old = buf[idx];
1627 let r = (((old >> 16) & 255) + ((color >> 16) & 255)).min(255);
1628 let g = (((old >> 8) & 255) + ((color >> 8) & 255)).min(255);
1629 let b = ((old & 255) + (color & 255)).min(255);
1630 buf[idx] = (r << 16) | (g << 8) | b;
1631 }
1632}
1633
1634#[inline]
1636fn rgb(r: f64, g: f64, b: f64) -> u32 {
1637 let r = (r as i64).clamp(0, 255) as u32;
1638 let g = (g as i64).clamp(0, 255) as u32;
1639 let b = (b as i64).clamp(0, 255) as u32;
1640 (r << 16) | (g << 8) | b
1641}
1642
1643#[allow(clippy::too_many_arguments)]
1644fn draw_circle_outline(
1645 buf: &mut [u32],
1646 w: i32,
1647 h: i32,
1648 cx: i32,
1649 cy: i32,
1650 r: i32,
1651 color: u32,
1652 blend: u8,
1653) {
1654 let r = r.clamp(0, 20000); if r == 0 {
1656 return;
1657 }
1658 let mut x = 0;
1659 let mut y = r;
1660 let mut d = 3 - 2 * r;
1661 while x <= y {
1662 plot_circle_points(buf, w, h, cx, cy, x, y, color, blend);
1663 if d < 0 {
1664 d += 4 * x + 6;
1665 } else {
1666 d += 4 * (x - y) + 10;
1667 y -= 1;
1668 }
1669 x += 1;
1670 }
1671}
1672
1673#[allow(clippy::too_many_arguments)]
1674fn plot_circle_points(
1675 buf: &mut [u32],
1676 w: i32,
1677 h: i32,
1678 cx: i32,
1679 cy: i32,
1680 x: i32,
1681 y: i32,
1682 color: u32,
1683 blend: u8,
1684) {
1685 let points = [
1686 (cx + x, cy + y),
1687 (cx - x, cy + y),
1688 (cx + x, cy - y),
1689 (cx - x, cy - y),
1690 (cx + y, cy + x),
1691 (cx - y, cy + x),
1692 (cx + y, cy - x),
1693 (cx - y, cy - x),
1694 ];
1695 for &(px, py) in &points {
1696 if px >= 0 && px < w && py >= 0 && py < h {
1697 put_px(buf, (py * w + px) as usize, color, blend);
1698 }
1699 }
1700}
1701
1702#[allow(clippy::too_many_arguments)]
1703fn draw_circle_filled(
1704 buf: &mut [u32],
1705 w: i32,
1706 h: i32,
1707 cx: i32,
1708 cy: i32,
1709 r: i32,
1710 color: u32,
1711 blend: u8,
1712) {
1713 if r <= 0 {
1714 return;
1715 }
1716 for dy in -r..=r {
1717 let dx_max = ((r * r - dy * dy) as f64).sqrt() as i32;
1718 let py = cy + dy;
1719 if py < 0 || py >= h {
1720 continue;
1721 }
1722 for dx in -dx_max..=dx_max {
1723 let px = cx + dx;
1724 if px >= 0 && px < w {
1725 put_px(buf, (py * w + px) as usize, color, blend);
1726 }
1727 }
1728 }
1729}
1730
1731#[cfg(test)]
1732mod draw_tests {
1733 use super::*;
1734
1735 #[test]
1736 fn filled_circle_actually_writes_pixels() {
1737 let mut buf = vec![0u32; 100 * 100];
1738 draw_circle_filled(&mut buf, 100, 100, 50, 50, 10, 0xFF00FF, 0);
1739 assert_eq!(buf[50 * 100 + 50], 0xFF00FF, "centre pixel must be filled");
1740 assert_eq!(buf[0], 0, "far corner must stay clear");
1741 let n = buf.iter().filter(|&&p| p != 0).count();
1742 assert!(n > 200 && n < 500, "r=10 disc area ≈ 314, got {n}");
1743 }
1744
1745 #[test]
1746 fn circle_outline_writes_a_ring() {
1747 let mut buf = vec![0u32; 100 * 100];
1748 draw_circle_outline(&mut buf, 100, 100, 50, 50, 20, 0x00FF00, 0);
1749 assert_eq!(buf[50 * 100 + 50], 0, "outline must NOT fill the centre");
1750 assert!(
1751 buf.iter().any(|&p| p == 0x00FF00),
1752 "outline must draw a ring"
1753 );
1754 }
1755
1756 #[test]
1757 fn additive_blend_accumulates_channels() {
1758 let mut buf = vec![0x202020u32; 1];
1759 put_px(&mut buf, 0, 0x404040, 1);
1760 assert_eq!(buf[0], 0x606060);
1761 }
1762}
1763
1764#[derive(Clone, Copy)]
1770pub struct UiTheme {
1771 pub primary: u32,
1772 pub accent: u32,
1773 pub track: u32,
1774 pub warn: u32,
1775 pub text: u32,
1776 pub bg: u32,
1777}
1778
1779impl Default for UiTheme {
1780 fn default() -> Self {
1781 Self {
1782 primary: 0x00D2FF, accent: 0x28FFB4, track: 0x2C3E64, warn: 0xFF5A5A, text: 0xBEEBFF, bg: 0x0A1018, }
1789 }
1790}
1791
1792#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
1793pub struct Interpreter {
1794 globals: HashMap<String, Expr>,
1795 global_seed: Env,
1798 functions: FxHashMap<String, Rc<FnDef>>,
1799 pub(crate) structs: HashMap<String, Vec<String>>,
1801 enum_variants: HashMap<String, (String, usize)>,
1803 _modules: HashMap<String, Vec<FnDef>>,
1804 gfx: RefCell<GfxState>,
1805 svg: RefCell<Option<SvgWriter>>,
1806 pub source_dir: Option<std::path::PathBuf>,
1808 loaded_files: std::collections::HashSet<String>,
1810 #[cfg(not(target_arch = "wasm32"))]
1812 audio: Option<AudioEngine>,
1813 #[cfg(not(target_arch = "wasm32"))]
1814 fft: RefCell<FftAnalyzer>,
1815 fft_bands_cache: RefCell<Vec<f32>>,
1816 start_time_secs: f64,
1819 frame_num: u64,
1821 #[cfg(target_arch = "wasm32")]
1823 wasm_target_fps: f64,
1824 #[cfg(target_arch = "wasm32")]
1826 wasm_next_present_ms: f64,
1827 rand_state: u64,
1829 #[cfg(not(target_arch = "wasm32"))]
1831 mic: Option<ling_mic::MicInput>,
1832 #[cfg(not(target_arch = "wasm32"))]
1834 crypto_ids: Vec<ling_crypto::KnotIdentity>,
1835 #[cfg(not(target_arch = "wasm32"))]
1837 ed25519_ids: Vec<ling_crypto::Ed25519Keypair>,
1838 #[cfg(not(target_arch = "wasm32"))]
1840 mldsa_ids: Vec<ling_crypto::MlDsa65Keypair>,
1841 text_buffer: String,
1843 record_n: u32,
1845 #[cfg(not(target_arch = "wasm32"))]
1847 mic_buffer: Vec<f32>,
1848 #[cfg(not(target_arch = "wasm32"))]
1850 fonts: Vec<ling_graphics::VectorFont>,
1851 #[cfg(not(target_arch = "wasm32"))]
1859 images: Vec<image::RgbaImage>,
1860 ui_theme: UiTheme,
1862 mouse_was_down: bool,
1864 #[cfg(not(target_arch = "wasm32"))]
1866 music: Option<ling_music::MusicEngine>,
1867 #[cfg(not(target_arch = "wasm32"))]
1868 music_init: bool,
1869 tracks: Vec<ling_music::DecodedAudio>,
1871 lyrics: Vec<ling_music::Lyrics>,
1873 midis: Vec<ling_music::MidiSong>,
1875 soft_bodies: Vec<ling_physics::soft::SoftBody>,
1877 rigid_world: ling_physics::rigid::PhysicsWorld,
1879 liquids: Vec<ling_physics::liquid::LiquidGrid>,
1881 meshes: Vec<crate::gfx::shapes::ColorMesh>,
1882 gltf_models: std::cell::RefCell<Vec<ling_physics::gltf::GltfModel>>,
1884 dialog: Option<ling_game::dialog::Dialog>,
1886 dialog_colors: [u32; 4],
1888 frames: Vec<String>,
1890 error_trace: Option<Vec<String>>,
1893 #[cfg(not(target_arch = "wasm32"))]
1897 input: RefCell<Option<InputState>>,
1898 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1903 http_routes: Vec<(String, String, Value)>,
1904 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1908 db: Option<ling_http::rusqlite::Connection>,
1909 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1912 http_static_dirs: Vec<(String, String)>,
1913 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1917 async_jobs: web::AsyncJobs,
1918 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1922 oauth_jobs: web::OAuthJobs,
1923 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
1928 last_http_status: u16,
1929 #[cfg(not(target_arch = "wasm32"))]
1934 lingtp_routes: Vec<(String, Value)>,
1935 #[cfg(not(target_arch = "wasm32"))]
1939 last_lingtp_status: i32,
1940}
1941
1942#[cfg(not(target_arch = "wasm32"))]
1944struct InputState {
1945 sensorium: ling_input::Sensorium,
1946 backend: ling_input::backend::GilrsBackend,
1947}
1948
1949impl Default for Interpreter {
1950 fn default() -> Self {
1951 Self::new()
1952 }
1953}
1954
1955impl Interpreter {
1956 pub fn new() -> Self {
1957 #[cfg(not(target_arch = "wasm32"))]
1958 let audio = AudioEngine::new()
1959 .map_err(|e| eprintln!("audio init failed (no sound): {e}"))
1960 .ok();
1961 Self {
1962 globals: HashMap::new(),
1963 global_seed: new_env(),
1964 functions: FxHashMap::default(),
1965 structs: HashMap::new(),
1966 enum_variants: HashMap::new(),
1967 _modules: HashMap::new(),
1968 gfx: RefCell::new(GfxState::new()),
1969 svg: RefCell::new(None),
1970 source_dir: None,
1971 loaded_files: std::collections::HashSet::new(),
1972 #[cfg(not(target_arch = "wasm32"))]
1973 audio,
1974 #[cfg(not(target_arch = "wasm32"))]
1975 fft: RefCell::new(FftAnalyzer::new(2048, 44100)),
1976 fft_bands_cache: RefCell::new(vec![]),
1977 start_time_secs: crate::runtime::now_secs(),
1978 frame_num: 0,
1979 #[cfg(target_arch = "wasm32")]
1980 wasm_target_fps: 60.0,
1981 #[cfg(target_arch = "wasm32")]
1982 wasm_next_present_ms: 0.0,
1983 rand_state: 0x123456789ABCDEF,
1984 #[cfg(not(target_arch = "wasm32"))]
1985 mic: None,
1986 #[cfg(not(target_arch = "wasm32"))]
1987 crypto_ids: Vec::new(),
1988 #[cfg(not(target_arch = "wasm32"))]
1989 ed25519_ids: Vec::new(),
1990 #[cfg(not(target_arch = "wasm32"))]
1991 mldsa_ids: Vec::new(),
1992 text_buffer: String::new(),
1993 record_n: 0,
1994 #[cfg(not(target_arch = "wasm32"))]
1995 mic_buffer: Vec::new(),
1996 #[cfg(not(target_arch = "wasm32"))]
1997 fonts: Vec::new(),
1998 #[cfg(not(target_arch = "wasm32"))]
1999 images: Vec::new(),
2000 ui_theme: UiTheme::default(),
2001 mouse_was_down: false,
2002 #[cfg(not(target_arch = "wasm32"))]
2003 music: None,
2004 #[cfg(not(target_arch = "wasm32"))]
2005 music_init: false,
2006 tracks: Vec::new(),
2007 lyrics: Vec::new(),
2008 midis: Vec::new(),
2009 soft_bodies: Vec::new(),
2010 rigid_world: ling_physics::rigid::PhysicsWorld::new(),
2011 liquids: Vec::new(),
2012 meshes: Vec::new(),
2013 gltf_models: std::cell::RefCell::new(Vec::new()),
2014 dialog: None,
2015 dialog_colors: [0xE6F2FF, 0xFFD24A, 0x4AD2FF, 0x6CFF8C], frames: Vec::new(),
2017 error_trace: None,
2018 #[cfg(not(target_arch = "wasm32"))]
2019 input: RefCell::new(None),
2020 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
2021 http_routes: Vec::new(),
2022 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
2023 db: None,
2024 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
2025 http_static_dirs: Vec::new(),
2026 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
2027 async_jobs: web::AsyncJobs::new(),
2028 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
2029 oauth_jobs: web::OAuthJobs::new(),
2030 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
2031 last_http_status: 0,
2032 #[cfg(not(target_arch = "wasm32"))]
2033 lingtp_routes: Vec::new(),
2034 #[cfg(not(target_arch = "wasm32"))]
2035 last_lingtp_status: 0,
2036 }
2037 }
2038
2039 #[cfg(not(target_arch = "wasm32"))]
2043 fn pad_poll(&self) -> usize {
2044 let mut slot = self.input.borrow_mut();
2045 if slot.is_none() {
2046 match ling_input::backend::GilrsBackend::new() {
2047 Ok(backend) => {
2048 *slot = Some(InputState { sensorium: ling_input::Sensorium::new(4), backend });
2049 },
2050 Err(_) => return 0,
2051 }
2052 }
2053 let st = slot.as_mut().unwrap();
2054 st.sensorium.begin_frame();
2055 st.sensorium.pump(&mut st.backend);
2056 st.sensorium.update(1.0 / 60.0);
2057 st.sensorium.devices.count()
2058 }
2059
2060 #[cfg(not(target_arch = "wasm32"))]
2063 fn with_pad<T>(&self, slot: usize, default: T, f: impl FnOnce(&ling_input::Gamepad) -> T) -> T {
2064 let inp = self.input.borrow();
2065 match inp.as_ref().and_then(|s| s.sensorium.player(slot)) {
2066 Some(p) => f(p),
2067 None => default,
2068 }
2069 }
2070
2071 pub fn take_error_trace(&mut self) -> Vec<String> {
2074 self.error_trace.take().unwrap_or_default()
2075 }
2076
2077 #[cfg(target_arch = "wasm32")]
2078 fn wasm_pace_frame(&mut self) {
2079 let fps = self.wasm_target_fps.max(1.0);
2080 let frame_ms = 1000.0 / fps;
2081 let now = js_sys::Date::now();
2082 if self.wasm_next_present_ms <= 0.0 {
2083 self.wasm_next_present_ms = now + frame_ms;
2084 return;
2085 }
2086
2087 let wait_ms = (self.wasm_next_present_ms - now).floor() as i32;
2088 if wait_ms > 0 {
2089 wasm_sleep_ms(wait_ms);
2090 }
2091
2092 let after = js_sys::Date::now();
2093 if after > self.wasm_next_present_ms + frame_ms * 3.0 {
2094 self.wasm_next_present_ms = after + frame_ms;
2095 } else {
2096 self.wasm_next_present_ms += frame_ms;
2097 }
2098 }
2099
2100 fn framed<T, F>(&mut self, name: &str, body: F) -> Result<T, EvalErr>
2103 where
2104 F: FnOnce(&mut Self) -> Result<T, EvalErr>,
2105 {
2106 self.frames.push(name.to_string());
2107 let result = body(self);
2108 if matches!(result, Err(EvalErr::Runtime(_))) && self.error_trace.is_none() {
2109 self.error_trace = Some(self.frames.clone());
2110 }
2111 self.frames.pop();
2112 result
2113 }
2114
2115 #[cfg(not(target_arch = "wasm32"))]
2119 fn render_dialog(&mut self, x: f32, y: f32, w: f32, h: f32, font: i64, t: f32) {
2120 let (runs, typing) = match &self.dialog {
2121 Some(d) if !d.is_closed() => {
2122 let runs: Vec<(String, usize, bool)> = d
2123 .visible_runs()
2124 .into_iter()
2125 .map(|r| (r.text, r.role.index(), r.newline_before))
2126 .collect();
2127 (runs, d.is_typing())
2128 },
2129 _ => return,
2130 };
2131 let colors = self.dialog_colors;
2132 let b = 12.0;
2134 let corners: Vec<[f32; 2]> = vec![
2135 [x + b, y],
2136 [x + w - b, y],
2137 [x + w, y + b],
2138 [x + w, y + h - b],
2139 [x + w - b, y + h],
2140 [x + b, y + h],
2141 [x, y + h - b],
2142 [x, y + b],
2143 [x + b, y],
2144 ];
2145 {
2146 let mut gfx = self.gfx.borrow_mut();
2147 let (bw, bh) = (gfx.width, gfx.height);
2148 crate::gfx::raster::fill_contours_aa(
2149 &mut gfx.buffer,
2150 bw,
2151 bh,
2152 0x0A1018,
2153 false,
2154 std::slice::from_ref(&corners),
2155 );
2156 for seg in corners.windows(2) {
2157 crate::gfx::raster::draw_line_aa(
2158 &mut gfx.buffer,
2159 bw,
2160 bh,
2161 0x00D2FF,
2162 false,
2163 seg[0][0],
2164 seg[0][1],
2165 seg[1][0],
2166 seg[1][1],
2167 );
2168 }
2169 }
2170 let px = 22.0f32;
2172 let pad = 20.0f32;
2173 let line_h = px * 1.45;
2174 let mut cx = x + pad;
2175 let mut cy = y + pad;
2176 let use_font = font >= 0 && (font as usize) < self.fonts.len();
2177 for (text, role, nl) in &runs {
2178 if *nl {
2179 cx = x + pad;
2180 cy += line_h;
2181 }
2182 for word in text.split_inclusive(' ') {
2183 let wpx = if use_font {
2184 self.fonts[font as usize].measure(word, px)
2185 } else {
2186 ling_ui::holo::text_width(word, px * 0.6, px * 0.24)
2187 };
2188 if cx + wpx > x + w - pad && cx > x + pad + 1.0 {
2189 cx = x + pad;
2190 cy += line_h;
2191 }
2192 if cy + line_h > y + h {
2193 break;
2194 }
2195 let col = colors[(*role).min(3)];
2196 if use_font {
2197 let glyphs = self.font_layout_2d_glyphs(font as usize, cx, cy, px, word);
2198 let mut gfx = self.gfx.borrow_mut();
2199 let (bw, bh, add) = (gfx.width, gfx.height, gfx.blend == 1);
2200 for contours in &glyphs {
2201 crate::gfx::raster::fill_contours_aa(
2202 &mut gfx.buffer,
2203 bw,
2204 bh,
2205 col,
2206 add,
2207 contours,
2208 );
2209 }
2210 } else {
2211 let segs = ling_ui::holo::text_lines(word, cx, cy, px * 0.6, px, px * 0.24);
2212 let mut gfx = self.gfx.borrow_mut();
2213 let (bw, bh) = (gfx.width, gfx.height);
2214 for s in segs {
2215 draw_line(&mut gfx.buffer, bw, bh, col, s[0], s[1], s[2], s[3]);
2216 }
2217 }
2218 cx += wpx;
2219 }
2220 }
2221 if !typing && (t * 3.0).sin() > 0.0 {
2223 let ax = x + w - 26.0;
2224 let ay = y + h - 22.0;
2225 let mut gfx = self.gfx.borrow_mut();
2226 let (bw, bh) = (gfx.width, gfx.height);
2227 crate::gfx::raster::fill_contours_aa(
2228 &mut gfx.buffer,
2229 bw,
2230 bh,
2231 0x00D2FF,
2232 false,
2233 std::slice::from_ref(&vec![
2234 [ax - 7.0, ay],
2235 [ax + 7.0, ay],
2236 [ax, ay + 9.0],
2237 [ax - 7.0, ay],
2238 ]),
2239 );
2240 }
2241 }
2242
2243 #[cfg(not(target_arch = "wasm32"))]
2246 fn ensure_music(&mut self) -> bool {
2247 if self.music.is_some() {
2248 return true;
2249 }
2250 if self.music_init {
2251 return false;
2252 }
2253 self.music_init = true;
2254 match ling_music::MusicEngine::new() {
2255 Ok(m) => {
2256 self.music = Some(m);
2257 true
2258 },
2259 Err(e) => {
2260 eprintln!("music engine init failed (no music playback): {e}");
2261 false
2262 },
2263 }
2264 }
2265
2266 #[cfg(target_arch = "wasm32")]
2267 fn wasm_resolve_source_path(&self, path: &str) -> String {
2268 let p = path.trim();
2269 if p.is_empty() {
2270 return String::new();
2271 }
2272 if p.contains("://") || p.starts_with('/') || p.starts_with("./") || p.starts_with("../") {
2273 return p.to_string();
2274 }
2275 if let Some(d) = &self.source_dir {
2276 let base = d.to_string_lossy().replace('\\', "/");
2277 if !base.is_empty() {
2278 return format!(
2279 "{}/{}",
2280 base.trim_end_matches('/'),
2281 p.trim_start_matches("./")
2282 );
2283 }
2284 }
2285 p.to_string()
2286 }
2287
2288 #[cfg(target_arch = "wasm32")]
2289 fn wasm_music_builtin(&mut self, name: &str, args: &[Value]) -> Result<Option<Value>, EvalErr> {
2290 match name {
2291 "music_load"
2293 | "载入音乐"
2294 | "音楽読込"
2295 | "음악로드"
2296 | "โหลดเพลง"
2297 | "بارگذاری_موسیقی"
2298 | "تحميل_الموسيقى"
2299 | "טעינת_מוזיקה"
2300 | "موسیقی_لوڈ"
2301 | "charger_musique"
2302 | "musik_laden"
2303 | "загрузить_музыку" => {
2304 let path = self.arg_str(args, 0, "");
2305 let resolved = self.wasm_resolve_source_path(&path);
2306 match wasm_fetch_bytes(&resolved)
2307 .and_then(|bytes| ling_music::from_bytes(&bytes).map_err(|e| e.to_string()))
2308 {
2309 Ok(t) => {
2310 let id = self.tracks.len();
2311 self.tracks.push(t);
2312 return Ok(Some(Value::Number(id as f64)));
2313 },
2314 Err(e) => {
2315 eprintln!("music_load failed ({path}): {e}");
2316 return Ok(Some(Value::Number(-1.0)));
2317 },
2318 }
2319 },
2320 "music_duration"
2321 | "音乐时长"
2322 | "音楽長さ"
2323 | "음악길이"
2324 | "ความยาวเพลง"
2325 | "مدت_موسیقی"
2326 | "مدة_الموسيقى"
2327 | "משך_מוזיקה"
2328 | "موسیقی_دورانیہ"
2329 | "durée_musique"
2330 | "musik_dauer"
2331 | "длительность_музыки" => {
2332 let id = self.arg_num(args, 0, 0.0)? as i64;
2333 let d = self
2334 .tracks
2335 .get(id as usize)
2336 .map(|t| t.duration)
2337 .unwrap_or(0.0);
2338 return Ok(Some(Value::Number(d as f64)));
2339 },
2340 "music_bpm"
2341 | "节拍速度"
2342 | "テンポ"
2343 | "템포"
2344 | "จังหวะต่อนาที"
2345 | "ضربان_در_دقیقه"
2346 | "نبضات_بالدقيقة"
2347 | "פעימות_לדקה"
2348 | "بی_پی_ایم"
2349 | "bpm_musique"
2350 | "musik_bpm"
2351 | "музыка_bpm" => {
2352 let id = self.arg_num(args, 0, 0.0)? as i64;
2353 let b = self
2354 .tracks
2355 .get(id as usize)
2356 .map(|t| ling_music::analysis::bpm(&t.mono, t.rate))
2357 .unwrap_or(0.0);
2358 return Ok(Some(Value::Number(b as f64)));
2359 },
2360 "music_key"
2361 | "调性"
2362 | "調性"
2363 | "조성"
2364 | "คีย์เพลง"
2365 | "گام_موسیقی"
2366 | "مقام_الموسيقى"
2367 | "סולם_מוזיקלי"
2368 | "موسیقی_کلید"
2369 | "tonalité_musique"
2370 | "musik_tonart"
2371 | "тональность_музыки" => {
2372 let id = self.arg_num(args, 0, 0.0)? as i64;
2373 let k = self
2374 .tracks
2375 .get(id as usize)
2376 .map(|t| ling_music::analysis::key_name(&t.mono, t.rate))
2377 .unwrap_or_default();
2378 return Ok(Some(Value::Str(k)));
2379 },
2380 "music_onsets"
2381 | "音符起点"
2382 | "オンセット"
2383 | "온셋"
2384 | "จุดเริ่มเสียง"
2385 | "آغازهای_نت"
2386 | "بدايات_النغمات"
2387 | "התחלות_תווים"
2388 | "نوٹ_شروعات"
2389 | "attaques_musique"
2390 | "musik_einsätze"
2391 | "атаки_музыки" => {
2392 let id = self.arg_num(args, 0, 0.0)? as i64;
2393 let v = self
2394 .tracks
2395 .get(id as usize)
2396 .map(|t| ling_music::analysis::onsets(&t.mono, t.rate))
2397 .unwrap_or_default();
2398 return Ok(Some(Value::List(
2399 v.into_iter()
2400 .map(|x| Value::Number(x as f64))
2401 .collect::<Vec<_>>()
2402 .into(),
2403 )));
2404 },
2405 "music_beat_grid"
2406 | "节拍网格"
2407 | "ビートグリッド"
2408 | "비트그리드"
2409 | "กริดจังหวะ"
2410 | "شبکه_ضرب"
2411 | "شبكة_الإيقاع"
2412 | "רשת_פעימות"
2413 | "بیٹ_گرڈ"
2414 | "grille_temps_musique"
2415 | "musik_taktraster"
2416 | "сетка_ритма_музыки" => {
2417 let id = self.arg_num(args, 0, 0.0)? as i64;
2418 let beats = self
2419 .tracks
2420 .get(id as usize)
2421 .map(|t| {
2422 let b = ling_music::analysis::bpm(&t.mono, t.rate);
2423 ling_music::analysis::beat_grid(&t.mono, t.rate, b)
2424 })
2425 .unwrap_or_default();
2426 return Ok(Some(Value::List(
2427 beats
2428 .into_iter()
2429 .map(|x| Value::Number(x as f64))
2430 .collect::<Vec<_>>()
2431 .into(),
2432 )));
2433 },
2434 "music_lrc"
2435 | "载入歌词"
2436 | "歌詞読込"
2437 | "가사로드"
2438 | "โหลดเนื้อเพลง"
2439 | "بارگذاری_متن_ترانه"
2440 | "تحميل_كلمات_الأغنية"
2441 | "טעינת_מילות_שיר"
2442 | "گیت_متن_لوڈ"
2443 | "lrc_musique"
2444 | "musik_lrc"
2445 | "lrc_музыки" => {
2446 let path = self.arg_str(args, 0, "");
2447 let resolved = self.wasm_resolve_source_path(&path);
2448 match wasm_fetch_text(&resolved) {
2449 Ok(text) => {
2450 let id = self.lyrics.len();
2451 self.lyrics.push(ling_music::Lyrics::parse(&text));
2452 return Ok(Some(Value::Number(id as f64)));
2453 },
2454 Err(e) => {
2455 eprintln!("music_lrc failed ({path}): {e}");
2456 return Ok(Some(Value::Number(-1.0)));
2457 },
2458 }
2459 },
2460 "music_lyric"
2461 | "当前歌词"
2462 | "現在歌詞"
2463 | "현재가사"
2464 | "เนื้อเพลงปัจจุบัน"
2465 | "متن_ترانه_فعلی"
2466 | "كلمات_الأغنية_الحالية"
2467 | "מילות_שיר_נוכחיות"
2468 | "موجودہ_گیت_متن"
2469 | "paroles_musique"
2470 | "musik_liedtext"
2471 | "текст_песни" => {
2472 let id = self.arg_num(args, 0, 0.0)? as i64;
2473 let t = self.arg_num(args, 1, 0.0)? as f32;
2474 let line = self
2475 .lyrics
2476 .get(id as usize)
2477 .map(|l| l.line_at(t).to_string())
2478 .unwrap_or_default();
2479 return Ok(Some(Value::Str(line)));
2480 },
2481 "music_midi_load"
2482 | "载入MIDI"
2483 | "MIDI読込"
2484 | "미디로드"
2485 | "โหลดมิดี"
2486 | "بارگذاری_MIDI"
2487 | "تحميل_MIDI"
2488 | "טעינת_MIDI"
2489 | "MIDI_لوڈ"
2490 | "charger_midi_musique"
2491 | "musik_midi_laden"
2492 | "загрузить_midi_музыки" => {
2493 let path = self.arg_str(args, 0, "");
2494 let resolved = self.wasm_resolve_source_path(&path);
2495 match wasm_fetch_bytes(&resolved).and_then(|bytes| {
2496 ling_music::midi::from_bytes(&bytes).map_err(|e| e.to_string())
2497 }) {
2498 Ok(m) => {
2499 let id = self.midis.len();
2500 self.midis.push(m);
2501 return Ok(Some(Value::Number(id as f64)));
2502 },
2503 Err(e) => {
2504 eprintln!("music_midi_load failed ({path}): {e}");
2505 return Ok(Some(Value::Number(-1.0)));
2506 },
2507 }
2508 },
2509 "music_midi_count"
2510 | "MIDI数量"
2511 | "MIDI数"
2512 | "미디수"
2513 | "จำนวนมิดี"
2514 | "تعداد_MIDI"
2515 | "عدد_MIDI"
2516 | "מספר_MIDI"
2517 | "MIDI_تعداد"
2518 | "nombre_midi_musique"
2519 | "musik_midi_anzahl"
2520 | "число_midi_музыки" => {
2521 let id = self.arg_num(args, 0, 0.0)? as i64;
2522 let n = self
2523 .midis
2524 .get(id as usize)
2525 .map(|m| m.notes.len())
2526 .unwrap_or(0);
2527 return Ok(Some(Value::Number(n as f64)));
2528 },
2529 "music_midi_notes"
2530 | "MIDI音符"
2531 | "MIDIノート"
2532 | "미디음표"
2533 | "โน้ตมิดี"
2534 | "نتهای_MIDI"
2535 | "نغمات_MIDI"
2536 | "תווי_MIDI"
2537 | "MIDI_نوٹس"
2538 | "notes_midi_musique"
2539 | "musik_midi_noten"
2540 | "ноты_midi_музыки" => {
2541 let id = self.arg_num(args, 0, 0.0)? as i64;
2542 let mut out = Vec::new();
2543 if let Some(m) = self.midis.get(id as usize) {
2544 for n in &m.notes {
2545 out.push(Value::Number(n.time as f64));
2546 out.push(Value::Number(n.midi as f64));
2547 }
2548 }
2549 return Ok(Some(Value::List(out.into())));
2550 },
2551 "music_midi_bars"
2552 | "MIDI音条"
2553 | "MIDIバー"
2554 | "미디바"
2555 | "แท่งมิดี"
2556 | "میلههای_MIDI"
2557 | "أعمدة_MIDI"
2558 | "עמודות_MIDI"
2559 | "MIDI_بارز"
2560 | "mesures_midi_musique"
2561 | "musik_midi_takte"
2562 | "такты_midi_музыки" => {
2563 let id = self.arg_num(args, 0, 0.0)? as i64;
2564 let mut out = Vec::new();
2565 if let Some(m) = self.midis.get(id as usize) {
2566 for n in &m.notes {
2567 out.push(Value::Number(n.time as f64));
2568 out.push(Value::Number(n.midi as f64));
2569 out.push(Value::Number(n.dur as f64));
2570 }
2571 }
2572 return Ok(Some(Value::List(out.into())));
2573 },
2574 "music_judge"
2575 | "判定"
2576 | "判定する"
2577 | "판정"
2578 | "ตัดสินจังหวะ"
2579 | "داوری_ضرب"
2580 | "حكم_الإيقاع"
2581 | "שיפוט_קצב"
2582 | "بیٹ_فیصلہ"
2583 | "juger_musique"
2584 | "musik_bewerten"
2585 | "оценить_музыку" => {
2586 let delta_ms = self.arg_num(args, 0, 9999.0)? as f32;
2587 return Ok(Some(Value::Number(
2588 ling_music::Grade::judge(delta_ms).index() as f64,
2589 )));
2590 },
2591 "music_grade_name"
2592 | "判定名"
2593 | "判定名称"
2594 | "판정이름"
2595 | "ชื่อการตัดสิน"
2596 | "نام_رتبه"
2597 | "اسم_التقييم"
2598 | "שם_דירוג"
2599 | "گریڈ_نام"
2600 | "nom_grade_musique"
2601 | "musik_bewertungsname"
2602 | "имя_оценки_музыки" => {
2603 let idx = self.arg_num(args, 0, 4.0)? as i32;
2604 return Ok(Some(Value::Str(
2605 ling_music::Grade::from_index(idx).name().to_string(),
2606 )));
2607 },
2608 "music_note_name"
2609 | "音名"
2610 | "音名称"
2611 | "음이름"
2612 | "ชื่อโน้ต"
2613 | "نام_نت"
2614 | "اسم_النغمة"
2615 | "שם_תו"
2616 | "نوٹ_نام"
2617 | "nom_note_musique"
2618 | "musik_notenname"
2619 | "имя_ноты_музыки" => {
2620 let hz = self.arg_num(args, 0, 0.0)? as f32;
2621 return Ok(Some(Value::Str(ling_music::note::hz_to_name(hz))));
2622 },
2623 "music_hz"
2624 | "音符频率"
2625 | "音符周波数"
2626 | "음표주파수"
2627 | "ความถี่โน้ต"
2628 | "فرکانس_نت"
2629 | "تردد_النغمة"
2630 | "תדר_תו"
2631 | "نوٹ_ہرٹز"
2632 | "hz_musique"
2633 | "musik_hz"
2634 | "музыка_гц" => {
2635 let midi = match args.get(0) {
2636 Some(Value::Str(s)) => ling_music::note::parse_pitch(s).unwrap_or(69),
2637 Some(Value::Number(n)) => *n as i32,
2638 _ => 69,
2639 };
2640 return Ok(Some(Value::Number(
2641 ling_music::note::midi_to_hz(midi as f32) as f64,
2642 )));
2643 },
2644 "music_pitch_score"
2645 | "音准评分"
2646 | "音程スコア"
2647 | "음정점수"
2648 | "คะแนนเสียง"
2649 | "امتیاز_زیروبمی"
2650 | "درجة_طبقة_الصوت"
2651 | "ציון_גובה_צליל"
2652 | "پچ_اسکور"
2653 | "score_hauteur_musique"
2654 | "musik_tonhöhen_punktzahl"
2655 | "счёт_высоты_тона" => {
2656 let hz = self.arg_num(args, 0, 0.0)? as f32;
2657 let target = self.arg_num(args, 1, 0.0)? as f32;
2658 return Ok(Some(Value::Number(
2659 ling_music::karaoke::pitch_score(hz, target) as f64,
2660 )));
2661 },
2662
2663 "music_play"
2665 | "播放音乐"
2666 | "音楽再生"
2667 | "음악재생"
2668 | "เล่นเพลง"
2669 | "پخش_موسیقی"
2670 | "شغّل_الموسيقى"
2671 | "נגן_מוזיקה"
2672 | "موسیقی_چلاؤ"
2673 | "jouer_musique"
2674 | "musik_abspielen"
2675 | "играть_музыку" => {
2676 let id = self.arg_num(args, 0, 0.0)? as usize;
2677 if let Some(t) = self.tracks.get(id) {
2678 crate::gfx::audio_web::play_music(id, &t.stereo, t.channels, t.rate, 1.0);
2679 }
2680 return Ok(Some(Value::Unit));
2681 },
2682 "music_pause"
2683 | "暂停音乐"
2684 | "音楽一時停止"
2685 | "음악일시정지"
2686 | "หยุดเพลงชั่วคราว"
2687 | "music_stop"
2688 | "停止音乐"
2689 | "音楽停止"
2690 | "음악정지"
2691 | "หยุดเพลง"
2692 | "مکث_موسیقی"
2693 | "ألبث_الموسيقى"
2694 | "השהה_מוזיקה"
2695 | "موسیقی_روکو_مؤقت"
2696 | "pause_musique"
2697 | "musik_pausieren"
2698 | "пауза_музыки" => {
2699 let id = self.arg_num(args, 0, 0.0)? as usize;
2700 crate::gfx::audio_web::stop_music(id);
2701 return Ok(Some(Value::Unit));
2702 },
2703 "music_seek"
2704 | "定位音乐"
2705 | "音楽シーク"
2706 | "음악탐색"
2707 | "ค้นหาเพลง"
2708 | "جستجوی_موسیقی"
2709 | "ابحث_في_الموسيقى"
2710 | "חפש_במוזיקה"
2711 | "موسیقی_تلاش"
2712 | "chercher_musique"
2713 | "musik_suchen"
2714 | "перемотать_музыку" => {
2715 return Ok(Some(Value::Unit));
2717 },
2718 "music_pos"
2719 | "音乐位置"
2720 | "音楽位置"
2721 | "음악위치"
2722 | "ตำแหน่งเพลง"
2723 | "موقعیت_موسیقی"
2724 | "موضع_الموسيقى"
2725 | "מיקום_מוזיקה"
2726 | "موسیقی_مقام"
2727 | "position_musique"
2728 | "musik_position"
2729 | "позиция_музыки" => {
2730 return Ok(Some(Value::Number(
2731 crate::gfx::audio_web::current_music_position(),
2732 )));
2733 },
2734 "music_volume"
2735 | "音乐音量"
2736 | "音楽音量"
2737 | "음악음량"
2738 | "ระดับเพลง"
2739 | "بلندی_موسیقی"
2740 | "مستوى_الموسيقى"
2741 | "עוצמת_מוזיקה"
2742 | "موسیقی_شدت"
2743 | "volume_musique"
2744 | "musik_lautstärke"
2745 | "громкость_музыки" => {
2746 let vol = self.arg_num(args, 0, 0.8)? as f32;
2747 crate::gfx::audio_web::set_music_volume(0, vol);
2749 return Ok(Some(Value::Unit));
2750 },
2751
2752 "music_fft"
2754 | "音乐频谱"
2755 | "音楽スペクトル"
2756 | "음악스펙트럼"
2757 | "สเปกตรัมเพลง"
2758 | "طیف_موسیقی"
2759 | "طيف_الموسيقى"
2760 | "ספקטרום_מוזיקה"
2761 | "میوزک_اسپیکٹرم"
2762 | "fft_musique"
2763 | "musik_fft"
2764 | "fft_музыки" => {
2765 let id = self.arg_num(args, 0, 0.0)? as usize;
2766 let nbands = self.arg_num(args, 1, 16.0)? as usize;
2767 let pos = crate::gfx::audio_web::current_music_position() as f32;
2768 let bands = if let Some(t) = self.tracks.get(id) {
2769 ling_music::analysis::fft_bands_at_pos(&t.mono, t.rate, pos, nbands)
2770 } else {
2771 vec![0.0f32; nbands]
2772 };
2773 return Ok(Some(Value::List(
2774 bands
2775 .into_iter()
2776 .map(|x| Value::Number(x as f64))
2777 .collect::<Vec<_>>()
2778 .into(),
2779 )));
2780 },
2781
2782 _ => {},
2783 }
2784 Ok(None)
2785 }
2786
2787 #[cfg(not(target_arch = "wasm32"))]
2791 fn font_layout_2d(
2792 &mut self,
2793 id: usize,
2794 x: f32,
2795 y: f32,
2796 px: f32,
2797 text: &str,
2798 ) -> Vec<Vec<[f32; 2]>> {
2799 let mut out = Vec::new();
2800 for g in self.font_layout_2d_glyphs(id, x, y, px, text) {
2801 out.extend(g);
2802 }
2803 out
2804 }
2805
2806 #[cfg(not(target_arch = "wasm32"))]
2809 fn font_layout_2d_glyphs(
2810 &mut self,
2811 id: usize,
2812 x: f32,
2813 y: f32,
2814 px: f32,
2815 text: &str,
2816 ) -> Vec<Vec<Vec<[f32; 2]>>> {
2817 let font = &mut self.fonts[id];
2818 let asc = font.ascent();
2819 let tol = 0.3 / px;
2820 let mut pen = 0.0f32;
2821 let mut glyphs = Vec::new();
2822 for ch in text.chars() {
2823 let go = font.glyph_outline(ch, tol);
2824 let mut contours = Vec::with_capacity(go.polylines.len());
2825 for pl in &go.polylines {
2826 let mapped: Vec<[f32; 2]> = pl
2827 .iter()
2828 .map(|p| [x + (pen + p[0]) * px, y + (asc - p[1]) * px])
2829 .collect();
2830 contours.push(mapped);
2831 }
2832 glyphs.push(contours);
2833 pen += go.advance;
2834 }
2835 glyphs
2836 }
2837
2838 pub fn register_program(&mut self, program: &Program) -> Result<(), String> {
2843 for item in &program.items {
2844 self.register_item("", item)?;
2845 }
2846 let mut env = new_env();
2847 let non_do: Vec<_> = self
2848 .globals
2849 .iter()
2850 .filter(|(_, e)| !matches!(e, Expr::Do(_)))
2851 .map(|(k, e)| (k.clone(), e.clone()))
2852 .collect();
2853 let mut pending: Vec<(String, Expr)> = Vec::new();
2854 for (k, expr) in &non_do {
2855 let mut tmp = new_env();
2856 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
2857 env.insert(k.clone(), v);
2858 } else {
2859 pending.push((k.clone(), expr.clone()));
2860 }
2861 }
2862 for (k, expr) in &pending {
2863 let mut tmp = env.clone();
2864 if let Ok(v) = self.eval_expr(expr, &mut tmp) {
2865 env.insert(k.clone(), v);
2866 }
2867 }
2868 self.global_seed = env;
2869 Ok(())
2870 }
2871
2872 pub fn run_program(&mut self, program: &Program) -> Result<(), String> {
2873 self.register_program(program)?;
2874 let entry = self
2875 .find_entry()
2876 .ok_or("no entry point — need `bind start = do {...}` or `ผูก เริ่ม = ทำ {...}`")?;
2877 let mut env = self.global_seed.clone();
2878 self.framed("start", |me| me.eval_expr(&entry, &mut env))
2879 .map(|_| ())
2880 .map_err(|e| match e {
2881 EvalErr::Runtime(s) => s,
2882 EvalErr::Return(_) => "unexpected top-level return".to_string(),
2883 EvalErr::Break => "unexpected break at top level".to_string(),
2884 })
2885 }
2886
2887 fn register_item(&mut self, ns: &str, item: &Item) -> Result<(), String> {
2888 match item {
2889 Item::Bind(name, expr) => {
2890 let key = if ns.is_empty() {
2891 name.clone()
2892 } else {
2893 format!("{ns}::{name}")
2894 };
2895 self.globals.insert(key, expr.clone());
2896 },
2897 Item::Fn(def) => {
2898 let key = if ns.is_empty() {
2899 def.name.clone()
2900 } else {
2901 format!("{ns}::{}", def.name)
2902 };
2903 self.functions.insert(key, Rc::new(def.clone()));
2904 },
2905 Item::Mod(name, body) => {
2906 let child_ns = if ns.is_empty() {
2907 name.clone()
2908 } else {
2909 format!("{ns}::{name}")
2910 };
2911 for child in body {
2912 self.register_item(&child_ns, child)?;
2913 }
2914 },
2915 Item::TypeAlias(_, _) => {},
2916 Item::Struct(name, fields) => {
2917 self.structs.insert(name.clone(), fields.clone());
2918 if !ns.is_empty() {
2919 self.structs.insert(format!("{ns}::{name}"), fields.clone());
2920 }
2921 },
2922 Item::Enum(name, variants) => {
2923 for v in variants {
2924 self.enum_variants
2925 .insert(v.name.clone(), (name.clone(), v.arity));
2926 self.enum_variants
2927 .insert(format!("{name}::{}", v.name), (name.clone(), v.arity));
2928 if !ns.is_empty() {
2929 self.enum_variants
2930 .insert(format!("{ns}::{name}::{}", v.name), (name.clone(), v.arity));
2931 }
2932 }
2933 },
2934 Item::Use { path, alias } => {
2935 self.load_module(path, alias.as_deref(), ns)?;
2936 },
2937 }
2938 Ok(())
2939 }
2940
2941 fn load_module(
2946 &mut self,
2947 path: &str,
2948 alias: Option<&str>,
2949 parent_ns: &str,
2950 ) -> Result<(), String> {
2951 #[cfg(target_arch = "wasm32")]
2953 let (source, sub_dir) = {
2954 if self.loaded_files.contains(path) {
2956 return Ok(());
2957 }
2958 self.loaded_files.insert(path.to_string());
2959
2960 let src = crate::runtime::get_wasm_module(path)
2961 .or_else(|| crate::runtime::get_wasm_module(&format!("{}.ling", path)))
2962 .ok_or_else(|| format!("use: cannot find module '{path}'"))?;
2963 (src, None::<std::path::PathBuf>)
2964 };
2965
2966 #[cfg(not(target_arch = "wasm32"))]
2968 let (source, sub_dir) = {
2969 let base_dir = self
2970 .source_dir
2971 .clone()
2972 .unwrap_or_else(|| std::path::PathBuf::from("."));
2973 let raw = std::path::Path::new(path);
2974 let candidates: Vec<std::path::PathBuf> = vec![
2975 base_dir.join(format!("{}.ling", path)),
2976 base_dir.join(format!("{}.灵", path)),
2977 base_dir.join(format!("{}.령", path)),
2978 base_dir.join(format!("{}.霊", path)),
2979 base_dir.join(format!("{}.ลิง", path)),
2980 base_dir.join(raw),
2981 std::path::PathBuf::from(format!("{}.ling", path)),
2982 std::path::PathBuf::from(path),
2983 ];
2984
2985 let resolved = candidates
2986 .into_iter()
2987 .find(|p| p.exists())
2988 .ok_or_else(|| format!("use: cannot find module '{path}'"))?;
2989
2990 let canonical = resolved
2991 .canonicalize()
2992 .unwrap_or_else(|_| resolved.clone())
2993 .to_string_lossy()
2994 .to_string();
2995
2996 if self.loaded_files.contains(&canonical) {
2998 return Ok(());
2999 }
3000 self.loaded_files.insert(canonical.clone());
3001
3002 let src = std::fs::read_to_string(&resolved)
3003 .map_err(|e| format!("use: failed to read '{path}': {e}"))?;
3004 let dir = resolved.parent().map(|p| p.to_path_buf());
3005 (src, dir)
3006 };
3007
3008 let program = crate::parser::parse(&source)
3009 .map_err(|e| format!("use: parse error in '{path}': {e}"))?;
3010
3011 let target_ns = match (parent_ns.is_empty(), alias) {
3013 (_, Some(a)) if !parent_ns.is_empty() => format!("{parent_ns}::{a}"),
3014 (_, Some(a)) => a.to_string(),
3015 (false, None) => parent_ns.to_string(),
3016 (true, None) => String::new(),
3017 };
3018
3019 let prev_dir = self.source_dir.clone();
3021 self.source_dir = sub_dir;
3022
3023 for item in &program.items {
3024 self.register_item(&target_ns, item)?;
3025 }
3026
3027 self.source_dir = prev_dir;
3028 Ok(())
3029 }
3030
3031 fn find_entry(&self) -> Option<Expr> {
3032 for key in crate::entry::ENTRY_NAMES {
3034 if let Some(e) = self.globals.get(*key) {
3035 return Some(e.clone());
3036 }
3037 }
3038 self.globals
3039 .values()
3040 .find(|e| matches!(e, Expr::Do(_)))
3041 .cloned()
3042 }
3043
3044 fn eval_expr(&mut self, expr: &Expr, env: &mut Env) -> EvalResult {
3047 match expr {
3048 Expr::Str(s) => Ok(Value::Str(s.clone())),
3049 Expr::Number(n) => Ok(Value::Number(*n)),
3050 Expr::Bool(b) => Ok(Value::Bool(*b)),
3051 Expr::Unit => Ok(Value::Unit),
3052 Expr::Array(elems) => {
3053 let vs: Vec<_> = elems
3054 .iter()
3055 .map(|e| self.eval_expr(e, env))
3056 .collect::<Result<_, _>>()?;
3057 Ok(Value::List(Rc::new(vs)))
3058 },
3059
3060 Expr::Ident(name) => self.lookup(name, env),
3061
3062 Expr::Path(segs) => {
3063 if segs.len() == 1 {
3064 return self.lookup(&segs[0], env);
3065 }
3066 Ok(Value::Str(segs.join("::")))
3067 },
3068
3069 Expr::Ref(inner) => self.eval_expr(inner, env),
3070 Expr::Await(inner) => self.eval_expr(inner, env),
3071
3072 Expr::Do(stmts) => {
3073 let mut local = env.clone();
3074 Ok(self.exec_block(stmts, &mut local)?.unwrap_or(Value::Unit))
3075 },
3076
3077 Expr::BinOp(op, lhs, rhs) => {
3078 let l = self.eval_expr(lhs, env)?;
3079 let r = self.eval_expr(rhs, env)?;
3080 self.apply_binop(op, l, r)
3081 },
3082
3083 Expr::If { cond, then, elseifs, else_body } => {
3084 let cond_val = self.eval_expr(cond, env)?;
3085 if self.is_truthy(&cond_val) {
3086 return Ok(self.exec_block(then, env)?.unwrap_or(Value::Unit));
3087 }
3088 for (ei_cond, ei_body) in elseifs {
3089 let ei_cond_val = self.eval_expr(ei_cond, env)?;
3090 if self.is_truthy(&ei_cond_val) {
3091 return Ok(self.exec_block(ei_body, env)?.unwrap_or(Value::Unit));
3092 }
3093 }
3094 if let Some(eb) = else_body {
3095 return Ok(self.exec_block(eb, env)?.unwrap_or(Value::Unit));
3096 }
3097 Ok(Value::Unit)
3098 },
3099
3100 Expr::While { cond, body } => {
3101 loop {
3105 let cv = self.eval_expr(cond, env)?;
3106 if !self.is_truthy(&cv) {
3107 break;
3108 }
3109 match self.exec_block(body, env) {
3110 Ok(_) => {},
3111 Err(EvalErr::Break) => break,
3112 Err(e) => return Err(e),
3113 }
3114 }
3115 Ok(Value::Unit)
3116 },
3117
3118 Expr::For { var, iter, body } => {
3119 let iter_val = self.eval_expr(iter, env)?;
3120 let items = self.value_to_iter(iter_val)?;
3121 for item in items {
3122 let mut local = env.clone();
3123 local.insert(var.clone(), item);
3124 match self.exec_block(body, &mut local) {
3125 Ok(_) => {},
3126 Err(EvalErr::Break) => break,
3127 Err(e) => return Err(e),
3128 }
3129 }
3130 Ok(Value::Unit)
3131 },
3132
3133 Expr::Match(subject, arms) => {
3134 let subj = self.eval_expr(subject, env)?;
3135 for arm in arms {
3136 if let Some(bindings) = self.match_pattern(&arm.pattern, &subj) {
3137 let mut local = env.clone();
3138 local.extend(bindings);
3139 return self.eval_expr(&arm.body, &mut local);
3140 }
3141 }
3142 Ok(Value::Unit)
3143 },
3144
3145 Expr::Range(lo, hi) => {
3146 let lo_v = self.eval_expr(lo, env)?;
3147 let hi_v = self.eval_expr(hi, env)?;
3148 let lo_n = self.to_number(&lo_v)? as i64;
3149 let hi_n = self.to_number(&hi_v)? as i64;
3150 Ok(Value::List(Rc::new(
3151 (lo_n..hi_n).map(|i| Value::Number(i as f64)).collect(),
3152 )))
3153 },
3154
3155 Expr::Index(base, idx) => {
3156 let b = self.eval_expr(base, env)?;
3157 let i = self.eval_expr(idx, env)?;
3158 let n = self.to_number(&i)? as usize;
3159 match b {
3160 Value::List(v) => v
3161 .get(n)
3162 .cloned()
3163 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
3164 Value::Str(s) => s
3165 .chars()
3166 .nth(n)
3167 .map(|c| Value::Str(c.to_string()))
3168 .ok_or_else(|| EvalErr::from(format!("index {n} out of bounds"))),
3169 other => Err(EvalErr::from(format!("cannot index {:?}", other))),
3170 }
3171 },
3172
3173 Expr::Call(callee, args) => {
3174 let arg_vals: Vec<Value> = args
3175 .iter()
3176 .map(|a| self.eval_expr(a, env))
3177 .collect::<Result<_, _>>()?;
3178 match callee.as_ref() {
3179 Expr::Ident(name) => self.call_named(name, arg_vals, env),
3180 Expr::Path(segs) => self.call_named(&segs.join("::"), arg_vals, env),
3181 _ => {
3182 let v = self.eval_expr(callee, env)?;
3183 self.call_value(v, arg_vals)
3184 },
3185 }
3186 },
3187
3188 Expr::MethodCall { receiver, method, args } => {
3189 let recv = self.eval_expr(receiver, env)?;
3190 let arg_vals: Vec<Value> = args
3191 .iter()
3192 .map(|a| self.eval_expr(a, env))
3193 .collect::<Result<_, _>>()?;
3194 self.call_method(recv, method, arg_vals)
3195 },
3196
3197 Expr::Closure(params, body) => Ok(Value::Fn(
3198 params.clone(),
3199 vec![Stmt::Expr(*body.clone())],
3200 env.clone(),
3201 )),
3202
3203 Expr::Asm(_) => Ok(Value::Unit),
3204 }
3205 }
3206
3207 fn exec_block(&mut self, stmts: &[Stmt], env: &mut Env) -> Result<Option<Value>, EvalErr> {
3210 let mut last: Option<Value> = None;
3211 for stmt in stmts {
3212 match stmt {
3213 Stmt::Bind(name, expr) => {
3214 match self.try_inplace_list_update(name, expr, env)? {
3215 Some(v) => env.insert(name.clone(), v),
3216 None => {
3217 let v = self.eval_expr(expr, env)?;
3218 env.insert(name.clone(), v)
3219 },
3220 };
3221 last = None;
3222 },
3223 Stmt::Return(expr) => {
3224 let v = self.eval_expr(expr, env)?;
3225 return Err(EvalErr::Return(v));
3226 },
3227 Stmt::Expr(expr) => {
3228 last = Some(self.eval_expr(expr, env)?);
3229 },
3230 }
3231 }
3232 Ok(last)
3233 }
3234
3235 fn try_inplace_list_update(
3242 &mut self,
3243 name: &str,
3244 expr: &Expr,
3245 env: &mut Env,
3246 ) -> Result<Option<Value>, EvalErr> {
3247 let Expr::Call(callee, args) = expr else { return Ok(None) };
3248 let Expr::Ident(fname) = callee.as_ref() else { return Ok(None) };
3249 let is_push = matches!(
3250 fname.as_str(),
3251 "list_push" | "เพิ่มรายการ" | "列表添加" | "リスト追加" | "목록추가"
3252 );
3253 let is_set = matches!(
3254 fname.as_str(),
3255 "list_set" | "ตั้งรายการ" | "设元素" | "要素設定" | "요소설정"
3256 );
3257 if !is_push && !is_set {
3258 return Ok(None);
3259 }
3260 match args.first() {
3263 Some(Expr::Ident(a0)) if a0 == name => {},
3264 _ => return Ok(None),
3265 }
3266 if self.functions.contains_key(fname.as_str()) {
3267 return Ok(None);
3268 }
3269 if is_push {
3270 if args.len() != 2 {
3271 return Ok(None);
3272 }
3273 let val = self.eval_expr(&args[1], env)?;
3274 match env.remove(name) {
3275 Some(Value::List(mut v)) => {
3276 Rc::make_mut(&mut v).push(val);
3277 Ok(Some(Value::List(v)))
3278 },
3279 other => {
3280 if let Some(o) = other {
3281 env.insert(name.to_string(), o);
3282 }
3283 Ok(None)
3284 },
3285 }
3286 } else {
3287 if args.len() != 3 {
3288 return Ok(None);
3289 }
3290 let idx_v = self.eval_expr(&args[1], env)?;
3291 let idx = self.to_number(&idx_v).unwrap_or(0.0) as usize;
3292 let val = self.eval_expr(&args[2], env)?;
3293 match env.remove(name) {
3294 Some(Value::List(mut v)) => {
3295 if idx < v.len() {
3296 Rc::make_mut(&mut v)[idx] = val;
3297 }
3298 Ok(Some(Value::List(v)))
3299 },
3300 other => {
3301 if let Some(o) = other {
3302 env.insert(name.to_string(), o);
3303 }
3304 Ok(None)
3305 },
3306 }
3307 }
3308 }
3309
3310 fn lookup(&self, name: &str, env: &Env) -> EvalResult {
3313 if let Some(v) = env.get(name) {
3314 return Ok(v.clone());
3315 }
3316 if let Some(v) = self.global_seed.get(name) {
3319 return Ok(v.clone());
3320 }
3321 if self.functions.contains_key(name) {
3322 let def = &self.functions[name];
3323 return Ok(Value::Fn(def.params.clone(), def.body.clone(), new_env()));
3324 }
3325 if let Some((enum_name, 0)) = self.enum_variants.get(name).cloned() {
3327 let variant = name.rsplit("::").next().unwrap_or(name).to_string();
3328 return Ok(Value::Variant { enum_name, variant, payload: Vec::new() });
3329 }
3330 match name {
3332 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" | "پی" | "باي" | "פאי" | "پائی"
3333 | "пи" => return Ok(Value::Number(std::f64::consts::PI)),
3334 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" | "تاو" | "טאו" | "ٹاؤ" | "тау" => {
3335 return Ok(Value::Number(std::f64::consts::TAU))
3336 },
3337 _ => {},
3338 }
3339 Err(EvalErr::from(format!("undefined: '{name}'")))
3340 }
3341
3342 pub(crate) fn call_named(&mut self, name: &str, args: Vec<Value>, env: &Env) -> EvalResult {
3351 if !ling_profile_enabled() {
3352 return self.call_named_inner(name, args, env);
3353 }
3354 let t0 = crate::runtime::now_secs();
3355 let r = self.call_named_inner(name, args, env);
3356 ling_profile_record(
3357 name,
3358 ((crate::runtime::now_secs() - t0) * 1_000_000_000.0) as u128,
3359 );
3360 r
3361 }
3362
3363 fn call_named_inner(&mut self, name: &str, args: Vec<Value>, env: &Env) -> EvalResult {
3364 if let Some(def) = self.functions.get(name).cloned() {
3367 let mut call_env =
3368 FxHashMap::with_capacity_and_hasher(def.params.len(), Default::default());
3369 let _ = env; for (param, arg) in def.params.iter().zip(args) {
3371 call_env.insert(param.clone(), arg);
3372 }
3373 return match self.framed(name, |me| me.exec_block(&def.body, &mut call_env)) {
3374 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
3375 Err(EvalErr::Return(v)) => Ok(v),
3376 Err(e) => Err(e),
3377 };
3378 }
3379
3380 #[cfg(target_arch = "wasm32")]
3381 if let Some(v) = self.wasm_music_builtin(name, &args)? {
3382 return Ok(v);
3383 }
3384
3385 match name {
3386 "__ling_global" => {
3389 if let Some(Value::Str(g)) = args.first() {
3390 if let Some(v) = self.global_seed.get(g.as_str()) {
3391 return Ok(v.clone());
3392 }
3393 }
3394 return Ok(Value::Unit);
3395 },
3396 "print" | "println" | "印" | "打印" | "印刷" | "พิมพ์" | "출력" | "вывести"
3398 | "imprimir" | "afficher" | "چاپ" | "اطبع" | "הדפס" | "چھاپو" | "drucken"
3399 | "печать" => {
3400 let s = args
3401 .iter()
3402 .map(|v| v.to_string())
3403 .collect::<Vec<_>>()
3404 .join("");
3405 println!("{s}");
3406 return Ok(Value::Unit);
3407 },
3408 "print_color" | "พิมพ์สี" | "چاپ_رنگی" | "اطبع_بلون" | "הדפס_בצבע" | "رنگین_چھاپو" =>
3411 {
3412 #[cfg(windows)]
3413 {
3414 use std::sync::Once;
3415 static VT: Once = Once::new();
3416 VT.call_once(|| {
3417 extern "system" {
3418 fn GetStdHandle(n: u32) -> *mut std::ffi::c_void;
3419 fn GetConsoleMode(h: *mut std::ffi::c_void, m: *mut u32) -> i32;
3420 fn SetConsoleMode(h: *mut std::ffi::c_void, m: u32) -> i32;
3421 }
3422 unsafe {
3423 let h = GetStdHandle(0xFFFF_FFF5u32); let mut mode = 0u32;
3425 if GetConsoleMode(h, &mut mode) != 0 {
3426 SetConsoleMode(h, mode | 0x0004); }
3428 }
3429 });
3430 }
3431 let col = self.arg_num(&args, 0, 7.0)? as i64;
3432 let s = args
3433 .iter()
3434 .skip(1)
3435 .map(|v| v.to_string())
3436 .collect::<Vec<_>>()
3437 .join("");
3438 let code = 90 + col.clamp(0, 7);
3439 println!("\x1b[1;{code}m{s}\x1b[0m");
3440 return Ok(Value::Unit);
3441 },
3442 "format"
3444 | "格式"
3445 | "フォーマット"
3446 | "서식"
3447 | "รูปแบบ"
3448 | "форматировать"
3449 | "formatear"
3450 | "formater"
3451 | "قالببندی"
3452 | "نسّق"
3453 | "פרמט"
3454 | "فارمیٹ"
3455 | "formatieren" => {
3456 return Ok(Value::Str(self.builtin_format(&args)?));
3457 },
3458 "格式::拼接" | "format::join" => match args.first() {
3460 Some(Value::List(items)) => {
3461 return Ok(Value::Str(items.iter().map(|v| v.to_string()).collect()));
3462 },
3463 _ => return Ok(Value::Str(self.builtin_format(&args)?)),
3464 },
3465 "ok" | "好" | "良し" | "좋아" | "โอเค" | "تایید" | "تمام" | "בסדר" | "ٹھیک" | "bon"
3467 | "gut" | "хорошо" => {
3468 let val = args.into_iter().next().unwrap_or(Value::Unit);
3469 return Ok(Value::Ok(Box::new(val)));
3470 },
3471 "bad" | "坏" | "err" | "悪い" | "나쁨" | "ผิด" | "بد" | "سيء" | "רע" | "برا"
3472 | "mauvais" | "schlecht" | "плохо" => {
3473 let val = args.into_iter().next().unwrap_or(Value::Unit);
3474 return Ok(Value::Err(Box::new(val)));
3475 },
3476 "向量::从" | "Vec::from" => {
3478 if let Some(Value::List(v)) = args.first() {
3479 return Ok(Value::List(v.clone()));
3480 }
3481 return Ok(Value::List(Rc::new(args)));
3482 },
3483 "向量::有容量" | "Vec::with_capacity" => {
3484 return Ok(Value::List(Rc::new(Vec::new())))
3485 },
3486 "计时::获取当前小时" | "Timer::hour" => return Ok(Value::Number(14.0)),
3488 "计时::现在" | "Timer::now" => return Ok(Value::Number(1000.0)),
3489 "sleep" | "หยุด" | "นอน" | "sleep_ms" | "睡眠" | "眠る" | "スリープ" | "잠자기"
3491 | "잠" | "流水::睡眠" | "Flow::sleep" | "خواب" | "نم" | "שינה" | "سو_جاؤ"
3492 | "dormir" | "schlafen" | "спать" => {
3493 if let Some(ms_val) = args.first() {
3494 if let Ok(ms) = self.to_number(ms_val) {
3495 #[cfg(target_arch = "wasm32")]
3496 wasm_sleep_ms(ms.max(0.0) as i32);
3497 #[cfg(not(target_arch = "wasm32"))]
3498 std::thread::sleep(std::time::Duration::from_millis(ms as u64));
3499 }
3500 }
3501 return Ok(Value::Unit);
3502 },
3503 "流水::并行" | "Flow::parallel" => {
3505 if let Some(Value::Fn(params, body, mut cap)) = args.first().cloned() {
3506 let _ = params;
3507 match self.exec_block(&body, &mut cap) {
3508 Ok(Some(v)) => return Ok(v),
3509 Ok(None) => return Ok(Value::Unit),
3510 Err(EvalErr::Return(v)) => return Ok(v),
3511 Err(e) => return Err(e),
3512 }
3513 }
3514 return Ok(Value::Unit);
3515 },
3516
3517 "sin" | "ไซน์" | "正弦" | "サイン" | "사인" | "سینوس" | "جا" | "סינוס" | "سائن"
3526 | "sinus" | "синус" => {
3527 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sin()));
3528 },
3529 "cos" | "โคไซน์" | "余弦" | "コサイン" | "코사인" | "کسینوس" | "جتا" | "קוסינוס"
3530 | "کوسائن" | "cosinus" | "kosinus" | "косинус" => {
3531 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cos()));
3532 },
3533
3534 "tanh" | "tanhf" | "双曲正切" | "双曲線正接" | "쌍곡탄젠트" => {
3537 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tanh()));
3538 },
3539
3540 "tan"
3541 | "แทนเจนต์"
3542 | "正切"
3543 | "タンジェント"
3544 | "탄젠트"
3545 | "تانژانت"
3546 | "ظا"
3547 | "טנגנס"
3548 | "ٹینجنٹ"
3549 | "tangente"
3550 | "tangens"
3551 | "тангенс" => {
3552 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.tan()));
3553 },
3554 "asin"
3555 | "arcsin"
3556 | "反正弦"
3557 | "アークサイン"
3558 | "아크사인"
3559 | "อาร์กไซน์"
3560 | "آرکسینوس"
3561 | "قوس_جا"
3562 | "ארקסינוס"
3563 | "آرک_سائن"
3564 | "arcsinus"
3565 | "arkussinus"
3566 | "арксинус" => {
3567 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.asin()));
3568 },
3569 "acos"
3570 | "arccos"
3571 | "反余弦"
3572 | "アークコサイン"
3573 | "아크코사인"
3574 | "อาร์กโคไซน์"
3575 | "آرککسینوس"
3576 | "قوس_جتا"
3577 | "ארקוקוסינוס"
3578 | "آرک_کوسائن"
3579 | "arccosinus"
3580 | "arkuskosinus"
3581 | "арккосинус" => {
3582 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.acos()));
3583 },
3584 "atan"
3585 | "arctan"
3586 | "反正切"
3587 | "アークタンジェント"
3588 | "아크탄젠트"
3589 | "อาร์กแทนเจนต์"
3590 | "آرکتانژانت"
3591 | "قوس_ظا"
3592 | "ארקטנגנס"
3593 | "آرک_ٹینجنٹ"
3594 | "arctangente"
3595 | "arkustangens"
3596 | "арктангенс" => {
3597 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.atan()));
3598 },
3599 "atan2" | "arctan2" | "反正切2" | "アークタンジェント2" | "아크탄젠트2" =>
3600 {
3601 let y = self.arg_num(&args, 0, 0.0)?;
3602 let x = self.arg_num(&args, 1, 1.0)?;
3603 return Ok(Value::Number(y.atan2(x)));
3604 },
3605
3606 "sqrt"
3608 | "รากที่สอง"
3609 | "平方根"
3610 | "根"
3611 | "제곱근"
3612 | "جذر"
3613 | "שורש"
3614 | "racine_carrée"
3615 | "quadratwurzel"
3616 | "корень" => {
3617 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.sqrt()));
3618 },
3619 "cbrt"
3620 | "立方根"
3621 | "세제곱근"
3622 | "รากที่สาม"
3623 | "ریشه_سوم"
3624 | "جذر_تكعيبي"
3625 | "שורש_שלישי"
3626 | "مکعب_جذر"
3627 | "racine_cubique"
3628 | "kubikwurzel"
3629 | "кубический_корень" => {
3630 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.cbrt()));
3631 },
3632 "pow"
3633 | "ยกกำลัง"
3634 | "幂"
3635 | "べき乗"
3636 | "거듭제곱"
3637 | "توان"
3638 | "أس"
3639 | "חזקה"
3640 | "قوت"
3641 | "puissance"
3642 | "potenz"
3643 | "степень" => {
3644 let base = self.arg_num(&args, 0, 0.0)?;
3645 let exp = self.arg_num(&args, 1, 1.0)?;
3646 return Ok(Value::Number(base.powf(exp)));
3647 },
3648 "exp" | "指数" | "指数関数" | "지수" => {
3649 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.exp()));
3650 },
3651 "hypot" | "斜边" | "斜辺" | "빗변" => {
3652 let x = self.arg_num(&args, 0, 0.0)?;
3653 let y = self.arg_num(&args, 1, 0.0)?;
3654 return Ok(Value::Number(x.hypot(y)));
3655 },
3656
3657 "ln"
3659 | "log"
3660 | "ลอการิทึม"
3661 | "对数"
3662 | "対数"
3663 | "로그"
3664 | "لگاریتم_طبیعی"
3665 | "لوغاريتم_طبيعي"
3666 | "לוגריתם_טבעי"
3667 | "فطری_لوگ" => {
3668 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.ln()));
3669 },
3670 "log2" | "对数2" | "対数2" | "로그2" => {
3671 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log2()));
3672 },
3673 "log10" | "对数10" | "対数10" | "로그10" => {
3674 return Ok(Value::Number(self.arg_num(&args, 0, 1.0)?.log10()));
3675 },
3676
3677 "abs"
3679 | "ค่าสัมบูรณ์"
3680 | "绝对值"
3681 | "绝对"
3682 | "絶対値"
3683 | "절댓값"
3684 | "절대값"
3685 | "قدرمطلق"
3686 | "مطلق"
3687 | "ערך_מוחלט"
3688 | "مطلق_قدر"
3689 | "valeur_absolue"
3690 | "betrag"
3691 | "модуль_числа" => {
3692 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.abs()));
3693 },
3694 "floor" | "ปัดลง" | "向下取整" | "下整" | "床関数" | "내림" | "کف" | "أرضية"
3695 | "רצפה" | "فرش" | "plancher" | "abrunden" | "вниз" => {
3696 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.floor()));
3697 },
3698 "ceil"
3699 | "ปัดขึ้น"
3700 | "向上取整"
3701 | "上整"
3702 | "天井関数"
3703 | "올림"
3704 | "سقف"
3705 | "תקרה"
3706 | "چھت"
3707 | "plafond"
3708 | "aufrunden"
3709 | "вверх" => {
3710 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.ceil()));
3711 },
3712 "round" | "ปัดเศษ" | "四舍五入" | "四舍" | "四捨五入" | "반올림" | "گرد_کردن"
3713 | "تقريب" | "עיגול" | "گول" | "arrondir" | "runden" | "округлить" =>
3714 {
3715 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.round()));
3716 },
3717 "trunc"
3718 | "int"
3719 | "ตัดทศนิยม"
3720 | "取整"
3721 | "整数化"
3722 | "整数"
3723 | "截整"
3724 | "정수화"
3725 | "정수"
3726 | "切り捨て"
3727 | "버림"
3728 | "برش"
3729 | "اقتطاع"
3730 | "קיטום"
3731 | "کٹائی"
3732 | "tronquer"
3733 | "abschneiden"
3734 | "усечь" => {
3735 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.trunc()));
3736 },
3737 "fract" | "小数部分" | "小数部" | "소수부" => {
3738 return Ok(Value::Number(self.arg_num(&args, 0, 0.0)?.fract()));
3739 },
3740
3741 "min" | "ต่ำสุด" | "最小" | "최솟값" | "کمینه" | "أصغر" | "מינימום" | "کم_ترین"
3743 | "minimum" | "минимум" => {
3744 let a = self.arg_num(&args, 0, 0.0)?;
3745 let b = self.arg_num(&args, 1, 0.0)?;
3746 return Ok(Value::Number(a.min(b)));
3747 },
3748 "max"
3749 | "สูงสุด"
3750 | "最大"
3751 | "최댓값"
3752 | "بیشینه"
3753 | "أكبر"
3754 | "מקסימום"
3755 | "زیادہ_ترین"
3756 | "maximum"
3757 | "максимум" => {
3758 let a = self.arg_num(&args, 0, 0.0)?;
3759 let b = self.arg_num(&args, 1, 0.0)?;
3760 return Ok(Value::Number(a.max(b)));
3761 },
3762 "clamp"
3763 | "จำกัด"
3764 | "截取"
3765 | "範囲制限"
3766 | "범위제한"
3767 | "محدود"
3768 | "قيّد"
3769 | "הגבל"
3770 | "limiter"
3771 | "begrenzen"
3772 | "ограничить" => {
3773 let x = self.arg_num(&args, 0, 0.0)?;
3774 let lo = self.arg_num(&args, 1, 0.0)?;
3775 let hi = self.arg_num(&args, 2, 1.0)?;
3776 return Ok(Value::Number(x.clamp(lo, hi)));
3777 },
3778
3779 "pi" | "π" | "พาย" | "圆周率" | "円周率" | "파이" | "پی" | "باي" | "פאי" | "پائی"
3781 | "пи" => return Ok(Value::Number(std::f64::consts::PI)),
3782 "tau" | "τ" | "双周率" | "タウ" | "타우" | "ทาว" | "تاو" | "טאו" | "ٹاؤ" | "тау" => {
3783 return Ok(Value::Number(std::f64::consts::TAU))
3784 },
3785
3786 "vnoise"
3792 | "noise2"
3793 | "นอยส์2ดี"
3794 | "柏林噪声2D"
3795 | "バリューノイズ2D"
3796 | "값노이즈2D"
3797 | "نویز_برداری"
3798 | "ضجيج_متجه"
3799 | "רעש_וקטורי"
3800 | "ویکٹر_نوائز"
3801 | "bruit_v"
3802 | "v_rauschen"
3803 | "шум_v" => {
3804 let x = self.arg_num(&args, 0, 0.0)? as f32;
3805 let y = self.arg_num(&args, 1, 0.0)? as f32;
3806 let seed = self.arg_num(&args, 2, 0.0)? as u32;
3807 return Ok(Value::Number(tex_vnoise(x, y, seed) as f64));
3808 },
3809
3810 "fbm"
3811 | "นอยส์ออร์แกนิก"
3812 | "分形噪声"
3813 | "フラクタルノイズ"
3814 | "프랙탈노이즈"
3815 | "نویز_فراکتالی"
3816 | "ضجيج_عضوي"
3817 | "רעש_פרקטלי"
3818 | "فریکٹل_نوائز" => {
3819 let x = self.arg_num(&args, 0, 0.0)? as f32;
3820 let y = self.arg_num(&args, 1, 0.0)? as f32;
3821 let octaves = self.arg_num(&args, 2, 4.0)? as u32;
3822 let seed = self.arg_num(&args, 3, 0.0)? as u32;
3823 return Ok(Value::Number(tex_fbm(x, y, octaves, seed) as f64));
3824 },
3825
3826 "perlin"
3827 | "perlin3"
3828 | "เพอร์ลิน3ดี"
3829 | "柏林噪声3D"
3830 | "パーリンノイズ3D"
3831 | "펄린노이즈3D"
3832 | "نویز_پرلین"
3833 | "بيرلين"
3834 | "רעש_פרלין"
3835 | "پرلن_نوائز"
3836 | "перлин" => {
3837 let x = self.arg_num(&args, 0, 0.0)? as f32;
3838 let y = self.arg_num(&args, 1, 0.0)? as f32;
3839 let z = self.arg_num(&args, 2, 0.0)? as f32;
3840 return Ok(Value::Number(perlin3(x, y, z) as f64));
3841 },
3842
3843 "lerp"
3845 | "ค่าระหว่าง"
3846 | "线性插值"
3847 | "線形補間"
3848 | "선형보간"
3849 | "میانیابی"
3850 | "استيفاء"
3851 | "אינטרפולציה"
3852 | "درمیانی_قدر"
3853 | "interpoler"
3854 | "interpolieren"
3855 | "интерполировать" => {
3856 let a = self.arg_num(&args, 0, 0.0)?;
3857 let b = self.arg_num(&args, 1, 1.0)?;
3858 let t = self.arg_num(&args, 2, 0.0)?;
3859 return Ok(Value::Number(a + (b - a) * t));
3860 },
3861
3862 "smoothstep"
3863 | "เปลี่ยนแบบนุ่ม"
3864 | "平滑步进"
3865 | "スムーズステップ"
3866 | "스무스스텝"
3867 | "گام_نرم"
3868 | "تدرج_ناعم"
3869 | "מדרגה_חלקה"
3870 | "ہموار_قدم"
3871 | "lissage"
3872 | "glättung"
3873 | "сглаживание" => {
3874 let lo = self.arg_num(&args, 0, 0.0)?;
3875 let hi = self.arg_num(&args, 1, 1.0)?;
3876 let x = self.arg_num(&args, 2, 0.5)?;
3877 let t = ((x - lo) / (hi - lo)).clamp(0.0, 1.0);
3878 return Ok(Value::Number(t * t * (3.0 - 2.0 * t)));
3879 },
3880
3881 "rand" | "สุ่ม" | "随机" | "乱数" | "난수" | "تصادفی" | "عشوائي" | "אקראי"
3882 | "بے_ترتیب" | "aléatoire" | "zufall" | "случайное" => {
3883 let val = fast_rand_f64(&mut self.rand_state);
3884 return Ok(Value::Number(val));
3885 },
3886
3887 "sign"
3888 | "เครื่องหมาย"
3889 | "符号"
3890 | "符号関数"
3891 | "부호"
3892 | "علامت"
3893 | "إشارة"
3894 | "סימן"
3895 | "نشان"
3896 | "signe"
3897 | "vorzeichen"
3898 | "знак" => {
3899 let x = self.arg_num(&args, 0, 0.0)?;
3900 return Ok(Value::Number(x.signum()));
3901 },
3902
3903 "hsv_to_rgb"
3904 | "เอชเอสวีเป็นRGB"
3905 | "HSV转RGB"
3906 | "HSV変換RGB"
3907 | "HSV변환RGB"
3908 | "HSV_به_RGB"
3909 | "HSV_إلى_RGB"
3910 | "HSV_ל_RGB"
3911 | "HSV_سے_RGB"
3912 | "hsv_vers_rgb"
3913 | "hsv_zu_rgb"
3914 | "hsv_в_rgb" => {
3915 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;
3919 let x = c * (1.0 - (((h / 60.0) % 2.0) - 1.0).abs());
3920 let m = v - c;
3921 let (r1, g1, b1) = if h < 60.0 {
3922 (c, x, 0.0)
3923 } else if h < 120.0 {
3924 (x, c, 0.0)
3925 } else if h < 180.0 {
3926 (0.0, c, x)
3927 } else if h < 240.0 {
3928 (0.0, x, c)
3929 } else if h < 300.0 {
3930 (x, 0.0, c)
3931 } else {
3932 (c, 0.0, x)
3933 };
3934 let r = ((r1 + m) * 255.0).round();
3935 let g = ((g1 + m) * 255.0).round();
3936 let b = ((b1 + m) * 255.0).round();
3937 return Ok(Value::List(Rc::new(vec![
3938 Value::Number(r),
3939 Value::Number(g),
3940 Value::Number(b),
3941 ])));
3942 },
3943
3944 "lerp_color"
3945 | "ไล่สี"
3946 | "颜色插值"
3947 | "色補間"
3948 | "색보간"
3949 | "میانیابی_رنگ"
3950 | "استيفاء_اللون"
3951 | "אינטרפולציית_צבע"
3952 | "رنگ_درمیانی_قدر"
3953 | "interpoler_couleur"
3954 | "farbe_interpolieren"
3955 | "интерполировать_цвет" => {
3956 let r1 = self.arg_num(&args, 0, 0.0)?;
3957 let g1 = self.arg_num(&args, 1, 0.0)?;
3958 let b1 = self.arg_num(&args, 2, 0.0)?;
3959 let r2 = self.arg_num(&args, 3, 255.0)?;
3960 let g2 = self.arg_num(&args, 4, 255.0)?;
3961 let b2 = self.arg_num(&args, 5, 255.0)?;
3962 let t = self.arg_num(&args, 6, 0.0)?;
3963 let r = r1 + (r2 - r1) * t;
3964 let g = g1 + (g2 - g1) * t;
3965 let b = b1 + (b2 - b1) * t;
3966 let c = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
3967 self.gfx.borrow_mut().color = c;
3968 return Ok(Value::Unit);
3969 },
3970
3971 "time_now"
3973 | "เวลาปัจจุบัน"
3974 | "当前时间"
3975 | "経過時間"
3976 | "현재시간"
3977 | "زمان_اکنون"
3978 | "الوقت_الآن"
3979 | "הזמן_עכשיו"
3980 | "ابھی_کا_وقت"
3981 | "temps_actuel"
3982 | "aktuelle_zeit"
3983 | "текущее_время" => {
3984 return Ok(Value::Number(
3985 crate::runtime::now_secs() - self.start_time_secs,
3986 ));
3987 },
3988
3989 "epoch_now"
3993 | "เวลาโลก"
3994 | "datetime"
3995 | "现在时刻"
3996 | "現在時刻"
3997 | "현재시각"
3998 | "مهر_زمانی_اکنون"
3999 | "طابع_الوقت_الآن"
4000 | "חותמת_זמן_עכשיו"
4001 | "ایپاک_وقت" => {
4002 return Ok(Value::Number(crate::runtime::now_secs()));
4003 },
4004
4005 "frame_count"
4006 | "เฟรม"
4007 | "帧数"
4008 | "フレーム数"
4009 | "프레임수"
4010 | "شمار_فریم"
4011 | "عدد_الإطارات"
4012 | "ספירת_פריימים"
4013 | "فریم_شمار"
4014 | "nombre_images"
4015 | "bildanzahl"
4016 | "число_кадров" => {
4017 return Ok(Value::Number(self.frame_num as f64));
4018 },
4019
4020 "mic_open"
4022 | "เปิดไมค์"
4023 | "开麦克风"
4024 | "マイク開く"
4025 | "마이크열기"
4026 | "باز_کردن_میکروفون"
4027 | "افتح_الميكروفون"
4028 | "פתח_מיקרופון"
4029 | "مائیکروفون_کھولو"
4030 | "ouvrir_micro"
4031 | "mikrofon_öffnen"
4032 | "открыть_микрофон" => {
4033 #[cfg(not(target_arch = "wasm32"))]
4034 {
4035 match ling_mic::MicInput::open(Default::default()) {
4036 Ok(mic) => {
4037 let _ = mic.start(|_samples: &[f32]| {}); self.mic = Some(mic);
4039 return Ok(Value::Number(1.0)); },
4041 Err(_e) => {
4044 self.mic = None;
4045 return Ok(Value::Number(0.0));
4046 },
4047 }
4048 }
4049 #[cfg(target_arch = "wasm32")]
4050 return Ok(Value::Unit);
4051 },
4052
4053 "mic_rms"
4054 | "เสียงRMS"
4055 | "麦克风音量"
4056 | "マイクRMS"
4057 | "마이크RMS"
4058 | "RMS_میکروفون"
4059 | "RMS_الميكروفون"
4060 | "RMS_מיקרופון"
4061 | "مائیکروفون_RMS"
4062 | "rms_micro"
4063 | "mikrofon_rms"
4064 | "rms_микрофона" => {
4065 #[cfg(not(target_arch = "wasm32"))]
4066 {
4067 let rms = self
4068 .mic
4069 .as_ref()
4070 .map(|m: &ling_mic::MicInput| m.rms())
4071 .unwrap_or(0.0);
4072 return Ok(Value::Number(rms as f64));
4073 }
4074 #[cfg(target_arch = "wasm32")]
4075 return Ok(Value::Number(0.0));
4076 },
4077
4078 "mic_peak"
4079 | "เสียงพีค"
4080 | "麦克风峰值"
4081 | "マイクピーク"
4082 | "마이크피크"
4083 | "اوج_میکروفون"
4084 | "ذروة_الميكروفون"
4085 | "שיא_מיקרופון"
4086 | "مائیکروفون_چوٹی"
4087 | "crête_micro"
4088 | "mikrofon_spitze"
4089 | "пик_микрофона" => {
4090 #[cfg(not(target_arch = "wasm32"))]
4091 {
4092 let peak = self
4093 .mic
4094 .as_ref()
4095 .map(|m: &ling_mic::MicInput| m.peak())
4096 .unwrap_or(0.0);
4097 return Ok(Value::Number(peak as f64));
4098 }
4099 #[cfg(target_arch = "wasm32")]
4100 return Ok(Value::Number(0.0));
4101 },
4102
4103 "mic_fft"
4104 | "วิเคราะห์เสียงสด"
4105 | "实时频谱"
4106 | "リアルタイムFFT"
4107 | "실시간FFT"
4108 | "FFT_میکروفون"
4109 | "FFT_الميكروفون"
4110 | "FFT_מיקרופון"
4111 | "مائیکروفون_FFT"
4112 | "fft_micro"
4113 | "mikrofon_fft"
4114 | "fft_микрофона" => {
4115 #[cfg(not(target_arch = "wasm32"))]
4116 {
4117 let n = self.arg_num(&args, 0, 8.0)? as usize;
4118 if let Some(mic) = self.mic.as_ref() {
4119 let samples = mic.latest_samples();
4120 self.fft.borrow_mut().push_samples(&samples);
4121 }
4122 let bands = self.fft.borrow().freq_bands(n);
4123 let result: Vec<Value> =
4124 bands.iter().map(|&v| Value::Number(v as f64)).collect();
4125 return Ok(Value::List(Rc::new(result)));
4126 }
4127 #[cfg(target_arch = "wasm32")]
4128 return Ok(Value::List(Vec::new().into()));
4129 },
4130
4131 "set_blend"
4133 | "โหมดผสม"
4134 | "混合模式"
4135 | "ブレンドモード"
4136 | "블렌드모드"
4137 | "تنظیم_ترکیب"
4138 | "عيّن_المزج"
4139 | "קבע_מיזוג"
4140 | "بلینڈ_مقرر_کرو"
4141 | "définir_mélange"
4142 | "mischmodus_setzen"
4143 | "задать_смешивание" => {
4144 let mode = self.arg_num(&args, 0, 0.0)? as u8;
4145 let mut gfx = self.gfx.borrow_mut();
4146 gfx.blend = mode;
4147 let a = gfx.alpha;
4148 gfx.depth_queue.set_state(mode, a); return Ok(Value::Unit);
4150 },
4151
4152 "set_antialias"
4156 | "ตั้งลบรอยหยัก"
4157 | "抗锯齿"
4158 | "アンチエイリアス"
4159 | "안티에일리어싱"
4160 | "تنظیم_ضدلبهدندانه"
4161 | "عيّن_مضاد_التسنن"
4162 | "קבע_החלקת_קצוות"
4163 | "اینٹی_الائیسنگ_مقرر_کرو" => {
4164 let on = self.arg_num(&args, 0, 1.0)? > 0.5;
4165 self.gfx.borrow_mut().antialias = on;
4166 return Ok(Value::Unit);
4167 },
4168 "get_antialias"
4170 | "อ่านลบรอยหยัก"
4171 | "读取抗锯齿"
4172 | "アンチエイリアス取得"
4173 | "안티에일리어싱상태"
4174 | "خواندن_ضدلبهدندانه"
4175 | "اقرأ_مضاد_التسنن"
4176 | "קרא_החלקת_קצוות"
4177 | "اینٹی_الائیسنگ_پڑھو" => {
4178 return Ok(Value::Bool(self.gfx.borrow().antialias));
4179 },
4180
4181 "set_font_antialias" | "글꼴안티에일리어싱" => {
4185 let on = self.arg_num(&args, 0, 1.0)? > 0.5;
4186 self.gfx.borrow_mut().font_antialias = on;
4187 return Ok(Value::Unit);
4188 },
4189 "get_font_antialias" | "글꼴안티에일리어싱상태" => {
4191 return Ok(Value::Bool(self.gfx.borrow().font_antialias));
4192 },
4193
4194 "draw_circle"
4196 | "วาดวงกลม"
4197 | "画圆"
4198 | "円描画"
4199 | "원그리기"
4200 | "رسم_دایره"
4201 | "ارسم_دائرة"
4202 | "צייר_עיגול"
4203 | "دائرہ_کھینچو"
4204 | "dessiner_cercle"
4205 | "kreis_zeichnen"
4206 | "рисовать_круг" => {
4207 let cx = self.arg_num(&args, 0, 0.0)? as i32;
4208 let cy = self.arg_num(&args, 1, 0.0)? as i32;
4209 let r = self.arg_num(&args, 2, 10.0)? as i32;
4210 let mut gfx = self.gfx.borrow_mut();
4211 let (w, h, color, blend) =
4212 (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
4213 if gfx.antialias {
4214 let (uw, uh) = (gfx.width, gfx.height);
4215 let segs = ((r.max(1) as u32) * 4).clamp(24, 512);
4216 crate::gfx::raster::draw_arc(
4217 &mut gfx.buffer,
4218 uw,
4219 uh,
4220 color,
4221 true,
4222 blend == 1,
4223 cx as f32,
4224 cy as f32,
4225 r as f32,
4226 0.0,
4227 std::f32::consts::TAU,
4228 segs,
4229 );
4230 } else {
4231 draw_circle_outline(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
4232 }
4233 return Ok(Value::Unit);
4234 },
4235
4236 "draw_filled_circle"
4237 | "draw_disc"
4238 | "วาดวงกลมทึบ"
4239 | "画实心圆"
4240 | "塗りつぶし円"
4241 | "원채우기"
4242 | "رسم_دایره_توپر"
4243 | "ارسم_دائرة_ممتلئة"
4244 | "צייר_עיגול_מלא"
4245 | "بھرا_دائرہ_کھینچو" => {
4246 let cx = self.arg_num(&args, 0, 0.0)? as i32;
4247 let cy = self.arg_num(&args, 1, 0.0)? as i32;
4248 let r = self.arg_num(&args, 2, 10.0)? as i32;
4249 let mut gfx = self.gfx.borrow_mut();
4250 let (w, h, color, blend) =
4251 (gfx.width as i32, gfx.height as i32, gfx.color, gfx.blend);
4252 draw_circle_filled(&mut gfx.buffer, w, h, cx, cy, r, color, blend);
4253 return Ok(Value::Unit);
4254 },
4255
4256 "draw_arc"
4260 | "arc"
4261 | "วาดส่วนโค้ง"
4262 | "画弧"
4263 | "円弧描画"
4264 | "호그리기"
4265 | "رسم_کمان"
4266 | "ارسم_قوسا"
4267 | "צייר_קשת"
4268 | "آرک_کھینچو" => {
4269 let cx = self.arg_num(&args, 0, 0.0)? as f32;
4270 let cy = self.arg_num(&args, 1, 0.0)? as f32;
4271 let r = self.arg_num(&args, 2, 10.0)? as f32;
4272 let a0 = self.arg_num(&args, 3, 0.0)? as f32;
4273 let a1 = self.arg_num(&args, 4, std::f64::consts::TAU)? as f32;
4274 let default_segs = ((r.abs() * (a1 - a0).abs()).ceil() as u32).clamp(8, 1024);
4275 let segs = self.arg_num(&args, 5, default_segs as f64)? as u32;
4276 let mut gfx = self.gfx.borrow_mut();
4277 let color = gfx.color;
4278 #[cfg(not(target_arch = "wasm32"))]
4279 {
4280 let (uw, uh, aa, add) = (gfx.width, gfx.height, gfx.antialias, gfx.blend == 1);
4281 crate::gfx::raster::draw_arc(
4282 &mut gfx.buffer,
4283 uw,
4284 uh,
4285 color,
4286 aa,
4287 add,
4288 cx,
4289 cy,
4290 r,
4291 a0,
4292 a1,
4293 segs,
4294 );
4295 }
4296 #[cfg(target_arch = "wasm32")]
4297 {
4298 let segs_f = segs.max(1);
4299 let step = (a1 - a0) / segs_f as f32;
4300 let mut px = cx + r * a0.cos();
4301 let mut py = cy + r * a0.sin();
4302 let mut i = 1u32;
4303 while i <= segs_f {
4304 let a = a0 + step * i as f32;
4305 let nx = cx + r * a.cos();
4306 let ny = cy + r * a.sin();
4307 gfx.depth_queue.push_line(0.0, color, px, py, nx, ny);
4308 px = nx;
4309 py = ny;
4310 i += 1;
4311 }
4312 }
4313 return Ok(Value::Unit);
4314 },
4315
4316 "set_alpha"
4322 | "ตั้งความโปร่งใส"
4323 | "设透明"
4324 | "アルファ設定"
4325 | "투명도설정"
4326 | "تنظیم_شفافیت"
4327 | "عيّن_الشفافية"
4328 | "קבע_שקיפות"
4329 | "شفافیت_مقرر_کرو"
4330 | "définir_alpha"
4331 | "alpha_setzen"
4332 | "задать_альфа" => {
4333 let a = self.arg_num(&args, 0, 1.0)? as f32;
4334 let mut gfx = self.gfx.borrow_mut();
4335 gfx.alpha = a.clamp(0.0, 1.0);
4336 let (m, al) = (gfx.blend, gfx.alpha);
4337 gfx.depth_queue.set_state(m, al); return Ok(Value::Unit);
4339 },
4340
4341 "mesh_hue" | "หมุนสีเมช" | "فام_مش" | "صبغة_الشبكة" | "גוון_רשת" | "میش_ہیو" =>
4344 {
4345 let h = self.arg_num(&args, 0, 0.0)? as f32;
4346 let g = self.arg_num(&args, 1, 1.0)? as f32;
4347 let mut gfx = self.gfx.borrow_mut();
4348 gfx.mesh_hue = h;
4349 gfx.mesh_hue_gain = g.max(0.0);
4350 return Ok(Value::Unit);
4351 },
4352
4353 "set_frame_blur"
4356 | "frame_blur"
4357 | "เบลอเฟรม"
4358 | "تنظیم_تاری_فریم"
4359 | "عيّن_ضبابية_الإطار"
4360 | "קבע_טשטוש_פריים"
4361 | "فریم_بلر_مقرر_کرو" => {
4362 let a = self.arg_num(&args, 0, 0.0)? as f32;
4363 let mut gfx = self.gfx.borrow_mut();
4364 gfx.frame_blur = a.clamp(0.0, 0.95);
4365 if gfx.frame_blur <= 0.0 {
4366 gfx.prev_frame = Vec::new();
4367 }
4368 return Ok(Value::Unit);
4369 },
4370
4371 "set_line_hue_cycle"
4375 | "ตั้งวนสีเส้น"
4376 | "تنظیم_چرخه_فام_خط"
4377 | "عيّن_دورة_صبغة_الخط"
4378 | "קבע_מחזור_גוון_קו"
4379 | "لائن_ہیو_سائیکل_مقرر_کرو" => {
4380 let rate = self.arg_num(&args, 0, 0.0)?;
4381 crate::runtime::set_line_hue_rate(rate);
4382 return Ok(Value::Unit);
4383 },
4384
4385 "set_color_space"
4389 | "ปริภูมิสี"
4390 | "色彩空间"
4391 | "色空間"
4392 | "색공간"
4393 | "تنظیم_فضای_رنگ"
4394 | "عيّن_فضاء_اللون"
4395 | "קבע_מרחב_צבע"
4396 | "کلر_اسپیس_مقرر_کرو"
4397 | "définir_espace_couleur"
4398 | "farbraum_setzen"
4399 | "задать_цветовое_пространство" => {
4400 let m = self.arg_num(&args, 0, 0.0)? as i64;
4401 self.gfx.borrow_mut().linear_blend = m != 0;
4402 return Ok(Value::Unit);
4403 },
4404
4405 "set_gradient_space"
4408 | "ปริภูมิไล่สี"
4409 | "渐变空间"
4410 | "グラデ空間"
4411 | "그라데이션공간"
4412 | "تنظیم_فضای_گرادیان"
4413 | "عيّن_فضاء_التدرج"
4414 | "קבע_מרחב_גרדיאנט"
4415 | "گریڈینٹ_اسپیس_مقرر_کرو"
4416 | "définir_espace_dégradé"
4417 | "verlaufsraum_setzen"
4418 | "задать_пространство_градиента" => {
4419 let m = self.arg_num(&args, 0, 1.0)? as i64;
4420 self.gfx.borrow_mut().grad_oklab = m != 0;
4421 return Ok(Value::Unit);
4422 },
4423
4424 "mix_color"
4428 | "ผสมสี"
4429 | "混合颜色"
4430 | "色混合"
4431 | "색혼합"
4432 | "ترکیب_رنگ"
4433 | "امزج_اللون"
4434 | "ערבב_צבע"
4435 | "رنگ_ملاؤ"
4436 | "mélanger_couleur"
4437 | "farbe_mischen"
4438 | "смешать_цвет" => {
4439 let c0 = rgb(
4440 self.arg_num(&args, 0, 0.0)?,
4441 self.arg_num(&args, 1, 0.0)?,
4442 self.arg_num(&args, 2, 0.0)?,
4443 );
4444 let c1 = rgb(
4445 self.arg_num(&args, 3, 255.0)?,
4446 self.arg_num(&args, 4, 255.0)?,
4447 self.arg_num(&args, 5, 255.0)?,
4448 );
4449 let t = self.arg_num(&args, 6, 0.5)? as f32;
4450 self.gfx.borrow_mut().color = crate::gfx::color::mix_oklab(c0, c1, t);
4451 return Ok(Value::Unit);
4452 },
4453
4454 "set_depth_test"
4458 | "ทดสอบความลึก"
4459 | "深度测试"
4460 | "深度テスト"
4461 | "깊이테스트"
4462 | "تنظیم_آزمون_عمق"
4463 | "عيّن_اختبار_العمق"
4464 | "קבע_בדיקת_עומק"
4465 | "ڈیپتھ_ٹیسٹ_مقرر_کرو"
4466 | "définir_test_profondeur"
4467 | "tiefentest_setzen"
4468 | "задать_тест_глубины" => {
4469 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
4470 self.gfx.borrow_mut().depth_test = on;
4471 return Ok(Value::Unit);
4472 },
4473
4474 "set_flat_shade"
4477 | "ตั้งแฟลตเชด"
4478 | "平面着色"
4479 | "フラット着色"
4480 | "평면음영"
4481 | "تنظیم_سایهپردازی_تخت"
4482 | "عيّن_تظليلا_مسطحا"
4483 | "קבע_הצללה_שטוחה"
4484 | "فلیٹ_شیڈ_مقرر_کرو" => {
4485 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
4486 self.gfx.borrow_mut().flat_shade = on;
4487 return Ok(Value::Unit);
4488 },
4489
4490 "set_normal_override" => {
4493 let x = self.arg_num(&args, 0, 0.0)? as f32;
4494 let y = self.arg_num(&args, 1, -1.0)? as f32;
4495 let z = self.arg_num(&args, 2, 0.0)? as f32;
4496 self.gfx.borrow_mut().normal_override = Some([x, y, z]);
4497 return Ok(Value::Unit);
4498 },
4499
4500 "reset_normal_override" => {
4501 self.gfx.borrow_mut().normal_override = None;
4502 return Ok(Value::Unit);
4503 },
4504
4505 "clear_depth"
4509 | "ล้างความลึก"
4510 | "清深度"
4511 | "深度クリア"
4512 | "깊이지우기"
4513 | "پاککردن_عمق"
4514 | "امسح_العمق"
4515 | "נקה_עומק"
4516 | "گہرائی_صاف_کرو" => {
4517 self.gfx.borrow_mut().zbuf_needs_clear = true;
4518 return Ok(Value::Unit);
4519 },
4520
4521 "depth_blur"
4527 | "เบลอความลึก"
4528 | "dof"
4529 | "depth_of_field"
4530 | "景深"
4531 | "تاری_عمق"
4532 | "ضبابية_العمق"
4533 | "טשטוש_עומק"
4534 | "ڈیپتھ_بلر" => {
4535 let focus = self.arg_num(&args, 0, 30.0)? as f32;
4536 let range = self.arg_num(&args, 1, 60.0)? as f32;
4537 let radius = self.arg_num(&args, 2, 3.0)?.max(0.0) as usize;
4538 let oil = self.arg_num(&args, 3, 0.0)? as f32;
4541 let mut gfx = self.gfx.borrow_mut();
4542 let w = gfx.width;
4543 let h = gfx.height;
4544 if gfx.depth_buf.len() == w * h {
4545 let g = &mut *gfx;
4546 crate::gfx::raster::depth_of_field(
4547 &mut g.buffer,
4548 &g.depth_buf,
4549 w,
4550 h,
4551 focus,
4552 range,
4553 radius,
4554 oil,
4555 );
4556 }
4557 return Ok(Value::Unit);
4558 },
4559
4560 "light_pool"
4566 | "แอ่งแสง"
4567 | "光池"
4568 | "ライトプール"
4569 | "빛웅덩이"
4570 | "برکه_نور"
4571 | "بركة_ضوء"
4572 | "בריכת_אור"
4573 | "روشنی_تالاب"
4574 | "bassin_lumière"
4575 | "lichtpfütze"
4576 | "лужа_света" => {
4577 let x = self.arg_num(&args, 0, 0.0)? as f32;
4578 let y = self.arg_num(&args, 1, 0.0)? as f32;
4579 let z = self.arg_num(&args, 2, 0.0)? as f32;
4580 let radius = self.arg_num(&args, 3, 20.0)? as f32;
4581 let r = self.arg_num(&args, 4, 255.0)? as f32 / 255.0;
4582 let g = self.arg_num(&args, 5, 255.0)? as f32 / 255.0;
4583 let b = self.arg_num(&args, 6, 255.0)? as f32 / 255.0;
4584 let inten = self.arg_num(&args, 7, 1.0)? as f32;
4585 self.gfx
4586 .borrow_mut()
4587 .emit_light_pool(x, y, z, radius, [r, g, b], inten);
4588 return Ok(Value::Unit);
4589 },
4590
4591 "light_beam"
4596 | "ลำแสงไฟ"
4597 | "光柱"
4598 | "ライトビーム"
4599 | "빛기둥"
4600 | "پرتو_نور"
4601 | "شعاع_ضوء"
4602 | "קרן_אור"
4603 | "روشنی_شعاع"
4604 | "faisceau_lumière"
4605 | "lichtstrahl"
4606 | "луч_света" => {
4607 let x = self.arg_num(&args, 0, 0.0)? as f32;
4608 let y = self.arg_num(&args, 1, 0.0)? as f32;
4609 let z = self.arg_num(&args, 2, 0.0)? as f32;
4610 let fy = self.arg_num(&args, 3, 0.0)? as f32;
4611 let radius = self.arg_num(&args, 4, 14.0)? as f32;
4612 let r = self.arg_num(&args, 5, 255.0)? as f32 / 255.0;
4613 let g = self.arg_num(&args, 6, 255.0)? as f32 / 255.0;
4614 let b = self.arg_num(&args, 7, 255.0)? as f32 / 255.0;
4615 let inten = self.arg_num(&args, 8, 1.0)? as f32;
4616 self.gfx
4617 .borrow_mut()
4618 .emit_light_beam(x, y, z, fy, radius, [r, g, b], inten);
4619 return Ok(Value::Unit);
4620 },
4621
4622 "grad_triangle"
4626 | "สามเหลี่ยมไล่สี"
4627 | "渐变三角"
4628 | "グラデ三角"
4629 | "그라데삼각"
4630 | "مثلث_گرادیان"
4631 | "مثلث_متدرج"
4632 | "משולש_גרדיאנט"
4633 | "گریڈینٹ_مثلث"
4634 | "triangle_dégradé"
4635 | "dreieck_verlauf"
4636 | "градиент_треугольник" => {
4637 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
4638 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
4639 let c0 = rgb(
4640 self.arg_num(&args, 2, 255.0)?,
4641 self.arg_num(&args, 3, 255.0)?,
4642 self.arg_num(&args, 4, 255.0)?,
4643 );
4644 let x1 = self.arg_num(&args, 5, 0.0)? as f32;
4645 let y1 = self.arg_num(&args, 6, 0.0)? as f32;
4646 let c1 = rgb(
4647 self.arg_num(&args, 7, 255.0)?,
4648 self.arg_num(&args, 8, 255.0)?,
4649 self.arg_num(&args, 9, 255.0)?,
4650 );
4651 let x2 = self.arg_num(&args, 10, 0.0)? as f32;
4652 let y2 = self.arg_num(&args, 11, 0.0)? as f32;
4653 let c2 = rgb(
4654 self.arg_num(&args, 12, 255.0)?,
4655 self.arg_num(&args, 13, 255.0)?,
4656 self.arg_num(&args, 14, 255.0)?,
4657 );
4658 let mut gfx = self.gfx.borrow_mut();
4659 let (w, h, alpha, mode, lin, ok) = (
4660 gfx.width,
4661 gfx.height,
4662 gfx.alpha,
4663 gfx.blend,
4664 gfx.linear_blend,
4665 gfx.grad_oklab,
4666 );
4667 crate::gfx::raster::fill_triangle_grad(
4668 &mut gfx.buffer,
4669 w,
4670 h,
4671 alpha,
4672 mode,
4673 lin,
4674 ok,
4675 x0,
4676 y0,
4677 c0,
4678 x1,
4679 y1,
4680 c1,
4681 x2,
4682 y2,
4683 c2,
4684 );
4685 return Ok(Value::Unit);
4686 },
4687
4688 "grad_rect"
4691 | "สี่เหลี่ยมไล่สี"
4692 | "渐变矩形"
4693 | "グラデ矩形"
4694 | "그라데사각"
4695 | "مستطیل_گرادیان"
4696 | "مستطيل_متدرج"
4697 | "מלבן_גרדיאנט"
4698 | "گریڈینٹ_مستطیل"
4699 | "rectangle_dégradé"
4700 | "rechteck_verlauf"
4701 | "градиент_прямоугольник" => {
4702 let x = self.arg_num(&args, 0, 0.0)? as f32;
4703 let y = self.arg_num(&args, 1, 0.0)? as f32;
4704 let rw = self.arg_num(&args, 2, 0.0)? as f32;
4705 let rh = self.arg_num(&args, 3, 0.0)? as f32;
4706 let c0 = rgb(
4707 self.arg_num(&args, 4, 255.0)?,
4708 self.arg_num(&args, 5, 255.0)?,
4709 self.arg_num(&args, 6, 255.0)?,
4710 );
4711 let c1 = rgb(
4712 self.arg_num(&args, 7, 0.0)?,
4713 self.arg_num(&args, 8, 0.0)?,
4714 self.arg_num(&args, 9, 0.0)?,
4715 );
4716 let dir = self.arg_num(&args, 10, 1.0)? as u8;
4717 let mut gfx = self.gfx.borrow_mut();
4718 let (w, h, alpha, mode, lin, ok) = (
4719 gfx.width,
4720 gfx.height,
4721 gfx.alpha,
4722 gfx.blend,
4723 gfx.linear_blend,
4724 gfx.grad_oklab,
4725 );
4726 crate::gfx::raster::fill_rect_grad(
4727 &mut gfx.buffer,
4728 w,
4729 h,
4730 alpha,
4731 mode,
4732 lin,
4733 ok,
4734 x,
4735 y,
4736 rw,
4737 rh,
4738 c0,
4739 c1,
4740 dir,
4741 );
4742 return Ok(Value::Unit);
4743 },
4744
4745 "shadow_blob"
4749 | "เงาวงรี"
4750 | "阴影斑"
4751 | "影ブロブ"
4752 | "그림자블롭"
4753 | "لکه_سایه"
4754 | "بقعة_ظل"
4755 | "כתם_צל"
4756 | "سایہ_دھبہ"
4757 | "tache_ombre"
4758 | "schattenklecks"
4759 | "пятно_тени" => {
4760 let cx = self.arg_num(&args, 0, 0.0)? as f32;
4761 let cy = self.arg_num(&args, 1, 0.0)? as f32;
4762 let rx = self.arg_num(&args, 2, 16.0)? as f32;
4763 let ry = self.arg_num(&args, 3, 8.0)? as f32;
4764 let a = self.arg_num(&args, 4, 0.5)? as f32;
4765 let mut gfx = self.gfx.borrow_mut();
4766 let (w, h, color, soft, mode, lin) = (
4767 gfx.width,
4768 gfx.height,
4769 gfx.color,
4770 gfx.shadow.soft,
4771 gfx.blend,
4772 gfx.linear_blend,
4773 );
4774 crate::gfx::raster::fill_disc_soft(
4775 &mut gfx.buffer,
4776 w,
4777 h,
4778 cx,
4779 cy,
4780 rx,
4781 ry,
4782 color,
4783 a,
4784 soft,
4785 mode,
4786 lin,
4787 );
4788 return Ok(Value::Unit);
4789 },
4790
4791 "cast_shadow"
4796 | "ทอดเงา"
4797 | "投射阴影"
4798 | "影を落とす"
4799 | "그림자드리우기"
4800 | "افکندن_سایه"
4801 | "ألقِ_ظلا"
4802 | "הטל_צל"
4803 | "سایہ_ڈالو"
4804 | "projeter_ombre"
4805 | "schatten_werfen"
4806 | "отбросить_тень" => {
4807 let cx = self.arg_num(&args, 0, 0.0)? as f32;
4808 let cy = self.arg_num(&args, 1, 0.0)? as f32;
4809 let height = (self.arg_num(&args, 2, 0.0)? as f32).max(0.0);
4810 let mut gfx = self.gfx.borrow_mut();
4811 let sp = gfx.shadow;
4812 let radius = (sp.base + sp.grow * height).max(0.5);
4813 let alpha = (sp.alpha - sp.fade * height).clamp(0.04, 1.0);
4814 let soft = (sp.soft + height * 0.004).clamp(0.0, 0.95);
4815 let (w, h, color, mode, lin) = (
4816 gfx.width,
4817 gfx.height,
4818 gfx.color,
4819 gfx.blend,
4820 gfx.linear_blend,
4821 );
4822 crate::gfx::raster::fill_disc_soft(
4823 &mut gfx.buffer,
4824 w,
4825 h,
4826 cx,
4827 cy,
4828 radius,
4829 radius * 0.62,
4830 color,
4831 alpha,
4832 soft,
4833 mode,
4834 lin,
4835 );
4836 return Ok(Value::Unit);
4837 },
4838
4839 "shadow_params"
4842 | "ตั้งค่าเงา"
4843 | "阴影参数"
4844 | "影設定"
4845 | "그림자설정"
4846 | "پارامترهای_سایه"
4847 | "معاملات_الظل"
4848 | "פרמטרי_צל"
4849 | "سایہ_پیرامیٹرز"
4850 | "paramètres_ombre"
4851 | "schattenparameter"
4852 | "параметры_тени" => {
4853 let cur = self.gfx.borrow().shadow;
4854 let base = self.arg_num(&args, 0, cur.base as f64)? as f32;
4855 let grow = self.arg_num(&args, 1, cur.grow as f64)? as f32;
4856 let alpha = self.arg_num(&args, 2, cur.alpha as f64)? as f32;
4857 let fade = self.arg_num(&args, 3, cur.fade as f64)? as f32;
4858 let soft = self.arg_num(&args, 4, cur.soft as f64)? as f32;
4859 self.gfx.borrow_mut().shadow =
4860 crate::gfx::ShadowParams { base, grow, alpha, fade, soft };
4861 return Ok(Value::Unit);
4862 },
4863
4864 "depth_triangle"
4869 | "สามเหลี่ยมเรียงลึก"
4870 | "深度三角"
4871 | "深度三角形"
4872 | "깊이삼각"
4873 | "مثلث_عمق"
4874 | "مثلث_العمق"
4875 | "משולש_עומק"
4876 | "گہرائی_مثلث"
4877 | "triangle_profondeur"
4878 | "tiefendreieck"
4879 | "глубина_треугольник" => {
4880 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
4881 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
4882 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
4883 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
4884 let x2 = self.arg_num(&args, 4, 0.0)? as f32;
4885 let y2 = self.arg_num(&args, 5, 0.0)? as f32;
4886 let z = self.arg_num(&args, 6, 0.0)? as f32;
4887 let mut gfx = self.gfx.borrow_mut();
4888 let color = gfx.color;
4889 gfx.depth_queue
4890 .push_triangle(z, color, x0, y0, x1, y1, x2, y2);
4891 return Ok(Value::Unit);
4892 },
4893
4894 "depth_line"
4897 | "เส้นเรียงลึก"
4898 | "深度线"
4899 | "深度線"
4900 | "깊이선"
4901 | "خط_عمق"
4902 | "خط_العمق"
4903 | "קו_עומק"
4904 | "گہرائی_لکیر"
4905 | "ligne_profondeur"
4906 | "tiefenlinie"
4907 | "глубина_линия" => {
4908 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
4909 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
4910 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
4911 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
4912 let z = self.arg_num(&args, 4, 0.0)? as f32;
4913 let mut gfx = self.gfx.borrow_mut();
4914 let color = gfx.color;
4915 gfx.depth_queue.push_line(z, color, x0, y0, x1, y1);
4916 return Ok(Value::Unit);
4917 },
4918
4919 "เปิดหน้าต่าง"
4926 | "open_window"
4927 | "gfx_window"
4928 | "开窗"
4929 | "ウィンドウ開く"
4930 | "창열기"
4931 | "باز_کردن_پنجره"
4932 | "افتح_نافذة"
4933 | "פתח_חלון"
4934 | "ونڈو_کھولو" => {
4935 let w = self.arg_num(&args, 0, 800.0)? as usize;
4936 let h = self.arg_num(&args, 1, 600.0)? as usize;
4937 #[cfg(not(target_arch = "wasm32"))]
4938 {
4939 let title = args
4940 .get(2)
4941 .map(|v| v.to_string())
4942 .unwrap_or_else(|| "Ling".into());
4943 let mut gfx = self.gfx.borrow_mut();
4944 let mut win = minifb::Window::new(
4945 &title,
4946 w,
4947 h,
4948 minifb::WindowOptions {
4949 resize: false,
4950 scale: minifb::Scale::X1,
4951 ..Default::default()
4952 },
4953 )
4954 .map_err(|e| EvalErr::from(format!("cannot open window: {e}")))?;
4955 apply_frame_pacing(&mut win, gfx.vsync);
4956 gfx.buffer = vec![0u32; w * h];
4957 gfx.width = w;
4958 gfx.height = h;
4959 gfx.window = Some(win);
4960 gfx.topmost_window = false;
4961 gfx.sync_projection();
4962 }
4963 #[cfg(target_arch = "wasm32")]
4964 {
4965 let mut gfx = self.gfx.borrow_mut();
4966 gfx.width = w;
4967 gfx.height = h;
4968 gfx.buffer.resize(w * h, 0); gfx.sync_projection();
4970 crate::gfx::webgl::resize(w as u32, h as u32);
4971 }
4972 return Ok(Value::Unit);
4973 },
4974
4975 "เติม" | "fill" | "gfx_fill" | "clear" | "填" | "塗り潰し" | "채우기" | "清"
4977 | "消去" | "지우기" | "پر_کن" | "املأ" | "מלא" | "بھرو" => {
4978 let r = self.arg_num(&args, 0, 0.0)? as u32;
4979 let g = self.arg_num(&args, 1, 0.0)? as u32;
4980 let b = self.arg_num(&args, 2, 0.0)? as u32;
4981 #[cfg(not(target_arch = "wasm32"))]
4982 {
4983 let c = (r << 16) | (g << 8) | b;
4984 let mut gfx = self.gfx.borrow_mut();
4985 gfx.buffer.fill(c);
4986 gfx.zbuf_needs_clear = true; gfx.edge_set.clear(); }
4989 #[cfg(target_arch = "wasm32")]
4990 {
4991 let mut gfx = self.gfx.borrow_mut();
4992 gfx.fill_r = r as f32 / 255.0;
4993 gfx.fill_g = g as f32 / 255.0;
4994 gfx.fill_b = b as f32 / 255.0;
4995 let c = (r << 16) | (g << 8) | b;
4996 gfx.buffer.fill(c);
4997 gfx.zbuf_needs_clear = true;
4998 gfx.edge_set.clear();
4999 }
5000 return Ok(Value::Unit);
5001 },
5002
5003 "set_color_hsl"
5006 | "颜色HSL"
5007 | "色相"
5008 | "HSL色"
5009 | "HSL색설정"
5010 | "สีHSLวาด"
5011 | "تنظیم_رنگ_HSL"
5012 | "عيّن_اللون_HSL"
5013 | "קבע_צבע_HSL"
5014 | "HSL_رنگ_مقرر_کرو"
5015 | "définir_couleur_hsl"
5016 | "farbe_hsl_setzen"
5017 | "задать_цвет_hsl" => {
5018 let h = self.arg_num(&args, 0, 0.0)?;
5019 let s = self.arg_num(&args, 1, 70.0)?;
5020 let l = self.arg_num(&args, 2, 50.0)?;
5021 let hex = hsl_to_hex(h, s, l);
5022 let r = u32::from_str_radix(&hex[1..3], 16).unwrap_or(255);
5023 let g = u32::from_str_radix(&hex[3..5], 16).unwrap_or(255);
5024 let b = u32::from_str_radix(&hex[5..7], 16).unwrap_or(255);
5025 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
5026 return Ok(Value::Unit);
5027 },
5028
5029 "สีดินสอ"
5031 | "set_color"
5032 | "gfx_color"
5033 | "color"
5034 | "设色"
5035 | "色設定"
5036 | "색설정"
5037 | "تنظیم_رنگ"
5038 | "عيّن_اللون"
5039 | "קבע_צבע"
5040 | "رنگ_مقرر_کرو" => {
5041 let r = self.arg_num(&args, 0, 255.0)? as u32;
5042 let g = self.arg_num(&args, 1, 255.0)? as u32;
5043 let b = self.arg_num(&args, 2, 255.0)? as u32;
5044 self.gfx.borrow_mut().color = (r << 16) | (g << 8) | b;
5045 return Ok(Value::Unit);
5046 },
5047
5048 "วาดสามเหลี่ยม"
5050 | "draw_triangle"
5051 | "gfx_triangle"
5052 | "triangle"
5053 | "画三角"
5054 | "三角形描画"
5055 | "삼각형그리기"
5056 | "رسم_مثلث"
5057 | "ارسم_مثلثا"
5058 | "צייר_משולש"
5059 | "مثلث_کھینچو" => {
5060 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
5061 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
5062 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
5063 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
5064 let x2 = self.arg_num(&args, 4, 0.0)? as f32;
5065 let y2 = self.arg_num(&args, 5, 0.0)? as f32;
5066 let mut gfx = self.gfx.borrow_mut();
5067 let color = gfx.color;
5068 #[cfg(not(target_arch = "wasm32"))]
5069 {
5070 let w = gfx.width;
5071 let h = gfx.height;
5072 fill_triangle(&mut gfx.buffer, w, h, color, x0, y0, x1, y1, x2, y2);
5073 }
5074 #[cfg(target_arch = "wasm32")]
5075 gfx.depth_queue
5076 .push_triangle(0.0, color, x0, y0, x1, y1, x2, y2);
5077 return Ok(Value::Unit);
5078 },
5079
5080 "วาดเส้น"
5082 | "draw_line"
5083 | "gfx_line"
5084 | "line"
5085 | "画线"
5086 | "線描く"
5087 | "선그리기"
5088 | "رسم_خط"
5089 | "ارسم_خط"
5090 | "צייר_קו"
5091 | "لکیر_کھینچو" => {
5092 let x0 = self.arg_num(&args, 0, 0.0)? as f32;
5093 let y0 = self.arg_num(&args, 1, 0.0)? as f32;
5094 let x1 = self.arg_num(&args, 2, 0.0)? as f32;
5095 let y1 = self.arg_num(&args, 3, 0.0)? as f32;
5096 let mut gfx = self.gfx.borrow_mut();
5097 let color = gfx.color;
5098 #[cfg(not(target_arch = "wasm32"))]
5099 {
5100 let w = gfx.width;
5101 let h = gfx.height;
5102 let aa = gfx.antialias;
5103 let add = gfx.blend == 1;
5104 if aa {
5105 crate::gfx::raster::draw_line_aa(
5106 &mut gfx.buffer,
5107 w,
5108 h,
5109 color,
5110 add,
5111 x0,
5112 y0,
5113 x1,
5114 y1,
5115 );
5116 } else {
5117 draw_line(&mut gfx.buffer, w, h, color, x0, y0, x1, y1);
5118 }
5119 }
5120 #[cfg(target_arch = "wasm32")]
5121 gfx.depth_queue.push_line(0.0, color, x0, y0, x1, y1);
5122 return Ok(Value::Unit);
5123 },
5124
5125 "วาดจุด"
5127 | "draw_pixel"
5128 | "gfx_pixel"
5129 | "pixel"
5130 | "画点"
5131 | "点描く"
5132 | "점그리기"
5133 | "رسم_نقطه"
5134 | "ارسم_نقطة"
5135 | "צייר_פיקסל"
5136 | "پکسل_کھینچو" => {
5137 let px = self.arg_num(&args, 0, 0.0)? as i32;
5138 let py = self.arg_num(&args, 1, 0.0)? as i32;
5139 #[cfg(not(target_arch = "wasm32"))]
5140 {
5141 let mut gfx = self.gfx.borrow_mut();
5142 let color = gfx.color;
5143 let w = gfx.width;
5144 let h = gfx.height;
5145 if px >= 0 && py >= 0 && (px as usize) < w && (py as usize) < h {
5146 gfx.buffer[py as usize * w + px as usize] = color;
5147 }
5148 }
5149 #[cfg(target_arch = "wasm32")]
5150 {
5151 let mut gfx = self.gfx.borrow_mut();
5153 let color = gfx.color;
5154 let x = px as f32;
5155 let y = py as f32;
5156 gfx.depth_queue
5157 .push_triangle(0.0, color, x, y, x + 1.0, y, x + 1.0, y + 1.0);
5158 gfx.depth_queue
5159 .push_triangle(0.0, color, x, y, x + 1.0, y + 1.0, x, y + 1.0);
5160 }
5161 return Ok(Value::Unit);
5162 },
5163
5164 "แสดงผล" | "present" | "gfx_present" | "show" | "显" | "呈现" | "表示" | "표시"
5166 | "نمایش" | "اعرض" | "הצג" | "دکھاؤ" => {
5167 #[cfg(not(target_arch = "wasm32"))]
5180 let pre_update_mouse_down = self
5181 .gfx
5182 .borrow()
5183 .window
5184 .as_ref()
5185 .map(|w| w.get_mouse_down(minifb::MouseButton::Left))
5186 .unwrap_or(false);
5187 #[cfg(not(target_arch = "wasm32"))]
5188 {
5189 ling_fps_tick();
5190 ling_phase_frame();
5191 {
5193 let mut gfx = self.gfx.borrow_mut();
5194 if !gfx.depth_queue.is_empty() {
5195 let w = gfx.width;
5196 let h = gfx.height;
5197 let dt = gfx.depth_test;
5198 let reset_z = gfx.zbuf_needs_clear;
5199 let (bm, ba) = (gfx.blend, gfx.alpha);
5200 let aa = gfx.antialias;
5201 let queue = std::mem::take(&mut gfx.depth_queue);
5202 {
5203 let g = &mut *gfx;
5204 let z = if dt { Some(&mut g.depth_buf) } else { None };
5205 queue.flush(&mut g.buffer, z, reset_z, w, h, aa);
5206 }
5207 gfx.depth_queue.set_state(bm, ba);
5208 gfx.zbuf_needs_clear = false;
5209 }
5210 let _t = std::time::Instant::now();
5211 if !gfx.post_done {
5212 gfx.toon_post_process();
5213 }
5214 gfx.post_done = false;
5215 ling_phase_add(phase::TOON, _t.elapsed().as_nanos());
5216 let w = gfx.width;
5217 let h = gfx.height;
5218 let g = &mut *gfx;
5219 if g.frame_blur > 0.0 {
5220 let a = (g.frame_blur.clamp(0.0, 0.995) * 256.0) as u32;
5225 if g.prev_frame.len() != g.buffer.len() {
5226 g.prev_frame = g.buffer.clone();
5227 }
5228 for (dst, prev) in g.buffer.iter_mut().zip(g.prev_frame.iter_mut()) {
5229 let c = *dst;
5230 let pv = *prev;
5231 let pr = (((pv >> 16) & 0xFF) * a) >> 8;
5232 let pg = (((pv >> 8) & 0xFF) * a) >> 8;
5233 let pb = ((pv & 0xFF) * a) >> 8;
5234 let cr = (c >> 16) & 0xFF;
5235 let cg = (c >> 8) & 0xFF;
5236 let cb = c & 0xFF;
5237 let outp = (cr.max(pr) << 16) | (cg.max(pg) << 8) | cb.max(pb);
5238 *dst = outp;
5239 *prev = outp;
5240 }
5241 }
5242 if let Some(win) = g.window.as_mut() {
5243 let _b = std::time::Instant::now();
5244 win.update_with_buffer(&g.buffer, w, h)
5245 .map_err(|e| EvalErr::from(format!("present error: {e}")))?;
5246 ling_phase_add(phase::BLIT, _b.elapsed().as_nanos());
5247 }
5248 }
5249 let mouse_pos = {
5251 let gfx = self.gfx.borrow();
5252 gfx.window
5253 .as_ref()
5254 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
5255 };
5256 let mut gfx = self.gfx.borrow_mut();
5257 if gfx.mouse_captured {
5258 let w = gfx.width as f32;
5259 let h = gfx.height as f32;
5260 if let Some((mx, my)) = mouse_pos {
5261 if gfx.last_mx.is_nan() {
5262 gfx.mouse_dx = 0.0;
5263 gfx.mouse_dy = 0.0;
5264 gfx.last_mx = mx;
5265 gfx.last_my = my;
5266 } else {
5267 gfx.mouse_dx = mx - gfx.last_mx;
5268 gfx.mouse_dy = my - gfx.last_my;
5269 let margin = 6.0;
5272 let (mut nx, mut ny, mut warp) = (mx, my, false);
5273 if mx < margin {
5274 nx = w - margin - 2.0;
5275 warp = true;
5276 } else if mx > w - margin {
5277 nx = margin + 2.0;
5278 warp = true;
5279 }
5280 if my < margin {
5281 ny = h - margin - 2.0;
5282 warp = true;
5283 } else if my > h - margin {
5284 ny = margin + 2.0;
5285 warp = true;
5286 }
5287 if warp {
5288 #[cfg(windows)]
5289 unsafe {
5290 #[repr(C)]
5291 struct RECT {
5292 left: i32,
5293 top: i32,
5294 right: i32,
5295 bottom: i32,
5296 }
5297 extern "system" {
5298 fn GetForegroundWindow() -> isize;
5299 fn GetWindowRect(hwnd: isize, lpRect: *mut RECT)
5300 -> i32;
5301 fn SetCursorPos(x: i32, y: i32) -> i32;
5302 }
5303 let hwnd = GetForegroundWindow();
5304 let mut rect =
5305 RECT { left: 0, top: 0, right: 0, bottom: 0 };
5306 if GetWindowRect(hwnd, &mut rect) != 0 {
5307 SetCursorPos(
5308 rect.left + nx as i32,
5309 rect.top + ny as i32,
5310 );
5311 }
5312 }
5313 gfx.last_mx = nx;
5314 gfx.last_my = ny;
5315 } else {
5316 gfx.last_mx = mx;
5317 gfx.last_my = my;
5318 }
5319 }
5320 } else {
5321 gfx.mouse_dx = 0.0;
5322 gfx.mouse_dy = 0.0;
5323 }
5324 } else if let Some((mx, my)) = mouse_pos {
5325 if gfx.last_mx.is_nan() {
5326 gfx.mouse_dx = 0.0;
5327 gfx.mouse_dy = 0.0;
5328 } else {
5329 gfx.mouse_dx = mx - gfx.last_mx;
5330 gfx.mouse_dy = my - gfx.last_my;
5331 }
5332 gfx.last_mx = mx;
5333 gfx.last_my = my;
5334 } else {
5335 gfx.mouse_dx = 0.0;
5336 gfx.mouse_dy = 0.0;
5337 }
5338
5339 let is_active = gfx.window.as_mut().map(|w| w.is_active()).unwrap_or(true);
5347 if is_active && !gfx.was_active {
5348 gfx.focus_grace_frames = 5;
5349 #[cfg(windows)]
5352 if gfx.topmost_window {
5353 if let Some(w) = gfx.window.as_ref() {
5354 set_window_topmost(w.get_window_handle() as isize, true);
5355 }
5356 }
5357 } else if !is_active && gfx.was_active {
5358 #[cfg(windows)]
5364 if gfx.topmost_window {
5365 if let Some(w) = gfx.window.as_ref() {
5366 set_window_topmost(w.get_window_handle() as isize, false);
5367 }
5368 }
5369 }
5370 gfx.was_active = is_active;
5371 if gfx.focus_grace_frames > 0 {
5372 gfx.focus_grace_frames -= 1;
5373 }
5374 }
5375 #[cfg(target_arch = "wasm32")]
5376 {
5377 {
5378 let mut gfx = self.gfx.borrow_mut();
5382 let w = gfx.width;
5383 let h = gfx.height;
5384 if gfx.buffer.len() != w * h {
5385 gfx.buffer.resize(w * h, 0);
5386 }
5387 if !gfx.depth_queue.is_empty() {
5388 let dt = gfx.depth_test;
5389 let reset_z = gfx.zbuf_needs_clear;
5390 let aa = gfx.antialias;
5391 let queue = std::mem::take(&mut gfx.depth_queue);
5392 {
5393 let g = &mut *gfx;
5394 let z = if dt { Some(&mut g.depth_buf) } else { None };
5395 queue.flush(&mut g.buffer, z, reset_z, w, h, aa);
5396 }
5397 gfx.zbuf_needs_clear = false;
5398 }
5399 if !gfx.post_done {
5400 gfx.toon_post_process();
5401 }
5402 gfx.post_done = false;
5403 crate::gfx::webgl::blit_rgb(&gfx.buffer, w, h);
5404 }
5405 self.wasm_pace_frame();
5406 }
5407 #[cfg(not(target_arch = "wasm32"))]
5411 {
5412 self.mouse_was_down = pre_update_mouse_down;
5413 }
5414 self.frame_num += 1;
5416 return Ok(Value::Unit);
5417 },
5418
5419 "เปิดหน้าต่างเต็มจอ"
5421 | "open_fullscreen"
5422 | "fullscreen"
5423 | "全屏"
5424 | "全画面"
5425 | "전체화면"
5426 | "باز_کردن_تمامصفحه"
5427 | "افتح_ملء_الشاشة"
5428 | "פתח_מסך_מלא"
5429 | "فل_سکرین_کھولو" => {
5430 #[cfg(target_arch = "wasm32")]
5433 let (default_w, default_h) = {
5434 let (cw, ch) = crate::gfx::webgl::canvas_size();
5435 (cw as f64, ch as f64)
5436 };
5437 #[cfg(all(not(target_arch = "wasm32"), windows))]
5439 let (default_w, default_h) = unsafe {
5440 extern "system" {
5441 fn GetSystemMetrics(nIndex: i32) -> i32;
5442 }
5443 (GetSystemMetrics(0) as f64, GetSystemMetrics(1) as f64)
5444 };
5445 #[cfg(all(not(target_arch = "wasm32"), not(windows)))]
5446 let (default_w, default_h) = native_screen_size();
5447
5448 let w = args
5449 .get(1)
5450 .map(|v| self.to_number(v).unwrap_or(default_w) as usize)
5451 .unwrap_or(default_w as usize);
5452 let h = args
5453 .get(2)
5454 .map(|v| self.to_number(v).unwrap_or(default_h) as usize)
5455 .unwrap_or(default_h as usize);
5456 #[cfg(not(target_arch = "wasm32"))]
5457 {
5458 let title = args
5459 .first()
5460 .map(|v| v.to_string())
5461 .unwrap_or_else(|| "Ling".into());
5462 let mut gfx = self.gfx.borrow_mut();
5463 let mut win = minifb::Window::new(
5464 &title,
5465 w,
5466 h,
5467 minifb::WindowOptions {
5468 borderless: true,
5469 title: false,
5470 resize: false,
5471 topmost: true,
5472 scale: minifb::Scale::X1,
5473 ..Default::default()
5474 },
5475 )
5476 .map_err(|e| EvalErr::from(format!("cannot open fullscreen: {e}")))?;
5477 apply_frame_pacing(&mut win, gfx.vsync);
5478 #[cfg(windows)]
5480 let hwnd = win.get_window_handle() as isize;
5481 gfx.buffer = vec![0u32; w * h];
5482 gfx.width = w;
5483 gfx.height = h;
5484 gfx.window = Some(win);
5485 gfx.topmost_window = true;
5486 #[cfg(windows)]
5487 {
5488 gfx.hwnd = hwnd;
5489 }
5490 gfx.sync_projection();
5491 #[cfg(windows)]
5493 make_borderless_fullscreen(hwnd, w as i32, h as i32);
5494 #[cfg(windows)]
5495 force_window_focus(hwnd);
5496 }
5497 #[cfg(target_arch = "wasm32")]
5498 {
5499 let mut gfx = self.gfx.borrow_mut();
5500 gfx.width = w;
5501 gfx.height = h;
5502 gfx.buffer.resize(w * h, 0); gfx.sync_projection();
5504 crate::gfx::webgl::resize(w as u32, h as u32);
5505 }
5506 return Ok(Value::Unit);
5507 },
5508
5509 "get_width"
5511 | "ความกว้าง"
5512 | "宽"
5513 | "幅取得"
5514 | "너비"
5515 | "عرض"
5516 | "العرض"
5517 | "רוחב"
5518 | "چوڑائی"
5519 | "obtenir_largeur"
5520 | "breite_abrufen"
5521 | "получить_ширину" => {
5522 return Ok(Value::Number(self.gfx.borrow().width as f64));
5523 },
5524 "get_height"
5525 | "ความสูง"
5526 | "高"
5527 | "高取得"
5528 | "높이"
5529 | "ارتفاع"
5530 | "الارتفاع"
5531 | "גובה"
5532 | "اونچائی"
5533 | "obtenir_hauteur"
5534 | "höhe_abrufen"
5535 | "получить_высоту" => {
5536 return Ok(Value::Number(self.gfx.borrow().height as f64));
5537 },
5538
5539 "monitor_width"
5542 | "screen_width"
5543 | "屏宽"
5544 | "画面幅"
5545 | "화면너비"
5546 | "ความกว้างจอ"
5547 | "عرض_مانیتور"
5548 | "عرض_الشاشة"
5549 | "רוחב_צג"
5550 | "مانیٹر_چوڑائی"
5551 | "largeur_moniteur"
5552 | "monitor_breite"
5553 | "ширина_монитора" => {
5554 return Ok(Value::Number(monitor_info().0 as f64));
5555 },
5556 "monitor_height"
5558 | "screen_height"
5559 | "屏高"
5560 | "画面高"
5561 | "화면높이"
5562 | "ความสูงจอ"
5563 | "ارتفاع_مانیتور"
5564 | "ارتفاع_الشاشة"
5565 | "גובה_צג"
5566 | "مانیٹر_اونچائی"
5567 | "hauteur_moniteur"
5568 | "monitor_höhe"
5569 | "высота_монитора" => {
5570 return Ok(Value::Number(monitor_info().1 as f64));
5571 },
5572 "monitor_refresh"
5574 | "monitor_hz"
5575 | "monitor_fps"
5576 | "refresh_rate"
5577 | "刷新率"
5578 | "リフレッシュレート"
5579 | "주사율"
5580 | "อัตรารีเฟรช"
5581 | "نرخ_بروزرسانی_مانیتور"
5582 | "معدل_تحديث_الشاشة"
5583 | "קצב_רענון_צג"
5584 | "مانیٹر_ریفریش_ریٹ"
5585 | "fréquence_moniteur"
5586 | "monitor_bildwiederholrate"
5587 | "частота_монитора" => {
5588 return Ok(Value::Number(monitor_info().2 as f64));
5589 },
5590 "monitor_info"
5592 | "screen_info"
5593 | "屏幕信息"
5594 | "画面情報"
5595 | "화면정보"
5596 | "ข้อมูลจอ"
5597 | "اطلاعات_مانیتور"
5598 | "معلومات_الشاشة"
5599 | "מידע_צג"
5600 | "مانیٹر_معلومات"
5601 | "info_moniteur"
5602 | "bildschirminfo"
5603 | "инфо_монитора" => {
5604 let (w, h, hz) = monitor_info();
5605 return Ok(Value::List(Rc::new(vec![
5606 Value::Number(w as f64),
5607 Value::Number(h as f64),
5608 Value::Number(hz as f64),
5609 ])));
5610 },
5611 "set_fps"
5613 | "set_target_fps"
5614 | "target_fps"
5615 | "设帧率"
5616 | "フレームレート設定"
5617 | "프레임설정"
5618 | "ตั้งเฟรมเรต"
5619 | "تنظیم_نرخ_فریم"
5620 | "عيّن_معدل_الإطارات"
5621 | "קבע_קצב_פריימים"
5622 | "ایف_پی_ایس_مقرر_کرو"
5623 | "définir_fps"
5624 | "fps_setzen"
5625 | "задать_fps" => {
5626 #[cfg(not(target_arch = "wasm32"))]
5627 {
5628 let fps = self.arg_num(&args, 0, 60.0)?.max(1.0) as usize;
5629 let mut gfx = self.gfx.borrow_mut();
5630 if let Some(win) = gfx.window.as_mut() {
5631 win.set_target_fps(fps);
5632 }
5633 }
5634 #[cfg(target_arch = "wasm32")]
5635 {
5636 self.wasm_target_fps = self.arg_num(&args, 0, 60.0)?.max(1.0);
5637 self.wasm_next_present_ms = 0.0;
5638 }
5639 return Ok(Value::Unit);
5640 },
5641
5642 "set_vsync"
5646 | "vsync"
5647 | "垂直同步"
5648 | "垂直同期"
5649 | "수직동기"
5650 | "ตั้งวีซิงก์"
5651 | "تنظیم_ویسینک"
5652 | "عيّن_تزامن_رأسي"
5653 | "קבע_וי_סינק"
5654 | "وی_سینک_مقرر_کرو"
5655 | "définir_vsync"
5656 | "vsync_setzen"
5657 | "задать_vsync" => {
5658 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
5659 #[cfg(not(target_arch = "wasm32"))]
5660 {
5661 let mut gfx = self.gfx.borrow_mut();
5662 gfx.vsync = on;
5663 if let Some(win) = gfx.window.as_mut() {
5664 apply_frame_pacing(win, on);
5665 }
5666 }
5667 #[cfg(target_arch = "wasm32")]
5668 {
5669 self.wasm_target_fps = if on { monitor_info().2 as f64 } else { 240.0 };
5670 self.wasm_next_present_ms = 0.0;
5671 }
5672 return Ok(Value::Unit);
5673 },
5674
5675 "หน้าต่างเปิดอยู่"
5677 | "window_is_open"
5678 | "gfx_is_open"
5679 | "is_open"
5680 | "窗开"
5681 | "開いている"
5682 | "창열림"
5683 | "پنجره_باز_است"
5684 | "النافذة_مفتوحة"
5685 | "החלון_פתוח"
5686 | "ونڈو_کھلی_ہے" => {
5687 #[cfg(not(target_arch = "wasm32"))]
5688 {
5689 let gfx = self.gfx.borrow();
5690 if gfx.want_quit {
5691 return Ok(Value::Bool(false));
5692 }
5693 #[cfg(windows)]
5699 let escape_down = if gfx.topmost_window {
5700 window_is_foreground(gfx.hwnd) && os_key_down(0x1B) } else {
5702 gfx.window
5703 .as_ref()
5704 .map(|w| w.is_key_down(minifb::Key::Escape))
5705 .unwrap_or(false)
5706 };
5707 #[cfg(not(windows))]
5708 let escape_down = gfx
5709 .window
5710 .as_ref()
5711 .map(|w| w.is_key_down(minifb::Key::Escape))
5712 .unwrap_or(false);
5713 let open =
5714 gfx.window.as_ref().map(|w| w.is_open()).unwrap_or(false) && !escape_down;
5715 return Ok(Value::Bool(open));
5716 }
5717 #[cfg(target_arch = "wasm32")]
5718 return Ok(Value::Bool(true));
5719 },
5720
5721 "quit" | "exit_game" | "close_window" => {
5724 #[cfg(not(target_arch = "wasm32"))]
5725 {
5726 self.gfx.borrow_mut().want_quit = true;
5727 }
5728 return Ok(Value::Unit);
5729 },
5730
5731 "key_down"
5733 | "กดค้าง"
5734 | "按键"
5735 | "キー押す"
5736 | "키누름"
5737 | "کلید_فشرده"
5738 | "المفتاح_مضغوط"
5739 | "מקש_לחוץ"
5740 | "بٹن_دبا_ہوا"
5741 | "touche_enfoncée"
5742 | "taste_gedrückt"
5743 | "клавиша_нажата" => {
5744 #[cfg(not(target_arch = "wasm32"))]
5745 {
5746 let name = self.arg_str(&args, 0, "");
5747 let mut gfx = self.gfx.borrow_mut();
5748 #[cfg(windows)]
5756 if gfx.topmost_window {
5757 if !window_is_foreground(gfx.hwnd) {
5758 return Ok(Value::Bool(false));
5759 }
5760 return Ok(Value::Bool(
5761 str_to_vk(&name).map(os_key_down).unwrap_or(false),
5762 ));
5763 }
5764 if gfx.input_suppressed() {
5765 return Ok(Value::Bool(false));
5766 }
5767 let down = gfx
5768 .window
5769 .as_ref()
5770 .and_then(|w| str_to_minifb_key(&name).map(|k| w.is_key_down(k)))
5771 .unwrap_or(false);
5772 return Ok(Value::Bool(down));
5773 }
5774 #[cfg(target_arch = "wasm32")]
5775 {
5776 let name = self.arg_str(&args, 0, "");
5777 return Ok(Value::Bool(crate::gfx::wasm_is_key_down(&name)));
5778 }
5779 },
5780
5781 "key_pressed"
5783 | "กดปุ่ม"
5784 | "键按"
5785 | "キー押した"
5786 | "키눌림"
5787 | "فشردن_کلید"
5788 | "ضغط_المفتاح"
5789 | "לחיצת_מקש"
5790 | "بٹن_دبانا"
5791 | "touche_appuyée"
5792 | "taste_getippt"
5793 | "клавиша_нажатие" => {
5794 #[cfg(not(target_arch = "wasm32"))]
5795 {
5796 let name = self.arg_str(&args, 0, "");
5797 let pressed = {
5798 let mut gfx = self.gfx.borrow_mut();
5799 #[cfg(windows)]
5800 let topmost = gfx.topmost_window;
5801 #[cfg(not(windows))]
5802 let topmost = false;
5803 if topmost {
5804 #[cfg(windows)]
5805 {
5806 if !window_is_foreground(gfx.hwnd) {
5807 false
5808 } else {
5809 match str_to_vk(&name) {
5810 Some(vk) => {
5811 let idx = (vk as usize) & 0xFF;
5812 let down = os_key_down(vk);
5813 let was = gfx.raw_keys_prev[idx];
5814 gfx.raw_keys_prev[idx] = down;
5815 down && !was
5816 },
5817 None => false,
5818 }
5819 }
5820 }
5821 #[cfg(not(windows))]
5822 {
5823 false
5824 }
5825 } else if gfx.input_suppressed() {
5826 false
5827 } else {
5828 gfx.window
5829 .as_ref()
5830 .and_then(|w| {
5831 str_to_minifb_key(&name)
5832 .map(|k| w.is_key_pressed(k, minifb::KeyRepeat::No))
5833 })
5834 .unwrap_or(false)
5835 }
5836 };
5837 let pressed =
5839 pressed || ((name == "enter" || name == "return") && gamepad::start_edge());
5840 return Ok(Value::Bool(pressed));
5841 }
5842 #[cfg(target_arch = "wasm32")]
5843 {
5844 let name = self.arg_str(&args, 0, "");
5845 let pressed = crate::gfx::wasm_is_key_pressed(&name);
5846 return Ok(Value::Bool(pressed));
5847 }
5848 },
5849
5850 "mouse_dx"
5852 | "เมาส์X"
5853 | "鼠ΔX"
5854 | "マウスΔX"
5855 | "마우스ΔX"
5856 | "دلتا_ماوس_ایکس"
5857 | "فارق_الفأرة_س"
5858 | "דלתא_עכבר_X"
5859 | "ماؤس_ڈیلٹا_ایکس"
5860 | "souris_dx"
5861 | "maus_dx"
5862 | "мышь_dx" => {
5863 #[cfg(not(target_arch = "wasm32"))]
5864 return Ok(Value::Number(self.gfx.borrow().mouse_dx as f64));
5865 #[cfg(target_arch = "wasm32")]
5866 return Ok(Value::Number(crate::gfx::wasm_mouse_dx() as f64));
5867 },
5868 #[cfg(not(target_arch = "wasm32"))]
5870 "mouse_scroll"
5871 | "ล้อเมาส์"
5872 | "滚轮"
5873 | "ホイール"
5874 | "스크롤"
5875 | "غلتک_ماوس"
5876 | "عجلة_الفأرة"
5877 | "גלגלת_עכבר"
5878 | "ماؤس_اسکرول" => {
5879 let gfx = self.gfx.borrow();
5880 let s = gfx
5881 .window
5882 .as_ref()
5883 .and_then(|w| w.get_scroll_wheel())
5884 .map(|(_, y)| y as f64)
5885 .unwrap_or(0.0);
5886 return Ok(Value::Number(s));
5887 },
5888 #[cfg(target_arch = "wasm32")]
5889 "mouse_scroll"
5890 | "ล้อเมาส์"
5891 | "滚轮"
5892 | "ホイール"
5893 | "스크롤"
5894 | "غلتک_ماوس"
5895 | "عجلة_الفأرة"
5896 | "גלגלת_עכבר"
5897 | "ماؤس_اسکرول" => {
5898 return Ok(Value::Number(0.0));
5899 },
5900 "mouse_dy"
5901 | "เมาส์Y"
5902 | "鼠ΔY"
5903 | "マウスΔY"
5904 | "마우스ΔY"
5905 | "دلتا_ماوس_ایگرگ"
5906 | "فارق_الفأرة_ص"
5907 | "דלתא_עכבר_Y"
5908 | "ماؤس_ڈیلٹا_وائی"
5909 | "souris_dy"
5910 | "maus_dy"
5911 | "мышь_dy" => {
5912 #[cfg(not(target_arch = "wasm32"))]
5913 return Ok(Value::Number(self.gfx.borrow().mouse_dy as f64));
5914 #[cfg(target_arch = "wasm32")]
5915 return Ok(Value::Number(crate::gfx::wasm_mouse_dy() as f64));
5916 },
5917
5918 "pad_poll"
5921 | "手柄轮询"
5922 | "パッド更新"
5923 | "패드폴링"
5924 | "อัปเดตแพด"
5925 | "بررسی_دسته"
5926 | "استطلع_اليد"
5927 | "בדוק_בקר"
5928 | "گیم_پیڈ_پول"
5929 | "interroger_manette"
5930 | "gamepad_abfragen"
5931 | "опросить_геймпад" => {
5932 #[cfg(not(target_arch = "wasm32"))]
5933 return Ok(Value::Number(self.pad_poll() as f64));
5934 #[cfg(target_arch = "wasm32")]
5935 return Ok(Value::Number(input_web::poll() as f64));
5936 },
5937 "pad_count"
5939 | "手柄数"
5940 | "パッド数"
5941 | "패드수"
5942 | "จำนวนแพด"
5943 | "تعداد_دسته"
5944 | "عدد_أيدي_التحكم"
5945 | "מספר_בקרים"
5946 | "گیم_پیڈ_تعداد"
5947 | "nombre_manettes"
5948 | "gamepad_anzahl"
5949 | "число_геймпадов" => {
5950 #[cfg(not(target_arch = "wasm32"))]
5951 {
5952 let inp = self.input.borrow();
5953 let n = inp.as_ref().map_or(0, |s| s.sensorium.devices.count());
5954 return Ok(Value::Number(n as f64));
5955 }
5956 #[cfg(target_arch = "wasm32")]
5957 return Ok(Value::Number(input_web::count() as f64));
5958 },
5959 "pad_connected"
5961 | "手柄连接"
5962 | "パッド接続"
5963 | "패드연결"
5964 | "แพดเชื่อม"
5965 | "دسته_متصل"
5966 | "يد_التحكم_متصلة"
5967 | "בקר_מחובר"
5968 | "گیم_پیڈ_منسلک"
5969 | "manette_connectée"
5970 | "gamepad_verbunden"
5971 | "геймпад_подключён" => {
5972 #[cfg(not(target_arch = "wasm32"))]
5973 {
5974 let i = self.arg_num(&args, 0, 0.0)? as usize;
5975 let inp = self.input.borrow();
5976 let c = inp
5977 .as_ref()
5978 .is_some_and(|s| s.sensorium.devices.for_player(i as u8).is_some());
5979 return Ok(Value::Bool(c));
5980 }
5981 #[cfg(target_arch = "wasm32")]
5982 {
5983 let i = self.arg_num(&args, 0, 0.0)? as usize;
5984 return Ok(Value::Bool(input_web::is_connected(i)));
5985 }
5986 },
5987 "pad_button"
5989 | "手柄按键"
5990 | "パッドボタン"
5991 | "패드버튼"
5992 | "ปุ่มแพด"
5993 | "دکمه_دسته"
5994 | "زر_اليد"
5995 | "כפתור_בקר"
5996 | "گیم_پیڈ_بٹن"
5997 | "bouton_manette"
5998 | "gamepad_taste"
5999 | "кнопка_геймпада" => {
6000 #[cfg(not(target_arch = "wasm32"))]
6001 {
6002 let i = self.arg_num(&args, 0, 0.0)? as usize;
6003 let name = self.arg_str(&args, 1, "");
6004 let down = parse_pad_button(&name)
6005 .is_some_and(|b| self.with_pad(i, false, |p| p.is_down(b)));
6006 return Ok(Value::Bool(down));
6007 }
6008 #[cfg(target_arch = "wasm32")]
6009 {
6010 let i = self.arg_num(&args, 0, 0.0)? as usize;
6011 let name = self.arg_str(&args, 1, "");
6012 return Ok(Value::Bool(input_web::button_down(i, &name)));
6013 }
6014 },
6015 "pad_pressed"
6018 | "手柄按下"
6019 | "パッド押下"
6020 | "패드눌림"
6021 | "แพดกด"
6022 | "دکمه_دسته_فشرده"
6023 | "زر_اليد_مضغوط"
6024 | "כפתור_בקר_לחוץ"
6025 | "گیم_پیڈ_دبایا"
6026 | "manette_appuyée"
6027 | "gamepad_gedrückt"
6028 | "геймпад_нажат" => {
6029 #[cfg(not(target_arch = "wasm32"))]
6030 {
6031 let i = self.arg_num(&args, 0, 0.0)? as usize;
6032 let name = self.arg_str(&args, 1, "");
6033 let p = parse_pad_button(&name)
6034 .is_some_and(|b| self.with_pad(i, false, |g| g.just_pressed(b)));
6035 return Ok(Value::Bool(p));
6036 }
6037 #[cfg(target_arch = "wasm32")]
6038 {
6039 let i = self.arg_num(&args, 0, 0.0)? as usize;
6040 let name = self.arg_str(&args, 1, "");
6041 return Ok(Value::Bool(input_web::button_down(i, &name)));
6042 }
6043 },
6044 "pad_lx"
6046 | "手柄左X"
6047 | "パッド左X"
6048 | "패드왼X"
6049 | "แพดซ้ายX"
6050 | "آنالوگ_چپ_ایکس"
6051 | "عصا_اليسرى_س"
6052 | "ג'ויסטיק_שמאל_X"
6053 | "بائیں_اسٹک_ایکس"
6054 | "manette_axe_gauche_x"
6055 | "gamepad_lx"
6056 | "геймпад_ось_лево_x" => {
6057 #[cfg(not(target_arch = "wasm32"))]
6058 {
6059 let i = self.arg_num(&args, 0, 0.0)? as usize;
6060 return Ok(Value::Number(
6061 self.with_pad(i, 0.0, |p| p.left_stick.x as f64),
6062 ));
6063 }
6064 #[cfg(target_arch = "wasm32")]
6065 {
6066 let i = self.arg_num(&args, 0, 0.0)? as usize;
6067 return Ok(Value::Number(input_web::axis_lx(i) as f64));
6068 }
6069 },
6070 "pad_ly"
6071 | "手柄左Y"
6072 | "パッド左Y"
6073 | "패드왼Y"
6074 | "แพดซ้ายY"
6075 | "آنالوگ_چپ_ایگرگ"
6076 | "عصا_اليسرى_ص"
6077 | "ג'ויסטיק_שמאל_Y"
6078 | "بائیں_اسٹک_وائی"
6079 | "manette_axe_gauche_y"
6080 | "gamepad_ly"
6081 | "геймпад_ось_лево_y" => {
6082 #[cfg(not(target_arch = "wasm32"))]
6083 {
6084 let i = self.arg_num(&args, 0, 0.0)? as usize;
6085 return Ok(Value::Number(
6086 self.with_pad(i, 0.0, |p| p.left_stick.y as f64),
6087 ));
6088 }
6089 #[cfg(target_arch = "wasm32")]
6090 {
6091 let i = self.arg_num(&args, 0, 0.0)? as usize;
6092 return Ok(Value::Number(input_web::axis_ly(i) as f64));
6093 }
6094 },
6095 "pad_rx"
6096 | "手柄右X"
6097 | "パッド右X"
6098 | "패드오X"
6099 | "แพดขวาX"
6100 | "آنالوگ_راست_ایکس"
6101 | "عصا_اليمنى_س"
6102 | "ג'ויסטיק_ימין_X"
6103 | "دائیں_اسٹک_ایکس"
6104 | "manette_axe_droit_x"
6105 | "gamepad_rx"
6106 | "геймпад_ось_право_x" => {
6107 #[cfg(not(target_arch = "wasm32"))]
6108 {
6109 let i = self.arg_num(&args, 0, 0.0)? as usize;
6110 return Ok(Value::Number(
6111 self.with_pad(i, 0.0, |p| p.right_stick.x as f64),
6112 ));
6113 }
6114 #[cfg(target_arch = "wasm32")]
6115 {
6116 let i = self.arg_num(&args, 0, 0.0)? as usize;
6117 return Ok(Value::Number(input_web::axis_rx(i) as f64));
6118 }
6119 },
6120 "pad_ry"
6121 | "手柄右Y"
6122 | "パッド右Y"
6123 | "패드오Y"
6124 | "แพดขวาY"
6125 | "آنالوگ_راست_ایگرگ"
6126 | "عصا_اليمنى_ص"
6127 | "ג'ויסטיק_ימין_Y"
6128 | "دائیں_اسٹک_وائی"
6129 | "manette_axe_droit_y"
6130 | "gamepad_ry"
6131 | "геймпад_ось_право_y" => {
6132 #[cfg(not(target_arch = "wasm32"))]
6133 {
6134 let i = self.arg_num(&args, 0, 0.0)? as usize;
6135 return Ok(Value::Number(
6136 self.with_pad(i, 0.0, |p| p.right_stick.y as f64),
6137 ));
6138 }
6139 #[cfg(target_arch = "wasm32")]
6140 {
6141 let i = self.arg_num(&args, 0, 0.0)? as usize;
6142 return Ok(Value::Number(input_web::axis_ry(i) as f64));
6143 }
6144 },
6145 "pad_lt"
6147 | "手柄左扳机"
6148 | "パッド左トリガー"
6149 | "패드왼트리거"
6150 | "ไกแพดซ้าย"
6151 | "ماشه_چپ"
6152 | "زناد_اليسار"
6153 | "הדק_שמאל"
6154 | "بائیں_ٹریگر"
6155 | "manette_gâchette_gauche"
6156 | "gamepad_lt"
6157 | "геймпад_триггер_лево" => {
6158 #[cfg(not(target_arch = "wasm32"))]
6159 {
6160 let i = self.arg_num(&args, 0, 0.0)? as usize;
6161 return Ok(Value::Number(
6162 self.with_pad(i, 0.0, |p| p.left_trigger as f64),
6163 ));
6164 }
6165 #[cfg(target_arch = "wasm32")]
6166 {
6167 let i = self.arg_num(&args, 0, 0.0)? as usize;
6168 return Ok(Value::Number(input_web::trigger_lt(i) as f64));
6169 }
6170 },
6171 "pad_rt"
6172 | "手柄右扳机"
6173 | "パッド右トリガー"
6174 | "패드오트리거"
6175 | "ไกแพดขวา"
6176 | "ماشه_راست"
6177 | "زناد_اليمين"
6178 | "הדק_ימין"
6179 | "دائیں_ٹریگر"
6180 | "manette_gâchette_droite"
6181 | "gamepad_rt"
6182 | "геймпад_триггер_право" => {
6183 #[cfg(not(target_arch = "wasm32"))]
6184 {
6185 let i = self.arg_num(&args, 0, 0.0)? as usize;
6186 return Ok(Value::Number(
6187 self.with_pad(i, 0.0, |p| p.right_trigger as f64),
6188 ));
6189 }
6190 #[cfg(target_arch = "wasm32")]
6191 {
6192 let i = self.arg_num(&args, 0, 0.0)? as usize;
6193 return Ok(Value::Number(input_web::trigger_rt(i) as f64));
6194 }
6195 },
6196 "pad_rumble"
6198 | "手柄震动"
6199 | "パッド振動"
6200 | "패드진동"
6201 | "แพดสั่น"
6202 | "لرزش_دسته"
6203 | "اهتزاز_اليد"
6204 | "רטט_בקר"
6205 | "گیم_پیڈ_تھرتھراہٹ"
6206 | "vibration_manette"
6207 | "gamepad_vibration"
6208 | "вибрация_геймпада" => {
6209 #[cfg(not(target_arch = "wasm32"))]
6210 {
6211 use ling_input::backend::InputBackend;
6212 let i = self.arg_num(&args, 0, 0.0)? as usize;
6213 let lo = self.arg_num(&args, 1, 0.0)? as f32;
6214 let hi = self.arg_num(&args, 2, lo as f64)? as f32;
6215 let mut inp = self.input.borrow_mut();
6216 if let Some(s) = inp.as_mut() {
6217 if let Some(dev) = s.sensorium.devices.for_player(i as u8).map(|d| d.id) {
6218 s.backend.set_rumble(
6219 dev,
6220 ling_input::Rumble { low: lo, high: hi, ..Default::default() },
6221 );
6222 }
6223 }
6224 return Ok(Value::Unit);
6225 }
6226 #[cfg(target_arch = "wasm32")]
6227 return Ok(Value::Unit);
6228 },
6229
6230 "set_camera_pos"
6232 | "ตั้งตำแหน่งกล้อง"
6233 | "镜坐标"
6234 | "カメラ座標"
6235 | "카메라좌표"
6236 | "تنظیم_موقعیت_دوربین"
6237 | "عيّن_موضع_الكاميرا"
6238 | "קבע_מיקום_מצלמה"
6239 | "کیمرہ_مقام_مقرر_کرو"
6240 | "définir_position_caméra"
6241 | "kameraposition_setzen"
6242 | "задать_позицию_камеры" => {
6243 let x = self.arg_num(&args, 0, 0.0)? as f32;
6244 let y = self.arg_num(&args, 1, 0.0)? as f32;
6245 let z = self.arg_num(&args, 2, 0.0)? as f32;
6246 {
6247 let mut gfx = self.gfx.borrow_mut();
6248 gfx.camera.tx = x;
6249 gfx.camera.ty = y;
6250 gfx.camera.tz = z;
6251 }
6252 #[cfg(not(target_arch = "wasm32"))]
6253 if let Some(audio) = &self.audio {
6254 audio.set_listener_pos(x, y, z);
6255 }
6256 return Ok(Value::Unit);
6257 },
6258
6259 "move_camera" => {
6261 let dx = self.arg_num(&args, 0, 0.0)? as f32;
6262 let dy = self.arg_num(&args, 1, 0.0)? as f32;
6263 let dz = self.arg_num(&args, 2, 0.0)? as f32;
6264 let mut gfx = self.gfx.borrow_mut();
6265 gfx.camera.tx += dx;
6266 gfx.camera.ty += dy;
6267 gfx.camera.tz += dz;
6268 return Ok(Value::Unit);
6269 },
6270
6271 "set_zdist"
6273 | "ตั้งระยะห่าง"
6274 | "镜距"
6275 | "Z距離設定"
6276 | "Z거리설정"
6277 | "تنظیم_فاصله_عمق"
6278 | "عيّن_مسافة_العمق"
6279 | "קבע_מרחק_עומק"
6280 | "گہرائی_فاصلہ_مقرر_کرو"
6281 | "définir_distance_z"
6282 | "z_abstand_setzen"
6283 | "задать_дистанцию_z" => {
6284 let d = self.arg_num(&args, 0, 5.0)? as f32;
6285 self.gfx.borrow_mut().camera.zdist = d;
6286 return Ok(Value::Unit);
6287 },
6288
6289 "capture_mouse"
6291 | "จับเมาส์"
6292 | "捕鼠"
6293 | "マウス捕捉"
6294 | "마우스잡기"
6295 | "ضبط_ماوس"
6296 | "امسك_الفأرة"
6297 | "לכוד_עכבר"
6298 | "ماؤس_پکڑو"
6299 | "capturer_souris"
6300 | "maus_erfassen"
6301 | "захватить_мышь" => {
6302 #[cfg(not(target_arch = "wasm32"))]
6303 {
6304 let mut gfx = self.gfx.borrow_mut();
6305 gfx.mouse_captured = true;
6306 gfx.last_mx = f32::NAN;
6307 if let Some(win) = gfx.window.as_mut() {
6308 win.set_cursor_visibility(false);
6309 }
6310 }
6311 return Ok(Value::Unit);
6312 },
6313
6314 "release_mouse" => {
6316 #[cfg(not(target_arch = "wasm32"))]
6317 {
6318 let mut gfx = self.gfx.borrow_mut();
6319 gfx.mouse_captured = false;
6320 gfx.last_mx = f32::NAN;
6321 if let Some(win) = gfx.window.as_mut() {
6322 win.set_cursor_visibility(true);
6323 }
6324 #[cfg(windows)]
6325 unsafe {
6326 extern "system" {
6328 fn ClipCursor(lpRect: *const std::ffi::c_void) -> i32;
6329 }
6330 ClipCursor(std::ptr::null());
6331 }
6332 }
6333 return Ok(Value::Unit);
6334 },
6335
6336 "cursor_hide" => {
6342 #[cfg(not(target_arch = "wasm32"))]
6343 if let Some(win) = self.gfx.borrow_mut().window.as_mut() {
6344 win.set_cursor_visibility(false);
6345 }
6346 return Ok(Value::Unit);
6347 },
6348 "cursor_show" => {
6349 #[cfg(not(target_arch = "wasm32"))]
6350 if let Some(win) = self.gfx.borrow_mut().window.as_mut() {
6351 win.set_cursor_visibility(true);
6352 }
6353 return Ok(Value::Unit);
6354 },
6355
6356 "set_camera"
6363 | "ตั้งกล้อง"
6364 | "设镜"
6365 | "设置摄像机"
6366 | "カメラ設定"
6367 | "카메라설정"
6368 | "تنظیم_دوربین"
6369 | "عيّن_الكاميرا"
6370 | "קבע_מצלמה"
6371 | "کیمرہ_مقرر_کرو"
6372 | "définir_caméra"
6373 | "kamera_setzen"
6374 | "задать_камеру" => {
6375 let cry = self.arg_num(&args, 0, 1.0)? as f32;
6376 let sry = self.arg_num(&args, 1, 0.0)? as f32;
6377 let crx = self.arg_num(&args, 2, 1.0)? as f32;
6378 let srx = self.arg_num(&args, 3, 0.0)? as f32;
6379 let mut gfx = self.gfx.borrow_mut();
6380 gfx.camera.cry = cry;
6381 gfx.camera.sry = sry;
6382 gfx.camera.crx = crx;
6383 gfx.camera.srx = srx;
6384 return Ok(Value::Unit);
6385 },
6386
6387 "set_projection"
6390 | "ตั้งโปรเจกชัน"
6391 | "投影"
6392 | "投影設定"
6393 | "투영설정"
6394 | "تنظیم_فرافکنی"
6395 | "عيّن_الإسقاط"
6396 | "קבע_הטלה"
6397 | "پروجیکشن_مقرر_کرو"
6398 | "définir_projection"
6399 | "projektion_setzen"
6400 | "задать_проекцию" => {
6401 let cx = self.arg_num(&args, 0, 960.0)? as f32;
6402 let cy = self.arg_num(&args, 1, 540.0)? as f32;
6403 let focal = self.arg_num(&args, 2, 1080.0)? as f32;
6404 let zdist = self.arg_num(&args, 3, 5.0)? as f32;
6405 let mut gfx = self.gfx.borrow_mut();
6406 gfx.camera.cx = cx;
6407 gfx.camera.cy = cy;
6408 gfx.camera.focal = focal;
6409 gfx.camera.zdist = zdist;
6410 return Ok(Value::Unit);
6411 },
6412
6413 "gltf_load" => {
6415 let path = self.arg_str(&args, 0, "");
6416 match ling_physics::gltf::GltfModel::load(&path) {
6417 Ok(m) => {
6418 self.gltf_models.borrow_mut().push(m);
6419 let h = self.gltf_models.borrow().len() - 1;
6420 return Ok(Value::Number(h as f64));
6421 },
6422 Err(e) => {
6423 eprintln!("mesh_load failed ({path}): {e}");
6424 return Ok(Value::Number(-1.0));
6425 },
6426 }
6427 },
6428 "gltf_anim_count" => {
6430 let h = self.arg_num(&args, 0, -1.0)? as i64;
6431 let n = self
6432 .gltf_models
6433 .borrow()
6434 .get(h as usize)
6435 .map(|m| m.animations.len())
6436 .unwrap_or(0);
6437 return Ok(Value::Number(n as f64));
6438 },
6439 "gltf_anim_name" => {
6441 let h = self.arg_num(&args, 0, -1.0)? as i64;
6442 let i = self.arg_num(&args, 1, 0.0)? as usize;
6443 let s = self
6444 .gltf_models
6445 .borrow()
6446 .get(h as usize)
6447 .and_then(|m| m.animations.get(i))
6448 .map(|a| a.name.clone())
6449 .unwrap_or_default();
6450 return Ok(Value::Str(s));
6451 },
6452 "gltf_anim_dur" => {
6454 let h = self.arg_num(&args, 0, -1.0)? as i64;
6455 let i = self.arg_num(&args, 1, 0.0)? as usize;
6456 let d = self
6457 .gltf_models
6458 .borrow()
6459 .get(h as usize)
6460 .and_then(|m| m.animations.get(i))
6461 .map(|a| a.duration)
6462 .unwrap_or(0.0);
6463 return Ok(Value::Number(d as f64));
6464 },
6465 "gltf_tris" => {
6467 let h = self.arg_num(&args, 0, -1.0)? as i64;
6468 let n: usize = self
6469 .gltf_models
6470 .borrow()
6471 .get(h as usize)
6472 .map(|m| m.meshes.iter().map(|mm| mm.indices.len()).sum::<usize>() / 3)
6473 .unwrap_or(0);
6474 return Ok(Value::Number(n as f64));
6475 },
6476
6477 "gltf_joint_count" => {
6479 let h = self.arg_num(&args, 0, -1.0)? as i64;
6480 let n = self
6481 .gltf_models
6482 .borrow()
6483 .get(h as usize)
6484 .and_then(|m| m.skins.first())
6485 .map(|s| s.joints.len())
6486 .unwrap_or(0);
6487 return Ok(Value::Number(n as f64));
6488 },
6489 "gltf_joint_name" => {
6491 let h = self.arg_num(&args, 0, -1.0)? as i64;
6492 let j = self.arg_num(&args, 1, 0.0)? as usize;
6493 let models = self.gltf_models.borrow();
6494 let s = models
6495 .get(h as usize)
6496 .and_then(|m| {
6497 m.skins
6498 .first()
6499 .and_then(|sk| sk.joints.get(j))
6500 .and_then(|jt| m.nodes.get(jt.node_idx))
6501 .map(|n| n.name.clone())
6502 })
6503 .unwrap_or_default();
6504 return Ok(Value::Str(s));
6505 },
6506
6507 "gltf_draw" => {
6512 let hh = self.arg_num(&args, 0, -1.0)? as i64;
6513 let ox = self.arg_num(&args, 1, 0.0)? as f32;
6514 let oy = self.arg_num(&args, 2, 0.0)? as f32;
6515 let oz = self.arg_num(&args, 3, 0.0)? as f32;
6516 let scale = self.arg_num(&args, 4, 1.0)? as f32;
6517 let yaw = self.arg_num(&args, 5, 0.0)? as f32;
6518 let (sy, cyy) = yaw.sin_cos();
6519 let models = self.gltf_models.borrow();
6520 let model = match models.get(hh as usize) {
6521 Some(m) => m,
6522 None => return Ok(Value::Unit),
6523 };
6524 let mut gfx = self.gfx.borrow_mut();
6525 let cp = {
6526 let c = &gfx.camera;
6527 ling_gpu::CameraParams {
6528 cry: c.cry,
6529 sry: c.sry,
6530 crx: c.crx,
6531 srx: c.srx,
6532 cx: c.cx,
6533 cy: c.cy,
6534 focal: c.focal,
6535 zdist: c.zdist,
6536 tx: c.tx,
6537 ty: c.ty,
6538 tz: c.tz,
6539 }
6540 };
6541 let near = -gfx.camera.zdist + 0.02;
6542 let ambient = gfx.ambient;
6543 for mesh in &model.meshes {
6544 let nlow = mesh.name.to_lowercase();
6545 let base: u32 = if nlow.contains("hair") {
6546 0x7a4a28
6547 } else if nlow.contains("cloth") || nlow.contains("top") {
6548 0x4a86e0
6549 } else if nlow.contains("wing") {
6550 0xe6ecf5
6551 } else if nlow.contains("star") {
6552 0xffd24d
6553 } else {
6554 0xf2d6b8
6555 };
6556 let nv = mesh.verts.len();
6557 if nv == 0 {
6558 continue;
6559 }
6560 let mut world = vec![0.0f32; nv * 3];
6561 for (i, v) in mesh.verts.iter().enumerate() {
6562 let gx = v.pos.x * scale;
6563 let gy = -v.pos.y * scale;
6564 let gz = -v.pos.z * scale;
6565 let rx = gx * cyy + gz * sy;
6566 let rz = -gx * sy + gz * cyy;
6567 world[i * 3] = ox + rx;
6568 world[i * 3 + 1] = oy + gy;
6569 world[i * 3 + 2] = oz + rz;
6570 }
6571 let mut proj = vec![0.0f32; nv * 3];
6572 ling_gpu::backend().project_points(&world, &cp, &mut proj);
6573 let idx = &mesh.indices;
6574 let nt = idx.len() / 3;
6575 for t in 0..nt {
6576 let ia = idx[t * 3] as usize;
6577 let ib = idx[t * 3 + 1] as usize;
6578 let ic = idx[t * 3 + 2] as usize;
6579 if ia >= nv || ib >= nv || ic >= nv {
6580 continue;
6581 }
6582 let (da, db, dc) = (proj[ia * 3 + 2], proj[ib * 3 + 2], proj[ic * 3 + 2]);
6583 if (da + db + dc) / 3.0 <= near {
6584 continue;
6585 }
6586 let col = {
6587 let (ax, ay, az) =
6588 (world[ia * 3], world[ia * 3 + 1], world[ia * 3 + 2]);
6589 let (bx, by, bz) =
6590 (world[ib * 3], world[ib * 3 + 1], world[ib * 3 + 2]);
6591 let (px, py, pz) =
6592 (world[ic * 3], world[ic * 3 + 1], world[ic * 3 + 2]);
6593 let (ux, uy, uz) = (bx - ax, by - ay, bz - az);
6594 let (vx, vy, vz) = (px - ax, py - ay, pz - az);
6595 let normal = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];
6596 let centroid = [
6597 (ax + bx + px) / 3.0,
6598 (ay + by + py) / 3.0,
6599 (az + bz + pz) / 3.0,
6600 ];
6601 if gfx.flat_shade {
6602 base
6603 } else {
6604 crate::gfx::light::compute_lit_color(
6605 base,
6606 normal,
6607 centroid,
6608 &gfx.lights,
6609 ambient,
6610 )
6611 }
6612 };
6613 let depth = (da + db + dc) / 3.0;
6614 let col = gfx.fog_apply(col, depth);
6615 gfx.depth_queue.push_triangle_zv(
6616 col,
6617 proj[ia * 3],
6618 proj[ia * 3 + 1],
6619 da,
6620 proj[ib * 3],
6621 proj[ib * 3 + 1],
6622 db,
6623 proj[ic * 3],
6624 proj[ic * 3 + 1],
6625 dc,
6626 );
6627 }
6628 }
6629 return Ok(Value::Unit);
6630 },
6631
6632 "gltf_autorig" => {
6634 let hh = self.arg_num(&args, 0, -1.0)? as i64;
6635 let mut models = self.gltf_models.borrow_mut();
6636 if let Some(m) = models.get_mut(hh as usize) {
6637 return Ok(Value::Number(m.autorig() as f64));
6638 }
6639 return Ok(Value::Number(0.0));
6640 },
6641
6642 "gltf_pose_draw" => {
6646 let hh = self.arg_num(&args, 0, -1.0)? as i64;
6647 let ox = self.arg_num(&args, 1, 0.0)? as f32;
6648 let oy = self.arg_num(&args, 2, 0.0)? as f32;
6649 let oz = self.arg_num(&args, 3, 0.0)? as f32;
6650 let scale = self.arg_num(&args, 4, 1.0)? as f32;
6651 let yaw = self.arg_num(&args, 5, 0.0)? as f32;
6652 let euler: Vec<f32> = match args.get(6) {
6653 Some(Value::List(v)) => v
6654 .iter()
6655 .map(|x| self.to_number(x).unwrap_or(0.0) as f32)
6656 .collect(),
6657 _ => Vec::new(),
6658 };
6659 let (sy, cyy) = yaw.sin_cos();
6660 let models = self.gltf_models.borrow();
6661 let model = match models.get(hh as usize) {
6662 Some(m) => m,
6663 None => return Ok(Value::Unit),
6664 };
6665 let skinned = model.skin_local(&euler);
6666 let mut gfx = self.gfx.borrow_mut();
6667 let cp = {
6668 let c = &gfx.camera;
6669 ling_gpu::CameraParams {
6670 cry: c.cry,
6671 sry: c.sry,
6672 crx: c.crx,
6673 srx: c.srx,
6674 cx: c.cx,
6675 cy: c.cy,
6676 focal: c.focal,
6677 zdist: c.zdist,
6678 tx: c.tx,
6679 ty: c.ty,
6680 tz: c.tz,
6681 }
6682 };
6683 let near = -gfx.camera.zdist + 0.02;
6684 let ambient = gfx.ambient;
6685 for (mi, mesh) in model.meshes.iter().enumerate() {
6686 let nlow = mesh.name.to_lowercase();
6687 let base: u32 = if nlow.contains("hair") {
6688 0x7a4a28
6689 } else if nlow.contains("cloth") || nlow.contains("top") {
6690 0x4a86e0
6691 } else if nlow.contains("wing") {
6692 0xe6ecf5
6693 } else if nlow.contains("star") {
6694 0xffd24d
6695 } else {
6696 0xf2d6b8
6697 };
6698 let sk = match skinned.get(mi) {
6699 Some(s) => s,
6700 None => continue,
6701 };
6702 let nv = sk.len();
6703 if nv == 0 {
6704 continue;
6705 }
6706 let mut world = vec![0.0f32; nv * 3];
6707 for i in 0..nv {
6708 let gx = sk[i][0] * scale;
6709 let gy = -sk[i][1] * scale;
6710 let gz = -sk[i][2] * scale;
6711 let rx = gx * cyy + gz * sy;
6712 let rz = -gx * sy + gz * cyy;
6713 world[i * 3] = ox + rx;
6714 world[i * 3 + 1] = oy + gy;
6715 world[i * 3 + 2] = oz + rz;
6716 }
6717 let mut proj = vec![0.0f32; nv * 3];
6718 ling_gpu::backend().project_points(&world, &cp, &mut proj);
6719 let idx = &mesh.indices;
6720 let nt = idx.len() / 3;
6721 for t in 0..nt {
6722 let ia = idx[t * 3] as usize;
6723 let ib = idx[t * 3 + 1] as usize;
6724 let ic = idx[t * 3 + 2] as usize;
6725 if ia >= nv || ib >= nv || ic >= nv {
6726 continue;
6727 }
6728 let (da, db, dc) = (proj[ia * 3 + 2], proj[ib * 3 + 2], proj[ic * 3 + 2]);
6729 if (da + db + dc) / 3.0 <= near {
6730 continue;
6731 }
6732 let col = {
6733 let (ax, ay, az) =
6734 (world[ia * 3], world[ia * 3 + 1], world[ia * 3 + 2]);
6735 let (bx, by, bz) =
6736 (world[ib * 3], world[ib * 3 + 1], world[ib * 3 + 2]);
6737 let (px, py, pz) =
6738 (world[ic * 3], world[ic * 3 + 1], world[ic * 3 + 2]);
6739 let (ux, uy, uz) = (bx - ax, by - ay, bz - az);
6740 let (vx, vy, vz) = (px - ax, py - ay, pz - az);
6741 let normal = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];
6742 let centroid = [
6743 (ax + bx + px) / 3.0,
6744 (ay + by + py) / 3.0,
6745 (az + bz + pz) / 3.0,
6746 ];
6747 if gfx.flat_shade {
6748 base
6749 } else {
6750 crate::gfx::light::compute_lit_color(
6751 base,
6752 normal,
6753 centroid,
6754 &gfx.lights,
6755 ambient,
6756 )
6757 }
6758 };
6759 let depth = (da + db + dc) / 3.0;
6760 let col = gfx.fog_apply(col, depth);
6761 gfx.depth_queue.push_triangle_zv(
6762 col,
6763 proj[ia * 3],
6764 proj[ia * 3 + 1],
6765 da,
6766 proj[ib * 3],
6767 proj[ib * 3 + 1],
6768 db,
6769 proj[ic * 3],
6770 proj[ic * 3 + 1],
6771 dc,
6772 );
6773 }
6774 }
6775 return Ok(Value::Unit);
6776 },
6777
6778 "draw_mesh"
6785 | "วาดเมช"
6786 | "رسم_مش"
6787 | "ارسم_شبكة"
6788 | "צייר_רשת"
6789 | "میش_کھینچو"
6790 | "dessiner_maillage"
6791 | "netz_zeichnen"
6792 | "рисовать_меш" => {
6793 let pos = match args.first() {
6794 Some(Value::List(v)) => v,
6795 _ => return Ok(Value::Unit),
6796 };
6797 let idx = match args.get(1) {
6798 Some(Value::List(v)) => v,
6799 _ => return Ok(Value::Unit),
6800 };
6801 let ox = self.arg_num(&args, 2, 0.0)? as f32;
6802 let oy = self.arg_num(&args, 3, 0.0)? as f32;
6803 let oz = self.arg_num(&args, 4, 0.0)? as f32;
6804 let scale = self.arg_num(&args, 5, 1.0)? as f32;
6805 let mode = self.arg_num(&args, 6, 0.0)? as i64;
6806 let nv = pos.len() / 3;
6807 if nv == 0 {
6808 return Ok(Value::Unit);
6809 }
6810 let mut world = vec![0.0f32; nv * 3];
6811 for i in 0..nv {
6812 world[i * 3] = ox + self.to_number(&pos[i * 3]).unwrap_or(0.0) as f32 * scale;
6813 world[i * 3 + 1] =
6814 oy + self.to_number(&pos[i * 3 + 1]).unwrap_or(0.0) as f32 * scale;
6815 world[i * 3 + 2] =
6816 oz + self.to_number(&pos[i * 3 + 2]).unwrap_or(0.0) as f32 * scale;
6817 }
6818 let mut gfx = self.gfx.borrow_mut();
6819 let cp = {
6820 let c = &gfx.camera;
6821 ling_gpu::CameraParams {
6822 cry: c.cry,
6823 sry: c.sry,
6824 crx: c.crx,
6825 srx: c.srx,
6826 cx: c.cx,
6827 cy: c.cy,
6828 focal: c.focal,
6829 zdist: c.zdist,
6830 tx: c.tx,
6831 ty: c.ty,
6832 tz: c.tz,
6833 }
6834 };
6835 let near = -gfx.camera.zdist + 0.02;
6836 let base = gfx.color;
6837 let ambient = gfx.ambient;
6838 let mut proj = vec![0.0f32; nv * 3]; ling_gpu::backend().project_points(&world, &cp, &mut proj);
6840 let nt = idx.len() / 3;
6841 for t in 0..nt {
6842 let ia = self.to_number(&idx[t * 3]).unwrap_or(0.0) as usize;
6843 let ib = self.to_number(&idx[t * 3 + 1]).unwrap_or(0.0) as usize;
6844 let ic = self.to_number(&idx[t * 3 + 2]).unwrap_or(0.0) as usize;
6845 if ia >= nv || ib >= nv || ic >= nv {
6846 continue;
6847 }
6848 let (da, db, dc) = (proj[ia * 3 + 2], proj[ib * 3 + 2], proj[ic * 3 + 2]);
6849 if (da + db + dc) / 3.0 <= near {
6850 continue;
6851 } let col = if mode == 1 {
6853 let h = t as f32 * 0.6;
6854 let r = ((h.sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
6855 let g = (((h + 2.094).sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
6856 let b = (((h + 4.189).sin() * 0.5 + 0.5) * 150.0 + 55.0) as u32;
6857 (r << 16) | (g << 8) | b
6858 } else {
6859 let (ax, ay, az) = (world[ia * 3], world[ia * 3 + 1], world[ia * 3 + 2]);
6860 let (bx, by, bz) = (world[ib * 3], world[ib * 3 + 1], world[ib * 3 + 2]);
6861 let (px, py, pz) = (world[ic * 3], world[ic * 3 + 1], world[ic * 3 + 2]);
6862 let (ux, uy, uz) = (bx - ax, by - ay, bz - az);
6863 let (vx, vy, vz) = (px - ax, py - ay, pz - az);
6864 let normal = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx];
6865 let centroid = [
6866 (ax + bx + px) / 3.0,
6867 (ay + by + py) / 3.0,
6868 (az + bz + pz) / 3.0,
6869 ];
6870 if gfx.flat_shade {
6871 base
6872 } else {
6873 crate::gfx::light::compute_lit_color(
6874 base,
6875 normal,
6876 centroid,
6877 &gfx.lights,
6878 ambient,
6879 )
6880 }
6881 };
6882 let depth = (da + db + dc) / 3.0;
6883 let col = gfx.fog_apply(col, depth);
6884 gfx.depth_queue.push_triangle_zv(
6888 col,
6889 proj[ia * 3],
6890 proj[ia * 3 + 1],
6891 da,
6892 proj[ib * 3],
6893 proj[ib * 3 + 1],
6894 db,
6895 proj[ic * 3],
6896 proj[ic * 3 + 1],
6897 dc,
6898 );
6899 }
6900 return Ok(Value::Unit);
6901 },
6902
6903 "add_light"
6907 | "เพิ่มแสง"
6908 | "加灯"
6909 | "ライト追加"
6910 | "조명추가"
6911 | "افزودن_نور"
6912 | "أضف_ضوء"
6913 | "הוסף_אור"
6914 | "روشنی_شامل_کرو"
6915 | "ajouter_lumière"
6916 | "licht_hinzufügen"
6917 | "добавить_свет" => {
6918 let x = self.arg_num(&args, 0, 0.0)? as f32;
6919 let y = self.arg_num(&args, 1, -3.0)? as f32;
6920 let z = self.arg_num(&args, 2, 3.0)? as f32;
6921 let mut r = self.arg_num(&args, 3, 1.0)? as f32;
6922 let mut g = self.arg_num(&args, 4, 1.0)? as f32;
6923 let mut b = self.arg_num(&args, 5, 1.0)? as f32;
6924 if r > 1.5 || g > 1.5 || b > 1.5 {
6927 r /= 255.0;
6928 g /= 255.0;
6929 b /= 255.0;
6930 }
6931 let intensity = self.arg_num(&args, 6, 1.0)? as f32;
6932 let radius = self.arg_num(&args, 7, 0.0)? as f32;
6933 self.gfx
6934 .borrow_mut()
6935 .lights
6936 .push(Light { x, y, z, r, g, b, intensity, radius });
6937 return Ok(Value::Unit);
6938 },
6939
6940 "clear_lights"
6942 | "ล้างแสง"
6943 | "清灯"
6944 | "ライト消去"
6945 | "조명초기화"
6946 | "پاککردن_نورها"
6947 | "امسح_الأضواء"
6948 | "נקה_אורות"
6949 | "روشنیاں_صاف_کرو"
6950 | "effacer_lumières"
6951 | "lichter_löschen"
6952 | "очистить_свет" => {
6953 self.gfx.borrow_mut().lights.clear();
6954 return Ok(Value::Unit);
6955 },
6956
6957 "set_material"
6967 | "ตั้งวัสดุ"
6968 | "设置材质"
6969 | "マテリアル設定"
6970 | "재질설정"
6971 | "تنظیم_متریال"
6972 | "عيّن_المادة"
6973 | "קבע_חומר"
6974 | "میٹریل_مقرر_کرو" => {
6975 let key = self.arg_str(&args, 0, "");
6976 let val = self.arg_num(&args, 1, 0.0)?;
6977 let mut gfx = self.gfx.borrow_mut();
6978 let mat = gfx
6979 .material
6980 .get_or_insert_with(crate::gfx::LingMaterial::default);
6981 match key.as_str() {
6982 "albedo" => mat.albedo = val as u32,
6983 "roughness" => mat.roughness = val as f32,
6984 "metallic" => mat.metallic = val as f32,
6985 "emission" => mat.emission = val as u32,
6986 "emission_strength" => mat.emission_strength = val as f32,
6987 "specular" => mat.specular = val as f32,
6988 "specular_tint" => mat.specular_tint = val as f32,
6989 "subsurface" => mat.subsurface = val as f32,
6990 "subsurface_color" => mat.subsurface_color = val as u32,
6991 "clearcoat" => mat.clearcoat = val as f32,
6992 "clearcoat_roughness" => mat.clearcoat_roughness = val as f32,
6993 "transmission" => mat.transmission = val as f32,
6994 "ior" => mat.ior = val as f32,
6995 "iridescence" => mat.iridescence = val as f32,
6996 "sheen" => mat.sheen = val as f32,
6997 "anisotropy" => mat.anisotropy = val as f32,
6998 "anisotropy_angle" => mat.anisotropy_angle = val as f32,
6999 "toon_bands" => mat.toon_bands = val as u32,
7000 "shadow_softness" => mat.shadow_softness = val as f32,
7001 "outline_px" => mat.outline_px = val as f32,
7002 "outline_color" => mat.outline_color = val as u32,
7003 "highlight_color" => mat.highlight_color = val as u32,
7004 _ => {},
7005 }
7006 return Ok(Value::Unit);
7007 },
7008
7009 "reset_material"
7012 | "รีเซ็ตวัสดุ"
7013 | "重置材质"
7014 | "マテリアルリセット"
7015 | "재질초기화"
7016 | "بازنشانی_متریال"
7017 | "أعد_ضبط_المادة"
7018 | "אפס_חומר"
7019 | "میٹریل_ری_سیٹ" => {
7020 self.gfx.borrow_mut().material = None;
7021 return Ok(Value::Unit);
7022 },
7023
7024 "toon_outlines"
7030 | "ตั้งเส้นขอบการ์ตูน"
7031 | "卡通轮廓"
7032 | "トゥーンアウトライン"
7033 | "툰아웃라인"
7034 | "خطوط_کارتونی"
7035 | "حدود_كرتونية"
7036 | "קווי_מתאר_מצוירים"
7037 | "ٹون_آؤٹ_لائنز" => {
7038 let px = self.arg_num(&args, 0, 0.0)? as f32;
7039 let color = self.arg_num(&args, 1, 0.0)? as u32;
7040 let thresh = self.arg_num(&args, 2, 0.05)? as f32;
7041 let mut gfx = self.gfx.borrow_mut();
7042 gfx.toon.outline_px = px;
7043 gfx.toon.outline_color = color;
7044 gfx.toon.outline_thresh = thresh;
7045 return Ok(Value::Unit);
7046 },
7047
7048 "tone_stop"
7054 | "ตั้งจุดโทน"
7055 | "色调停止"
7056 | "トーンストップ"
7057 | "톤스톱"
7058 | "نقطه_توقف_تن_رنگ"
7059 | "نقطة_توقف_اللون"
7060 | "נקודת_עצירת_גוון"
7061 | "ٹون_اسٹاپ" => {
7062 let t = self.arg_num(&args, 0, 0.0)? as f32;
7063 let val = self.arg_num(&args, 1, 1.0)? as f32;
7064 let mut gfx = self.gfx.borrow_mut();
7065 gfx.toon.ramp.stops.push(crate::gfx::toon::ToneStop {
7066 t: t.clamp(0.0, 1.0),
7067 value: val.clamp(0.0, 1.0),
7068 });
7069 gfx.toon
7070 .ramp
7071 .stops
7072 .sort_by(|a, b| a.t.partial_cmp(&b.t).unwrap_or(std::cmp::Ordering::Equal));
7073 return Ok(Value::Unit);
7074 },
7075
7076 "tone_smooth"
7079 | "ตั้งโทนนุ่ม"
7080 | "色调平滑"
7081 | "トーンスムーズ"
7082 | "톤스무스"
7083 | "تن_رنگ_نرم"
7084 | "تدرج_لون_ناعم"
7085 | "גוון_חלק"
7086 | "ٹون_ہموار" => {
7087 let v = self.arg_num(&args, 0, 0.0)? as f32;
7088 self.gfx.borrow_mut().toon.ramp.smooth = v > 0.5;
7089 return Ok(Value::Unit);
7090 },
7091
7092 "tone_bezier"
7100 | "ตั้งโทนเบซิเยร์"
7101 | "色调贝塞尔"
7102 | "トーンベジェ"
7103 | "톤베지어"
7104 | "تن_رنگ_بزیه"
7105 | "تدرج_لون_بيزيه"
7106 | "גוון_בזייה"
7107 | "بیزیئر_ٹون" => {
7108 let y1 = self.arg_num(&args, 0, 1.0 / 3.0)? as f32;
7109 let y2 = self.arg_num(&args, 1, 2.0 / 3.0)? as f32;
7110 self.gfx.borrow_mut().toon.ramp.bezier = Some([y1, y2]);
7111 return Ok(Value::Unit);
7112 },
7113
7114 "tone_bezier_off"
7116 | "ปิดโทนเบซิเยร์"
7117 | "关闭色调贝塞尔"
7118 | "トーンベジェオフ"
7119 | "톤베지어끄기"
7120 | "خاموشکردن_بزیه"
7121 | "إيقاف_تدرج_بيزيه"
7122 | "כבה_גוון_בזייה"
7123 | "بیزیئر_ٹون_بند" => {
7124 self.gfx.borrow_mut().toon.ramp.bezier = None;
7125 return Ok(Value::Unit);
7126 },
7127
7128 "tone_ramp_reset"
7130 | "รีเซ็ตการไล่โทน"
7131 | "重置色调渐变"
7132 | "トーンランプリセット"
7133 | "톤램프리셋"
7134 | "بازنشانی_شیب_تن_رنگ"
7135 | "أعد_ضبط_تدرج_اللون"
7136 | "אפס_שיפוע_גוון"
7137 | "ٹون_ریمپ_ری_سیٹ" => {
7138 self.gfx.borrow_mut().toon.ramp = crate::gfx::toon::ToneRamp::default();
7139 return Ok(Value::Unit);
7140 },
7141
7142 "tone_ramp_clear"
7144 | "ล้างการไล่โทน"
7145 | "清除色调渐变"
7146 | "トーンランプクリア"
7147 | "톤램프클리어"
7148 | "پاککردن_شیب_تن_رنگ"
7149 | "امسح_تدرج_اللون"
7150 | "נקה_שיפוע_גוון"
7151 | "ٹون_ریمپ_صاف" => {
7152 self.gfx.borrow_mut().toon.ramp.stops.clear();
7153 return Ok(Value::Unit);
7154 },
7155
7156 "tone_soft"
7164 | "โทนขอบนุ่ม"
7165 | "色调柔边"
7166 | "トーンソフト"
7167 | "톤소프트"
7168 | "تن_رنگ_لبهنرم"
7169 | "تدرج_ناعم_الحواف"
7170 | "גוון_קצה_רך"
7171 | "نرم_کنارہ_ٹون"
7172 | "tonalité_douce"
7173 | "weicher_ton"
7174 | "мягкий_тон" => {
7175 let s = self.arg_num(&args, 0, 0.32)? as f32;
7176 let sh = self.arg_num(&args, 1, 0.65)? as f32;
7177 let mut gfx = self.gfx.borrow_mut();
7178 gfx.toon.ramp.soft = s.clamp(0.0, 1.0);
7179 gfx.toon.ramp.sheen = sh.clamp(0.0, 1.0);
7180 return Ok(Value::Unit);
7181 },
7182
7183 "set_ssao"
7188 | "ตั้งเงาสัมผัส"
7189 | "环境光遮蔽"
7190 | "アンビエントオクルージョン"
7191 | "앰비언트오클루전"
7192 | "تنظیم_انسداد_محیطی"
7193 | "عيّن_تظليل_محيطي"
7194 | "קבע_הצללה_סביבתית"
7195 | "ایس_ایس_اے_او_مقرر_کرو"
7196 | "définir_ssao"
7197 | "ssao_setzen"
7198 | "задать_ssao" => {
7199 let s = self.arg_num(&args, 0, 0.35)? as f32;
7200 let r = self.arg_num(&args, 1, 6.0)? as f32;
7201 let z = self.arg_num(&args, 2, 12.0)? as f32;
7202 let mut gfx = self.gfx.borrow_mut();
7203 gfx.toon.ao_strength = s.clamp(0.0, 1.0);
7204 gfx.toon.ao_radius = r.max(1.0);
7205 gfx.toon.ao_range = z.max(0.01);
7206 return Ok(Value::Unit);
7207 },
7208
7209 "set_fxaa"
7215 | "ลบรอยหยัก"
7216 | "屏幕抗锯齿"
7217 | "画面アンチエイリアス"
7218 | "화면안티앨리어싱"
7219 | "تنظیم_ضدلبهدندانه_سریع"
7220 | "عيّن_مضاد_التسنن_السريع"
7221 | "קבע_החלקת_מסך"
7222 | "ایف_ایکس_اے_اے_مقرر_کرو"
7223 | "définir_fxaa"
7224 | "fxaa_setzen"
7225 | "задать_fxaa" => {
7226 let on = self.arg_num(&args, 0, 1.0)? as i64 != 0;
7227 self.gfx.borrow_mut().toon.fxaa = on;
7228 return Ok(Value::Unit);
7229 },
7230
7231 "set_bloom"
7236 | "ตั้งบลูม"
7237 | "泛光"
7238 | "ブルーム"
7239 | "블룸"
7240 | "تنظیم_درخشش"
7241 | "عيّن_التوهج"
7242 | "קבע_זוהר"
7243 | "بلوم_مقرر_کرو"
7244 | "définir_bloom"
7245 | "bloom_setzen"
7246 | "задать_блум" => {
7247 let s = self.arg_num(&args, 0, 0.45)? as f32;
7248 let t = self.arg_num(&args, 1, 0.74)? as f32;
7249 let mut gfx = self.gfx.borrow_mut();
7250 gfx.toon.bloom_strength = s.max(0.0);
7251 gfx.toon.bloom_thresh = t.clamp(0.0, 0.99);
7252 return Ok(Value::Unit);
7253 },
7254
7255 "shadow_smooth"
7258 | "ตั้งเงานุ่ม"
7259 | "柔化阴影"
7260 | "影ソフト"
7261 | "그림자부드럽게"
7262 | "سایه_نرم"
7263 | "ظل_ناعم"
7264 | "צל_חלק"
7265 | "نرم_سایہ" => {
7266 let s = self.arg_num(&args, 0, 0.0)? as f32;
7267 let mut gfx = self.gfx.borrow_mut();
7268 gfx.toon.ramp.smooth = s > 0.05;
7269 if s > 0.05 {
7270 let y1 = (0.333 + s * 0.2).clamp(0.0, 1.0);
7271 let y2 = (0.667 - s * 0.2).clamp(0.0, 1.0);
7272 gfx.toon.ramp.bezier = Some([y1, y2]);
7273 } else {
7274 gfx.toon.ramp.bezier = None;
7275 }
7276 return Ok(Value::Unit);
7277 },
7278
7279 "toon_highlight"
7281 | "ตั้งไฮไลท์การ์ตูน"
7282 | "卡通高光"
7283 | "トゥーンハイライト"
7284 | "툰하이라이트"
7285 | "هایلایت_کارتونی"
7286 | "إبراز_كرتوني"
7287 | "הדגשה_מצוירת"
7288 | "ٹون_ہائی_لائٹ" => {
7289 let _strength = self.arg_num(&args, 0, 0.0)? as f32;
7291 let _thresh = self.arg_num(&args, 2, 0.78)? as f32;
7292 return Ok(Value::Unit);
7294 },
7295
7296 "set_ambient"
7298 | "ตั้งแสงรอบข้าง"
7299 | "环境光"
7300 | "環境光設定"
7301 | "환경광설정"
7302 | "تنظیم_نور_محیطی"
7303 | "عيّن_الإضاءة_المحيطة"
7304 | "קבע_תאורה_סביבתית"
7305 | "ماحولیاتی_روشنی_مقرر_کرو"
7306 | "définir_ambiante"
7307 | "umgebungslicht_setzen"
7308 | "задать_фон" => {
7309 let v = self.arg_num(&args, 0, 0.15)? as f32;
7310 self.gfx.borrow_mut().ambient = v;
7311 return Ok(Value::Unit);
7312 },
7313
7314 "set_fog"
7317 | "ตั้งหมอก"
7318 | "雾"
7319 | "霧設定"
7320 | "안개설정"
7321 | "تنظیم_مه"
7322 | "عيّن_الضباب"
7323 | "קבע_ערפל"
7324 | "دھند_مقرر_کرو" => {
7325 let r = self.arg_num(&args, 0, 0.0)?.clamp(0.0, 255.0) as u32;
7326 let g = self.arg_num(&args, 1, 0.0)?.clamp(0.0, 255.0) as u32;
7327 let b = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 255.0) as u32;
7328 let start = self.arg_num(&args, 3, 0.0)? as f32;
7329 let end = self.arg_num(&args, 4, 0.0)? as f32;
7330 let mut gfx = self.gfx.borrow_mut();
7331 gfx.fog_color = (r << 16) | (g << 8) | b;
7332 gfx.fog_start = start;
7333 gfx.fog_end = end;
7334 return Ok(Value::Unit);
7335 },
7336
7337 "วาดสามเหลี่ยม3มิติ"
7341 | "draw_triangle_3d"
7342 | "triangle3d"
7343 | "رسم_مثلث_سهبعدی"
7344 | "ارسم_مثلثا_ثلاثي_الأبعاد"
7345 | "צייר_משולש_תלת_ממדי"
7346 | "تھری_ڈی_مثلث_کھینچو" => {
7347 let ax = self.arg_num(&args, 0, 0.0)? as f32;
7348 let ay = self.arg_num(&args, 1, 0.0)? as f32;
7349 let az = self.arg_num(&args, 2, 0.0)? as f32;
7350 let bx = self.arg_num(&args, 3, 0.0)? as f32;
7351 let by = self.arg_num(&args, 4, 0.0)? as f32;
7352 let bz = self.arg_num(&args, 5, 0.0)? as f32;
7353 let cx = self.arg_num(&args, 6, 0.0)? as f32;
7354 let cy = self.arg_num(&args, 7, 0.0)? as f32;
7355 let cz = self.arg_num(&args, 8, 0.0)? as f32;
7356
7357 let mut gfx = self.gfx.borrow_mut();
7358
7359 if gfx.mesh_capture.is_some() {
7361 let col = gfx.color;
7362 gfx.mesh_capture
7363 .as_mut()
7364 .unwrap()
7365 .push(([ax, ay, az, bx, by, bz, cx, cy, cz], col));
7366 return Ok(Value::Unit);
7367 }
7368
7369 gfx.submit_triangle(ax, ay, az, bx, by, bz, cx, cy, cz);
7370 return Ok(Value::Unit);
7371 },
7372
7373 "เริ่มอบเมช"
7375 | "mesh_bake_begin"
7376 | "شروع_پخت_مش"
7377 | "ابدأ_خبز_الشبكة"
7378 | "התחל_אפיית_רשת"
7379 | "میش_بیک_شروع" => {
7380 self.gfx.borrow_mut().mesh_capture = Some(Vec::new());
7381 return Ok(Value::Unit);
7382 },
7383
7384 "เมชแคชรับ"
7386 | "mesh_cache_get"
7387 | "دریافت_کش_مش"
7388 | "اجلب_مخبأ_الشبكة"
7389 | "קבל_מטמון_רשת"
7390 | "میش_کیش_حاصل_کرو" => {
7391 let key = self.arg_num(&args, 0, 0.0)? as i64;
7392 let h = self.gfx.borrow().mesh_cache.get(&key).copied();
7393 return Ok(Value::Number(h.map(|x| x as f64).unwrap_or(-1.0)));
7394 },
7395
7396 "เมชแคชตั้ง"
7398 | "mesh_cache_put"
7399 | "ذخیره_در_کش_مش"
7400 | "ضع_في_مخبأ_الشبكة"
7401 | "שמור_במטמון_רשת"
7402 | "میش_کیش_رکھو" => {
7403 let key = self.arg_num(&args, 0, 0.0)? as i64;
7404 let h = self.arg_num(&args, 1, 0.0)? as usize;
7405 let mut gfx = self.gfx.borrow_mut();
7406 const CAP: usize = 256;
7407 if gfx.mesh_cache.len() >= CAP {
7408 let evict: Vec<usize> = gfx.mesh_cache.values().copied().collect();
7409 gfx.mesh_cache.clear();
7410 for id in evict {
7411 if id < gfx.meshes.len() {
7412 gfx.meshes[id].clear();
7413 gfx.mesh_free.push(id);
7414 }
7415 }
7416 }
7417 gfx.mesh_cache.insert(key, h);
7418 return Ok(Value::Unit);
7419 },
7420
7421 "เมชแคชล้าง"
7423 | "mesh_cache_clear"
7424 | "پاککردن_کش_مش"
7425 | "امسح_مخبأ_الشبكة"
7426 | "נקה_מטמון_רשת"
7427 | "میش_کیش_صاف" => {
7428 let mut gfx = self.gfx.borrow_mut();
7429 let evict: Vec<usize> = gfx.mesh_cache.values().copied().collect();
7430 gfx.mesh_cache.clear();
7431 for id in evict {
7432 if id < gfx.meshes.len() {
7433 gfx.meshes[id].clear();
7434 gfx.mesh_free.push(id);
7435 }
7436 }
7437 return Ok(Value::Unit);
7438 },
7439
7440 "จบอบเมช"
7442 | "mesh_bake_end"
7443 | "پایان_پخت_مش"
7444 | "أنهِ_خبز_الشبكة"
7445 | "סיים_אפיית_רשת"
7446 | "میش_بیک_ختم" => {
7447 let mut gfx = self.gfx.borrow_mut();
7448 let tris = gfx.mesh_capture.take().unwrap_or_default();
7449 let id = gfx.mesh_register(tris);
7450 return Ok(Value::Number(id as f64));
7451 },
7452
7453 "วาดอบเมช"
7457 | "mesh_bake_draw"
7458 | "วาดอบเมชสี"
7459 | "mesh_bake_draw_col"
7460 | "رسم_مش_پخته"
7461 | "ارسم_شبكة_مخبوزة"
7462 | "צייר_רשת_אפויה"
7463 | "بیکڈ_میش_کھینچو" => {
7464 let baked_col = matches!(name, "วาดอบเมชสี" | "mesh_bake_draw_col");
7465 let id = self.arg_num(&args, 0, 0.0)? as usize;
7466 let ox = self.arg_num(&args, 1, 0.0)? as f32;
7467 let oy = self.arg_num(&args, 2, 0.0)? as f32;
7468 let oz = self.arg_num(&args, 3, 0.0)? as f32;
7469 let rx = self.arg_num(&args, 4, 1.0)? as f32;
7470 let ry = self.arg_num(&args, 5, 0.0)? as f32;
7471 let rz = self.arg_num(&args, 6, 0.0)? as f32;
7472 let ux = self.arg_num(&args, 7, 0.0)? as f32;
7473 let uy = self.arg_num(&args, 8, 1.0)? as f32;
7474 let uz = self.arg_num(&args, 9, 0.0)? as f32;
7475 let s = self.arg_num(&args, 10, 1.0)? as f32;
7476 self.gfx
7477 .borrow_mut()
7478 .mesh_draw(id, ox, oy, oz, rx, ry, rz, ux, uy, uz, s, baked_col);
7479 return Ok(Value::Unit);
7480 },
7481
7482 "draw_quad_3d"
7486 | "quad3d"
7487 | "วาดสี่เหลี่ยม3มิติ"
7488 | "draw_pent_3d"
7489 | "pent3d"
7490 | "วาดห้าเหลี่ยม3มิติ"
7491 | "draw_hex_3d"
7492 | "hex3d"
7493 | "วาดหกเหลี่ยม3มิติ"
7494 | "draw_polygon_3d"
7495 | "polygon3d"
7496 | "วาดรูปหลายเหลี่ยม3มิติ"
7497 | "رسم_چهارضلعی_سهبعدی"
7498 | "ارسم_رباعيا_ثلاثي_الأبعاد"
7499 | "צייר_מרובע_תלת_ממדי"
7500 | "تھری_ڈی_چوکور_کھینچو" => {
7501 let mut wxs: [f32; 8] = [0.0; 8];
7503 let mut wys: [f32; 8] = [0.0; 8];
7504 let mut wzs: [f32; 8] = [0.0; 8];
7505 let n_verts;
7506
7507 if args.len() == 1 {
7508 let list = match &args[0] {
7510 Value::List(l) => l.clone(),
7511 _ => {
7512 return Err(EvalErr::from("draw_polygon_3d: expected list".to_string()))
7513 },
7514 };
7515 let coords: Vec<f32> = list
7516 .iter()
7517 .map(|v| match v {
7518 Value::Number(n) => *n as f32,
7519 _ => 0.0,
7520 })
7521 .collect();
7522 n_verts = (coords.len() / 3).min(8);
7523 for i in 0..n_verts {
7524 wxs[i] = coords[i * 3];
7525 wys[i] = coords[i * 3 + 1];
7526 wzs[i] = coords[i * 3 + 2];
7527 }
7528 } else {
7529 n_verts = (args.len() / 3).min(8);
7531 for i in 0..n_verts {
7532 wxs[i] = self.arg_num(&args, i * 3, 0.0)? as f32;
7533 wys[i] = self.arg_num(&args, i * 3 + 1, 0.0)? as f32;
7534 wzs[i] = self.arg_num(&args, i * 3 + 2, 0.0)? as f32;
7535 }
7536 }
7537 if n_verts < 3 {
7538 return Ok(Value::Unit);
7539 }
7540
7541 let mut gfx = self.gfx.borrow_mut();
7542
7543 if gfx.mesh_capture.is_some() {
7549 let col = gfx.color;
7550 let cap = gfx.mesh_capture.as_mut().unwrap();
7551 for i in 1..n_verts - 1 {
7552 cap.push((
7553 [
7554 wxs[0],
7555 wys[0],
7556 wzs[0],
7557 wxs[i],
7558 wys[i],
7559 wzs[i],
7560 wxs[i + 1],
7561 wys[i + 1],
7562 wzs[i + 1],
7563 ],
7564 col,
7565 ));
7566 }
7567 return Ok(Value::Unit);
7568 }
7569
7570 let normal = crate::gfx::poly::face_normal(
7572 wxs[0], wys[0], wzs[0], wxs[1], wys[1], wzs[1], wxs[2], wys[2], wzs[2],
7573 );
7574
7575 let mut wcs: [u32; 8] = [0; 8];
7577 if gfx.flat_shade {
7578 let c = gfx.color;
7579 for wc in wcs.iter_mut().take(n_verts) {
7580 *wc = c;
7581 }
7582 } else if let Some(ref mat) = gfx.material.clone() {
7583 let cam = [gfx.camera.cx, gfx.camera.cy, gfx.camera.zdist];
7584 let lights: Vec<_> = gfx.lights.clone();
7585 let ambient = gfx.ambient;
7586 for i in 0..n_verts {
7587 let v = [wxs[i], wys[i], wzs[i]];
7588 let vd = [cam[0] - v[0], cam[1] - v[1], cam[2] - v[2]];
7589 wcs[i] = crate::gfx::material::shade(mat, normal, vd, v, &lights, ambient);
7590 }
7591 } else {
7592 let base = gfx.color;
7593 let lights: Vec<_> = gfx.lights.clone();
7594 let ambient = gfx.ambient;
7595 for i in 0..n_verts {
7596 wcs[i] = crate::gfx::light::compute_lit_color_linear(
7597 base,
7598 normal,
7599 [wxs[i], wys[i], wzs[i]],
7600 &lights,
7601 ambient,
7602 );
7603 }
7604 }
7605
7606 let near = -gfx.camera.zdist + 0.05;
7608 let mut clip_in: [(f32, f32, f32, f32, u32); crate::gfx::poly::MAX_CLIP_VERTS] =
7609 [(0.0, 0.0, 0.0, 0.0, 0); crate::gfx::poly::MAX_CLIP_VERTS];
7610 for i in 0..n_verts {
7611 let d = gfx.camera.depth(wxs[i], wys[i], wzs[i]);
7612 clip_in[i] = (wxs[i], wys[i], wzs[i], d, wcs[i]);
7613 }
7614 let mut clip_out: [(f32, f32, f32, f32, u32); crate::gfx::poly::MAX_CLIP_VERTS] =
7615 [(0.0, 0.0, 0.0, 0.0, 0); crate::gfx::poly::MAX_CLIP_VERTS];
7616 let pn = crate::gfx::poly::clip_near(&clip_in, n_verts, near, &mut clip_out);
7617 if pn < 3 {
7618 return Ok(Value::Unit);
7619 }
7620
7621 let mut proj: [(f32, f32, f32, u32); crate::gfx::poly::MAX_CLIP_VERTS] =
7623 [(0.0, 0.0, 0.0, 0); crate::gfx::poly::MAX_CLIP_VERTS];
7624 for i in 0..pn {
7625 let (sx, sy, sz) =
7626 gfx.camera
7627 .project(clip_out[i].0, clip_out[i].1, clip_out[i].2);
7628 let fc = gfx.fog_apply(clip_out[i].4, sz);
7629 proj[i] = (sx, sy, sz, fc);
7630 }
7631
7632 let unlit = gfx.flat_shade;
7634 crate::gfx::poly::fan_emit_proj(
7635 &proj,
7636 pn,
7637 |x0, y0, z0, c0, x1, y1, z1, c1, x2, y2, z2, c2| {
7638 gfx.depth_queue.push_triangle_g_zv(
7639 x0, y0, z0, c0, x1, y1, z1, c1, x2, y2, z2, c2, 3, unlit,
7640 );
7641 },
7642 );
7643 return Ok(Value::Unit);
7644 },
7645
7646 "วาดเส้น3มิติ"
7650 | "draw_line_3d"
7651 | "line3d"
7652 | "画3D线"
7653 | "3D線描く"
7654 | "3D선그리기"
7655 | "رسم_خط_سهبعدی"
7656 | "ارسم_خطا_ثلاثي_الأبعاد"
7657 | "צייר_קו_תלת_ממדי"
7658 | "تھری_ڈی_لکیر_کھینچو" => {
7659 let ax = self.arg_num(&args, 0, 0.0)? as f32;
7660 let ay = self.arg_num(&args, 1, 0.0)? as f32;
7661 let az = self.arg_num(&args, 2, 0.0)? as f32;
7662 let bx = self.arg_num(&args, 3, 0.0)? as f32;
7663 let by = self.arg_num(&args, 4, 0.0)? as f32;
7664 let bz = self.arg_num(&args, 5, 0.0)? as f32;
7665
7666 let mut gfx = self.gfx.borrow_mut();
7667 let color = gfx.color;
7668 let near = -gfx.camera.zdist + 0.05;
7670 let mut lax = ax;
7671 let mut lay = ay;
7672 let mut laz = az;
7673 let mut lbx = bx;
7674 let mut lby = by;
7675 let mut lbz = bz;
7676 let da_raw = gfx.camera.depth(lax, lay, laz);
7677 let db_raw = gfx.camera.depth(lbx, lby, lbz);
7678 if da_raw <= near && db_raw <= near {
7679 return Ok(Value::Unit);
7680 }
7681 if da_raw <= near {
7682 let t = (near - da_raw) / (db_raw - da_raw);
7683 lax += t * (lbx - lax);
7684 lay += t * (lby - lay);
7685 laz += t * (lbz - laz);
7686 } else if db_raw <= near {
7687 let t = (near - da_raw) / (db_raw - da_raw);
7688 lbx = lax + t * (lbx - lax);
7689 lby = lay + t * (lby - lay);
7690 lbz = laz + t * (lbz - laz);
7691 }
7692 if !gfx.edge_set.try_insert(lax, lay, laz, lbx, lby, lbz) {
7694 return Ok(Value::Unit);
7695 }
7696 let (sax, say, da) = gfx.camera.project(lax, lay, laz);
7697 let (sbx, sby, db) = gfx.camera.project(lbx, lby, lbz);
7698 let depth = (da + db) / 2.0;
7699 let color = gfx.fog_apply(color, depth);
7700 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
7701 return Ok(Value::Unit);
7702 },
7703
7704 #[cfg(not(target_arch = "wasm32"))]
7714 "orb_shell"
7715 | "球壳"
7716 | "オーブ殻"
7717 | "오브껍질"
7718 | "เปลือกทรงกลม"
7719 | "پوسته_کروی"
7720 | "قشرة_كروية"
7721 | "קליפת_כדור"
7722 | "کروی_خول"
7723 | "coque_orbe"
7724 | "orb_hülle"
7725 | "оболочка_сферы" => {
7726 let cx = self.arg_num(&args, 0, 0.)? as f32;
7727 let cy = self.arg_num(&args, 1, 0.)? as f32;
7728 let cz = self.arg_num(&args, 2, 0.)? as f32;
7729 let radius = self.arg_num(&args, 3, 1.0)? as f32;
7730 let ry = self.arg_num(&args, 4, 0.)? as f32;
7731 let rx = self.arg_num(&args, 5, 0.)? as f32;
7732 let density = (self.arg_num(&args, 6, 10.)? as i32).clamp(1, 48);
7733 let tr = (self.arg_num(&args, 7, 230.)? as f32).clamp(0., 255.);
7734 let tg = (self.arg_num(&args, 8, 230.)? as f32).clamp(0., 255.);
7735 let tb = (self.arg_num(&args, 9, 235.)? as f32).clamp(0., 255.);
7736 let (cyr, syr) = (ry.cos(), ry.sin());
7737 let (cxr, sxr) = (rx.cos(), rx.sin());
7738 let tau = std::f32::consts::TAU;
7739 let pi = std::f32::consts::PI;
7740 let turns = 6.0_f32; let nseg = 96; let inv_r = if radius.abs() > 1e-5 {
7743 1.0 / radius
7744 } else {
7745 0.0
7746 };
7747 let pt = |u: f32, theta0: f32, dir: f32| -> ([f32; 3], f32) {
7750 let phi = pi * u; let th = dir * turns * tau * u + theta0;
7752 let (mut x, y, mut z) = (
7753 phi.sin() * th.cos() * radius,
7754 phi.cos() * radius,
7755 phi.sin() * th.sin() * radius,
7756 );
7757 let x1 = x * cyr + z * syr; let z1 = -x * syr + z * cyr;
7759 x = x1;
7760 z = z1;
7761 let y2 = y * cxr - z * sxr; let z2 = y * sxr + z * cxr;
7763 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.0, 1.0);
7765 ([cx + x, cy + y2, cz + z2], facing)
7766 };
7767 let mut gfx = self.gfx.borrow_mut();
7768 let near = -gfx.camera.zdist + 0.05;
7769 let seg = |gfx: &mut crate::gfx::GfxState, a: [f32; 3], b: [f32; 3], lum: f32| {
7771 let (mut lax, mut lay, mut laz) = (a[0], a[1], a[2]);
7772 let (mut lbx, mut lby, mut lbz) = (b[0], b[1], b[2]);
7773 let da = gfx.camera.depth(lax, lay, laz);
7774 let db = gfx.camera.depth(lbx, lby, lbz);
7775 if da <= near && db <= near {
7776 return;
7777 }
7778 if da <= near {
7779 let t = (near - da) / (db - da);
7780 lax += t * (lbx - lax);
7781 lay += t * (lby - lay);
7782 laz += t * (lbz - laz);
7783 } else if db <= near {
7784 let t = (near - da) / (db - da);
7785 lbx = lax + t * (lbx - lax);
7786 lby = lay + t * (lby - lay);
7787 lbz = laz + t * (lbz - laz);
7788 }
7789 let (sax, say, da2) = gfx.camera.project(lax, lay, laz);
7790 let (sbx, sby, db2) = gfx.camera.project(lbx, lby, lbz);
7791 let l = (0.12 + 0.88 * lum).clamp(0.0, 1.0);
7793 let cr = (tr * l) as u32;
7794 let cg = (tg * l) as u32;
7795 let cb = (tb * l) as u32;
7796 let color = (cr << 16) | (cg << 8) | cb;
7797 gfx.depth_queue
7798 .push_line((da2 + db2) * 0.5, color, sax, say, sbx, sby);
7799 };
7800 for &dir in &[1.0_f32, -1.0_f32] {
7802 for s in 0..density {
7803 let theta0 = s as f32 * tau / density as f32;
7804 let mut prev = pt(0.0, theta0, dir);
7805 for k in 1..=nseg {
7806 let cur = pt(k as f32 / nseg as f32, theta0, dir);
7807 seg(&mut gfx, prev.0, cur.0, (prev.1 + cur.1) * 0.5);
7808 prev = cur;
7809 }
7810 }
7811 }
7812 return Ok(Value::Unit);
7813 },
7814
7815 #[cfg(not(target_arch = "wasm32"))]
7823 "orb_particles"
7824 | "球内粒子"
7825 | "オーブ粒子"
7826 | "오브입자"
7827 | "อนุภาคทรงกลม"
7828 | "ذرات_کروی"
7829 | "جسيمات_كروية"
7830 | "חלקיקי_כדור"
7831 | "کروی_ذرات"
7832 | "particules_orbe"
7833 | "orb_partikel"
7834 | "частицы_сферы" => {
7835 let cx = self.arg_num(&args, 0, 0.)? as f32;
7836 let cy = self.arg_num(&args, 1, 0.)? as f32;
7837 let cz = self.arg_num(&args, 2, 0.)? as f32;
7838 let radius = self.arg_num(&args, 3, 1.0)? as f32;
7839 let count = (self.arg_num(&args, 4, 160.)? as i32).clamp(1, 4000);
7840 let t = self.arg_num(&args, 5, 0.)? as f32;
7841 let tr = (self.arg_num(&args, 6, 255.)? as f32).clamp(0., 255.);
7842 let tg = (self.arg_num(&args, 7, 255.)? as f32).clamp(0., 255.);
7843 let tb = (self.arg_num(&args, 8, 255.)? as f32).clamp(0., 255.);
7844 let inv_r = if radius.abs() > 1e-5 {
7845 1.0 / radius
7846 } else {
7847 0.0
7848 };
7849 let h = |mut x: u32| -> f32 {
7851 x = x.wrapping_mul(747796405).wrapping_add(2891336453);
7852 x = ((x >> ((x >> 28).wrapping_add(4))) ^ x).wrapping_mul(277803737);
7853 (((x >> 22) ^ x) & 0xFFFFFF) as f32 / 16_777_216.0
7854 };
7855 let tau = std::f32::consts::TAU;
7856 let (cyr, syr) = ((t * 0.5).cos(), (t * 0.5).sin());
7858 let (cxr, sxr) = ((t * 0.23).cos(), (t * 0.23).sin());
7859 let mut gfx = self.gfx.borrow_mut();
7860 let near = -gfx.camera.zdist + 0.05;
7861 let (sw, sh) = (gfx.width as i32, gfx.height as i32);
7862 for i in 0..count {
7863 let i = i as u32;
7864 let u = h(i.wrapping_mul(3) + 1);
7866 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) = (
7870 rr * ph.sin() * th.cos(),
7871 rr * ph.cos(),
7872 rr * ph.sin() * th.sin(),
7873 );
7874 let x1 = x * cyr + z * syr;
7876 let z1 = -x * syr + z * cyr;
7877 x = x1;
7878 z = z1;
7879 let y2 = y * cxr - z * sxr;
7880 let z2 = y * sxr + z * cxr;
7881 let (wx, wy, wz) = (cx + x, cy + y2, cz + z2);
7882 if gfx.camera.depth(wx, wy, wz) <= near {
7883 continue;
7884 }
7885 let (sx, sy, dep) = gfx.camera.project(wx, wy, wz);
7886 let sxi = sx as i32;
7887 let syi = sy as i32;
7888 if sxi < 0 || syi < 0 || sxi >= sw || syi >= sh {
7889 continue;
7890 }
7891 let facing = (0.5 - 0.5 * z2 * inv_r).clamp(0.15, 1.0);
7893 let l = facing;
7894 let cr = (tr * l) as u32;
7895 let cg = (tg * l) as u32;
7896 let cb = (tb * l) as u32;
7897 let color = (cr << 16) | (cg << 8) | cb;
7898 let len = if facing > 0.7 { 1.0 } else { 0.0 };
7900 gfx.depth_queue.push_line(dep, color, sx, sy, sx + len, sy);
7901 }
7902 return Ok(Value::Unit);
7903 },
7904
7905 "project_3d"
7909 | "投影3D"
7910 | "3D投影"
7911 | "3D투영"
7912 | "ฉาย3มิติ"
7913 | "فرافکنی_سهبعدی"
7914 | "إسقاط_ثلاثي_الأبعاد"
7915 | "הטלה_תלת_ממדית"
7916 | "تھری_ڈی_پروجیکشن"
7917 | "projeter_3d"
7918 | "projizieren_3d"
7919 | "проекция_3d" => {
7920 let x = self.arg_num(&args, 0, 0.0)? as f32;
7921 let y = self.arg_num(&args, 1, 0.0)? as f32;
7922 let z = self.arg_num(&args, 2, 0.0)? as f32;
7923 let gfx = self.gfx.borrow();
7924 let near = -gfx.camera.zdist + 0.05;
7925 let d = gfx.camera.depth(x, y, z);
7926 if d <= near {
7927 return Ok(Value::List(Rc::new(vec![
7928 Value::Number(-99999.0),
7929 Value::Number(-99999.0),
7930 Value::Number(d as f64),
7931 ])));
7932 }
7933 let (sx, sy, depth) = gfx.camera.project(x, y, z);
7934 return Ok(Value::List(Rc::new(vec![
7935 Value::Number(sx as f64),
7936 Value::Number(sy as f64),
7937 Value::Number(depth as f64),
7938 ])));
7939 },
7940
7941 #[cfg(not(target_arch = "wasm32"))]
7948 "mouse_ray" => {
7949 let gfx = self.gfx.borrow();
7950 let (mx, my) = gfx
7951 .window
7952 .as_ref()
7953 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
7954 .unwrap_or((gfx.camera.cx, gfx.camera.cy));
7955 let cam = &gfx.camera;
7956 let dcx = (mx - cam.cx) / cam.focal;
7959 let dcy = (my - cam.cy) / cam.focal;
7960 let a_x = dcx;
7963 let a_y = cam.crx * dcy + cam.srx * 1.0;
7964 let a_z = 0.0 - cam.srx * dcy + cam.crx * 1.0;
7965 let dir_x = cam.cry * a_x + cam.sry * a_z;
7966 let dir_y = a_y;
7967 let dir_z = 0.0 - cam.sry * a_x + cam.cry * a_z;
7968 let dlen = (dir_x * dir_x + dir_y * dir_y + dir_z * dir_z)
7969 .sqrt()
7970 .max(1e-6);
7971 let (dir_x, dir_y, dir_z) = (dir_x / dlen, dir_y / dlen, dir_z / dlen);
7972 let ox = cam.tx;
7984 let oy = cam.ty;
7985 let oz = cam.tz;
7986 return Ok(Value::List(Rc::new(vec![
7987 Value::Number(ox as f64),
7988 Value::Number(oy as f64),
7989 Value::Number(oz as f64),
7990 Value::Number(dir_x as f64),
7991 Value::Number(dir_y as f64),
7992 Value::Number(dir_z as f64),
7993 ])));
7994 },
7995 #[cfg(target_arch = "wasm32")]
7996 "mouse_ray" => {
7997 let gfx = self.gfx.borrow();
7998 let mx = crate::gfx::wasm_mouse_x();
7999 let my = crate::gfx::wasm_mouse_y();
8000 let cam = &gfx.camera;
8001 let dcx = (mx - cam.cx) / cam.focal;
8002 let dcy = (my - cam.cy) / cam.focal;
8003 let a_x = dcx;
8004 let a_y = cam.crx * dcy + cam.srx * 1.0;
8005 let a_z = 0.0 - cam.srx * dcy + cam.crx * 1.0;
8006 let dir_x = cam.cry * a_x + cam.sry * a_z;
8007 let dir_y = a_y;
8008 let dir_z = 0.0 - cam.sry * a_x + cam.cry * a_z;
8009 let dlen = (dir_x * dir_x + dir_y * dir_y + dir_z * dir_z)
8010 .sqrt()
8011 .max(1e-6);
8012 let (dir_x, dir_y, dir_z) = (dir_x / dlen, dir_y / dlen, dir_z / dlen);
8013 let ox = cam.tx;
8014 let oy = cam.ty;
8015 let oz = cam.tz;
8016 return Ok(Value::List(Rc::new(vec![
8017 Value::Number(ox as f64),
8018 Value::Number(oy as f64),
8019 Value::Number(oz as f64),
8020 Value::Number(dir_x as f64),
8021 Value::Number(dir_y as f64),
8022 Value::Number(dir_z as f64),
8023 ])));
8024 },
8025 #[cfg(not(target_arch = "wasm32"))]
8028 "draw_poly"
8029 | "填充多边形"
8030 | "ポリゴン塗り"
8031 | "다각형채우기"
8032 | "เติมรูปหลายเหลี่ยม"
8033 | "رسم_چندضلعی"
8034 | "ارسم_مضلع"
8035 | "צייר_מצולע"
8036 | "کثیر_الاضلاع_کھینچو"
8037 | "dessiner_polygone"
8038 | "polygon_zeichnen"
8039 | "рисовать_полигон" => {
8040 let mut pts: Vec<[f32; 2]> = Vec::new();
8041 if let Some(Value::List(v)) = args.first() {
8042 let mut i = 0;
8043 while i + 1 < v.len() {
8044 let x = self.to_number(&v[i]).unwrap_or(0.0) as f32;
8045 let y = self.to_number(&v[i + 1]).unwrap_or(0.0) as f32;
8046 pts.push([x, y]);
8047 i += 2;
8048 }
8049 }
8050 if pts.len() >= 3 {
8051 if pts[0] != pts[pts.len() - 1] {
8052 let p0 = pts[0];
8053 pts.push(p0);
8054 } let mut gfx = self.gfx.borrow_mut();
8056 let (w, h, color, add) = (gfx.width, gfx.height, gfx.color, gfx.blend == 1);
8057 crate::gfx::raster::fill_contours_aa(
8058 &mut gfx.buffer,
8059 w,
8060 h,
8061 color,
8062 add,
8063 std::slice::from_ref(&pts),
8064 );
8065 }
8066 return Ok(Value::Unit);
8067 },
8068
8069 "vtex_grid"
8078 | "ลายตาราง"
8079 | "纹格"
8080 | "格子模様"
8081 | "격자무늬"
8082 | "الگوی_شبکه"
8083 | "نقش_شبكة"
8084 | "דוגמת_רשת"
8085 | "نقش_جالی"
8086 | "motif_grille"
8087 | "muster_gitter"
8088 | "узор_сетка" => {
8089 let cx = self.arg_num(&args, 0, 0.)? as f32;
8090 let cy = self.arg_num(&args, 1, 0.)? as f32;
8091 let cz = self.arg_num(&args, 2, 0.)? as f32;
8092 let ux = self.arg_num(&args, 3, 1.)? as f32;
8093 let uy = self.arg_num(&args, 4, 0.)? as f32;
8094 let uz = self.arg_num(&args, 5, 0.)? as f32;
8095 let vx = self.arg_num(&args, 6, 0.)? as f32;
8096 let vy = self.arg_num(&args, 7, 0.)? as f32;
8097 let vz = self.arg_num(&args, 8, 1.)? as f32;
8098 let cols = self.arg_num(&args, 9, 10.)? as usize;
8099 let rows = self.arg_num(&args, 10, 10.)? as usize;
8100 let cw = self.arg_num(&args, 11, 1.)? as f32;
8101 let ch = self.arg_num(&args, 12, 1.)? as f32;
8102 let fr = self.arg_num(&args, 13, 0.)? as f32;
8103 let hue = self.arg_num(&args, 14, 0.)? as f32;
8104 let mut gfx = self.gfx.borrow_mut();
8105 let cam = gfx.camera.clone();
8106 crate::gfx::vtex::draw_grid(
8107 &mut gfx.depth_queue,
8108 &cam,
8109 cx,
8110 cy,
8111 cz,
8112 ux,
8113 uy,
8114 uz,
8115 vx,
8116 vy,
8117 vz,
8118 cols,
8119 rows,
8120 cw,
8121 ch,
8122 fr,
8123 hue,
8124 );
8125 return Ok(Value::Unit);
8126 },
8127
8128 "vtex_rings"
8130 | "ลายวงซ้อน"
8131 | "纹环"
8132 | "同心円"
8133 | "동심원"
8134 | "الگوی_حلقه"
8135 | "نقش_حلقات"
8136 | "דוגמת_טבעות"
8137 | "نقش_حلقے"
8138 | "motif_anneaux"
8139 | "muster_ringe"
8140 | "узор_кольца" => {
8141 let cx = self.arg_num(&args, 0, 0.)? as f32;
8142 let cy = self.arg_num(&args, 1, 0.)? as f32;
8143 let cz = self.arg_num(&args, 2, 0.)? as f32;
8144 let ux = self.arg_num(&args, 3, 1.)? as f32;
8145 let uy = self.arg_num(&args, 4, 0.)? as f32;
8146 let uz = self.arg_num(&args, 5, 0.)? as f32;
8147 let vx = self.arg_num(&args, 6, 0.)? as f32;
8148 let vy = self.arg_num(&args, 7, 0.)? as f32;
8149 let vz = self.arg_num(&args, 8, 1.)? as f32;
8150 let nr = self.arg_num(&args, 9, 6.)? as usize;
8151 let ns = self.arg_num(&args, 10, 6.)? as usize;
8152 let mr = self.arg_num(&args, 11, 3.)? as f32;
8153 let tw = self.arg_num(&args, 12, 0.)? as f32;
8154 let fr = self.arg_num(&args, 13, 0.)? as f32;
8155 let hue = self.arg_num(&args, 14, 0.)? as f32;
8156 let mut gfx = self.gfx.borrow_mut();
8157 let cam = gfx.camera.clone();
8158 crate::gfx::vtex::draw_rings(
8159 &mut gfx.depth_queue,
8160 &cam,
8161 cx,
8162 cy,
8163 cz,
8164 ux,
8165 uy,
8166 uz,
8167 vx,
8168 vy,
8169 vz,
8170 nr,
8171 ns,
8172 mr,
8173 tw,
8174 fr,
8175 hue,
8176 );
8177 return Ok(Value::Unit);
8178 },
8179
8180 "vtex_star"
8182 | "ลายดาว"
8183 | "纹星"
8184 | "星模様"
8185 | "별무늬"
8186 | "الگوی_ستاره"
8187 | "نقش_نجمة"
8188 | "דוגמת_כוכב"
8189 | "نقش_ستارہ"
8190 | "motif_étoile"
8191 | "muster_stern"
8192 | "узор_звезда" => {
8193 let cx = self.arg_num(&args, 0, 0.)? as f32;
8194 let cy = self.arg_num(&args, 1, 0.)? as f32;
8195 let cz = self.arg_num(&args, 2, 0.)? as f32;
8196 let ux = self.arg_num(&args, 3, 1.)? as f32;
8197 let uy = self.arg_num(&args, 4, 0.)? as f32;
8198 let uz = self.arg_num(&args, 5, 0.)? as f32;
8199 let vx = self.arg_num(&args, 6, 0.)? as f32;
8200 let vy = self.arg_num(&args, 7, 0.)? as f32;
8201 let vz = self.arg_num(&args, 8, 1.)? as f32;
8202 let np = self.arg_num(&args, 9, 6.)? as usize;
8203 let ro = self.arg_num(&args, 10, 2.)? as f32;
8204 let ri = self.arg_num(&args, 11, 1.)? as f32;
8205 let rs = self.arg_num(&args, 12, 0.01)? as f32;
8206 let fr = self.arg_num(&args, 13, 0.)? as f32;
8207 let hue = self.arg_num(&args, 14, 0.)? as f32;
8208 let mut gfx = self.gfx.borrow_mut();
8209 let cam = gfx.camera.clone();
8210 crate::gfx::vtex::draw_star(
8211 &mut gfx.depth_queue,
8212 &cam,
8213 cx,
8214 cy,
8215 cz,
8216 ux,
8217 uy,
8218 uz,
8219 vx,
8220 vy,
8221 vz,
8222 np,
8223 ro,
8224 ri,
8225 rs,
8226 fr,
8227 hue,
8228 );
8229 return Ok(Value::Unit);
8230 },
8231
8232 "vtex_spiral"
8234 | "ลายเกลียว"
8235 | "纹螺"
8236 | "螺旋"
8237 | "나선"
8238 | "الگوی_مارپیچ"
8239 | "نقش_حلزوني"
8240 | "דוגמת_ספירלה"
8241 | "نقش_سرپیچ"
8242 | "motif_spirale"
8243 | "muster_spirale"
8244 | "узор_спираль" => {
8245 let cx = self.arg_num(&args, 0, 0.)? as f32;
8246 let cy = self.arg_num(&args, 1, 0.)? as f32;
8247 let cz = self.arg_num(&args, 2, 0.)? as f32;
8248 let ux = self.arg_num(&args, 3, 1.)? as f32;
8249 let uy = self.arg_num(&args, 4, 0.)? as f32;
8250 let uz = self.arg_num(&args, 5, 0.)? as f32;
8251 let vx = self.arg_num(&args, 6, 0.)? as f32;
8252 let vy = self.arg_num(&args, 7, 0.)? as f32;
8253 let vz = self.arg_num(&args, 8, 1.)? as f32;
8254 let nt = self.arg_num(&args, 9, 3.)? as f32;
8255 let mr = self.arg_num(&args, 10, 3.)? as f32;
8256 let st = self.arg_num(&args, 11, 120.)? as usize;
8257 let fr = self.arg_num(&args, 12, 0.)? as f32;
8258 let hue = self.arg_num(&args, 13, 0.)? as f32;
8259 let mut gfx = self.gfx.borrow_mut();
8260 let cam = gfx.camera.clone();
8261 crate::gfx::vtex::draw_spiral(
8262 &mut gfx.depth_queue,
8263 &cam,
8264 cx,
8265 cy,
8266 cz,
8267 ux,
8268 uy,
8269 uz,
8270 vx,
8271 vy,
8272 vz,
8273 nt,
8274 mr,
8275 st,
8276 fr,
8277 hue,
8278 );
8279 return Ok(Value::Unit);
8280 },
8281
8282 "vtex_flower"
8284 | "ลายดอก"
8285 | "纹花"
8286 | "花模様"
8287 | "꽃무늬"
8288 | "الگوی_گل"
8289 | "نقش_زهرة"
8290 | "דוגמת_פרח"
8291 | "نقش_پھول"
8292 | "motif_fleur"
8293 | "muster_blume"
8294 | "узор_цветок" => {
8295 let cx = self.arg_num(&args, 0, 0.)? as f32;
8296 let cy = self.arg_num(&args, 1, 0.)? as f32;
8297 let cz = self.arg_num(&args, 2, 0.)? as f32;
8298 let ux = self.arg_num(&args, 3, 1.)? as f32;
8299 let uy = self.arg_num(&args, 4, 0.)? as f32;
8300 let uz = self.arg_num(&args, 5, 0.)? as f32;
8301 let vx = self.arg_num(&args, 6, 0.)? as f32;
8302 let vy = self.arg_num(&args, 7, 0.)? as f32;
8303 let vz = self.arg_num(&args, 8, 1.)? as f32;
8304 let r = self.arg_num(&args, 9, 1.)? as f32;
8305 let ns = self.arg_num(&args, 10, 24.)? as usize;
8306 let fr = self.arg_num(&args, 11, 0.)? as f32;
8307 let hue = self.arg_num(&args, 12, 0.)? as f32;
8308 let mut gfx = self.gfx.borrow_mut();
8309 let cam = gfx.camera.clone();
8310 crate::gfx::vtex::draw_flower(
8311 &mut gfx.depth_queue,
8312 &cam,
8313 cx,
8314 cy,
8315 cz,
8316 ux,
8317 uy,
8318 uz,
8319 vx,
8320 vy,
8321 vz,
8322 r,
8323 ns,
8324 fr,
8325 hue,
8326 );
8327 return Ok(Value::Unit);
8328 },
8329
8330 "vtex_letter_rain"
8332 | "ลายอักษรไหล"
8333 | "纹字雨"
8334 | "文字雨"
8335 | "글자비"
8336 | "الگوی_باران_حروف"
8337 | "نقش_مطر_الحروف"
8338 | "דוגמת_גשם_אותיות"
8339 | "نقش_حروف_بارش"
8340 | "motif_pluie_lettres"
8341 | "muster_buchstabenregen"
8342 | "узор_дождь_букв" => {
8343 let cx = self.arg_num(&args, 0, 0.)? as f32;
8344 let cy = self.arg_num(&args, 1, 0.)? as f32;
8345 let cz = self.arg_num(&args, 2, 0.)? as f32;
8346 let ux = self.arg_num(&args, 3, 1.)? as f32;
8347 let uy = self.arg_num(&args, 4, 0.)? as f32;
8348 let uz = self.arg_num(&args, 5, 0.)? as f32;
8349 let vx = self.arg_num(&args, 6, 0.)? as f32;
8350 let vy = self.arg_num(&args, 7, 0.)? as f32;
8351 let vz = self.arg_num(&args, 8, 1.)? as f32;
8352 let nc = self.arg_num(&args, 9, 16.)? as usize;
8353 let nv = self.arg_num(&args, 10, 14.)? as usize;
8354 let cw = self.arg_num(&args, 11, 0.65)? as f32;
8355 let rh = self.arg_num(&args, 12, 0.60)? as f32;
8356 let sp = self.arg_num(&args, 13, 0.025)? as f32;
8357 let fr = self.arg_num(&args, 14, 0.)? as f32;
8358 let hue = self.arg_num(&args, 15, 0.)? as f32;
8359 let mut gfx = self.gfx.borrow_mut();
8360 let cam = gfx.camera.clone();
8361 crate::gfx::vtex::draw_letter_rain(
8362 &mut gfx.depth_queue,
8363 &cam,
8364 cx,
8365 cy,
8366 cz,
8367 ux,
8368 uy,
8369 uz,
8370 vx,
8371 vy,
8372 vz,
8373 nc,
8374 nv,
8375 cw,
8376 rh,
8377 sp,
8378 fr,
8379 hue,
8380 );
8381 return Ok(Value::Unit);
8382 },
8383
8384 "vtex_hyperbolic_uv"
8386 | "ลายไฮเพอร์โบลิก"
8387 | "纹曲面"
8388 | "双曲線"
8389 | "쌍곡선"
8390 | "الگوی_هذلولی"
8391 | "نقش_زائدي"
8392 | "דוגמת_היפרבולית"
8393 | "نقش_ہائپربولک"
8394 | "motif_uv_hyperbolique"
8395 | "muster_hyperbolische_uv"
8396 | "узор_гиперболический_uv" => {
8397 let cx = self.arg_num(&args, 0, 0.)? as f32;
8398 let cy = self.arg_num(&args, 1, 0.)? as f32;
8399 let cz = self.arg_num(&args, 2, 0.)? as f32;
8400 let ux = self.arg_num(&args, 3, 1.)? as f32;
8401 let uy = self.arg_num(&args, 4, 0.)? as f32;
8402 let uz = self.arg_num(&args, 5, 0.)? as f32;
8403 let vx = self.arg_num(&args, 6, 0.)? as f32;
8404 let vy = self.arg_num(&args, 7, 0.)? as f32;
8405 let vz = self.arg_num(&args, 8, 1.)? as f32;
8406 let mr = self.arg_num(&args, 9, 5.)? as f32;
8407 let nc = self.arg_num(&args, 10, 12.)? as usize;
8408 let nr = self.arg_num(&args, 11, 18.)? as usize;
8409 let fr = self.arg_num(&args, 12, 0.)? as f32;
8410 let hue = self.arg_num(&args, 13, 0.)? as f32;
8411 let mut gfx = self.gfx.borrow_mut();
8412 let cam = gfx.camera.clone();
8413 crate::gfx::vtex::draw_hyperbolic_uv(
8414 &mut gfx.depth_queue,
8415 &cam,
8416 cx,
8417 cy,
8418 cz,
8419 ux,
8420 uy,
8421 uz,
8422 vx,
8423 vy,
8424 vz,
8425 mr,
8426 nc,
8427 nr,
8428 fr,
8429 hue,
8430 );
8431 return Ok(Value::Unit);
8432 },
8433
8434 "vtex_halftone"
8436 | "ลายจุด"
8437 | "纹半调"
8438 | "網点模様"
8439 | "망점"
8440 | "الگوی_نیمتن"
8441 | "نقش_نصفي"
8442 | "דוגמת_חצי_גוון"
8443 | "نقش_ہاف_ٹون"
8444 | "motif_demi_ton"
8445 | "muster_halbton"
8446 | "узор_растр" => {
8447 let cx = self.arg_num(&args, 0, 0.)? as f32;
8448 let cy = self.arg_num(&args, 1, 0.)? as f32;
8449 let cz = self.arg_num(&args, 2, 0.)? as f32;
8450 let ux = self.arg_num(&args, 3, 1.)? as f32;
8451 let uy = self.arg_num(&args, 4, 0.)? as f32;
8452 let uz = self.arg_num(&args, 5, 0.)? as f32;
8453 let vx = self.arg_num(&args, 6, 0.)? as f32;
8454 let vy = self.arg_num(&args, 7, 0.)? as f32;
8455 let vz = self.arg_num(&args, 8, 1.)? as f32;
8456 let cols = self.arg_num(&args, 9, 16.)? as usize;
8457 let rows = self.arg_num(&args, 10, 12.)? as usize;
8458 let cw = self.arg_num(&args, 11, 0.5)? as f32;
8459 let ch = self.arg_num(&args, 12, 0.5)? as f32;
8460 let dens = self.arg_num(&args, 13, 0.4)? as f32;
8461 let fr = self.arg_num(&args, 14, 0.)? as f32;
8462 let hue = self.arg_num(&args, 15, 0.)? as f32;
8463 let mut gfx = self.gfx.borrow_mut();
8464 let cam = gfx.camera.clone();
8465 crate::gfx::vtex::draw_halftone(
8466 &mut gfx.depth_queue,
8467 &cam,
8468 cx,
8469 cy,
8470 cz,
8471 ux,
8472 uy,
8473 uz,
8474 vx,
8475 vy,
8476 vz,
8477 cols,
8478 rows,
8479 cw,
8480 ch,
8481 dens,
8482 fr,
8483 hue,
8484 );
8485 return Ok(Value::Unit);
8486 },
8487
8488 "vtex_tessellated"
8490 | "ลายตาข่าย"
8491 | "纹镶嵌"
8492 | "網目模様"
8493 | "격자망"
8494 | "الگوی_کاشیکاری"
8495 | "نقش_مرصوف_متكرر"
8496 | "דוגמת_ריצוף_חוזר"
8497 | "نقش_ٹائلنگ"
8498 | "motif_tesselle"
8499 | "muster_tessellation"
8500 | "узор_мозаика" => {
8501 let cx = self.arg_num(&args, 0, 0.)? as f32;
8502 let cy = self.arg_num(&args, 1, 0.)? as f32;
8503 let cz = self.arg_num(&args, 2, 0.)? as f32;
8504 let ux = self.arg_num(&args, 3, 1.)? as f32;
8505 let uy = self.arg_num(&args, 4, 0.)? as f32;
8506 let uz = self.arg_num(&args, 5, 0.)? as f32;
8507 let vx = self.arg_num(&args, 6, 0.)? as f32;
8508 let vy = self.arg_num(&args, 7, 0.)? as f32;
8509 let vz = self.arg_num(&args, 8, 1.)? as f32;
8510 let cols = self.arg_num(&args, 9, 14.)? as usize;
8511 let rows = self.arg_num(&args, 10, 10.)? as usize;
8512 let cell = self.arg_num(&args, 11, 0.6)? as f32;
8513 let amp = self.arg_num(&args, 12, 0.25)? as f32;
8514 let freq = self.arg_num(&args, 13, 4.)? as f32;
8515 let fr = self.arg_num(&args, 14, 0.)? as f32;
8516 let hue = self.arg_num(&args, 15, 0.)? as f32;
8517 let mut gfx = self.gfx.borrow_mut();
8518 let cam = gfx.camera.clone();
8519 crate::gfx::vtex::draw_tessellated(
8520 &mut gfx.depth_queue,
8521 &cam,
8522 cx,
8523 cy,
8524 cz,
8525 ux,
8526 uy,
8527 uz,
8528 vx,
8529 vy,
8530 vz,
8531 cols,
8532 rows,
8533 cell,
8534 amp,
8535 freq,
8536 fr,
8537 hue,
8538 );
8539 return Ok(Value::Unit);
8540 },
8541
8542 "vtex_lotus"
8544 | "ลายดอกบัว"
8545 | "纹莲"
8546 | "蓮模様"
8547 | "연꽃무늬"
8548 | "الگوی_لوتوس"
8549 | "نقش_لوتس"
8550 | "דוגמת_לוטוס"
8551 | "نقش_کنول"
8552 | "motif_lotus"
8553 | "muster_lotus"
8554 | "узор_лотос" => {
8555 let cx = self.arg_num(&args, 0, 0.)? as f32;
8556 let cy = self.arg_num(&args, 1, 0.)? as f32;
8557 let cz = self.arg_num(&args, 2, 0.)? as f32;
8558 let ux = self.arg_num(&args, 3, 1.)? as f32;
8559 let uy = self.arg_num(&args, 4, 0.)? as f32;
8560 let uz = self.arg_num(&args, 5, 0.)? as f32;
8561 let vx = self.arg_num(&args, 6, 0.)? as f32;
8562 let vy = self.arg_num(&args, 7, 0.)? as f32;
8563 let vz = self.arg_num(&args, 8, 1.)? as f32;
8564 let ri = self.arg_num(&args, 9, 1.)? as f32;
8565 let ro = self.arg_num(&args, 10, 2.)? as f32;
8566 let np = self.arg_num(&args, 11, 12.)? as usize;
8567 let fr = self.arg_num(&args, 12, 0.)? as f32;
8568 let hue = self.arg_num(&args, 13, 0.)? as f32;
8569 let mut gfx = self.gfx.borrow_mut();
8570 let cam = gfx.camera.clone();
8571 crate::gfx::vtex::draw_lotus(
8572 &mut gfx.depth_queue,
8573 &cam,
8574 cx,
8575 cy,
8576 cz,
8577 ux,
8578 uy,
8579 uz,
8580 vx,
8581 vy,
8582 vz,
8583 ri,
8584 ro,
8585 np,
8586 fr,
8587 hue,
8588 );
8589 return Ok(Value::Unit);
8590 },
8591
8592 "vtex_chakra"
8594 | "ลายจักร"
8595 | "纹轮"
8596 | "輪模様"
8597 | "바퀴무늬"
8598 | "الگوی_چاکرا"
8599 | "نقش_تشاكرا"
8600 | "דוגמת_צ'אקרה"
8601 | "نقش_چکر"
8602 | "motif_chakra"
8603 | "muster_chakra"
8604 | "узор_чакра" => {
8605 let cx = self.arg_num(&args, 0, 0.)? as f32;
8606 let cy = self.arg_num(&args, 1, 0.)? as f32;
8607 let cz = self.arg_num(&args, 2, 0.)? as f32;
8608 let ux = self.arg_num(&args, 3, 1.)? as f32;
8609 let uy = self.arg_num(&args, 4, 0.)? as f32;
8610 let uz = self.arg_num(&args, 5, 0.)? as f32;
8611 let vx = self.arg_num(&args, 6, 0.)? as f32;
8612 let vy = self.arg_num(&args, 7, 0.)? as f32;
8613 let vz = self.arg_num(&args, 8, 1.)? as f32;
8614 let r = self.arg_num(&args, 9, 2.)? as f32;
8615 let ns = self.arg_num(&args, 10, 8.)? as usize;
8616 let fr = self.arg_num(&args, 11, 0.)? as f32;
8617 let hue = self.arg_num(&args, 12, 0.)? as f32;
8618 let mut gfx = self.gfx.borrow_mut();
8619 let cam = gfx.camera.clone();
8620 crate::gfx::vtex::draw_chakra(
8621 &mut gfx.depth_queue,
8622 &cam,
8623 cx,
8624 cy,
8625 cz,
8626 ux,
8627 uy,
8628 uz,
8629 vx,
8630 vy,
8631 vz,
8632 r,
8633 ns,
8634 fr,
8635 hue,
8636 );
8637 return Ok(Value::Unit);
8638 },
8639
8640 "vtex_yantra"
8642 | "ลายยันต์"
8643 | "纹咒"
8644 | "護符模様"
8645 | "부적무늬"
8646 | "الگوی_یانترا"
8647 | "نقش_يانترا"
8648 | "דוגמת_יאנטרה"
8649 | "نقش_ینترا"
8650 | "motif_yantra"
8651 | "muster_yantra"
8652 | "узор_янтра" => {
8653 let cx = self.arg_num(&args, 0, 0.)? as f32;
8654 let cy = self.arg_num(&args, 1, 0.)? as f32;
8655 let cz = self.arg_num(&args, 2, 0.)? as f32;
8656 let ux = self.arg_num(&args, 3, 1.)? as f32;
8657 let uy = self.arg_num(&args, 4, 0.)? as f32;
8658 let uz = self.arg_num(&args, 5, 0.)? as f32;
8659 let vx = self.arg_num(&args, 6, 0.)? as f32;
8660 let vy = self.arg_num(&args, 7, 0.)? as f32;
8661 let vz = self.arg_num(&args, 8, 1.)? as f32;
8662 let nl = self.arg_num(&args, 9, 4.)? as usize;
8663 let mr = self.arg_num(&args, 10, 3.)? as f32;
8664 let fr = self.arg_num(&args, 11, 0.)? as f32;
8665 let hue = self.arg_num(&args, 12, 0.)? as f32;
8666 let mut gfx = self.gfx.borrow_mut();
8667 let cam = gfx.camera.clone();
8668 crate::gfx::vtex::draw_yantra(
8669 &mut gfx.depth_queue,
8670 &cam,
8671 cx,
8672 cy,
8673 cz,
8674 ux,
8675 uy,
8676 uz,
8677 vx,
8678 vy,
8679 vz,
8680 nl,
8681 mr,
8682 fr,
8683 hue,
8684 );
8685 return Ok(Value::Unit);
8686 },
8687
8688 "vtex_spiked_cog"
8690 | "ฟันเฟืองหนาม"
8691 | "纹棘轮"
8692 | "歯車模様"
8693 | "톱니바퀴"
8694 | "الگوی_چرخدنده_خاردار"
8695 | "نقش_ترس_شائك"
8696 | "דוגמת_גלגל_קוצני"
8697 | "نقش_خاردار_گیئر"
8698 | "motif_engrenage_pointes"
8699 | "muster_stachelzahnrad"
8700 | "узор_шестерня_шипы" => {
8701 let cx = self.arg_num(&args, 0, 0.)? as f32;
8702 let cy = self.arg_num(&args, 1, 0.)? as f32;
8703 let cz = self.arg_num(&args, 2, 0.)? as f32;
8704 let ux = self.arg_num(&args, 3, 1.)? as f32;
8705 let uy = self.arg_num(&args, 4, 0.)? as f32;
8706 let uz = self.arg_num(&args, 5, 0.)? as f32;
8707 let vx = self.arg_num(&args, 6, 0.)? as f32;
8708 let vy = self.arg_num(&args, 7, 0.)? as f32;
8709 let vz = self.arg_num(&args, 8, 1.)? as f32;
8710 let nt = self.arg_num(&args, 9, 12.)? as usize;
8711 let rb = self.arg_num(&args, 10, 1.)? as f32;
8712 let rs = self.arg_num(&args, 11, 1.3)? as f32;
8713 let rh = self.arg_num(&args, 12, 0.2)? as f32;
8714 let ns = self.arg_num(&args, 13, 6.)? as usize;
8715 let fr = self.arg_num(&args, 14, 0.)? as f32;
8716 let hue = self.arg_num(&args, 15, 0.)? as f32;
8717 let mut gfx = self.gfx.borrow_mut();
8718 let cam = gfx.camera.clone();
8719 crate::gfx::vtex::draw_spiked_cog(
8720 &mut gfx.depth_queue,
8721 &cam,
8722 cx,
8723 cy,
8724 cz,
8725 ux,
8726 uy,
8727 uz,
8728 vx,
8729 vy,
8730 vz,
8731 nt,
8732 rb,
8733 rs,
8734 rh,
8735 ns,
8736 fr,
8737 hue,
8738 );
8739 return Ok(Value::Unit);
8740 },
8741
8742 "vtex_torii"
8744 | "ประตูโทริอิ"
8745 | "纹鸟居"
8746 | "鳥居"
8747 | "도리이"
8748 | "الگوی_توری_ژاپنی"
8749 | "نقش_توري"
8750 | "דוגמת_טוריי"
8751 | "نقش_توری_گیٹ"
8752 | "motif_torii"
8753 | "muster_torii"
8754 | "узор_тории" => {
8755 let cx = self.arg_num(&args, 0, 0.)? as f32;
8756 let cy = self.arg_num(&args, 1, 0.)? as f32;
8757 let cz = self.arg_num(&args, 2, 0.)? as f32;
8758 let ux = self.arg_num(&args, 3, 1.)? as f32;
8759 let uy = self.arg_num(&args, 4, 0.)? as f32;
8760 let uz = self.arg_num(&args, 5, 0.)? as f32;
8761 let vx = self.arg_num(&args, 6, 0.)? as f32;
8762 let vy = self.arg_num(&args, 7, 0.)? as f32;
8763 let vz = self.arg_num(&args, 8, 1.)? as f32;
8764 let w = self.arg_num(&args, 9, 4.)? as f32;
8765 let h = self.arg_num(&args, 10, 5.)? as f32;
8766 let fr = self.arg_num(&args, 11, 0.)? as f32;
8767 let hue = self.arg_num(&args, 12, 0.)? as f32;
8768 let mut gfx = self.gfx.borrow_mut();
8769 let cam = gfx.camera.clone();
8770 crate::gfx::vtex::draw_torii(
8771 &mut gfx.depth_queue,
8772 &cam,
8773 cx,
8774 cy,
8775 cz,
8776 ux,
8777 uy,
8778 uz,
8779 vx,
8780 vy,
8781 vz,
8782 w,
8783 h,
8784 fr,
8785 hue,
8786 );
8787 return Ok(Value::Unit);
8788 },
8789
8790 "vtex_pagoda"
8792 | "เจดีย์"
8793 | "纹塔"
8794 | "塔"
8795 | "탑"
8796 | "الگوی_پاگودا"
8797 | "نقش_باغودا"
8798 | "דוגמת_פגודה"
8799 | "نقش_پگوڈا"
8800 | "motif_pagode"
8801 | "muster_pagode"
8802 | "узор_пагода" => {
8803 let cx = self.arg_num(&args, 0, 0.)? as f32;
8804 let cy = self.arg_num(&args, 1, 0.)? as f32;
8805 let cz = self.arg_num(&args, 2, 0.)? as f32;
8806 let ux = self.arg_num(&args, 3, 1.)? as f32;
8807 let uy = self.arg_num(&args, 4, 0.)? as f32;
8808 let uz = self.arg_num(&args, 5, 0.)? as f32;
8809 let vx = self.arg_num(&args, 6, 0.)? as f32;
8810 let vy = self.arg_num(&args, 7, 0.)? as f32;
8811 let vz = self.arg_num(&args, 8, 1.)? as f32;
8812 let nt = self.arg_num(&args, 9, 5.)? as usize;
8813 let bw = self.arg_num(&args, 10, 2.)? as f32;
8814 let th = self.arg_num(&args, 11, 1.)? as f32;
8815 let tp = self.arg_num(&args, 12, 0.72)? as f32;
8816 let eo = self.arg_num(&args, 13, 0.28)? as f32;
8817 let fr = self.arg_num(&args, 14, 0.)? as f32;
8818 let hue = self.arg_num(&args, 15, 0.)? as f32;
8819 let mut gfx = self.gfx.borrow_mut();
8820 let cam = gfx.camera.clone();
8821 crate::gfx::vtex::draw_pagoda(
8822 &mut gfx.depth_queue,
8823 &cam,
8824 cx,
8825 cy,
8826 cz,
8827 ux,
8828 uy,
8829 uz,
8830 vx,
8831 vy,
8832 vz,
8833 nt,
8834 bw,
8835 th,
8836 tp,
8837 eo,
8838 fr,
8839 hue,
8840 );
8841 return Ok(Value::Unit);
8842 },
8843
8844 #[cfg(not(target_arch = "wasm32"))]
8850 "audio_tone"
8851 | "เสียงโทน"
8852 | "音调"
8853 | "音調"
8854 | "음조"
8855 | "空间音"
8856 | "空間音"
8857 | "공간음"
8858 | "تن_صدا"
8859 | "نغمة"
8860 | "צליל"
8861 | "آواز_کا_سر"
8862 | "tonalité_audio"
8863 | "audioton"
8864 | "звук_тон" => {
8865 let idx = self.arg_num(&args, 0, 0.0)? as usize;
8866 let x = self.arg_num(&args, 1, 0.0)? as f32;
8867 let y = self.arg_num(&args, 2, 0.0)? as f32;
8868 let z = self.arg_num(&args, 3, 0.0)? as f32;
8869 let w = self.arg_num(&args, 4, 1.0)? as f32;
8870 let freq = self.arg_num(&args, 5, 220.0)? as f32;
8871 let amp = self.arg_num(&args, 6, 0.15)? as f32;
8872 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
8873 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
8874 if let Some(audio) = &self.audio {
8875 audio.set_tone(
8876 idx,
8877 ToneParams { x, y, z, w, freq, amp, lfo_rate, lfo_depth },
8878 );
8879 }
8880 return Ok(Value::Unit);
8881 },
8882
8883 #[cfg(not(target_arch = "wasm32"))]
8884 "audio_listener"
8885 | "ผู้ฟัง"
8886 | "音频监听"
8887 | "音声リスナー"
8888 | "오디오리스너"
8889 | "شنونده_صدا"
8890 | "مستمع_الصوت"
8891 | "מאזין_קול"
8892 | "آواز_سننے_والا"
8893 | "auditeur_audio"
8894 | "audiohörer"
8895 | "звук_слушатель" => {
8896 let cry = self.arg_num(&args, 0, 1.0)? as f32;
8897 let sry = self.arg_num(&args, 1, 0.0)? as f32;
8898 let crx = self.arg_num(&args, 2, 1.0)? as f32;
8899 let srx = self.arg_num(&args, 3, 0.0)? as f32;
8900 if let Some(audio) = &self.audio {
8901 audio.set_listener(cry, sry, crx, srx);
8902 }
8903 return Ok(Value::Unit);
8904 },
8905
8906 #[cfg(not(target_arch = "wasm32"))]
8907 "audio_bgm"
8908 | "เพลงพื้นหลัง"
8909 | "เพลงประกอบ"
8910 | "背景乐"
8911 | "BGM"
8912 | "배경음악"
8913 | "موسیقی_پسزمینه"
8914 | "موسيقى_خلفية"
8915 | "מוזיקת_רקע"
8916 | "پس_منظر_موسیقی"
8917 | "musique_fond"
8918 | "hintergrundmusik"
8919 | "фоновая_музыка" => {
8920 let path = match args.first() {
8921 Some(Value::Str(s)) => s.clone(),
8922 _ => return Ok(Value::Unit),
8923 };
8924 let vol = self.arg_num(&args, 1, 0.5)? as f32;
8925 if let Some(audio) = &self.audio {
8926 audio.load_bgm(&path, vol);
8927 }
8928 return Ok(Value::Unit);
8929 },
8930
8931 #[cfg(not(target_arch = "wasm32"))]
8932 "audio_bgm_volume"
8933 | "ระดับเสียงพื้นหลัง"
8934 | "ระดับเพลงประกอบ"
8935 | "背景乐音量"
8936 | "BGM音量"
8937 | "배경음악음량"
8938 | "بلندی_موسیقی_پسزمینه"
8939 | "مستوى_موسيقى_الخلفية"
8940 | "עוצמת_מוזיקת_רקע"
8941 | "پس_منظر_موسیقی_شدت"
8942 | "volume_musique_fond"
8943 | "hintergrundmusiklautstärke"
8944 | "громкость_фоновой_музыки" => {
8945 let vol = self.arg_num(&args, 0, 0.5)? as f32;
8946 if let Some(audio) = &self.audio {
8947 audio.set_bgm_volume(vol);
8948 }
8949 return Ok(Value::Unit);
8950 },
8951
8952 #[cfg(not(target_arch = "wasm32"))]
8953 "audio_volume"
8954 | "ระดับเสียง"
8955 | "音量"
8956 | "음량"
8957 | "بلندی_صدا"
8958 | "مستوى_الصوت"
8959 | "עוצמת_קול"
8960 | "آواز_کی_شدت"
8961 | "volume_audio"
8962 | "audiolautstärke"
8963 | "звук_громкость" => {
8964 let vol = self.arg_num(&args, 0, 0.7)? as f32;
8965 if let Some(audio) = &self.audio {
8966 audio.set_master_volume(vol);
8967 }
8968 return Ok(Value::Unit);
8969 },
8970
8971 #[cfg(target_arch = "wasm32")]
8973 "audio_tone"
8974 | "เสียงโทน"
8975 | "音调"
8976 | "音調"
8977 | "음조"
8978 | "空间音"
8979 | "空間音"
8980 | "공간음"
8981 | "تن_صدا"
8982 | "نغمة"
8983 | "צליל"
8984 | "آواز_کا_سر"
8985 | "tonalité_audio"
8986 | "audioton"
8987 | "звук_тон" => {
8988 let idx = self.arg_num(&args, 0, 0.0)? as usize;
8989 let x = self.arg_num(&args, 1, 0.0)? as f32;
8990 let y = self.arg_num(&args, 2, 0.0)? as f32;
8991 let z = self.arg_num(&args, 3, 0.0)? as f32;
8992 let w = self.arg_num(&args, 4, 1.0)? as f32;
8993 let freq = self.arg_num(&args, 5, 220.0)? as f32;
8994 let amp = self.arg_num(&args, 6, 0.15)? as f32;
8995 let lfo_rate = self.arg_num(&args, 7, 0.5)? as f32;
8996 let lfo_depth = self.arg_num(&args, 8, 0.02)? as f32;
8997 crate::gfx::audio_web::set_tone(idx, x, y, z, w, freq, amp, lfo_rate, lfo_depth);
8998 return Ok(Value::Unit);
8999 },
9000
9001 #[cfg(target_arch = "wasm32")]
9002 "audio_listener"
9003 | "ผู้ฟัง"
9004 | "音频监听"
9005 | "音声リスナー"
9006 | "오디오리스너"
9007 | "شنونده_صدا"
9008 | "مستمع_الصوت"
9009 | "מאזין_קול"
9010 | "آواز_سننے_والا"
9011 | "auditeur_audio"
9012 | "audiohörer"
9013 | "звук_слушатель" => {
9014 let cry = self.arg_num(&args, 0, 1.0)? as f32;
9015 let sry = self.arg_num(&args, 1, 0.0)? as f32;
9016 let crx = self.arg_num(&args, 2, 1.0)? as f32;
9017 let srx = self.arg_num(&args, 3, 0.0)? as f32;
9018 crate::gfx::audio_web::set_listener(cry, sry, crx, srx);
9019 return Ok(Value::Unit);
9020 },
9021
9022 #[cfg(target_arch = "wasm32")]
9023 "audio_bgm"
9024 | "เพลงพื้นหลัง"
9025 | "เพลงประกอบ"
9026 | "背景乐"
9027 | "BGM"
9028 | "배경음악"
9029 | "موسیقی_پسزمینه"
9030 | "موسيقى_خلفية"
9031 | "מוזיקת_רקע"
9032 | "پس_منظر_موسیقی"
9033 | "musique_fond"
9034 | "hintergrundmusik"
9035 | "фоновая_музыка" => {
9036 let path = self.arg_str(&args, 0, "");
9037 let vol = self.arg_num(&args, 1, 0.5)? as f32;
9038 crate::gfx::audio_web::load_bgm(&path, vol);
9039 return Ok(Value::Unit);
9040 },
9041
9042 #[cfg(target_arch = "wasm32")]
9043 "audio_bgm_volume"
9044 | "ระดับเสียงพื้นหลัง"
9045 | "ระดับเพลงประกอบ"
9046 | "背景乐音量"
9047 | "BGM音量"
9048 | "배경음악음량"
9049 | "بلندی_موسیقی_پسزمینه"
9050 | "مستوى_موسيقى_الخلفية"
9051 | "עוצמת_מוזיקת_רקע"
9052 | "پس_منظر_موسیقی_شدت"
9053 | "volume_musique_fond"
9054 | "hintergrundmusiklautstärke"
9055 | "громкость_фоновой_музыки" => {
9056 let vol = self.arg_num(&args, 0, 0.5)? as f32;
9057 crate::gfx::audio_web::set_bgm_volume(vol);
9058 return Ok(Value::Unit);
9059 },
9060
9061 #[cfg(target_arch = "wasm32")]
9062 "audio_volume"
9063 | "ระดับเสียง"
9064 | "音量"
9065 | "음량"
9066 | "بلندی_صدا"
9067 | "مستوى_الصوت"
9068 | "עוצמת_קול"
9069 | "آواز_کی_شدت"
9070 | "volume_audio"
9071 | "audiolautstärke"
9072 | "звук_громкость" => {
9073 let vol = self.arg_num(&args, 0, 0.7)? as f32;
9074 crate::gfx::audio_web::set_master_volume(vol);
9075 return Ok(Value::Unit);
9076 },
9077
9078 #[cfg(target_arch = "wasm32")]
9080 "audio_sample_load"
9081 | "载入采样"
9082 | "サンプル読込"
9083 | "샘플로드"
9084 | "โหลดตัวอย่างเสียง"
9085 | "بارگذاری_نمونه_صدا"
9086 | "تحميل_عينة_صوتية"
9087 | "טעינת_דגימת_קול"
9088 | "آواز_نمونہ_لوڈ"
9089 | "charger_échantillon"
9090 | "sample_laden"
9091 | "загрузить_семпл" => {
9092 let path = self.arg_str(&args, 0, "");
9093 let resolved = self.wasm_resolve_source_path(&path);
9094 match wasm_fetch_bytes(&resolved)
9095 .and_then(|bytes| ling_music::from_bytes(&bytes).map_err(|e| e.to_string()))
9096 {
9097 Ok(t) => {
9098 let id = crate::gfx::audio_web::add_sample(&t.stereo, t.channels, t.rate);
9099 return Ok(Value::Number(id as f64));
9100 },
9101 Err(e) => {
9102 eprintln!("audio_sample_load failed ({path}): {e}");
9103 return Ok(Value::Number(-1.0));
9104 },
9105 }
9106 },
9107 #[cfg(target_arch = "wasm32")]
9108 "audio_sample_play"
9109 | "播放采样"
9110 | "サンプル再生"
9111 | "샘플재생"
9112 | "เล่นตัวอย่างเสียง"
9113 | "پخش_نمونه_صدا"
9114 | "تشغيل_عينة_صوتية"
9115 | "נגינת_דגימת_קול"
9116 | "آواز_نمونہ_چلاؤ"
9117 | "jouer_échantillon"
9118 | "sample_abspielen"
9119 | "играть_семпл" => {
9120 let id = self.arg_num(&args, 0, 0.0)? as usize;
9121 let x = self.arg_num(&args, 1, 0.0)? as f32;
9122 let y = self.arg_num(&args, 2, 0.0)? as f32;
9123 let z = self.arg_num(&args, 3, 0.0)? as f32;
9124 let vol = self.arg_num(&args, 5, 1.0)? as f32;
9126 let looping = self.arg_num(&args, 6, 0.0)? > 0.5;
9127 crate::gfx::audio_web::play_sample(id, x, y, z, vol, looping);
9128 return Ok(Value::Number(0.0));
9129 },
9130 #[cfg(target_arch = "wasm32")]
9131 "audio_sample_stop"
9132 | "停止采样"
9133 | "サンプル停止"
9134 | "샘플정지"
9135 | "หยุดตัวอย่างเสียง"
9136 | "audio_fx_reverb"
9137 | "混响"
9138 | "リバーブ"
9139 | "리버브"
9140 | "เสียงก้อง"
9141 | "audio_fx_delay"
9142 | "回声"
9143 | "ディレイ効果"
9144 | "딜레이"
9145 | "เสียงสะท้อน"
9146 | "audio_fx_lowpass"
9147 | "低通滤波"
9148 | "ローパス"
9149 | "저역통과"
9150 | "กรองความถี่ต่ำ"
9151 | "توقف_نمونه_صدا"
9152 | "إيقاف_عينة_صوتية"
9153 | "עצירת_דגימת_קול"
9154 | "آواز_نمونہ_روکو"
9155 | "arrêter_échantillon"
9156 | "sample_stoppen"
9157 | "остановить_семпл" => {
9158 return Ok(Value::Unit);
9159 },
9160
9161 "รอหน้าต่าง"
9163 | "wait_window"
9164 | "gfx_wait"
9165 | "انتظار_پنجره"
9166 | "انتظر_النافذة"
9167 | "המתן_לחלון"
9168 | "ونڈو_انتظار" => {
9169 #[cfg(not(target_arch = "wasm32"))]
9170 loop {
9171 let still_open = {
9172 let gfx = self.gfx.borrow();
9173 gfx.window
9174 .as_ref()
9175 .map(|w| w.is_open() && !w.is_key_down(minifb::Key::Escape))
9176 .unwrap_or(false)
9177 };
9178 if !still_open {
9179 break;
9180 }
9181 let (buf, w, h) = {
9182 let gfx = self.gfx.borrow();
9183 (gfx.buffer.clone(), gfx.width, gfx.height)
9184 };
9185 let mut gfx = self.gfx.borrow_mut();
9186 if let Some(win) = gfx.window.as_mut() {
9187 if win.update_with_buffer(&buf, w, h).is_err() {
9188 break;
9189 }
9190 }
9191 }
9192 return Ok(Value::Unit);
9193 },
9194
9195 "read_file" | "อ่านไฟล์" | "خواندن_فایل" | "اقرأ_الملف" | "קרא_קובץ" | "فائل_پڑھو" =>
9197 {
9198 #[cfg(target_arch = "wasm32")]
9199 return Ok(Value::Str(String::new()));
9200 #[cfg(not(target_arch = "wasm32"))]
9201 {
9202 let path = self.arg_str(&args, 0, "").replace('\\', "/");
9203 return std::fs::read_to_string(&path)
9204 .map(Value::Str)
9205 .map_err(|e| EvalErr::from(format!("read_file '{path}': {e}")));
9206 }
9207 },
9208 #[cfg(not(target_arch = "wasm32"))]
9210 "net_host" | "เน็ตโฮสต์" | "میزبانی_شبکه" | "استضف_الشبكة" | "ארח_רשת" | "نیٹ_ہوسٹ" =>
9211 {
9212 let port = self.arg_num(&args, 0, 7777.0)? as u16;
9213 net::host(port);
9214 return Ok(Value::Unit);
9215 },
9216 #[cfg(not(target_arch = "wasm32"))]
9217 "net_join" | "เน็ตจอย" | "پیوستن_شبکه" | "انضم_للشبكة" | "הצטרף_לרשת" | "نیٹ_شمولیت" =>
9218 {
9219 let ip = self.arg_str(&args, 0, "127.0.0.1");
9220 let port = self.arg_num(&args, 1, 7777.0)? as u16;
9221 net::join(&ip, port);
9222 return Ok(Value::Unit);
9223 },
9224 #[cfg(not(target_arch = "wasm32"))]
9225 "net_send" | "เน็ตส่ง" | "ارسال_شبکه" | "أرسل_عبر_الشبكة" | "שלח_ברשת" | "نیٹ_بھیجو" =>
9226 {
9227 let s = self.arg_str(&args, 0, "");
9228 net::send(&s);
9229 return Ok(Value::Unit);
9230 },
9231 #[cfg(not(target_arch = "wasm32"))]
9232 "net_recv" | "เน็ตรับ" | "دریافت_شبکه" | "استقبل_من_الشبكة" | "קבל_מרשת" | "نیٹ_وصول" =>
9233 {
9234 return Ok(Value::Str(net::recv()));
9235 },
9236 #[cfg(not(target_arch = "wasm32"))]
9237 "net_status" | "เน็ตสถานะ" | "وضعیت_شبکه" | "حالة_الشبكة" | "סטטוס_רשת" | "نیٹ_حالت" =>
9238 {
9239 return Ok(Value::Number(net::status() as f64));
9240 },
9241 #[cfg(not(target_arch = "wasm32"))]
9242 "net_recv_from" => {
9243 return Ok(Value::Str(net::recv_from()));
9244 },
9245 #[cfg(not(target_arch = "wasm32"))]
9246 "net_send_to" => {
9247 let id = self.arg_num(&args, 0, 0.0)? as u64;
9248 let s = self.arg_str(&args, 1, "");
9249 net::send_to(id, &s);
9250 return Ok(Value::Unit);
9251 },
9252 #[cfg(not(target_arch = "wasm32"))]
9253 "net_close" | "연결종료" => {
9254 net::close();
9255 return Ok(Value::Unit);
9256 },
9257 #[cfg(not(target_arch = "wasm32"))]
9258 "net_clients" => {
9259 return Ok(Value::Str(net::clients()));
9260 },
9261 #[cfg(not(target_arch = "wasm32"))]
9262 "net_client_count" => {
9263 return Ok(Value::Number(net::client_count() as f64));
9264 },
9265 #[cfg(not(target_arch = "wasm32"))]
9266 "net_events" => {
9267 return Ok(Value::Str(net::events()));
9268 },
9269 #[cfg(not(target_arch = "wasm32"))]
9271 "net_announce"
9272 | "เน็ตประกาศ"
9273 | "اعلام_شبکه"
9274 | "أعلن_في_الشبكة"
9275 | "הכרז_ברשת"
9276 | "نیٹ_اعلان" => {
9277 let port = self.arg_num(&args, 0, 7778.0)? as u16;
9278 let info = self.arg_str(&args, 1, "");
9279 net::announce(port, &info);
9280 return Ok(Value::Unit);
9281 },
9282 #[cfg(not(target_arch = "wasm32"))]
9283 "net_announce_stop"
9284 | "เน็ตหยุดประกาศ"
9285 | "توقف_اعلام"
9286 | "أوقف_الإعلان"
9287 | "עצור_הכרזה"
9288 | "اعلان_روکو" => {
9289 net::announce_stop();
9290 return Ok(Value::Unit);
9291 },
9292 #[cfg(not(target_arch = "wasm32"))]
9293 "net_discover" | "เน็ตค้นหา" | "کشف_شبکه" | "اكتشف_الشبكة" | "גלה_רשת" | "نیٹ_دریافت" =>
9294 {
9295 let port = self.arg_num(&args, 0, 7778.0)? as u16;
9296 return Ok(Value::Str(net::discover(port)));
9297 },
9298 #[cfg(not(target_arch = "wasm32"))]
9299 "net_test" | "เน็ตทดสอบ" | "آزمون_شبکه" | "اختبر_الشبكة" | "בדוק_רשת" | "نیٹ_ٹیسٹ" =>
9300 {
9301 let port = self.arg_num(&args, 0, 7777.0)? as u16;
9302 return Ok(Value::Str(net::test_bind(port)));
9303 },
9304 #[cfg(not(target_arch = "wasm32"))]
9306 "ssh_config" => {
9307 let port = self.arg_num(&args, 0, 22.0)? as u16;
9308 let user = self.arg_str(&args, 1, "");
9309 let password = self.arg_str(&args, 2, "");
9310 let host_key_path = self.arg_str(&args, 3, "");
9311 ssh::configure(port, &user, &password, &host_key_path);
9312 return Ok(Value::Unit);
9313 },
9314 #[cfg(not(target_arch = "wasm32"))]
9315 "ssh_serve" => {
9316 return Ok(Value::Bool(ssh::serve()));
9317 },
9318 #[cfg(not(target_arch = "wasm32"))]
9319 "ssh_stop" => {
9320 ssh::stop();
9321 return Ok(Value::Unit);
9322 },
9323 #[cfg(not(target_arch = "wasm32"))]
9324 "ssh_serve_send" => {
9325 let id = self.arg_num(&args, 0, 0.0)? as u64;
9326 let s = self.arg_str(&args, 1, "");
9327 ssh::serve_send(id, &s);
9328 return Ok(Value::Unit);
9329 },
9330 #[cfg(not(target_arch = "wasm32"))]
9331 "ssh_serve_recv" => {
9332 return Ok(Value::Str(ssh::serve_recv()));
9333 },
9334 #[cfg(not(target_arch = "wasm32"))]
9335 "ssh_serve_exit" => {
9336 let id = self.arg_num(&args, 0, 0.0)? as u64;
9337 let code = self.arg_num(&args, 1, 0.0)? as u32;
9338 ssh::serve_exit(id, code);
9339 return Ok(Value::Unit);
9340 },
9341 #[cfg(not(target_arch = "wasm32"))]
9342 "ssh_serve_clients" => {
9343 return Ok(Value::Str(ssh::serve_clients()));
9344 },
9345 #[cfg(not(target_arch = "wasm32"))]
9346 "ssh_serve_client_count" => {
9347 return Ok(Value::Number(ssh::serve_client_count() as f64));
9348 },
9349 #[cfg(not(target_arch = "wasm32"))]
9350 "ssh_serve_events" => {
9351 return Ok(Value::Str(ssh::serve_events()));
9352 },
9353 #[cfg(not(target_arch = "wasm32"))]
9354 "ssh_serve_status" => {
9355 return Ok(Value::Number(ssh::serve_status() as f64));
9356 },
9357 #[cfg(not(target_arch = "wasm32"))]
9358 "ssh_connect" => {
9359 let host = self.arg_str(&args, 0, "");
9360 let port = self.arg_num(&args, 1, 22.0)? as u16;
9361 let user = self.arg_str(&args, 2, "");
9362 let password = self.arg_str(&args, 3, "");
9363 return Ok(Value::Number(ssh::connect(&host, port, &user, &password)));
9364 },
9365 #[cfg(not(target_arch = "wasm32"))]
9366 "ssh_exec" => {
9367 let cmd = self.arg_str(&args, 0, "");
9368 return Ok(Value::Str(ssh::exec(&cmd)));
9369 },
9370 #[cfg(not(target_arch = "wasm32"))]
9371 "ssh_exit_code" => {
9372 return Ok(Value::Number(ssh::exit_code()));
9373 },
9374 #[cfg(not(target_arch = "wasm32"))]
9375 "ssh_client_status" => {
9376 return Ok(Value::Number(ssh::client_status() as f64));
9377 },
9378 #[cfg(not(target_arch = "wasm32"))]
9379 "ssh_close" => {
9380 ssh::close();
9381 return Ok(Value::Unit);
9382 },
9383 #[cfg(not(target_arch = "wasm32"))]
9387 "lingtp_get" => {
9388 let host = self.arg_str(&args, 0, "");
9389 let port = self.arg_num(&args, 1, 7780.0)? as u16;
9390 let path = self.arg_str(&args, 2, "/");
9391 let (status, body) = lingtp::get(&host, port, &path);
9392 self.last_lingtp_status = status;
9393 return Ok(Value::Str(body));
9394 },
9395 #[cfg(not(target_arch = "wasm32"))]
9396 "lingtp_get_status" => {
9397 return Ok(Value::Number(self.last_lingtp_status as f64));
9398 },
9399 #[cfg(not(target_arch = "wasm32"))]
9403 "lingtp_config" => {
9404 let port = self.arg_num(&args, 0, 7780.0)? as u16;
9405 let host_key_path = self.arg_str(&args, 1, "");
9406 lingtp::configure(port, &host_key_path);
9407 return Ok(Value::Unit);
9408 },
9409 #[cfg(not(target_arch = "wasm32"))]
9410 "lingtp_route" => {
9411 let path = self.arg_str(&args, 0, "/");
9412 let handler = args.get(1).cloned().unwrap_or(Value::Unit);
9413 self.lingtp_routes.push((path, handler));
9414 return Ok(Value::Unit);
9415 },
9416 #[cfg(not(target_arch = "wasm32"))]
9423 "lingtp_serve" => {
9424 if !lingtp::serve() {
9425 return Ok(Value::Bool(false));
9426 }
9427 let routes = std::mem::take(&mut self.lingtp_routes);
9428 loop {
9429 let Some(pending) = lingtp::recv_request() else {
9430 if lingtp::status() == 0 {
9431 break;
9432 }
9433 std::thread::sleep(std::time::Duration::from_millis(10));
9434 continue;
9435 };
9436 let matched = routes.iter().find(|(p, _)| p == &pending.path);
9437 let body = match matched {
9438 Some((_, handler)) => match self.call_value(handler.clone(), vec![Value::Str(pending.path.clone())]) {
9439 Ok(v) => v.to_string(),
9440 Err(e) => format!("handler error: {e:?}"),
9441 },
9442 None => String::new(),
9443 };
9444 let _ = pending.respond_to.send(body);
9445 }
9446 return Ok(Value::Bool(true));
9447 },
9448 #[cfg(not(target_arch = "wasm32"))]
9449 "lingtp_stop" => {
9450 lingtp::stop();
9451 return Ok(Value::Unit);
9452 },
9453 #[cfg(not(target_arch = "wasm32"))]
9454 "lingtp_status" => {
9455 return Ok(Value::Number(lingtp::status() as f64));
9456 },
9457 #[cfg(not(target_arch = "wasm32"))]
9458 "lingtp_client_count" => {
9459 return Ok(Value::Number(lingtp::client_count() as f64));
9460 },
9461 #[cfg(not(target_arch = "wasm32"))]
9462 "lingtp_events" => {
9463 return Ok(Value::Str(lingtp::events()));
9464 },
9465 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9467 "http_route" | "เว็บเส้นทาง" | "مسیر_HTTP" | "مسار_HTTP" | "נתיב_HTTP" | "HTTP_روٹ" =>
9468 {
9469 let method = self.arg_str(&args, 0, "GET").to_uppercase();
9470 let path = self.arg_str(&args, 1, "/");
9471 let handler = args.get(2).cloned().unwrap_or(Value::Unit);
9472 self.http_routes.push((method, path, handler));
9473 return Ok(Value::Unit);
9474 },
9475 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9479 "http_static"
9480 | "เว็บสแตติก"
9481 | "فایل_ایستای_HTTP"
9482 | "ملفات_HTTP_ثابتة"
9483 | "קבצים_סטטיים_HTTP"
9484 | "HTTP_مستقل_فائل" => {
9485 let prefix = self.arg_str(&args, 0, "/static");
9486 let dir = self.arg_str(&args, 1, "static");
9487 self.http_static_dirs.push((prefix, dir));
9488 return Ok(Value::Unit);
9489 },
9490 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9491 "http_serve" | "เว็บเสิร์ฟ" | "سرویس_HTTP" | "قدّم_HTTP" | "הגש_HTTP" | "HTTP_سرو" =>
9492 {
9493 let host = self.arg_str(&args, 0, "127.0.0.1");
9494 let port = self.arg_num(&args, 1, 8080.0)? as u16;
9495 let routes = std::mem::take(&mut self.http_routes);
9496 let static_dirs = std::mem::take(&mut self.http_static_dirs);
9497 let rx = web::spawn_server(host.clone(), port, static_dirs, web::TlsMode::Plain);
9503 return self.drain_http_requests(rx, routes);
9504 },
9505 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9510 "http_serve_tls" => {
9511 let host = self.arg_str(&args, 0, "127.0.0.1");
9512 let port = self.arg_num(&args, 1, 8080.0)? as u16;
9513 let cert_pem = self.arg_str(&args, 2, "");
9514 let key_pem = self.arg_str(&args, 3, "");
9515 let routes = std::mem::take(&mut self.http_routes);
9516 let static_dirs = std::mem::take(&mut self.http_static_dirs);
9517 let rx = web::spawn_server(
9518 host.clone(),
9519 port,
9520 static_dirs,
9521 web::TlsMode::Certs { cert_pem, key_pem },
9522 );
9523 return self.drain_http_requests(rx, routes);
9524 },
9525 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9530 "http_serve_dev_tls" => {
9531 let host = self.arg_str(&args, 0, "127.0.0.1");
9532 let port = self.arg_num(&args, 1, 8080.0)? as u16;
9533 let routes = std::mem::take(&mut self.http_routes);
9534 let static_dirs = std::mem::take(&mut self.http_static_dirs);
9535 let rx = web::spawn_server(host.clone(), port, static_dirs, web::TlsMode::Dev);
9536 return self.drain_http_requests(rx, routes);
9537 },
9538 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9546 "http_get" | "เว็บรับ" | "دریافت_HTTP" | "اجلب_HTTP" | "קבל_HTTP" | "HTTP_حاصل_کرو" => {
9547 let url = self.arg_str(&args, 0, "");
9548 let (status, body) = web::get_blocking(&url);
9549 self.last_http_status = status;
9550 return Ok(Value::Str(body));
9551 },
9552 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9556 "http_get_status" => {
9557 return Ok(Value::Number(self.last_http_status as f64));
9558 },
9559 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9563 "http_post_async"
9564 | "เว็บโพสต์ไม่บล็อก"
9565 | "ارسال_ناهمگام_HTTP"
9566 | "أرسل_HTTP_غير_متزامن"
9567 | "שלח_HTTP_אסינכרוני"
9568 | "HTTP_غیر_ہمزمان_بھیجو" => {
9569 let url = self.arg_str(&args, 0, "");
9570 let body = self.arg_str(&args, 1, "");
9571 let content_type = self.arg_str(&args, 2, "application/json");
9572 let id = self.async_jobs.start_post(url, content_type, body);
9573 return Ok(Value::Str(id));
9574 },
9575 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9579 "http_job_poll"
9580 | "เว็บงานสำรวจ"
9581 | "بررسی_وظیفه_HTTP"
9582 | "استطلع_مهمة_HTTP"
9583 | "בדוק_משימת_HTTP"
9584 | "HTTP_کام_پول" => {
9585 let id = self.arg_str(&args, 0, "");
9586 return Ok(Value::Str(self.async_jobs.poll(&id).unwrap_or_default()));
9587 },
9588 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9595 "sdai_generate_start"
9596 | "เอสดีเอไอเริ่มสร้าง"
9597 | "شروع_تولید_هوش"
9598 | "ابدأ_توليد_الذكاء"
9599 | "התחל_יצירת_בינה"
9600 | "اے_آئی_تخلیق_شروع" => {
9601 let base_url = self.arg_str(&args, 0, "http://127.0.0.1:1342");
9602 let prompt = self.arg_str(&args, 1, "");
9603 let width = self.arg_num(&args, 2, 512.0)? as u32;
9604 let height = self.arg_num(&args, 3, 512.0)? as u32;
9605 let id = self
9606 .async_jobs
9607 .start_sdai_txt2img(base_url, prompt, width, height);
9608 return Ok(Value::Str(id));
9609 },
9610 "query_param"
9612 | "พารามิเตอร์"
9613 | "پارامتر_پرسوجو"
9614 | "معامل_الاستعلام"
9615 | "פרמטר_שאילתה"
9616 | "کوئری_پیرامیٹر" => {
9617 let qs = self.arg_str(&args, 0, "");
9618 let name = self.arg_str(&args, 1, "");
9619 let default = self.arg_str(&args, 2, "");
9620 let mut found = default;
9621 for pair in qs.split('&') {
9622 let mut it = pair.splitn(2, '=');
9623 if it.next().unwrap_or("") == name {
9624 found = url_decode(it.next().unwrap_or(""));
9625 break;
9626 }
9627 }
9628 return Ok(Value::Str(found));
9629 },
9630 "cookie_get"
9632 | "รับคุกกี้"
9633 | "دریافت_کوکی"
9634 | "اجلب_الكعكة"
9635 | "קבל_עוגייה"
9636 | "کوکی_حاصل_کرو" => {
9637 let header = self.arg_str(&args, 0, "");
9638 let name = self.arg_str(&args, 1, "");
9639 let default = self.arg_str(&args, 2, "");
9640 let mut found = default;
9641 for pair in header.split(';') {
9642 let p = pair.trim();
9643 let mut it = p.splitn(2, '=');
9644 if it.next().unwrap_or("") == name {
9645 found = it.next().unwrap_or("").to_string();
9646 break;
9647 }
9648 }
9649 return Ok(Value::Str(found));
9650 },
9651 "html_escape"
9653 | "กันเอชทีเอ็มแอล"
9654 | "فرار_HTML"
9655 | "أفلت_HTML"
9656 | "בריחת_HTML"
9657 | "HTML_ایسکیپ" => {
9658 let s = self.arg_str(&args, 0, "");
9659 return Ok(Value::Str(
9660 s.replace('&', "&")
9661 .replace('<', "<")
9662 .replace('>', ">")
9663 .replace('"', """)
9664 .replace('\'', "'"),
9665 ));
9666 },
9667 "json_escape" | "หนีเจสัน" | "فرار_JSON" | "أفلت_JSON" | "בריחת_JSON" | "JSON_ایسکیپ" =>
9672 {
9673 let s = self.arg_str(&args, 0, "");
9674 let mut out = String::with_capacity(s.len() + 8);
9675 for c in s.chars() {
9676 match c {
9677 '"' => out.push_str("\\\""),
9678 '\\' => out.push_str("\\\\"),
9679 '\n' => out.push_str("\\n"),
9680 '\r' => out.push_str("\\r"),
9681 '\t' => out.push_str("\\t"),
9682 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
9683 c => out.push(c),
9684 }
9685 }
9686 return Ok(Value::Str(out));
9687 },
9688 "cli_arg"
9690 | "อาร์กิวเมนต์"
9691 | "آرگومان_خطفرمان"
9692 | "معامل_سطر_الأوامر"
9693 | "ארגומנט_שורת_פקודה"
9694 | "سی_ایل_آئی_دلیل" => {
9695 let name = self.arg_str(&args, 0, "");
9696 let default = self.arg_str(&args, 1, "");
9697 let flag = format!("--{name}");
9698 let argv: Vec<String> = std::env::args().collect();
9699 let found = argv
9700 .iter()
9701 .position(|a| a == &flag)
9702 .and_then(|i| argv.get(i + 1))
9703 .cloned()
9704 .unwrap_or(default);
9705 return Ok(Value::Str(found));
9706 },
9707 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9709 "db_open"
9710 | "ฐานข้อมูลเปิด"
9711 | "باز_کردن_پایگاه_داده"
9712 | "افتح_قاعدة_البيانات"
9713 | "פתח_מסד_נתונים"
9714 | "ڈیٹا_بیس_کھولو" => {
9715 let path = self.arg_str(&args, 0, "app.db");
9716 let conn = ling_http::rusqlite::Connection::open(&path)
9717 .map_err(|e| EvalErr::from(format!("db_open '{path}': {e}")))?;
9718 let _ = conn.execute_batch("PRAGMA foreign_keys = ON; PRAGMA journal_mode = WAL;");
9719 self.db = Some(conn);
9720 return Ok(Value::Unit);
9721 },
9722 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9725 "db_exec"
9726 | "ฐานข้อมูลรัน"
9727 | "اجرای_پایگاه_داده"
9728 | "نفّذ_في_قاعدة_البيانات"
9729 | "בצע_במסד_נתונים"
9730 | "ڈیٹا_بیس_عمل" => {
9731 let sql = self.arg_str(&args, 0, "");
9732 let params = values_to_sql_params(&args[1.min(args.len())..]);
9733 let conn = self
9734 .db
9735 .as_ref()
9736 .ok_or_else(|| EvalErr::from("db_exec: call db_open first".to_string()))?;
9737 let n = conn
9738 .execute(&sql, ling_http::rusqlite::params_from_iter(params.iter()))
9739 .map_err(|e| EvalErr::from(format!("db_exec: {e}\n sql: {sql}")))?;
9740 return Ok(Value::Number(n as f64));
9741 },
9742 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
9744 "db_query"
9745 | "ฐานข้อมูลถาม"
9746 | "پرسوجوی_پایگاه_داده"
9747 | "استعلم_قاعدة_البيانات"
9748 | "שאילתת_מסד_נתונים"
9749 | "ڈیٹا_بیس_سوال" => {
9750 let sql = self.arg_str(&args, 0, "");
9751 let params = values_to_sql_params(&args[1.min(args.len())..]);
9752 let conn = self
9753 .db
9754 .as_ref()
9755 .ok_or_else(|| EvalErr::from("db_query: call db_open first".to_string()))?;
9756 let mut stmt = conn
9757 .prepare(&sql)
9758 .map_err(|e| EvalErr::from(format!("db_query: {e}\n sql: {sql}")))?;
9759 let col_names: Vec<String> =
9760 stmt.column_names().iter().map(|s| s.to_string()).collect();
9761 let mut rows = stmt
9762 .query(ling_http::rusqlite::params_from_iter(params.iter()))
9763 .map_err(|e| EvalErr::from(format!("db_query: {e}")))?;
9764 let mut out = Vec::new();
9765 while let Some(row) = rows
9766 .next()
9767 .map_err(|e| EvalErr::from(format!("db_query row: {e}")))?
9768 {
9769 let mut fields = Vec::with_capacity(col_names.len());
9770 for (i, col) in col_names.iter().enumerate() {
9771 use ling_http::rusqlite::types::ValueRef;
9772 let v = match row.get_ref(i) {
9773 Ok(ValueRef::Null) => Value::Str(String::new()),
9774 Ok(ValueRef::Integer(n)) => Value::Number(n as f64),
9775 Ok(ValueRef::Real(n)) => Value::Number(n),
9776 Ok(ValueRef::Text(t)) => {
9777 Value::Str(String::from_utf8_lossy(t).into_owned())
9778 },
9779 Ok(ValueRef::Blob(b)) => {
9780 use base64::Engine as _;
9781 Value::Str(base64::engine::general_purpose::STANDARD.encode(b))
9782 },
9783 Err(_) => Value::Str(String::new()),
9784 };
9785 fields.push((col.clone(), v));
9786 }
9787 out.push(Value::Struct { name: "Row".to_string(), fields });
9788 }
9789 return Ok(Value::List(Rc::new(out)));
9790 },
9791 #[cfg(not(target_arch = "wasm32"))]
9793 "gamepad_poll"
9794 | "จอยโพล"
9795 | "بررسی_دسته_بازی"
9796 | "استطلع_يد_اللعب"
9797 | "בדוק_בקר_משחק"
9798 | "گیم_پیڈ_پول_کرو" => {
9799 gamepad::poll();
9800 return Ok(Value::Unit);
9801 },
9802 #[cfg(not(target_arch = "wasm32"))]
9803 "gamepad_button"
9804 | "จอยปุ่ม"
9805 | "دکمه_دسته_بازی"
9806 | "زر_يد_اللعب"
9807 | "כפתור_בקר_משחק"
9808 | "گیم_پیڈ_بٹن_دبایا" => {
9809 let name = self.arg_str(&args, 0, "");
9810 return Ok(Value::Number(if gamepad::button(&name) {
9811 1.0
9812 } else {
9813 0.0
9814 }));
9815 },
9816 #[cfg(not(target_arch = "wasm32"))]
9817 "gamepad_axis"
9818 | "จอยแกน"
9819 | "محور_دسته_بازی"
9820 | "محور_يد_اللعب"
9821 | "ציר_בקר_משחק"
9822 | "گیم_پیڈ_محور" => {
9823 let name = self.arg_str(&args, 0, "");
9824 return Ok(Value::Number(gamepad::axis(&name) as f64));
9825 },
9826 #[cfg(not(target_arch = "wasm32"))]
9827 "gamepad_rumble"
9828 | "จอยสั่น"
9829 | "لرزش_دسته_بازی"
9830 | "اهتزاز_يد_اللعب"
9831 | "רטט_בקר_משחק"
9832 | "گیم_پیڈ_لرزش" => {
9833 let low = self.arg_num(&args, 0, 0.0)? as f32;
9834 let high = self.arg_num(&args, 1, 0.0)? as f32;
9835 let ms = self.arg_num(&args, 2, 200.0)? as u32;
9836 gamepad::rumble(low, high, ms);
9837 return Ok(Value::Unit);
9838 },
9839 #[cfg(not(target_arch = "wasm32"))]
9840 "gamepad_list"
9841 | "จอยรายการ"
9842 | "فهرست_دستههای_بازی"
9843 | "قائمة_أيدي_اللعب"
9844 | "רשימת_בקרי_משחק"
9845 | "گیم_پیڈ_فہرست" => {
9846 return Ok(Value::Str(gamepad::list()));
9847 },
9848 #[cfg(not(target_arch = "wasm32"))]
9849 "gamepad_any"
9850 | "จอยใดๆ"
9851 | "هر_دسته_بازی"
9852 | "أي_يد_لعب"
9853 | "בקר_כלשהו"
9854 | "کوئی_بھی_گیم_پیڈ" => {
9855 return Ok(Value::Number(if gamepad::any_button() { 1.0 } else { 0.0 }));
9856 },
9857 #[cfg(target_arch = "wasm32")]
9859 "gamepad_poll"
9860 | "จอยโพล"
9861 | "gamepad_rumble"
9862 | "จอยสั่น"
9863 | "بررسی_دسته_بازی"
9864 | "استطلع_يد_اللعب"
9865 | "בדוק_בקר_משחק"
9866 | "گیم_پیڈ_پول_کرو" => {
9867 return Ok(Value::Unit);
9868 },
9869 #[cfg(target_arch = "wasm32")]
9870 "gamepad_button"
9871 | "จอยปุ่ม"
9872 | "gamepad_axis"
9873 | "จอยแกน"
9874 | "gamepad_any"
9875 | "จอยใดๆ"
9876 | "دکمه_دسته_بازی"
9877 | "زر_يد_اللعب"
9878 | "כפתור_בקר_משחק"
9879 | "گیم_پیڈ_بٹن" => {
9880 return Ok(Value::Number(0.0));
9881 },
9882 #[cfg(target_arch = "wasm32")]
9883 "gamepad_list"
9884 | "จอยรายการ"
9885 | "فهرست_دستههای_بازی"
9886 | "قائمة_أيدي_اللعب"
9887 | "רשימת_בקרי_משחק"
9888 | "گیم_پیڈ_فہرست" => {
9889 return Ok(Value::Str(String::new()));
9890 },
9891
9892 #[cfg(not(target_arch = "wasm32"))]
9895 "nn_new"
9896 | "建神经网"
9897 | "ニューラル作成"
9898 | "신경망생성"
9899 | "สร้างโครงข่าย"
9900 | "شبکه_جدید"
9901 | "شبكة_جديدة"
9902 | "רשת_חדשה"
9903 | "نئی_نیورل_نیٹ"
9904 | "nouveau_réseau"
9905 | "neues_netz"
9906 | "новая_сеть" => {
9907 let n_in = self.arg_num(&args, 0, 1.0)?.max(0.0) as usize;
9908 let seed = self.arg_num(&args, 1, 1.0)? as u64;
9909 return Ok(Value::Number(ai::nn_new(n_in, seed) as f64));
9910 },
9911 #[cfg(not(target_arch = "wasm32"))]
9913 "nn_dense"
9914 | "密集层"
9915 | "密層追加"
9916 | "밀집층"
9917 | "ชั้นหนาแน่น"
9918 | "لایه_متراکم"
9919 | "طبقة_كثيفة"
9920 | "שכבה_צפופה"
9921 | "ڈینس_لیئر"
9922 | "réseau_dense"
9923 | "netz_dicht"
9924 | "плотная_сеть" => {
9925 let id = self.arg_num(&args, 0, -1.0)? as i64;
9926 let units = self.arg_num(&args, 1, 1.0)?.max(1.0) as usize;
9927 let act = self.arg_str(&args, 2, "relu");
9928 ai::nn_dense(id, units, &act);
9929 return Ok(Value::Unit);
9930 },
9931 #[cfg(not(target_arch = "wasm32"))]
9933 "nn_forward"
9934 | "神经前向"
9935 | "順伝播"
9936 | "순전파"
9937 | "ส่งต่อโครงข่าย"
9938 | "پیشروی_شبکه"
9939 | "تمرير_أمامي"
9940 | "העברה_קדימה"
9941 | "فارورڈ_پاس"
9942 | "propager_réseau"
9943 | "netz_vorwärts"
9944 | "прямой_проход_сети" => {
9945 let id = self.arg_num(&args, 0, -1.0)? as i64;
9946 let input = self.arg_list_f32(&args, 1);
9947 let out = ai::nn_forward(id, &input);
9948 return Ok(Value::List(Rc::new(
9949 out.into_iter().map(|v| Value::Number(v as f64)).collect(),
9950 )));
9951 },
9952 #[cfg(not(target_arch = "wasm32"))]
9954 "nn_train"
9955 | "训练网"
9956 | "ニューラル学習"
9957 | "신경망학습"
9958 | "ฝึกโครงข่าย"
9959 | "آموزش_شبکه"
9960 | "درّب_الشبكة"
9961 | "אמן_רשת"
9962 | "نیٹ_ٹریننگ"
9963 | "entraîner_réseau"
9964 | "netz_trainieren"
9965 | "обучить_сеть" => {
9966 let id = self.arg_num(&args, 0, -1.0)? as i64;
9967 let input = self.arg_list_f32(&args, 1);
9968 let target = self.arg_list_f32(&args, 2);
9969 let lr = self.arg_num(&args, 3, 0.01)? as f32;
9970 return Ok(Value::Number(ai::nn_train(id, &input, &target, lr) as f64));
9971 },
9972 #[cfg(not(target_arch = "wasm32"))]
9974 "nn_save"
9975 | "保存网"
9976 | "網保存"
9977 | "신경망저장"
9978 | "บันทึกโครงข่าย"
9979 | "ذخیره_شبکه"
9980 | "احفظ_الشبكة"
9981 | "שמור_רשת"
9982 | "نیٹ_محفوظ_کرو"
9983 | "sauvegarder_réseau"
9984 | "netz_speichern"
9985 | "сохранить_сеть" => {
9986 let id = self.arg_num(&args, 0, -1.0)? as i64;
9987 let path = self.arg_str(&args, 1, "model.lnn");
9988 return Ok(Value::Bool(ai::nn_save(id, &path)));
9989 },
9990 #[cfg(not(target_arch = "wasm32"))]
9992 "nn_load"
9993 | "载入网"
9994 | "網読込"
9995 | "신경망불러오기"
9996 | "โหลดโครงข่าย"
9997 | "بارگذاری_شبکه"
9998 | "حمّل_الشبكة"
9999 | "טען_רשת"
10000 | "نیٹ_لوڈ"
10001 | "charger_réseau"
10002 | "netz_laden"
10003 | "загрузить_сеть" => {
10004 let path = self.arg_str(&args, 0, "model.lnn");
10005 return Ok(Value::Number(ai::nn_load(&path) as f64));
10006 },
10007
10008 #[cfg(not(target_arch = "wasm32"))]
10011 "bt_build"
10012 | "建行为树"
10013 | "行動木構築"
10014 | "행동트리구성"
10015 | "สร้างต้นไม้พฤติกรรม"
10016 | "ساخت_درخت_رفتار"
10017 | "ابنِ_شجرة_السلوك"
10018 | "בנה_עץ_התנהגות"
10019 | "بی_ٹی_تعمیر"
10020 | "construire_arbre_comportement"
10021 | "verhaltensbaum_bauen"
10022 | "построить_дерево_поведения" => {
10023 let spec = self.arg_str(&args, 0, "");
10024 return Ok(Value::Number(ai::bt_build(&spec) as f64));
10025 },
10026 #[cfg(not(target_arch = "wasm32"))]
10028 "bt_set"
10029 | "设事实"
10030 | "事実設定"
10031 | "사실설정"
10032 | "ตั้งข้อเท็จจริง"
10033 | "تنظیم_واقعیت"
10034 | "عيّن_حقيقة"
10035 | "קבע_עובדה"
10036 | "بی_ٹی_سیٹ"
10037 | "définir_arbre_comportement"
10038 | "verhaltensbaum_setzen"
10039 | "задать_дерево_поведения" => {
10040 let id = self.arg_num(&args, 0, -1.0)? as i64;
10041 let key = self.arg_str(&args, 1, "");
10042 let val = self.arg_num(&args, 2, 0.0)? as f32;
10043 ai::bt_set(id, &key, val);
10044 return Ok(Value::Unit);
10045 },
10046 #[cfg(not(target_arch = "wasm32"))]
10048 "bt_tick"
10049 | "行为树滴答"
10050 | "行動木更新"
10051 | "행동트리틱"
10052 | "เดินต้นไม้พฤติกรรม"
10053 | "تیک_درخت_رفتار"
10054 | "نبضة_شجرة_السلوك"
10055 | "טיק_עץ_התנהגות"
10056 | "بی_ٹی_ٹک"
10057 | "tick_arbre_comportement"
10058 | "verhaltensbaum_tick"
10059 | "тик_дерева_поведения" => {
10060 let id = self.arg_num(&args, 0, -1.0)? as i64;
10061 return Ok(Value::Str(ai::bt_tick(id)));
10062 },
10063 #[cfg(not(target_arch = "wasm32"))]
10065 "bt_status"
10066 | "行为树状态"
10067 | "行動木状態"
10068 | "행동트리상태"
10069 | "สถานะต้นไม้พฤติกรรม"
10070 | "وضعیت_درخت_رفتار"
10071 | "حالة_شجرة_السلوك"
10072 | "סטטוס_עץ_התנהגות"
10073 | "بی_ٹی_حالت"
10074 | "statut_arbre_comportement"
10075 | "verhaltensbaum_status"
10076 | "статус_дерева_поведения" => {
10077 let id = self.arg_num(&args, 0, -1.0)? as i64;
10078 return Ok(Value::Number(ai::bt_status(id) as f64));
10079 },
10080
10081 #[cfg(not(target_arch = "wasm32"))]
10084 "dialog_new"
10085 | "建对话模型"
10086 | "対話モデル作成"
10087 | "대화모델생성"
10088 | "สร้างโมเดลสนทนา"
10089 | "مدل_گفتگوی_جدید"
10090 | "نموذج_حوار_جديد"
10091 | "מודל_דיאלוג_חדש"
10092 | "نیا_مکالمہ_ماڈل"
10093 | "nouveau_dialogue"
10094 | "neuer_dialog"
10095 | "новый_диалог" => {
10096 let ctx = self.arg_num(&args, 0, 3.0)?.max(1.0) as usize;
10097 let embed = self.arg_num(&args, 1, 32.0)?.max(1.0) as usize;
10098 let hidden = self.arg_num(&args, 2, 64.0)?.max(1.0) as usize;
10099 let seed = self.arg_num(&args, 3, 1.0)? as u64;
10100 return Ok(Value::Number(
10101 ai::dialog_new(ctx, embed, hidden, seed) as f64
10102 ));
10103 },
10104 #[cfg(not(target_arch = "wasm32"))]
10106 "dialog_learn"
10107 | "对话学习"
10108 | "対話学習"
10109 | "대화학습"
10110 | "เรียนรู้สนทนา"
10111 | "یادگیری_گفتگو"
10112 | "تعلّم_الحوار"
10113 | "למד_דיאלוג"
10114 | "مکالمہ_سیکھو"
10115 | "apprendre_dialogue"
10116 | "dialog_lernen"
10117 | "обучить_диалог" => {
10118 let id = self.arg_num(&args, 0, -1.0)? as i64;
10119 let text = self.arg_str(&args, 1, "");
10120 ai::dialog_learn(id, &text);
10121 return Ok(Value::Unit);
10122 },
10123 #[cfg(not(target_arch = "wasm32"))]
10125 "dialog_load"
10126 | "对话载入"
10127 | "対話読込"
10128 | "대화불러오기"
10129 | "โหลดชุดสนทนา"
10130 | "بارگذاری_مجموعه_گفتگو"
10131 | "حمّل_مجموعة_الحوار"
10132 | "טען_מערך_דיאלוג"
10133 | "مکالمہ_مجموعہ_لوڈ"
10134 | "charger_dialogue"
10135 | "dialog_laden"
10136 | "загрузить_диалог" => {
10137 let id = self.arg_num(&args, 0, -1.0)? as i64;
10138 let path = self.arg_str(&args, 1, "");
10139 return Ok(Value::Number(ai::dialog_load(id, &path) as f64));
10140 },
10141 #[cfg(not(target_arch = "wasm32"))]
10143 "dialog_train"
10144 | "对话训练"
10145 | "対話訓練"
10146 | "대화훈련"
10147 | "ฝึกสนทนา"
10148 | "آموزش_گفتگو"
10149 | "درّب_الحوار"
10150 | "אמן_דיאלוג"
10151 | "مکالمہ_ٹریننگ"
10152 | "entraîner_dialogue"
10153 | "dialog_trainieren"
10154 | "тренировать_диалог" => {
10155 let id = self.arg_num(&args, 0, -1.0)? as i64;
10156 let epochs = self.arg_num(&args, 1, 20.0)?.max(1.0) as usize;
10157 let lr = self.arg_num(&args, 2, 0.1)? as f32;
10158 return Ok(Value::Number(ai::dialog_train(id, epochs, lr) as f64));
10159 },
10160 #[cfg(not(target_arch = "wasm32"))]
10162 "dialog_say"
10163 | "对话生成"
10164 | "対話生成"
10165 | "대화생성"
10166 | "พูดสนทนา"
10167 | "بگو"
10168 | "قل"
10169 | "אמור"
10170 | "کہو"
10171 | "dire_dialogue"
10172 | "dialog_sagen"
10173 | "сказать_диалог" => {
10174 let id = self.arg_num(&args, 0, -1.0)? as i64;
10175 let prompt = self.arg_str(&args, 1, "");
10176 let max = self.arg_num(&args, 2, 24.0)?.max(1.0) as usize;
10177 let temp = self.arg_num(&args, 3, 0.8)? as f32;
10178 return Ok(Value::Str(ai::dialog_say(id, &prompt, max, temp)));
10179 },
10180 #[cfg(not(target_arch = "wasm32"))]
10182 "dialog_save"
10183 | "对话存模"
10184 | "対話モデル保存"
10185 | "대화모델저장"
10186 | "บันทึกโมเดลสนทนา"
10187 | "ذخیره_مدل_گفتگو"
10188 | "احفظ_نموذج_الحوار"
10189 | "שמור_מודל_דיאלוג"
10190 | "مکالمہ_ماڈل_محفوظ"
10191 | "sauvegarder_dialogue"
10192 | "dialog_speichern"
10193 | "сохранить_диалог" => {
10194 let id = self.arg_num(&args, 0, -1.0)? as i64;
10195 let path = self.arg_str(&args, 1, "model.llm");
10196 return Ok(Value::Bool(ai::dialog_save(id, &path)));
10197 },
10198 #[cfg(not(target_arch = "wasm32"))]
10200 "dialog_load_model"
10201 | "对话载模"
10202 | "対話モデル読込"
10203 | "대화모델불러오기"
10204 | "โหลดโมเดลสนทนา"
10205 | "بارگذاری_مدل_گفتگو"
10206 | "حمّل_نموذج_الحوار"
10207 | "טען_מודל_דיאלוג"
10208 | "مکالمہ_ماڈل_لوڈ"
10209 | "charger_modèle_dialogue"
10210 | "dialog_modell_laden"
10211 | "загрузить_модель_диалога" => {
10212 let path = self.arg_str(&args, 0, "model.llm");
10213 return Ok(Value::Number(ai::dialog_load_model(&path) as f64));
10214 },
10215
10216 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10223 "llm_load" => {
10224 let gguf_path = self.arg_str(&args, 0, "");
10225 let tokenizer_path = self.arg_str(&args, 1, "");
10226 let device_index = self.arg_num(&args, 2, 0.0)? as i64;
10227 return Ok(Value::Number(
10228 llm::llm_load(&gguf_path, &tokenizer_path, device_index) as f64,
10229 ));
10230 },
10231 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10233 "llm_generate_start" => {
10234 let handle = self.arg_num(&args, 0, -1.0)? as i64;
10235 let system_prompt = self.arg_str(&args, 1, "");
10236 let history = self.arg_str(&args, 2, "");
10237 let max_tokens = self.arg_num(&args, 3, 512.0)? as i64;
10238 let temperature = self.arg_num(&args, 4, 0.7)?;
10239 let top_p = self.arg_num(&args, 5, 0.9)?;
10240 return Ok(Value::Str(llm::llm_generate_start(
10241 handle,
10242 &system_prompt,
10243 &history,
10244 max_tokens,
10245 temperature,
10246 top_p,
10247 )));
10248 },
10249 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10251 "llm_generate_poll" => {
10252 let job_id = self.arg_str(&args, 0, "");
10253 return Ok(Value::Str(llm::llm_generate_poll(&job_id)));
10254 },
10255 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10257 "llm_generate_done" => {
10258 let job_id = self.arg_str(&args, 0, "");
10259 return Ok(Value::Bool(llm::llm_generate_done(&job_id)));
10260 },
10261 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10263 "llm_job_status" => {
10264 let job_id = self.arg_str(&args, 0, "");
10265 return Ok(Value::Str(llm::llm_job_status(&job_id)));
10266 },
10267 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10269 "llm_job_tool_name" => {
10270 let job_id = self.arg_str(&args, 0, "");
10271 return Ok(Value::Str(llm::llm_job_tool_name(&job_id)));
10272 },
10273 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10275 "llm_job_provide_tool_result" => {
10276 let job_id = self.arg_str(&args, 0, "");
10277 let result_text = self.arg_str(&args, 1, "");
10278 return Ok(Value::Bool(llm::llm_job_provide_tool_result(
10279 &job_id,
10280 &result_text,
10281 )));
10282 },
10283 #[cfg(all(not(target_arch = "wasm32"), feature = "llm"))]
10285 "llm_job_cancel" => {
10286 let job_id = self.arg_str(&args, 0, "");
10287 llm::llm_job_cancel(&job_id);
10288 return Ok(Value::Unit);
10289 },
10290
10291 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10297 "oauth_google_start" => {
10298 let code = self.arg_str(&args, 0, "");
10299 let client_id = self.arg_str(&args, 1, "");
10300 let client_secret = self.arg_str(&args, 2, "");
10301 let redirect_uri = self.arg_str(&args, 3, "");
10302 let id = self.oauth_jobs.start_google_login(
10303 code,
10304 client_id,
10305 client_secret,
10306 redirect_uri,
10307 );
10308 return Ok(Value::Str(id));
10309 },
10310 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10311 "oauth_google_done" => {
10312 let id = self.arg_str(&args, 0, "");
10313 return Ok(Value::Bool(self.oauth_jobs.done(&id)));
10314 },
10315 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10317 "oauth_google_error" => {
10318 let id = self.arg_str(&args, 0, "");
10319 return Ok(Value::Str(self.oauth_jobs.error(&id)));
10320 },
10321 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10322 "oauth_google_sub" => {
10323 let id = self.arg_str(&args, 0, "");
10324 return Ok(Value::Str(self.oauth_jobs.sub(&id)));
10325 },
10326 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10327 "oauth_google_email" => {
10328 let id = self.arg_str(&args, 0, "");
10329 return Ok(Value::Str(self.oauth_jobs.email(&id)));
10330 },
10331 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10332 "oauth_google_name" => {
10333 let id = self.arg_str(&args, 0, "");
10334 return Ok(Value::Str(self.oauth_jobs.name(&id)));
10335 },
10336
10337 #[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
10341 "vision_load" => {
10342 let gguf_path = self.arg_str(&args, 0, "");
10343 let tokenizer_path = self.arg_str(&args, 1, "");
10344 let device_index = self.arg_num(&args, 2, 0.0)? as i64;
10345 return Ok(Value::Number(vision::vision_load(
10346 &gguf_path,
10347 &tokenizer_path,
10348 device_index,
10349 ) as f64));
10350 },
10351 #[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
10353 "vision_analyze_start" => {
10354 let handle = self.arg_num(&args, 0, -1.0)? as i64;
10355 let image_base64 = self.arg_str(&args, 1, "");
10356 let question = self.arg_str(&args, 2, "Describe this image.");
10357 let max_tokens = self.arg_num(&args, 3, 256.0)? as i64;
10358 let temperature = self.arg_num(&args, 4, 0.7)?;
10359 let top_p = self.arg_num(&args, 5, 0.9)?;
10360 return Ok(Value::Str(vision::vision_analyze_start(
10361 handle,
10362 &image_base64,
10363 &question,
10364 max_tokens,
10365 temperature,
10366 top_p,
10367 )));
10368 },
10369 #[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
10370 "vision_analyze_poll" => {
10371 let job_id = self.arg_str(&args, 0, "");
10372 return Ok(Value::Str(vision::vision_analyze_poll(&job_id)));
10373 },
10374 #[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
10375 "vision_analyze_done" => {
10376 let job_id = self.arg_str(&args, 0, "");
10377 return Ok(Value::Bool(vision::vision_analyze_done(&job_id)));
10378 },
10379 #[cfg(all(not(target_arch = "wasm32"), feature = "vision"))]
10381 "vision_analyze_error" => {
10382 let job_id = self.arg_str(&args, 0, "");
10383 return Ok(Value::Str(vision::vision_analyze_error(&job_id)));
10384 },
10385
10386 "url_decode" | "网址解码" => {
10389 let s = self.arg_str(&args, 0, "");
10390 let bytes = s.as_bytes();
10391 let mut out = Vec::with_capacity(bytes.len());
10392 let mut i = 0;
10393 while i < bytes.len() {
10394 match bytes[i] {
10395 b'+' => {
10396 out.push(b' ');
10397 i += 1;
10398 },
10399 b'%' if i + 2 < bytes.len() => {
10400 let hex = std::str::from_utf8(&bytes[i + 1..i + 3]).unwrap_or("");
10401 match u8::from_str_radix(hex, 16) {
10402 Ok(b) => {
10403 out.push(b);
10404 i += 3;
10405 },
10406 Err(_) => {
10407 out.push(bytes[i]);
10408 i += 1;
10409 },
10410 }
10411 },
10412 b => {
10413 out.push(b);
10414 i += 1;
10415 },
10416 }
10417 }
10418 return Ok(Value::Str(String::from_utf8_lossy(&out).into_owned()));
10419 },
10420 "now_unix" | "现在时间" => {
10424 return Ok(Value::Number(now_secs()));
10425 },
10426 "file_exists" | "文件存在" => {
10427 #[cfg(target_arch = "wasm32")]
10428 return Ok(Value::Bool(false));
10429 #[cfg(not(target_arch = "wasm32"))]
10430 {
10431 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10432 return Ok(Value::Bool(std::path::Path::new(&path).exists()));
10433 }
10434 },
10435 "write_file" | "เขียนไฟล์" | "نوشتن_فایل" | "اكتب_الملف" | "כתוב_קובץ" | "فائل_لکھو" =>
10436 {
10437 #[cfg(target_arch = "wasm32")]
10438 return Ok(Value::Unit);
10439 #[cfg(not(target_arch = "wasm32"))]
10440 {
10441 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10442 let content = self.arg_str(&args, 1, "");
10443 if let Some(parent) = std::path::Path::new(&path).parent() {
10444 if !parent.as_os_str().is_empty() {
10445 let _ = std::fs::create_dir_all(parent);
10446 }
10447 }
10448 std::fs::write(&path, content.as_bytes())
10449 .map_err(|e| EvalErr::from(format!("write_file '{path}': {e}")))?;
10450 return Ok(Value::Unit);
10451 }
10452 },
10453 "print_file" | "พิมพ์ไฟล์" | "چاپ_فایل" | "اطبع_الملف" | "הדפס_קובץ" | "فائل_چھاپو" =>
10454 {
10455 let content = self.arg_str(&args, 0, "");
10456 print!("{content}");
10457 return Ok(Value::Unit);
10458 },
10459
10460 "get_args"
10462 | "รับอาร์กิวเมนต์"
10463 | "دریافت_آرگومانها"
10464 | "اجلب_المعاملات"
10465 | "קבל_ארגומנטים"
10466 | "دلائل_حاصل_کرو" => {
10467 let v: Vec<Value> = std::env::args().map(Value::Str).collect();
10468 return Ok(Value::List(Rc::new(v)));
10469 },
10470
10471 #[cfg(not(target_arch = "wasm32"))]
10476 "list_dir"
10477 | "รายการไดเรกทอรี"
10478 | "فهرست_پوشه"
10479 | "اسرد_المجلد"
10480 | "רשום_תיקייה"
10481 | "فولڈر_فہرست" => {
10482 let path = self.arg_str(&args, 0, ".").replace('\\', "/");
10483 let mut paths: Vec<String> = Vec::new();
10484 if let Ok(rd) = std::fs::read_dir(&path) {
10485 for e in rd.flatten() {
10486 paths.push(e.path().to_string_lossy().replace('\\', "/"));
10487 }
10488 }
10489 paths.sort();
10490 let out: Vec<Value> = paths.into_iter().map(Value::Str).collect();
10491 return Ok(Value::List(Rc::new(out)));
10492 },
10493 #[cfg(not(target_arch = "wasm32"))]
10494 "is_dir"
10495 | "เป็นไดเรกทอรี"
10496 | "آیا_پوشه_است"
10497 | "هل_مجلد"
10498 | "האם_תיקייה"
10499 | "کیا_فولڈر_ہے" => {
10500 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10501 return Ok(Value::Bool(std::path::Path::new(&path).is_dir()));
10502 },
10503 #[cfg(not(target_arch = "wasm32"))]
10504 "is_file" | "เป็นไฟล์" | "آیا_فایل_است" | "هل_ملف" | "האם_קובץ" | "کیا_فائل_ہے" =>
10505 {
10506 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10507 return Ok(Value::Bool(std::path::Path::new(&path).is_file()));
10508 },
10509 #[cfg(not(target_arch = "wasm32"))]
10510 "path_name" | "ชื่อไฟล์" | "نام_مسیر" | "اسم_المسار" | "שם_נתיב" | "پاتھ_نام" =>
10511 {
10512 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10513 let name = std::path::Path::new(&path)
10514 .file_name()
10515 .map(|s| s.to_string_lossy().into_owned())
10516 .unwrap_or_default();
10517 return Ok(Value::Str(name));
10518 },
10519 #[cfg(not(target_arch = "wasm32"))]
10520 "path_ext"
10521 | "นามสกุลไฟล์"
10522 | "پسوند_مسیر"
10523 | "امتداد_المسار"
10524 | "סיומת_נתיב"
10525 | "پاتھ_ایکسٹینشن" => {
10526 let path = self.arg_str(&args, 0, "").replace('\\', "/");
10527 let ext = std::path::Path::new(&path)
10528 .extension()
10529 .map(|s| s.to_string_lossy().to_lowercase())
10530 .unwrap_or_default();
10531 return Ok(Value::Str(ext));
10532 },
10533 #[cfg(not(target_arch = "wasm32"))]
10534 "file_size" | "ขนาดไฟล์" | "اندازه_فایل" | "حجم_الملف" | "גודל_קובץ" | "فائل_سائز" =>
10535 {
10536 let path = self.arg_str(&args, 0, "");
10537 let sz = std::fs::metadata(&path).map(|m| m.len()).unwrap_or(0);
10538 return Ok(Value::Number(sz as f64));
10539 },
10540 #[cfg(not(target_arch = "wasm32"))]
10541 "file_modified"
10542 | "เวลาที่แก้ไข"
10543 | "زمان_تغییر_فایل"
10544 | "وقت_تعديل_الملف"
10545 | "זמן_עדכון_קובץ"
10546 | "فائل_ترمیم_وقت" => {
10547 let path = self.arg_str(&args, 0, "");
10548 let secs = std::fs::metadata(&path)
10549 .and_then(|m| m.modified())
10550 .ok()
10551 .and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
10552 .map(|d| d.as_secs_f64())
10553 .unwrap_or(0.0);
10554 return Ok(Value::Number(secs));
10555 },
10556 #[cfg(not(target_arch = "wasm32"))]
10557 "file_created"
10558 | "เวลาที่สร้าง"
10559 | "زمان_ایجاد_فایل"
10560 | "وقت_إنشاء_الملف"
10561 | "זמן_יצירת_קובץ"
10562 | "فائل_تخلیق_وقت" => {
10563 let path = self.arg_str(&args, 0, "");
10564 let secs = std::fs::metadata(&path)
10565 .ok()
10566 .and_then(|m| m.created().or_else(|_| m.modified()).ok())
10567 .and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
10568 .map(|d| d.as_secs_f64())
10569 .unwrap_or(0.0);
10570 return Ok(Value::Number(secs));
10571 },
10572 #[cfg(not(target_arch = "wasm32"))]
10573 "make_dir"
10574 | "สร้างไดเรกทอรี"
10575 | "ساخت_پوشه"
10576 | "أنشئ_مجلدا"
10577 | "צור_תיקייה"
10578 | "فولڈر_بناؤ" => {
10579 let path = self.arg_str(&args, 0, "");
10580 return Ok(Value::Bool(std::fs::create_dir_all(&path).is_ok()));
10581 },
10582 #[cfg(not(target_arch = "wasm32"))]
10588 "remove_dir_all" | "ลบไดเรกทอรีทั้งหมด" => {
10589 let path = self.arg_str(&args, 0, "");
10590 let ok = match std::fs::remove_dir_all(&path) {
10591 Ok(()) => true,
10592 Err(e) => e.kind() == std::io::ErrorKind::NotFound,
10593 };
10594 return Ok(Value::Bool(ok));
10595 },
10596 #[cfg(not(target_arch = "wasm32"))]
10605 "absolute_path" | "พาธสัมบูรณ์" => {
10606 let path = self.arg_str(&args, 0, "");
10607 let p = std::path::Path::new(&path);
10608 let joined = if p.is_absolute() {
10609 p.to_path_buf()
10610 } else {
10611 std::env::current_dir()
10612 .map(|cwd| cwd.join(p))
10613 .unwrap_or_else(|_| p.to_path_buf())
10614 };
10615 return Ok(Value::Str(joined.to_string_lossy().replace('\\', "/")));
10616 },
10617 "str_strip_prefix"
10619 | "ตัดคำนำหน้า"
10620 | "حذف_پیشوند"
10621 | "أزل_البادئة"
10622 | "הסר_קידומת"
10623 | "سابقہ_ہٹاؤ" => {
10624 let s = self.arg_str(&args, 0, "");
10625 let prefix = self.arg_str(&args, 1, "");
10626 return Ok(Value::Str(
10627 s.strip_prefix(&prefix).unwrap_or(&s).to_string(),
10628 ));
10629 },
10630 #[cfg(not(target_arch = "wasm32"))]
10633 "file_magic"
10634 | "มายาไฟล์"
10635 | "امضای_فایل"
10636 | "توقيع_الملف"
10637 | "חתימת_קובץ"
10638 | "فائل_میجک" => {
10639 let path = self.arg_str(&args, 0, "");
10640 let kind = match std::fs::File::open(&path) {
10641 Ok(mut f) => {
10642 use std::io::Read;
10643 let mut buf = [0u8; 8];
10644 let n = f.read(&mut buf).unwrap_or(0);
10645 if n >= 2 && buf[0] == 0x1f && buf[1] == 0x8b {
10646 "gzip"
10647 } else if n >= 4 && &buf[0..2] == b"PK" {
10648 "zip"
10649 } else if n >= 8 && &buf[0..8] == b"\x89PNG\r\n\x1a\n" {
10650 "png"
10651 } else {
10652 "other"
10653 }
10654 },
10655 Err(_) => "missing",
10656 };
10657 return Ok(Value::Str(kind.to_string()));
10658 },
10659 #[cfg(not(target_arch = "wasm32"))]
10661 "copy_file" | "คัดลอกไฟล์" | "کپی_فایل" | "انسخ_الملف" | "העתק_קובץ" | "فائل_کاپی" =>
10662 {
10663 let src = self.arg_str(&args, 0, "");
10664 let dst = self.arg_str(&args, 1, "");
10665 if let Some(parent) = std::path::Path::new(&dst).parent() {
10666 let _ = std::fs::create_dir_all(parent);
10667 }
10668 return Ok(Value::Bool(std::fs::copy(&src, &dst).is_ok()));
10669 },
10670 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10674 "tar_gz_list"
10675 | "รายการทาร์"
10676 | "فهرست_TAR_GZ"
10677 | "اسرد_TAR_GZ"
10678 | "רשום_TAR_GZ"
10679 | "TAR_GZ_فہرست" => {
10680 let path = self.arg_str(&args, 0, "");
10681 let mut names: Vec<String> = Vec::new();
10682 if let Ok(file) = std::fs::File::open(&path) {
10683 let gz = flate2::read::GzDecoder::new(file);
10684 let mut ar = tar::Archive::new(gz);
10685 if let Ok(entries) = ar.entries() {
10686 for entry in entries.flatten() {
10687 if entry.header().entry_type().is_file() {
10688 if let Ok(p) = entry.path() {
10689 names.push(
10690 p.to_string_lossy()
10691 .trim_start_matches("./")
10692 .replace('\\', "/"),
10693 );
10694 }
10695 }
10696 }
10697 }
10698 }
10699 names.sort();
10700 names.dedup();
10701 let out: Vec<Value> = names.into_iter().map(Value::Str).collect();
10702 return Ok(Value::List(Rc::new(out)));
10703 },
10704 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10708 "tar_gz_read"
10709 | "อ่านทาร์"
10710 | "خواندن_TAR_GZ"
10711 | "اقرأ_TAR_GZ"
10712 | "קרא_TAR_GZ"
10713 | "TAR_GZ_پڑھو" => {
10714 let path = self.arg_str(&args, 0, "");
10715 let want = self.arg_str(&args, 1, "");
10716 let want = want.trim_start_matches("./").replace('\\', "/");
10717 let mut content = String::new();
10718 if let Ok(file) = std::fs::File::open(&path) {
10719 let gz = flate2::read::GzDecoder::new(file);
10720 let mut ar = tar::Archive::new(gz);
10721 if let Ok(entries) = ar.entries() {
10722 for entry in entries.flatten() {
10723 let name = match entry.path() {
10724 Ok(p) => p
10725 .to_string_lossy()
10726 .trim_start_matches("./")
10727 .replace('\\', "/"),
10728 Err(_) => continue,
10729 };
10730 if name == want {
10731 use std::io::Read;
10732 let mut buf = Vec::new();
10733 let cap = 256 * 1024;
10734 if entry.take(cap as u64 + 1).read_to_end(&mut buf).is_ok() {
10735 let slice = if buf.len() > cap {
10736 &buf[..cap]
10737 } else {
10738 &buf[..]
10739 };
10740 content = String::from_utf8_lossy(slice).into_owned();
10741 }
10742 break;
10743 }
10744 }
10745 }
10746 }
10747 return Ok(Value::Str(content));
10748 },
10749 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10757 "run_process" | "รันโปรเซส" => {
10758 let cmd = self.arg_str(&args, 0, "");
10759 let argv = self.arg_list_str(&args, 1);
10760 let timeout_secs = self.arg_num(&args, 2, 60.0)?.max(1.0) as u64;
10761 let log_path = self.arg_str(&args, 3, "");
10762
10763 let make_stdio = || -> std::process::Stdio {
10764 if log_path.is_empty() {
10765 std::process::Stdio::null()
10766 } else {
10767 std::fs::OpenOptions::new()
10768 .create(true)
10769 .append(true)
10770 .open(&log_path)
10771 .map(std::process::Stdio::from)
10772 .unwrap_or_else(|_| std::process::Stdio::null())
10773 }
10774 };
10775
10776 let spawned = std::process::Command::new(&cmd)
10777 .args(&argv)
10778 .stdin(std::process::Stdio::null())
10779 .stdout(make_stdio())
10780 .stderr(make_stdio())
10781 .spawn();
10782
10783 let mut child = match spawned {
10784 Ok(c) => c,
10785 Err(_) => return Ok(Value::Number(-1.0)),
10786 };
10787
10788 let start = std::time::Instant::now();
10789 let timeout = std::time::Duration::from_secs(timeout_secs);
10790 loop {
10791 match child.try_wait() {
10792 Ok(Some(status)) => {
10793 return Ok(Value::Number(status.code().unwrap_or(-1) as f64));
10794 },
10795 Ok(None) => {
10796 if start.elapsed() > timeout {
10797 let _ = child.kill();
10798 let _ = child.wait();
10799 return Ok(Value::Number(-2.0));
10800 }
10801 std::thread::sleep(std::time::Duration::from_millis(300));
10802 },
10803 Err(_) => return Ok(Value::Number(-1.0)),
10804 }
10805 }
10806 },
10807 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10813 "tar_gz_extract_to" | "แตกไฟล์ทาร์" => {
10814 let path = self.arg_str(&args, 0, "");
10815 let want = self.arg_str(&args, 1, "");
10816 let want = want.trim_start_matches("./").replace('\\', "/");
10817 let out_path = self.arg_str(&args, 2, "");
10818 let mut found = false;
10819 if let Ok(file) = std::fs::File::open(&path) {
10820 let gz = flate2::read::GzDecoder::new(file);
10821 let mut ar = tar::Archive::new(gz);
10822 if let Ok(entries) = ar.entries() {
10823 for mut entry in entries.flatten() {
10824 let name = match entry.path() {
10825 Ok(p) => p
10826 .to_string_lossy()
10827 .trim_start_matches("./")
10828 .replace('\\', "/"),
10829 Err(_) => continue,
10830 };
10831 if name == want {
10832 if let Some(parent) = std::path::Path::new(&out_path).parent() {
10833 let _ = std::fs::create_dir_all(parent);
10834 }
10835 if let Ok(mut out) = std::fs::File::create(&out_path) {
10836 use std::io::copy;
10837 found = copy(&mut entry, &mut out).is_ok();
10838 }
10839 break;
10840 }
10841 }
10842 }
10843 }
10844 return Ok(Value::Bool(found));
10845 },
10846 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10856 "tar_gz_extract_all" | "แตกทาร์ทั้งหมด" => {
10857 let path = self.arg_str(&args, 0, "");
10858 let dest = self.arg_str(&args, 1, "");
10859 let ok = (|| -> std::io::Result<()> {
10860 std::fs::create_dir_all(&dest)?;
10861 let file = std::fs::File::open(&path)?;
10862 let gz = flate2::read::GzDecoder::new(file);
10863 let mut ar = tar::Archive::new(gz);
10864 ar.unpack(&dest)
10865 })()
10866 .is_ok();
10867 return Ok(Value::Bool(ok));
10868 },
10869 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10874 "zip_dir" | "บีบอัดโฟลเดอร์" => {
10875 let dir_path = self.arg_str(&args, 0, "");
10876 let out_path = self.arg_str(&args, 1, "out.zip");
10877 fn add_dir(
10878 writer: &mut zip::ZipWriter<std::fs::File>,
10879 base: &std::path::Path,
10880 dir: &std::path::Path,
10881 options: zip::write::FileOptions<'_, ()>,
10882 ) -> std::io::Result<()> {
10883 for entry in std::fs::read_dir(dir)? {
10884 let entry = entry?;
10885 let path = entry.path();
10886 let rel = path
10887 .strip_prefix(base)
10888 .unwrap_or(&path)
10889 .to_string_lossy()
10890 .replace('\\', "/");
10891 if path.is_dir() {
10892 add_dir(writer, base, &path, options)?;
10893 } else {
10894 let bytes = std::fs::read(&path)?;
10895 writer.start_file(rel, options)?;
10896 std::io::Write::write_all(writer, &bytes)?;
10897 }
10898 }
10899 Ok(())
10900 }
10901 let ok = (|| -> std::io::Result<()> {
10902 let base = std::path::Path::new(&dir_path);
10903 let file = std::fs::File::create(&out_path)?;
10904 let mut writer = zip::ZipWriter::new(file);
10905 let options: zip::write::FileOptions<'_, ()> =
10906 zip::write::FileOptions::default()
10907 .compression_method(zip::CompressionMethod::Deflated);
10908 add_dir(&mut writer, base, base, options)?;
10909 writer.finish()?;
10910 Ok(())
10911 })()
10912 .is_ok();
10913 return Ok(Value::Bool(ok));
10914 },
10915 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10919 "icon_convert_to_png" | "แปลงไอคอนเป็นพีเอ็นจี" => {
10920 let src = self.arg_str(&args, 0, "");
10921 let out = self.arg_str(&args, 1, "");
10922 let size = self.arg_num(&args, 2, 300.0)? as u32;
10923 let ok = match ling_icon::png_bytes(std::path::Path::new(&src), size) {
10924 Ok(bytes) => std::fs::write(&out, bytes).is_ok(),
10925 Err(_) => false,
10926 };
10927 return Ok(Value::Bool(ok));
10928 },
10929 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10936 "totp_secret" | "โทเทนลับ" | "راز_TOTP" | "سر_TOTP" | "סוד_TOTP" | "TOTP_راز" =>
10937 {
10938 let mut bytes = [0u8; 20];
10939 rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut bytes);
10940 return Ok(Value::Str(base32_encode(&bytes)));
10941 },
10942 "totp_uri"
10946 | "โทเทนยูอาร์ไอ"
10947 | "آدرس_TOTP"
10948 | "رابط_TOTP"
10949 | "כתובת_TOTP"
10950 | "TOTP_یو_آر_آئی" => {
10951 let secret = self.arg_str(&args, 0, "");
10952 let account = self.arg_str(&args, 1, "user");
10953 let issuer = self.arg_str(&args, 2, "lingfu");
10954 return Ok(Value::Str(format!(
10955 "otpauth://totp/{issuer}:{account}?secret={secret}&issuer={issuer}&algorithm=SHA1&digits=6&period=30"
10956 )));
10957 },
10958 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10960 "totp_verify"
10961 | "โทเทนตรวจ"
10962 | "تایید_TOTP"
10963 | "تحقق_TOTP"
10964 | "אמת_TOTP"
10965 | "TOTP_تصدیق" => {
10966 let secret = self.arg_str(&args, 0, "");
10967 let code = self.arg_str(&args, 1, "");
10968 let ok = totp_check(&secret, code.trim());
10969 return Ok(Value::Bool(ok));
10970 },
10971 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
10973 "totp_now" | "โทเทนตอนนี้" | "TOTP_اکنون" | "TOTP_الآن" | "TOTP_עכשיו" | "TOTP_ابھی" =>
10974 {
10975 let secret = self.arg_str(&args, 0, "");
10976 let step = (crate::runtime::now_secs() as u64) / 30;
10977 return Ok(Value::Str(totp_code(&secret, step).unwrap_or_default()));
10978 },
10979 #[cfg(not(target_arch = "wasm32"))]
10981 "file_hash" | "แฮชไฟล์" | "درهم_فایل" | "بصمة_الملف" | "גיבוב_קובץ" | "فائل_ہیش" =>
10982 {
10983 let path = self.arg_str(&args, 0, "");
10984 match std::fs::read(&path) {
10985 Ok(bytes) => {
10986 return Ok(Value::Str(hex_encode(&ling_crypto::Blake3::hash(&bytes))))
10987 },
10988 Err(_) => return Ok(Value::Str(String::new())),
10989 }
10990 },
10991 "hash_hex"
10993 | "แฮชสตริง"
10994 | "درهم_هگزادسیمال"
10995 | "بصمة_سداسية"
10996 | "גיבוב_הקסדצימלי"
10997 | "ہیکس_ہیش" => {
10998 let s = self.arg_str(&args, 0, "");
10999 return Ok(Value::Str(hex_encode(&ling_crypto::Blake3::hash(
11000 s.as_bytes(),
11001 ))));
11002 },
11003 #[cfg(not(target_arch = "wasm32"))]
11005 "env_get"
11006 | "รับตัวแปรแวดล้อม"
11007 | "دریافت_متغیر_محیطی"
11008 | "اجلب_متغير_البيئة"
11009 | "קבל_משתנה_סביבה"
11010 | "ماحولیاتی_متغیر_حاصل_کرو" => {
11011 let name = self.arg_str(&args, 0, "");
11012 let dflt = self.arg_str(&args, 1, "");
11013 return Ok(Value::Str(std::env::var(&name).unwrap_or(dflt)));
11014 },
11015
11016 "to_number" | "转数字" => {
11020 let s = self.arg_str(&args, 0, "");
11021 return Ok(Value::Number(s.trim().parse().unwrap_or(0.0)));
11022 },
11023 "sha256_hex" | "SHA256哈希" => {
11028 use sha2::Digest;
11029 let s = self.arg_str(&args, 0, "");
11030 let mut h = sha2::Sha256::new();
11031 h.update(s.as_bytes());
11032 return Ok(Value::Str(hex_encode(&h.finalize())));
11033 },
11034 "hex_to_number" | "十六进制转数字" => {
11037 let s = self.arg_str(&args, 0, "");
11038 let v = u64::from_str_radix(s.trim(), 16).unwrap_or(0);
11039 return Ok(Value::Number(v as f64));
11040 },
11041 "split" | "str_split" | "แยก" | "جداسازی" | "قسّم" | "פצל" | "تقسیم_کرو" =>
11042 {
11043 let s = self.arg_str(&args, 0, "");
11044 let sep = self.arg_str(&args, 1, "\n");
11045 let sep = if sep.is_empty() { "\n".into() } else { sep };
11046 let parts: Vec<Value> = s
11047 .split(sep.as_str())
11048 .map(|p| Value::Str(p.to_string()))
11049 .collect();
11050 return Ok(Value::List(Rc::new(parts)));
11051 },
11052 "trim"
11053 | "str_trim"
11054 | "ตัดช่องว่าง"
11055 | "حذف_فاصله"
11056 | "اقتطع_الفراغات"
11057 | "חתוך_רווחים"
11058 | "خالی_جگہ_کاٹو" => {
11059 let s = self.arg_str(&args, 0, "");
11060 return Ok(Value::Str(s.trim().to_string()));
11061 },
11062 "starts_with"
11063 | "str_starts_with"
11064 | "เริ่มด้วย"
11065 | "شروع_میشود_با"
11066 | "يبدأ_بـ"
11067 | "מתחיל_ב"
11068 | "شروع_ہوتا_ہے" => {
11069 let s = self.arg_str(&args, 0, "");
11070 let prefix = self.arg_str(&args, 1, "");
11071 return Ok(Value::Bool(s.starts_with(prefix.as_str())));
11072 },
11073 "ends_with"
11074 | "str_ends_with"
11075 | "ลงท้ายด้วย"
11076 | "پایان_مییابد_با"
11077 | "ينتهي_بـ"
11078 | "מסתיים_ב"
11079 | "ختم_ہوتا_ہے" => {
11080 let s = self.arg_str(&args, 0, "");
11081 let suffix = self.arg_str(&args, 1, "");
11082 return Ok(Value::Bool(s.ends_with(suffix.as_str())));
11083 },
11084 "str_replace"
11085 | "แทนสตริง"
11086 | "جایگزینی_رشته"
11087 | "استبدل_النص"
11088 | "החלף_מחרוזת"
11089 | "اسٹرنگ_تبدیل" => {
11090 let s = self.arg_str(&args, 0, "");
11091 let from = self.arg_str(&args, 1, "");
11092 let to = self.arg_str(&args, 2, "");
11093 return Ok(Value::Str(s.replace(from.as_str(), to.as_str())));
11094 },
11095 "str_find"
11096 | "หาในสตริง"
11097 | "جستجوی_رشته"
11098 | "ابحث_في_النص"
11099 | "חפש_מחרוזת"
11100 | "اسٹرنگ_تلاش" => {
11101 let s = self.arg_str(&args, 0, "");
11102 let needle = self.arg_str(&args, 1, "");
11103 let pos = s
11105 .find(needle.as_str())
11106 .map(|byte_i| s[..byte_i].chars().count() as f64)
11107 .unwrap_or(-1.0);
11108 return Ok(Value::Number(pos));
11109 },
11110 "substr"
11111 | "str_slice"
11112 | "ส่วนสตริง"
11113 | "زیررشته"
11114 | "جزء_النص"
11115 | "תת_מחרוזת"
11116 | "ذیلی_اسٹرنگ" => {
11117 let s = self.arg_str(&args, 0, "");
11118 let start = self.arg_num(&args, 1, 0.0)? as usize;
11119 let len = args
11120 .get(2)
11121 .map(|v| self.to_number(v).unwrap_or(999999.0) as usize)
11122 .unwrap_or_else(|| s.chars().count().saturating_sub(start));
11123 let chars: Vec<char> = s.chars().collect();
11124 let end = (start + len).min(chars.len());
11125 let slice: String = chars.get(start..end).unwrap_or(&[]).iter().collect();
11126 return Ok(Value::Str(slice));
11127 },
11128 "to_str"
11129 | "str"
11130 | "num_str"
11131 | "แปลงสตริง"
11132 | "تبدیل_به_رشته"
11133 | "حوّل_لنص"
11134 | "המר_למחרוזת"
11135 | "اسٹرنگ_میں_بدلو" => {
11136 let v = args.into_iter().next().unwrap_or(Value::Unit);
11137 return Ok(Value::Str(v.to_string()));
11138 },
11139 "str_repeat"
11140 | "ทำซ้ำสตริง"
11141 | "تکرار_رشته"
11142 | "كرّر_النص"
11143 | "חזור_על_מחרוזת"
11144 | "اسٹرنگ_دہراؤ" => {
11145 let s = self.arg_str(&args, 0, "");
11146 let n = self.arg_num(&args, 1, 1.0)? as usize;
11147 return Ok(Value::Str(s.repeat(n)));
11148 },
11149 "str_upper" => {
11150 let s = self.arg_str(&args, 0, "");
11151 return Ok(Value::Str(s.to_uppercase()));
11152 },
11153 "str_lower" => {
11154 let s = self.arg_str(&args, 0, "");
11155 return Ok(Value::Str(s.to_lowercase()));
11156 },
11157 "str_len"
11158 | "len"
11159 | "ความยาว"
11160 | "长度"
11161 | "長さ"
11162 | "길이"
11163 | "طول_رشته"
11164 | "طول_النص"
11165 | "אורך_מחרוזת"
11166 | "اسٹرنگ_لمبائی" => match args.first() {
11167 Some(Value::Str(s)) => return Ok(Value::Number(s.chars().count() as f64)),
11168 Some(Value::List(v)) => return Ok(Value::Number(v.len() as f64)),
11169 _ => return Ok(Value::Number(0.0)),
11170 },
11171
11172 "hash_str" | "แฮช" | "درهم_رشته" | "بصمة_نص" | "גיבוב_מחרוזת" | "اسٹرنگ_ہیش" =>
11174 {
11175 let s = self.arg_str(&args, 0, "");
11176 let mut h: u64 = 14695981039346656037_u64;
11177 for b in s.bytes() {
11178 h ^= b as u64;
11179 h = h.wrapping_mul(1099511628211);
11180 }
11181 return Ok(Value::Number((h & 0xFFFFFF) as f64 / 16777215.0));
11182 },
11183 "hash_int" | "แฮชจำนวน" | "درهم_عدد" | "بصمة_عدد" | "גיבוב_מספר" | "نمبر_ہیش" =>
11184 {
11185 let s = self.arg_str(&args, 0, "");
11186 let n = self.arg_num(&args, 1, 100.0)? as u64;
11187 let mut h: u64 = 14695981039346656037_u64;
11188 for b in s.bytes() {
11189 h ^= b as u64;
11190 h = h.wrapping_mul(1099511628211);
11191 }
11192 return Ok(Value::Number((h % n.max(1)) as f64));
11193 },
11194
11195 "list_new"
11197 | "รายการใหม่"
11198 | "新建列表"
11199 | "新規リスト"
11200 | "새목록"
11201 | "فهرست_جدید"
11202 | "قائمة_جديدة"
11203 | "רשימה_חדשה"
11204 | "نئی_فہرست"
11205 | "nouvelle_liste"
11206 | "neue_liste"
11207 | "новый_список" => {
11208 return Ok(Value::List(Rc::new(Vec::new())));
11209 },
11210 "list_push"
11211 | "เพิ่มรายการ"
11212 | "列表添加"
11213 | "リスト追加"
11214 | "목록추가"
11215 | "افزودن_به_فهرست"
11216 | "أضف_للقائمة"
11217 | "הוסף_לרשימה"
11218 | "فہرست_میں_شامل_کرو"
11219 | "ajouter_liste"
11220 | "liste_anhängen"
11221 | "добавить_в_список" => {
11222 let lst = args
11223 .first()
11224 .cloned()
11225 .unwrap_or(Value::List(Rc::new(vec![])));
11226 let val = args.get(1).cloned().unwrap_or(Value::Unit);
11227 if let Value::List(mut v) = lst {
11228 Rc::make_mut(&mut v).push(val);
11229 return Ok(Value::List(v));
11230 }
11231 return Ok(Value::List(Rc::new(vec![val])));
11232 },
11233 "list_get"
11234 | "รับรายการ"
11235 | "取元素"
11236 | "要素取得"
11237 | "요소가져오기"
11238 | "دریافت_از_فهرست"
11239 | "اجلب_من_القائمة"
11240 | "קבל_מרשימה"
11241 | "فہرست_سے_حاصل_کرو"
11242 | "obtenir_liste"
11243 | "liste_abrufen"
11244 | "получить_из_списка" => {
11245 let i = self.arg_num(&args, 1, 0.0)? as usize;
11247 if let Some(Value::List(v)) = args.first() {
11248 return Ok(v.get(i).cloned().unwrap_or(Value::Str(String::new())));
11249 }
11250 return Ok(Value::Str(String::new()));
11251 },
11252 "list_max" | "列表最大值" => {
11256 let lst = args
11257 .first()
11258 .cloned()
11259 .unwrap_or(Value::List(Rc::new(vec![])));
11260 let default = self.arg_num(&args, 1, 0.0)?;
11261 if let Value::List(v) = lst {
11262 let mut best = default;
11263 let mut any = false;
11264 for item in v.iter() {
11265 if let Value::Number(n) = item {
11266 if !any || *n > best {
11267 best = *n;
11268 any = true;
11269 }
11270 }
11271 }
11272 return Ok(Value::Number(best));
11273 }
11274 return Ok(Value::Number(default));
11275 },
11276 "list_min" | "列表最小值" => {
11277 let lst = args
11278 .first()
11279 .cloned()
11280 .unwrap_or(Value::List(Rc::new(vec![])));
11281 let default = self.arg_num(&args, 1, 0.0)?;
11282 if let Value::List(v) = lst {
11283 let mut best = default;
11284 let mut any = false;
11285 for item in v.iter() {
11286 if let Value::Number(n) = item {
11287 if !any || *n < best {
11288 best = *n;
11289 any = true;
11290 }
11291 }
11292 }
11293 return Ok(Value::Number(best));
11294 }
11295 return Ok(Value::Number(default));
11296 },
11297 "list_set"
11301 | "ตั้งรายการ"
11302 | "设元素"
11303 | "要素設定"
11304 | "요소설정"
11305 | "تنظیم_عنصر_فهرست"
11306 | "عيّن_عنصر_القائمة"
11307 | "קבע_איבר_רשימה"
11308 | "فہرست_سیٹ" => {
11309 let idx = self.arg_num(&args, 1, 0.0)? as usize;
11310 let mut ai = args.into_iter();
11311 let lst = ai.next().unwrap_or(Value::List(Rc::new(vec![])));
11312 ai.next(); let val = ai.next().unwrap_or(Value::Unit);
11314 if let Value::List(mut v) = lst {
11315 if idx < v.len() {
11316 Rc::make_mut(&mut v)[idx] = val;
11317 }
11318 return Ok(Value::List(v));
11319 }
11320 return Ok(Value::List(Rc::new(vec![])));
11321 },
11322 "list_join"
11323 | "join"
11324 | "รวมรายการ"
11325 | "连接"
11326 | "連結"
11327 | "연결"
11328 | "پیوستن_فهرست"
11329 | "اربط_القائمة"
11330 | "חבר_רשימה"
11331 | "فہرست_جوڑو" => {
11332 let lst = args
11333 .first()
11334 .cloned()
11335 .unwrap_or(Value::List(Rc::new(vec![])));
11336 let sep = args.get(1).map(|v| v.to_string()).unwrap_or_default();
11337 if let Value::List(v) = lst {
11338 return Ok(Value::Str(
11339 v.iter()
11340 .map(|x| x.to_string())
11341 .collect::<Vec<_>>()
11342 .join(&sep),
11343 ));
11344 }
11345 return Ok(Value::Str(String::new()));
11346 },
11347 "list_map" | "映射列表" => {
11356 let lst = args
11357 .first()
11358 .cloned()
11359 .unwrap_or(Value::List(Rc::new(vec![])));
11360 let f = args.get(1).cloned().unwrap_or(Value::Unit);
11361 if let Value::List(v) = lst {
11362 let mut out = Vec::with_capacity(v.len());
11363 for item in v.iter() {
11364 out.push(self.call_value(f.clone(), vec![item.clone()])?);
11365 }
11366 return Ok(Value::List(Rc::new(out)));
11367 }
11368 return Ok(Value::List(Rc::new(vec![])));
11369 },
11370 "list_filter" | "过滤列表" => {
11371 let lst = args
11372 .first()
11373 .cloned()
11374 .unwrap_or(Value::List(Rc::new(vec![])));
11375 let f = args.get(1).cloned().unwrap_or(Value::Unit);
11376 if let Value::List(v) = lst {
11377 let mut out = Vec::new();
11378 for item in v.iter() {
11379 if matches!(
11380 self.call_value(f.clone(), vec![item.clone()])?,
11381 Value::Bool(true)
11382 ) {
11383 out.push(item.clone());
11384 }
11385 }
11386 return Ok(Value::List(Rc::new(out)));
11387 }
11388 return Ok(Value::List(Rc::new(vec![])));
11389 },
11390 "list_find" | "查找列表" => {
11392 let lst = args
11393 .first()
11394 .cloned()
11395 .unwrap_or(Value::List(Rc::new(vec![])));
11396 let f = args.get(1).cloned().unwrap_or(Value::Unit);
11397 if let Value::List(v) = lst {
11398 for item in v.iter() {
11399 if matches!(
11400 self.call_value(f.clone(), vec![item.clone()])?,
11401 Value::Bool(true)
11402 ) {
11403 return Ok(item.clone());
11404 }
11405 }
11406 }
11407 return Ok(Value::Unit);
11408 },
11409 #[cfg(not(target_arch = "wasm32"))]
11413 "blob_f32" | "blob_i32" => {
11414 let s = self.arg_str(&args, 0, "");
11415 let is_i32 = name == "blob_i32";
11416 match decode_blob(&s) {
11417 Ok(bytes) => {
11418 let mut out = Vec::with_capacity(bytes.len() / 4);
11419 for ch in bytes.chunks_exact(4) {
11420 let arr = [ch[0], ch[1], ch[2], ch[3]];
11421 let n = if is_i32 {
11422 i32::from_le_bytes(arr) as f64
11423 } else {
11424 f32::from_le_bytes(arr) as f64
11425 };
11426 out.push(Value::Number(n));
11427 }
11428 return Ok(Value::List(Rc::new(out)));
11429 },
11430 Err(e) => {
11431 eprintln!("blob decode failed: {e}");
11432 return Ok(Value::List(Rc::new(vec![])));
11433 },
11434 }
11435 },
11436
11437 "svg_begin" | "开始SVG" | "เริ่มSVG" | "شروع_SVG" | "ابدأ_SVG" | "התחל_SVG"
11444 | "SVG_شروع" | "commencer_svg" | "svg_beginnen" | "начать_svg" => {
11445 let path = self.arg_str(&args, 0, "output.svg");
11446 let width = self.arg_num(&args, 1, 800.0)?;
11447 let height = self.arg_num(&args, 2, 600.0)?;
11448 *self.svg.borrow_mut() = Some(SvgWriter::new(path, width, height));
11449 return Ok(Value::Unit);
11450 },
11451
11452 "svg_rect"
11453 | "SVG矩形"
11454 | "SVGสี่เหลี่ยม"
11455 | "مستطیل_SVG"
11456 | "مستطيل_SVG"
11457 | "מלבן_SVG"
11458 | "SVG_مستطیل"
11459 | "rectangle_svg"
11460 | "svg_rechteck"
11461 | "прямоугольник_svg" => {
11462 let x = self.arg_num(&args, 0, 0.0)?;
11463 let y = self.arg_num(&args, 1, 0.0)?;
11464 let w = self.arg_num(&args, 2, 10.0)?;
11465 let h = self.arg_num(&args, 3, 10.0)?;
11466 let fill = self.arg_str(&args, 4, "#ffffff");
11467 if let Some(svg) = self.svg.borrow_mut().as_mut() {
11468 svg.elements.push(format!(
11469 "<rect x=\"{x:.1}\" y=\"{y:.1}\" width=\"{w:.1}\" \
11470 height=\"{h:.1}\" fill=\"{fill}\"/>"
11471 ));
11472 }
11473 return Ok(Value::Unit);
11474 },
11475
11476 "svg_circle" | "SVG圆形" | "SVGวงกลม" | "دایره_SVG" | "دائرة_SVG" | "עיגול_SVG"
11477 | "SVG_دائرہ" | "cercle_svg" | "svg_kreis" | "круг_svg" => {
11478 let cx = self.arg_num(&args, 0, 0.0)?;
11479 let cy = self.arg_num(&args, 1, 0.0)?;
11480 let r = self.arg_num(&args, 2, 5.0)?;
11481 let fill = self.arg_str(&args, 3, "#ffffff");
11482 if let Some(svg) = self.svg.borrow_mut().as_mut() {
11483 svg.elements.push(format!(
11484 "<circle cx=\"{cx:.1}\" cy=\"{cy:.1}\" r=\"{r:.1}\" fill=\"{fill}\"/>"
11485 ));
11486 }
11487 return Ok(Value::Unit);
11488 },
11489
11490 "svg_line" | "SVG线段" | "SVGเส้น" | "خط_SVG" | "קו_SVG" | "SVG_لکیر" | "ligne_svg"
11491 | "svg_linie" | "линия_svg" => {
11492 let x1 = self.arg_num(&args, 0, 0.0)?;
11493 let y1 = self.arg_num(&args, 1, 0.0)?;
11494 let x2 = self.arg_num(&args, 2, 0.0)?;
11495 let y2 = self.arg_num(&args, 3, 0.0)?;
11496 let stroke = self.arg_str(&args, 4, "#ffffff");
11497 let sw = self.arg_num(&args, 5, 1.0)?;
11498 if let Some(svg) = self.svg.borrow_mut().as_mut() {
11499 svg.elements.push(format!(
11500 "<line x1=\"{x1:.1}\" y1=\"{y1:.1}\" x2=\"{x2:.1}\" y2=\"{y2:.1}\" \
11501 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"
11502 ));
11503 }
11504 return Ok(Value::Unit);
11505 },
11506
11507 "svg_polyline"
11508 | "SVG折线"
11509 | "SVGเส้นหัก"
11510 | "چندخطی_SVG"
11511 | "خط_متعدد_SVG"
11512 | "קו_שבור_SVG"
11513 | "SVG_پولی_لائن"
11514 | "polyligne_svg"
11515 | "svg_polylinie"
11516 | "ломаная_svg" => {
11517 let pts = self.arg_str(&args, 0, "");
11518 let stroke = self.arg_str(&args, 1, "#ffffff");
11519 let sw = self.arg_num(&args, 2, 1.0)?;
11520 if let Some(svg) = self.svg.borrow_mut().as_mut() {
11521 svg.elements.push(format!(
11522 "<polyline points=\"{pts}\" fill=\"none\" \
11523 stroke=\"{stroke}\" stroke-width=\"{sw:.1}\"/>"
11524 ));
11525 }
11526 return Ok(Value::Unit);
11527 },
11528
11529 "svg_text"
11530 | "SVG文本"
11531 | "SVGข้อความ"
11532 | "متن_SVG"
11533 | "نص_SVG"
11534 | "טקסט_SVG"
11535 | "SVG_متن"
11536 | "texte_svg"
11537 | "текст_svg" => {
11538 let x = self.arg_num(&args, 0, 0.0)?;
11539 let y = self.arg_num(&args, 1, 0.0)?;
11540 let text = self.arg_str(&args, 2, "");
11541 let fill = self.arg_str(&args, 3, "#ffffff");
11542 let size = self.arg_num(&args, 4, 12.0)?;
11543 if let Some(svg) = self.svg.borrow_mut().as_mut() {
11544 let safe = text
11545 .replace('&', "&")
11546 .replace('<', "<")
11547 .replace('>', ">");
11548 svg.elements.push(format!(
11549 "<text x=\"{x:.1}\" y=\"{y:.1}\" fill=\"{fill}\" \
11550 font-family=\"monospace\" font-size=\"{size:.0}\">{safe}</text>"
11551 ));
11552 }
11553 return Ok(Value::Unit);
11554 },
11555
11556 "svg_end"
11557 | "结束SVG"
11558 | "จบSVG"
11559 | "پایان_SVG"
11560 | "أنهِ_SVG"
11561 | "סיים_SVG"
11562 | "SVG_ختم"
11563 | "terminer_svg"
11564 | "svg_beenden"
11565 | "закончить_svg" => {
11566 {
11567 let borrow = self.svg.borrow();
11568 if let Some(svg) = borrow.as_ref() {
11569 svg.save()
11570 .map_err(|e| EvalErr::from(format!("svg_end: {e}")))?;
11571 }
11572 }
11573 *self.svg.borrow_mut() = None;
11574 return Ok(Value::Unit);
11575 },
11576
11577 "hsl_color" | "HSL颜色" | "สีHSL" | "رنگ_HSL" | "لون_HSL" | "צבע_HSL" | "HSL_رنگ"
11578 | "couleur_hsl" | "hsl_farbe" | "цвет_hsl" => {
11579 let h = self.arg_num(&args, 0, 0.0)?;
11580 let s = self.arg_num(&args, 1, 70.0)?;
11581 let l = self.arg_num(&args, 2, 50.0)?;
11582 return Ok(Value::Str(hsl_to_hex(h, s, l)));
11583 },
11584
11585 #[cfg(not(target_arch = "wasm32"))]
11591 "fft_push"
11592 | "วิเคราะห์เสียง"
11593 | "频谱输入"
11594 | "FFT入力"
11595 | "FFT입력"
11596 | "ورودی_FFT"
11597 | "أدخل_FFT"
11598 | "הזנת_FFT"
11599 | "FFT_ان_پٹ"
11600 | "fft_entrée"
11601 | "fft_eingabe"
11602 | "fft_вход" => {
11603 if let Some(Value::List(v)) = args.first() {
11604 let samples: Vec<f32> = v
11605 .iter()
11606 .filter_map(|x| {
11607 if let Value::Number(n) = x {
11608 Some(*n as f32)
11609 } else {
11610 None
11611 }
11612 })
11613 .collect();
11614 self.fft.borrow_mut().push_samples(&samples);
11615 }
11616 return Ok(Value::Unit);
11617 },
11618
11619 #[cfg(not(target_arch = "wasm32"))]
11621 "fft_bands"
11622 | "แถบความถี่"
11623 | "频段"
11624 | "周波数帯"
11625 | "주파수대"
11626 | "باندهای_FFT"
11627 | "نطاقات_FFT"
11628 | "פסי_FFT"
11629 | "FFT_بینڈز"
11630 | "fft_bandes"
11631 | "fft_bänder"
11632 | "fft_полосы" => {
11633 let n = self.arg_num(&args, 0, 32.0)? as usize;
11634 let bands = self.fft.borrow().freq_bands(n);
11635 *self.fft_bands_cache.borrow_mut() = bands.clone();
11636 return Ok(Value::List(Rc::new(
11637 bands.into_iter().map(|v| Value::Number(v as f64)).collect(),
11638 )));
11639 },
11640
11641 #[cfg(not(target_arch = "wasm32"))]
11643 "fft_beat"
11644 | "จังหวะเสียง"
11645 | "节拍检测"
11646 | "ビート検出"
11647 | "비트"
11648 | "ضرب_FFT"
11649 | "نبضة_FFT"
11650 | "פעימת_FFT"
11651 | "FFT_دھڑکن"
11652 | "fft_battement"
11653 | "fft_takt"
11654 | "fft_удар" => {
11655 return Ok(Value::Bool(self.fft.borrow().is_beat()));
11656 },
11657
11658 #[cfg(not(target_arch = "wasm32"))]
11660 "fft_beat_ratio"
11661 | "อัตราจังหวะ"
11662 | "节拍比"
11663 | "ビート比"
11664 | "비트비율"
11665 | "نسبت_ضرب_FFT"
11666 | "نسبة_نبضة_FFT"
11667 | "יחס_פעימת_FFT"
11668 | "FFT_بیٹ_تناسب" => {
11669 return Ok(Value::Number(self.fft.borrow().beat_ratio() as f64));
11670 },
11671
11672 #[cfg(not(target_arch = "wasm32"))]
11674 "fft_rms"
11675 | "ระดับRMS"
11676 | "均方根"
11677 | "二乗平均"
11678 | "RMS레벨"
11679 | "RMS_صدا"
11680 | "جذر_متوسط_مربع_FFT"
11681 | "RMS_של_FFT"
11682 | "FFT_RMS" => {
11683 return Ok(Value::Number(self.fft.borrow().rms() as f64));
11684 },
11685
11686 #[cfg(not(target_arch = "wasm32"))]
11688 "fft_dominant_freq"
11689 | "ความถี่หลัก"
11690 | "主频"
11691 | "主要周波数"
11692 | "주파수"
11693 | "فرکانس_غالب"
11694 | "التردد_السائد"
11695 | "תדר_דומיננטי"
11696 | "غالب_فریکوئنسی"
11697 | "fft_fréquence_dominante"
11698 | "fft_dominante_frequenz"
11699 | "fft_доминирующая_частота" => {
11700 return Ok(Value::Number(self.fft.borrow().dominant_freq() as f64));
11701 },
11702
11703 #[cfg(target_arch = "wasm32")]
11705 "fft_push"
11706 | "วิเคราะห์เสียง"
11707 | "频谱输入"
11708 | "FFT入力"
11709 | "FFT입력"
11710 | "ورودی_FFT"
11711 | "أدخل_FFT"
11712 | "הזנת_FFT"
11713 | "FFT_ان_پٹ"
11714 | "fft_entrée"
11715 | "fft_eingabe"
11716 | "fft_вход" => {
11717 return Ok(Value::Unit);
11718 },
11719 #[cfg(target_arch = "wasm32")]
11720 "fft_bands"
11721 | "แถบความถี่"
11722 | "频段"
11723 | "周波数帯"
11724 | "주파수대"
11725 | "باندهای_FFT"
11726 | "نطاقات_FFT"
11727 | "פסי_FFT"
11728 | "FFT_بینڈز"
11729 | "fft_bandes"
11730 | "fft_bänder"
11731 | "fft_полосы" => {
11732 let n = self.arg_num(&args, 0, 32.0)? as usize;
11733 return Ok(Value::List(vec![Value::Number(0.0); n].into()));
11734 },
11735 #[cfg(target_arch = "wasm32")]
11736 "fft_beat"
11737 | "จังหวะเสียง"
11738 | "节拍检测"
11739 | "ビート検出"
11740 | "비트"
11741 | "ضرب_FFT"
11742 | "نبضة_FFT"
11743 | "פעימת_FFT"
11744 | "FFT_دھڑکن"
11745 | "fft_battement"
11746 | "fft_takt"
11747 | "fft_удар" => {
11748 return Ok(Value::Bool(false));
11749 },
11750 #[cfg(target_arch = "wasm32")]
11751 "fft_beat_ratio"
11752 | "อัตราจังหวะ"
11753 | "节拍比"
11754 | "ビート比"
11755 | "비트비율"
11756 | "نسبت_ضرب_FFT"
11757 | "نسبة_نبضة_FFT"
11758 | "יחס_פעימת_FFT"
11759 | "FFT_بیٹ_تناسب" => {
11760 return Ok(Value::Number(1.0));
11761 },
11762 #[cfg(target_arch = "wasm32")]
11763 "fft_rms"
11764 | "ระดับRMS"
11765 | "均方根"
11766 | "二乗平均"
11767 | "RMS레벨"
11768 | "RMS_صدا"
11769 | "جذر_متوسط_مربع_FFT"
11770 | "RMS_של_FFT"
11771 | "FFT_RMS" => {
11772 return Ok(Value::Number(0.0));
11773 },
11774 #[cfg(target_arch = "wasm32")]
11775 "fft_dominant_freq"
11776 | "ความถี่หลัก"
11777 | "主频"
11778 | "主要周波数"
11779 | "주파수"
11780 | "فرکانس_غالب"
11781 | "التردد_السائد"
11782 | "תדר_דומיננטי"
11783 | "غالب_فریکوئنسی"
11784 | "fft_fréquence_dominante"
11785 | "fft_dominante_frequenz"
11786 | "fft_доминирующая_частота" => {
11787 return Ok(Value::Number(0.0));
11788 },
11789
11790 "tex_checkerboard"
11798 | "ลายตารางหมากรุก"
11799 | "بافت_شطرنجی"
11800 | "نسيج_رقعة_الشطرنج"
11801 | "מרקם_שחמט"
11802 | "شطرنج_ٹیکسچر" => {
11803 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11804 let tiles = self.arg_num(&args, 4, 8.0)? as u32;
11805 let (r1, g1, b1) = (
11806 self.arg_num(&args, 5, 255.)? as u32,
11807 self.arg_num(&args, 6, 255.)? as u32,
11808 self.arg_num(&args, 7, 255.)? as u32,
11809 );
11810 let (r2, g2, b2) = (
11811 self.arg_num(&args, 8, 0.)? as u32,
11812 self.arg_num(&args, 9, 0.)? as u32,
11813 self.arg_num(&args, 10, 0.)? as u32,
11814 );
11815 let c1 = (r1 << 16) | (g1 << 8) | b1;
11816 let c2 = (r2 << 16) | (g2 << 8) | b2;
11817 let mut gfx = self.gfx.borrow_mut();
11818 let (bw, bh) = (gfx.width, gfx.height);
11819 for row in 0..th {
11820 for col in 0..tw {
11821 let cx = col as u32 * tiles / tw as u32;
11822 let cy = row as u32 * tiles / th as u32;
11823 let (dx, dy) = (tx + col, ty + row);
11824 if dx < bw && dy < bh {
11825 gfx.buffer[dy * bw + dx] = if (cx + cy) % 2 == 0 { c1 } else { c2 };
11826 }
11827 }
11828 }
11829 return Ok(Value::Unit);
11830 },
11831
11832 "tex_gradient"
11834 | "ลายไล่สี"
11835 | "بافت_گرادیان"
11836 | "نسيج_متدرج"
11837 | "מרקם_גרדיאנט"
11838 | "گریڈینٹ_ٹیکسچر" => {
11839 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11840 let angle = self.arg_num(&args, 4, 0.0)? as f32;
11841 let (r1, g1, b1) = (
11842 self.arg_num(&args, 5, 0.)? as f32 / 255.,
11843 self.arg_num(&args, 6, 0.)? as f32 / 255.,
11844 self.arg_num(&args, 7, 0.)? as f32 / 255.,
11845 );
11846 let (r2, g2, b2) = (
11847 self.arg_num(&args, 8, 255.)? as f32 / 255.,
11848 self.arg_num(&args, 9, 255.)? as f32 / 255.,
11849 self.arg_num(&args, 10, 255.)? as f32 / 255.,
11850 );
11851 let (ca, sa) = (angle.to_radians().cos(), angle.to_radians().sin());
11852 let mut gfx = self.gfx.borrow_mut();
11853 let (bw, bh) = (gfx.width, gfx.height);
11854 for row in 0..th {
11855 for col in 0..tw {
11856 let nx = col as f32 / tw as f32 - 0.5;
11857 let ny = row as f32 / th as f32 - 0.5;
11858 let t = ((nx * ca + ny * sa + 0.707) / 1.414).clamp(0., 1.);
11859 let (dx, dy) = (tx + col, ty + row);
11860 if dx < bw && dy < bh {
11861 gfx.buffer[dy * bw + dx] =
11862 tex_rgb(r1 + (r2 - r1) * t, g1 + (g2 - g1) * t, b1 + (b2 - b1) * t);
11863 }
11864 }
11865 }
11866 return Ok(Value::Unit);
11867 },
11868
11869 "tex_noise" | "ลายนอยส์" | "بافت_نویز" | "نسيج_ضجيج" | "מרקם_רעש" | "نوائز_ٹیکسچر" =>
11871 {
11872 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11873 let scale = self.arg_num(&args, 4, 4.0)? as f32;
11874 let octaves = self.arg_num(&args, 5, 4.0)? as u32;
11875 let seed = self.arg_num(&args, 6, 0.0)? as u32;
11876 let palette = self.arg_str(&args, 7, "rainbow");
11877 let mut gfx = self.gfx.borrow_mut();
11878 let (bw, bh) = (gfx.width, gfx.height);
11879 for row in 0..th {
11880 for col in 0..tw {
11881 let v = tex_fbm(
11882 col as f32 * scale / tw as f32,
11883 row as f32 * scale / th as f32,
11884 octaves,
11885 seed,
11886 );
11887 let [r, g, b] = tex_palette(&palette, v);
11888 let (dx, dy) = (tx + col, ty + row);
11889 if dx < bw && dy < bh {
11890 gfx.buffer[dy * bw + dx] = tex_rgb(r, g, b);
11891 }
11892 }
11893 }
11894 return Ok(Value::Unit);
11895 },
11896
11897 "tex_freq_map"
11900 | "ลายความถี่"
11901 | "نقشه_فرکانس_بافت"
11902 | "خريطة_تردد_النسيج"
11903 | "מפת_תדר_מרקם"
11904 | "فریکوئنسی_میپ_ٹیکسچر" => {
11905 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11906 let time = self.arg_num(&args, 4, 0.0)? as f32;
11907 let speed = self.arg_num(&args, 5, 0.3)? as f32;
11908 let palette = self.arg_str(&args, 6, "rainbow");
11909 let bands: Vec<f32> = {
11910 let c = self.fft_bands_cache.borrow();
11911 if c.is_empty() {
11912 vec![0.0; 32]
11913 } else {
11914 c.clone()
11915 }
11916 };
11917 let n = bands.len().max(1);
11918 let mut gfx = self.gfx.borrow_mut();
11919 let (bw, bh) = (gfx.width, gfx.height);
11920 for row in 0..th {
11921 for col in 0..tw {
11922 let band_idx = (col * n / tw.max(1)).min(n - 1);
11923 let mag = bands[band_idx].clamp(0., 1.);
11924 let fill_y = (mag * th as f32) as usize;
11925 if row >= th.saturating_sub(fill_y) {
11926 let t = (col as f32 / tw as f32 + time * speed) % 1.0;
11927 let [r, g, b] = tex_palette(&palette, t);
11928 let bright = mag * (1.0 - row as f32 / th as f32 * 0.5);
11929 let (dx, dy) = (tx + col, ty + row);
11930 if dx < bw && dy < bh {
11931 gfx.buffer[dy * bw + dx] =
11932 tex_rgb(r * bright, g * bright, b * bright);
11933 }
11934 }
11935 }
11936 }
11937 return Ok(Value::Unit);
11938 },
11939
11940 "tex_spiral"
11942 | "ลายเกลียวหมุน"
11943 | "بافت_مارپیچ"
11944 | "نسيج_حلزوني"
11945 | "מרקם_ספירלה"
11946 | "سرپیچ_ٹیکسچر" => {
11947 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11948 let freq = self.arg_num(&args, 4, 5.0)? as f32;
11949 let n_bands = self.arg_num(&args, 5, 8.0)? as f32;
11950 let time = self.arg_num(&args, 6, 0.0)? as f32;
11951 let palette = self.arg_str(&args, 7, "rainbow");
11952 let mut gfx = self.gfx.borrow_mut();
11953 let (bw, bh) = (gfx.width, gfx.height);
11954 for row in 0..th {
11955 for col in 0..tw {
11956 let nx = col as f32 / tw as f32 - 0.5;
11957 let ny = row as f32 / th as f32 - 0.5;
11958 let r = (nx * nx + ny * ny).sqrt();
11959 let theta = ny.atan2(nx);
11960 let t = ((r * freq - theta / std::f32::consts::TAU + time * 0.5) * n_bands
11961 % 1.0)
11962 .abs();
11963 let [cr, cg, cb] = tex_palette(&palette, t);
11964 let (dx, dy) = (tx + col, ty + row);
11965 if dx < bw && dy < bh {
11966 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
11967 }
11968 }
11969 }
11970 return Ok(Value::Unit);
11971 },
11972
11973 "tex_ripple" | "ลายระลอก" | "بافت_موج" | "نسيج_تموج" | "מרקם_אדווה" | "ریپل_ٹیکسچر" =>
11975 {
11976 let (tx, ty, tw, th) = self.tex_rect(&args)?;
11977 let freq = self.arg_num(&args, 4, 10.0)? as f32;
11978 let rcx = self.arg_num(&args, 5, 0.5)? as f32;
11979 let rcy = self.arg_num(&args, 6, 0.5)? as f32;
11980 let time = self.arg_num(&args, 7, 0.0)? as f32;
11981 let palette = self.arg_str(&args, 8, "ocean");
11982 let mut gfx = self.gfx.borrow_mut();
11983 let (bw, bh) = (gfx.width, gfx.height);
11984 for row in 0..th {
11985 for col in 0..tw {
11986 let nx = col as f32 / tw as f32 - rcx;
11987 let ny = row as f32 / th as f32 - rcy;
11988 let r = (nx * nx + ny * ny).sqrt();
11989 let t = ((r * freq - time) % 1.0).abs();
11990 let [cr, cg, cb] = tex_palette(&palette, t);
11991 let (dx, dy) = (tx + col, ty + row);
11992 if dx < bw && dy < bh {
11993 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
11994 }
11995 }
11996 }
11997 return Ok(Value::Unit);
11998 },
11999
12000 "tex_mandelbrot"
12002 | "ลายแมนเดลบรอต"
12003 | "بافت_ماندلبرو"
12004 | "نسيج_مانديلبروت"
12005 | "מרקם_מנדלברוט"
12006 | "مینڈل_بروٹ_ٹیکسچر" => {
12007 let (tx, ty, tw, th) = self.tex_rect(&args)?;
12008 let zoom = self.arg_num(&args, 4, 1.0)?;
12009 let mcx = self.arg_num(&args, 5, -0.5)?;
12010 let mcy = self.arg_num(&args, 6, 0.0)?;
12011 let max_iter = self.arg_num(&args, 7, 64.0)? as u32;
12012 let palette = self.arg_str(&args, 8, "psychedelic");
12013 let mut gfx = self.gfx.borrow_mut();
12014 let (bw, bh) = (gfx.width, gfx.height);
12015 for row in 0..th {
12016 for col in 0..tw {
12017 let zx0 = (col as f64 / tw as f64 - 0.5) / zoom + mcx;
12018 let zy0 = (row as f64 / th as f64 - 0.5) / zoom + mcy;
12019 let mut x = 0.0f64;
12020 let mut y = 0.0f64;
12021 let mut i = 0u32;
12022 while i < max_iter && x * x + y * y < 4.0 {
12023 let t = x * x - y * y + zx0;
12024 y = 2.0 * x * y + zy0;
12025 x = t;
12026 i += 1;
12027 }
12028 let t = if i == max_iter {
12029 0.0f32
12030 } else {
12031 (i as f32
12032 - (x as f32 * x as f32 + y as f32 * y as f32).ln().ln()
12033 / 2.0f32.ln())
12034 / max_iter as f32
12035 };
12036 let [cr, cg, cb] = tex_palette(&palette, t.clamp(0., 1.));
12037 let (dx, dy) = (tx + col, ty + row);
12038 if dx < bw && dy < bh {
12039 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
12040 }
12041 }
12042 }
12043 return Ok(Value::Unit);
12044 },
12045
12046 "tex_julia"
12048 | "ลายจูเลีย"
12049 | "بافت_ژولیا"
12050 | "نسيج_جوليا"
12051 | "מרקם_ג'וליה"
12052 | "جولیا_ٹیکسچر" => {
12053 let (tx, ty, tw, th) = self.tex_rect(&args)?;
12054 let c_re = self.arg_num(&args, 4, -0.7)?;
12055 let c_im = self.arg_num(&args, 5, 0.27)?;
12056 let max_iter = self.arg_num(&args, 6, 64.0)? as u32;
12057 let palette = self.arg_str(&args, 7, "neon");
12058 let mut gfx = self.gfx.borrow_mut();
12059 let (bw, bh) = (gfx.width, gfx.height);
12060 for row in 0..th {
12061 for col in 0..tw {
12062 let mut zx = (col as f64 / tw as f64 - 0.5) * 3.5;
12063 let mut zy = (row as f64 / th as f64 - 0.5) * 3.5;
12064 let mut i = 0u32;
12065 while i < max_iter && zx * zx + zy * zy < 4.0 {
12066 let t = zx * zx - zy * zy + c_re;
12067 zy = 2.0 * zx * zy + c_im;
12068 zx = t;
12069 i += 1;
12070 }
12071 let t = i as f32 / max_iter as f32;
12072 let [cr, cg, cb] = tex_palette(&palette, t);
12073 let (dx, dy) = (tx + col, ty + row);
12074 if dx < bw && dy < bh {
12075 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
12076 }
12077 }
12078 }
12079 return Ok(Value::Unit);
12080 },
12081
12082 "tex_voronoi"
12084 | "ลายโวโรนอย"
12085 | "بافت_ورونوی"
12086 | "نسيج_فورونوي"
12087 | "מרקם_וורונוי"
12088 | "ورونوئی_ٹیکسچر" => {
12089 let (tx, ty, tw, th) = self.tex_rect(&args)?;
12090 let cells = self.arg_num(&args, 4, 16.0)? as u32;
12091 let seed = self.arg_num(&args, 5, 42.0)? as u32;
12092 let palette = self.arg_str(&args, 6, "rainbow");
12093 let pts: Vec<[f32; 2]> = (0..cells)
12094 .map(|i| {
12095 [
12096 tex_hash(i as i32, 0, seed),
12097 tex_hash(i as i32, 1, seed + 999),
12098 ]
12099 })
12100 .collect();
12101 let mut gfx = self.gfx.borrow_mut();
12102 let (bw, bh) = (gfx.width, gfx.height);
12103 for row in 0..th {
12104 for col in 0..tw {
12105 let (fx, fy) = (col as f32 / tw as f32, row as f32 / th as f32);
12106 let (min_d, nearest) = pts.iter().enumerate().fold(
12107 (f32::MAX, 0usize),
12108 |(d, idx), (i, &[cx, cy])| {
12109 let dd = (fx - cx).powi(2) + (fy - cy).powi(2);
12110 if dd < d {
12111 (dd, i)
12112 } else {
12113 (d, idx)
12114 }
12115 },
12116 );
12117 let t = (nearest as f32 / cells as f32 + min_d * 4.0) % 1.0;
12118 let [cr, cg, cb] = tex_palette(&palette, t);
12119 let (dx, dy) = (tx + col, ty + row);
12120 if dx < bw && dy < bh {
12121 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
12122 }
12123 }
12124 }
12125 return Ok(Value::Unit);
12126 },
12127
12128 "tex_halftone"
12130 | "ลายฮาล์ฟโทน"
12131 | "بافت_نیمتن"
12132 | "نسيج_نصفي"
12133 | "מרקם_חצי_גוון"
12134 | "ہاف_ٹون_ٹیکسچر" => {
12135 let (tx, ty, tw, th) = self.tex_rect(&args)?;
12136 let dot_size = self.arg_num(&args, 4, 0.05)? as f32;
12137 let time = self.arg_num(&args, 5, 0.0)? as f32;
12138 let palette = self.arg_str(&args, 6, "rainbow");
12139 let mut gfx = self.gfx.borrow_mut();
12140 let (bw, bh) = (gfx.width, gfx.height);
12141 for row in 0..th {
12142 for col in 0..tw {
12143 let (fx, fy) = (col as f32 / tw as f32, row as f32 / th as f32);
12144 let gx = (fx / dot_size).floor();
12145 let gy = (fy / dot_size).floor();
12146 let lx = (fx / dot_size - gx - 0.5) * 2.0;
12147 let ly = (fy / dot_size - gy - 0.5) * 2.0;
12148 let r = (lx * lx + ly * ly).sqrt();
12149 let t = (gx / (1.0 / dot_size) + time * 0.1) % 1.0;
12150 let a = if r < 0.7 {
12151 ((0.7 - r) / 0.7).clamp(0., 1.)
12152 } else {
12153 0.0
12154 };
12155 if a > 0.0 {
12156 let [cr, cg, cb] = tex_palette(&palette, t);
12157 let (dx, dy) = (tx + col, ty + row);
12158 if dx < bw && dy < bh {
12159 gfx.buffer[dy * bw + dx] = tex_rgb(cr, cg, cb);
12160 }
12161 }
12162 }
12163 }
12164 return Ok(Value::Unit);
12165 },
12166
12167 "set_shade_mode"
12172 | "设置着色"
12173 | "シェード設定"
12174 | "셰이드모드"
12175 | "ตั้งการแรเงา"
12176 | "تنظیم_حالت_سایهپردازی"
12177 | "عيّن_نمط_التظليل"
12178 | "קבע_מצב_הצללה"
12179 | "شیڈ_موڈ_مقرر_کرو"
12180 | "définir_mode_ombrage"
12181 | "schattierungsmodus_setzen"
12182 | "задать_режим_затенения" => {
12183 let m = self.arg_num(&args, 0, 2.0)? as u8;
12184 self.gfx.borrow_mut().shade_mode = m;
12185 return Ok(Value::Unit);
12186 },
12187 "set_cel_bands"
12189 | "设置色阶"
12190 | "セル段数"
12191 | "셀밴드"
12192 | "ตั้งระดับสี"
12193 | "تنظیم_باندهای_سل"
12194 | "عيّن_نطاقات_التظليل"
12195 | "קבע_רצועות_הצללה"
12196 | "سیل_بینڈز_مقرر_کرو"
12197 | "définir_bandes_cel"
12198 | "cel_bänder_setzen"
12199 | "задать_полосы_cel" => {
12200 let n = (self.arg_num(&args, 0, 4.0)? as u32).max(2);
12201 self.gfx.borrow_mut().shade.bands = n;
12202 return Ok(Value::Unit);
12203 },
12204 "set_shadow_color"
12206 | "设置阴影色"
12207 | "影の色"
12208 | "그림자색"
12209 | "ตั้งสีเงา"
12210 | "تنظیم_رنگ_سایه"
12211 | "عيّن_لون_الظل"
12212 | "קבע_צבע_צל"
12213 | "سایہ_رنگ_مقرر_کرو"
12214 | "définir_couleur_ombre"
12215 | "schattenfarbe_setzen"
12216 | "задать_цвет_тени" => {
12217 let r = self.arg_num(&args, 0, 26.)? as f32 / 255.0;
12218 let g = self.arg_num(&args, 1, 33.)? as f32 / 255.0;
12219 let b = self.arg_num(&args, 2, 77.)? as f32 / 255.0;
12220 self.gfx.borrow_mut().shade.shadow = [r, g, b];
12221 return Ok(Value::Unit);
12222 },
12223 #[cfg(not(target_arch = "wasm32"))]
12229 "crypto_hash"
12230 | "แฮชเข้ารหัส"
12231 | "几何哈希"
12232 | "幾何ハッシュ"
12233 | "기하해시"
12234 | "درهم_رمزنگاری"
12235 | "بصمة_تشفير"
12236 | "גיבוב_הצפנה"
12237 | "خفیہ_ہیش"
12238 | "hachage_crypto"
12239 | "krypto_hash"
12240 | "крипто_хеш" => {
12241 let s = self.arg_str(&args, 0, "");
12242 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(
12243 s.as_bytes(),
12244 ))));
12245 },
12246 #[cfg(target_arch = "wasm32")]
12247 "crypto_hash"
12248 | "แฮชเข้ารหัส"
12249 | "几何哈希"
12250 | "幾何ハッシュ"
12251 | "기하해시"
12252 | "درهم_رمزنگاری"
12253 | "بصمة_تشفير"
12254 | "גיבוב_הצפנה"
12255 | "خفیہ_ہیش"
12256 | "hachage_crypto"
12257 | "krypto_hash"
12258 | "крипто_хеш" => {
12259 let s = self.arg_str(&args, 0, "");
12260 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(
12261 s.as_bytes(),
12262 ))));
12263 },
12264 #[cfg(not(target_arch = "wasm32"))]
12266 "knot_points"
12267 | "จุดปม"
12268 | "结点坐标"
12269 | "結び目点"
12270 | "매듭점"
12271 | "نقاط_گره"
12272 | "نقاط_العقدة"
12273 | "נקודות_קשר"
12274 | "گرہ_پوائنٹس"
12275 | "points_nœud"
12276 | "knotenpunkte"
12277 | "точки_узла" => {
12278 let s = self.arg_str(&args, 0, "");
12279 let shape = ling_crypto::geo::KnotShape::from_bytes(s.as_bytes());
12280 let mut out = Vec::with_capacity(shape.points.len() * 3);
12281 for p in &shape.points {
12282 out.push(Value::Number(p[0] as f64));
12283 out.push(Value::Number(p[1] as f64));
12284 out.push(Value::Number(p[2] as f64));
12285 }
12286 return Ok(Value::List(Rc::new(out)));
12287 },
12288 #[cfg(target_arch = "wasm32")]
12289 "knot_points"
12290 | "จุดปม"
12291 | "结点坐标"
12292 | "結び目点"
12293 | "매듭점"
12294 | "نقاط_گره"
12295 | "نقاط_العقدة"
12296 | "נקודות_קשר"
12297 | "گرہ_پوائنٹس"
12298 | "points_nœud"
12299 | "knotenpunkte"
12300 | "точки_узла" => {
12301 let s = self.arg_str(&args, 0, "");
12302 let shape = ling_crypto::geo::KnotShape::from_bytes(s.as_bytes());
12303 let mut out = Vec::with_capacity(shape.points.len() * 3);
12304 for p in &shape.points {
12305 out.push(Value::Number(p[0] as f64));
12306 out.push(Value::Number(p[1] as f64));
12307 out.push(Value::Number(p[2] as f64));
12308 }
12309 return Ok(Value::List(out.into()));
12310 },
12311 #[cfg(not(target_arch = "wasm32"))]
12312 "knot_label"
12313 | "ป้ายปม"
12314 | "结点标签"
12315 | "結び目ラベル"
12316 | "매듭라벨"
12317 | "برچسب_گره"
12318 | "تسمية_العقدة"
12319 | "תווית_קשר"
12320 | "گرہ_لیبل"
12321 | "étiquette_nœud"
12322 | "knotenbezeichnung"
12323 | "метка_узла" => {
12324 let s = self.arg_str(&args, 0, "");
12325 return Ok(Value::Str(
12326 ling_crypto::geo::KnotShape::from_bytes(s.as_bytes()).label(),
12327 ));
12328 },
12329 #[cfg(target_arch = "wasm32")]
12330 "knot_label"
12331 | "ป้ายปม"
12332 | "结点标签"
12333 | "結び目ラベル"
12334 | "매듭라벨"
12335 | "برچسب_گره"
12336 | "تسمية_العقدة"
12337 | "תווית_קשר"
12338 | "گرہ_لیبل"
12339 | "étiquette_nœud"
12340 | "knotenbezeichnung"
12341 | "метка_узла" => {
12342 let s = self.arg_str(&args, 0, "");
12343 return Ok(Value::Str(
12344 ling_crypto::geo::KnotShape::from_bytes(s.as_bytes()).label(),
12345 ));
12346 },
12347 #[cfg(not(target_arch = "wasm32"))]
12349 "knot_keygen"
12350 | "hybrid_keygen"
12351 | "สร้างกุญแจปม"
12352 | "生成密钥"
12353 | "鍵生成"
12354 | "키생성"
12355 | "تولید_کلید_گره"
12356 | "توليد_مفتاح_العقدة"
12357 | "יצירת_מפתח_קשר"
12358 | "گرہ_کلید_تخلیق"
12359 | "génération_clé_nœud"
12360 | "knotenschlüsselerzeugung"
12361 | "генерация_ключа_узла" => {
12362 self.crypto_ids.push(ling_crypto::KnotIdentity::generate());
12363 return Ok(Value::Number((self.crypto_ids.len() - 1) as f64));
12364 },
12365 #[cfg(not(target_arch = "wasm32"))]
12366 "knot_public"
12367 | "hybrid_public"
12368 | "กุญแจสาธารณะปม"
12369 | "公钥"
12370 | "公開鍵"
12371 | "공개키"
12372 | "کلید_عمومی_گره"
12373 | "مفتاح_العقدة_العام"
12374 | "מפתח_ציבורי_קשר"
12375 | "گرہ_عوامی_کلید"
12376 | "clé_publique_nœud"
12377 | "knoten_öffentlicher_schlüssel"
12378 | "публичный_ключ_узла" => {
12379 let h = self.arg_num(&args, 0, 0.0)? as usize;
12380 let pk = self
12381 .crypto_ids
12382 .get(h)
12383 .map(|id| hex_encode(id.public_key()))
12384 .unwrap_or_default();
12385 return Ok(Value::Str(pk));
12386 },
12387 #[cfg(not(target_arch = "wasm32"))]
12389 "knot_encapsulate"
12390 | "hybrid_encapsulate"
12391 | "ห่อกุญแจปม"
12392 | "封装密钥"
12393 | "カプセル化"
12394 | "캡슐화"
12395 | "کپسولهسازی_گره"
12396 | "تغليف_مفتاح_العقدة"
12397 | "עטיפת_קשר"
12398 | "گرہ_احاطہ"
12399 | "encapsuler_nœud"
12400 | "knoten_kapseln"
12401 | "инкапсулировать_узел" => {
12402 let pk = hex_decode(&self.arg_str(&args, 0, ""));
12403 match ling_crypto::geo::knot_encapsulate(&pk) {
12404 Ok((ct, ss)) => {
12405 return Ok(Value::List(Rc::new(vec![
12406 Value::Str(hex_encode(&ct)),
12407 Value::Str(hex_encode(&ss)),
12408 ])))
12409 },
12410 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
12411 }
12412 },
12413 #[cfg(not(target_arch = "wasm32"))]
12415 "knot_decapsulate"
12416 | "hybrid_decapsulate"
12417 | "แกะกุญแจปม"
12418 | "解封装密钥"
12419 | "カプセル解除"
12420 | "캡슐해제"
12421 | "بازکردن_کپسوله_گره"
12422 | "فك_تغليف_مفتاح_العقدة"
12423 | "פתיחת_עטיפת_קשר"
12424 | "گرہ_احاطہ_کھولو"
12425 | "décapsuler_nœud"
12426 | "knoten_entkapseln"
12427 | "декапсулировать_узел" => {
12428 let h = self.arg_num(&args, 0, 0.0)? as usize;
12429 let ct = hex_decode(&self.arg_str(&args, 1, ""));
12430 let ss = self
12431 .crypto_ids
12432 .get(h)
12433 .and_then(|id| id.decapsulate(&ct).ok())
12434 .map(|s| hex_encode(&s))
12435 .unwrap_or_default();
12436 return Ok(Value::Str(ss));
12437 },
12438 #[cfg(not(target_arch = "wasm32"))]
12440 "crypto_seal"
12441 | "ผนึก"
12442 | "封印"
12443 | "封印する"
12444 | "봉인"
12445 | "مهر_رمزنگاری"
12446 | "ختم_تشفير"
12447 | "חתימת_הצפנה"
12448 | "خفیہ_مہر"
12449 | "sceller_crypto"
12450 | "krypto_versiegeln"
12451 | "запечатать_крипто" => {
12452 let key = hex_to_32(&self.arg_str(&args, 0, ""));
12453 let pt = self.arg_str(&args, 1, "");
12454 match ling_crypto::geo::holo_seal(key, pt.as_bytes()) {
12455 Ok(ct) => return Ok(Value::Str(hex_encode(&ct))),
12456 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
12457 }
12458 },
12459 #[cfg(not(target_arch = "wasm32"))]
12460 "crypto_open"
12461 | "เปิดผนึก"
12462 | "解封"
12463 | "封印解除"
12464 | "봉인해제"
12465 | "بازکردن_مهر"
12466 | "فتح_الختم"
12467 | "פתיחת_חתימה"
12468 | "مہر_کھولو"
12469 | "ouvrir_crypto"
12470 | "krypto_öffnen"
12471 | "открыть_крипто" => {
12472 let key = hex_to_32(&self.arg_str(&args, 0, ""));
12473 let ct = hex_decode(&self.arg_str(&args, 1, ""));
12474 match ling_crypto::geo::holo_open(key, &ct) {
12475 Ok(pt) => return Ok(Value::Str(String::from_utf8_lossy(&pt).into_owned())),
12476 Err(e) => return Ok(Value::Err(Box::new(Value::Str(e.to_string())))),
12477 }
12478 },
12479 #[cfg(not(target_arch = "wasm32"))]
12481 "holo_points"
12482 | "จุดโฮโลแกรม"
12483 | "全息点"
12484 | "ホログラム点"
12485 | "홀로그램점"
12486 | "نقاط_هولوگرام"
12487 | "نقاط_الهولوغرام"
12488 | "נקודות_הולוגרמה"
12489 | "ہولوگرام_پوائنٹس"
12490 | "points_holo"
12491 | "holo_punkte"
12492 | "точки_голо" => {
12493 let s = self.arg_str(&args, 0, "");
12494 let frags = ling_crypto::geo::scatter(s.as_bytes());
12495 let mut out = Vec::with_capacity(frags.len() * 4);
12496 for f in &frags {
12497 for c in f.coord {
12498 out.push(Value::Number(c as f64));
12499 }
12500 }
12501 return Ok(Value::List(Rc::new(out)));
12502 },
12503 #[cfg(not(target_arch = "wasm32"))]
12504 "holo_fragment_count"
12505 | "จำนวนชิ้นโฮโลแกรม"
12506 | "全息碎片数"
12507 | "ホログラム断片数"
12508 | "홀로그램조각수"
12509 | "تعداد_قطعات_هولوگرام"
12510 | "عدد_شظايا_الهولوغرام"
12511 | "מספר_שברי_הולוגרמה"
12512 | "ہولوگرام_ٹکڑے_تعداد"
12513 | "nombre_fragments_holo"
12514 | "holo_fragmentanzahl"
12515 | "число_фрагментов_голо" => {
12516 let s = self.arg_str(&args, 0, "");
12517 return Ok(Value::Number(
12518 ling_crypto::geo::scatter(s.as_bytes()).len() as f64
12519 ));
12520 },
12521 #[cfg(not(target_arch = "wasm32"))]
12524 "shake_hex" | "SHAKE哈希" => {
12525 let s = self.arg_str(&args, 0, "");
12526 let len = self.arg_num(&args, 1, 32.0)?.max(0.0) as usize;
12527 return Ok(Value::Str(hex_encode(&ling_crypto::Shake256::hash(
12528 s.as_bytes(),
12529 len,
12530 ))));
12531 },
12532 #[cfg(not(target_arch = "wasm32"))]
12534 "ed25519_keygen"
12535 | "생성서명키"
12536 | "สร้างกุญแจลายเซ็น"
12537 | "生成签名密钥"
12538 | "署名鍵生成"
12539 | "تولید_کلید_امضا"
12540 | "توليد_مفتاح_التوقيع"
12541 | "יצירת_מפתח_חתימה"
12542 | "دستخط_کلید_تخلیق"
12543 | "génération_clé_signature"
12544 | "signaturschlüsselerzeugung"
12545 | "генерация_ключа_подписи" => {
12546 self.ed25519_ids
12547 .push(ling_crypto::Ed25519Keypair::generate());
12548 return Ok(Value::Number((self.ed25519_ids.len() - 1) as f64));
12549 },
12550 #[cfg(not(target_arch = "wasm32"))]
12555 "ed25519_keygen_from_seed"
12556 | "씨앗에서생성서명키"
12557 | "สร้างกุญแจลายเซ็นจากเมล็ด"
12558 | "从种子生成签名密钥"
12559 | "シードから署名鍵生成"
12560 | "تولید_کلید_امضا_از_دانه"
12561 | "توليد_مفتاح_التوقيع_من_البذرة"
12562 | "יצירת_מפתח_חתימה_מזרע"
12563 | "بیج_سے_دستخط_کلید_تخلیق"
12564 | "génération_clé_signature_depuis_graine"
12565 | "signaturschlüsselerzeugung_aus_saat"
12566 | "генерация_ключа_подписи_из_семени" => {
12567 let seed = hex_to_32(&self.arg_str(&args, 0, ""));
12568 self.ed25519_ids
12569 .push(ling_crypto::Ed25519Keypair::from_seed(seed));
12570 return Ok(Value::Number((self.ed25519_ids.len() - 1) as f64));
12571 },
12572 #[cfg(not(target_arch = "wasm32"))]
12573 "ed25519_public"
12574 | "서명공개키"
12575 | "กุญแจสาธารณะลายเซ็น"
12576 | "签名公钥"
12577 | "署名公開鍵"
12578 | "کلید_عمومی_امضا"
12579 | "مفتاح_التوقيع_العام"
12580 | "מפתח_חתימה_ציבורי"
12581 | "دستخط_عوامی_کلید"
12582 | "clé_publique_signature"
12583 | "signatur_öffentlicher_schlüssel"
12584 | "публичный_ключ_подписи" => {
12585 let h = self.arg_num(&args, 0, 0.0)? as usize;
12586 let pk = self
12587 .ed25519_ids
12588 .get(h)
12589 .map(|kp| hex_encode(&kp.public_key()))
12590 .unwrap_or_default();
12591 return Ok(Value::Str(pk));
12592 },
12593 #[cfg(not(target_arch = "wasm32"))]
12595 "ed25519_sign"
12596 | "서명하다"
12597 | "เซ็นชื่อ"
12598 | "签名"
12599 | "署名する"
12600 | "امضا_کردن"
12601 | "توقيع"
12602 | "לחתום"
12603 | "دستخط_کریں"
12604 | "signer"
12605 | "signieren"
12606 | "подписать" => {
12607 let h = self.arg_num(&args, 0, 0.0)? as usize;
12608 let msg = self.arg_str(&args, 1, "");
12609 let sig = self
12610 .ed25519_ids
12611 .get(h)
12612 .map(|kp| hex_encode(&kp.sign(msg.as_bytes())))
12613 .unwrap_or_default();
12614 return Ok(Value::Str(sig));
12615 },
12616 #[cfg(not(target_arch = "wasm32"))]
12618 "ed25519_verify"
12619 | "서명확인"
12620 | "ยืนยันลายเซ็น"
12621 | "验证签名"
12622 | "署名検証"
12623 | "تایید_امضا"
12624 | "التحقق_من_التوقيع"
12625 | "אימות_חתימה"
12626 | "دستخط_تصدیق"
12627 | "vérifier_signature"
12628 | "signatur_verifizieren"
12629 | "проверить_подпись" => {
12630 let pk_hex = self.arg_str(&args, 0, "");
12631 let msg = self.arg_str(&args, 1, "");
12632 let sig_hex = self.arg_str(&args, 2, "");
12633 let pk_bytes = hex_decode(&pk_hex);
12634 let sig_bytes = hex_decode(&sig_hex);
12635 let ok = (|| {
12636 let pk: [u8; 32] = pk_bytes.try_into().ok()?;
12637 let sig: [u8; 64] = sig_bytes.try_into().ok()?;
12638 Some(ling_crypto::Ed25519Keypair::verify(&pk, msg.as_bytes(), &sig).is_ok())
12639 })()
12640 .unwrap_or(false);
12641 return Ok(Value::Bool(ok));
12642 },
12643 #[cfg(not(target_arch = "wasm32"))]
12650 "mldsa_keygen"
12651 | "양자서명키생성"
12652 | "สร้างกุญแจลายเซ็นควอนตัม"
12653 | "生成量子签名密钥"
12654 | "量子署名鍵生成"
12655 | "تولید_کلید_امضای_کوانتومی"
12656 | "توليد_مفتاح_التوقيع_الكمي"
12657 | "יצירת_מפתח_חתימה_קוונטי"
12658 | "کوانٹم_دستخط_کلید_تخلیق"
12659 | "génération_clé_signature_quantique"
12660 | "quantensignaturschlüsselerzeugung"
12661 | "генерация_ключа_квантовой_подписи" => {
12662 self.mldsa_ids.push(ling_crypto::MlDsa65Keypair::generate());
12663 return Ok(Value::Number((self.mldsa_ids.len() - 1) as f64));
12664 },
12665 #[cfg(not(target_arch = "wasm32"))]
12668 "mldsa_keygen_from_seed"
12669 | "씨앗에서양자서명키생성"
12670 | "สร้างกุญแจลายเซ็นควอนตัมจากเมล็ด"
12671 | "从种子生成量子签名密钥"
12672 | "シードから量子署名鍵生成"
12673 | "تولید_کلید_امضای_کوانتومی_از_دانه"
12674 | "توليد_مفتاح_التوقيع_الكمي_من_البذرة"
12675 | "יצירת_מפתח_חתימה_קוונטי_מזרע"
12676 | "بیج_سے_کوانٹم_دستخط_کلید_تخلیق"
12677 | "génération_clé_signature_quantique_depuis_graine"
12678 | "quantensignaturschlüsselerzeugung_aus_saat"
12679 | "генерация_ключа_квантовой_подписи_из_семени" =>
12680 {
12681 let seed = hex_to_32(&self.arg_str(&args, 0, ""));
12682 self.mldsa_ids
12683 .push(ling_crypto::MlDsa65Keypair::from_seed(seed));
12684 return Ok(Value::Number((self.mldsa_ids.len() - 1) as f64));
12685 },
12686 #[cfg(not(target_arch = "wasm32"))]
12687 "mldsa_public"
12688 | "양자서명공개키"
12689 | "กุญแจสาธารณะลายเซ็นควอนตัม"
12690 | "量子签名公钥"
12691 | "量子署名公開鍵"
12692 | "کلید_عمومی_امضای_کوانتومی"
12693 | "مفتاح_التوقيع_الكمي_العام"
12694 | "מפתח_חתימה_קוונטי_ציבורי"
12695 | "کوانٹم_دستخط_عوامی_کلید"
12696 | "clé_publique_signature_quantique"
12697 | "quantensignatur_öffentlicher_schlüssel"
12698 | "публичный_ключ_квантовой_подписи" => {
12699 let h = self.arg_num(&args, 0, 0.0)? as usize;
12700 let pk = self
12701 .mldsa_ids
12702 .get(h)
12703 .map(|kp| hex_encode(&kp.public_key()))
12704 .unwrap_or_default();
12705 return Ok(Value::Str(pk));
12706 },
12707 #[cfg(not(target_arch = "wasm32"))]
12709 "mldsa_sign"
12710 | "양자서명하다"
12711 | "เซ็นชื่อควอนตัม"
12712 | "量子签名"
12713 | "量子署名する"
12714 | "امضای_کوانتومی_کردن"
12715 | "توقيع_كمي"
12716 | "לחתום_קוונטית"
12717 | "کوانٹم_دستخط_کریں"
12718 | "signer_quantique"
12719 | "quantensignieren"
12720 | "подписать_квантово" => {
12721 let h = self.arg_num(&args, 0, 0.0)? as usize;
12722 let msg = self.arg_str(&args, 1, "");
12723 let sig = self
12724 .mldsa_ids
12725 .get(h)
12726 .map(|kp| hex_encode(&kp.sign(msg.as_bytes())))
12727 .unwrap_or_default();
12728 return Ok(Value::Str(sig));
12729 },
12730 #[cfg(not(target_arch = "wasm32"))]
12732 "mldsa_verify"
12733 | "양자서명확인"
12734 | "ยืนยันลายเซ็นควอนตัม"
12735 | "验证量子签名"
12736 | "量子署名検証"
12737 | "تایید_امضای_کوانتومی"
12738 | "التحقق_من_التوقيع_الكمي"
12739 | "אימות_חתימה_קוונטית"
12740 | "کوانٹم_دستخط_تصدیق"
12741 | "vérifier_signature_quantique"
12742 | "quantensignatur_verifizieren"
12743 | "проверить_квантовую_подпись" => {
12744 let pk_hex = self.arg_str(&args, 0, "");
12745 let msg = self.arg_str(&args, 1, "");
12746 let sig_hex = self.arg_str(&args, 2, "");
12747 let pk_bytes = hex_decode(&pk_hex);
12748 let sig_bytes = hex_decode(&sig_hex);
12749 let ok = ling_crypto::MlDsa65Keypair::verify(&pk_bytes, msg.as_bytes(), &sig_bytes)
12750 .is_ok();
12751 return Ok(Value::Bool(ok));
12752 },
12753 #[cfg(not(target_arch = "wasm32"))]
12756 "password_hash" | "비밀번호해시" => {
12757 let pw = self.arg_str(&args, 0, "");
12758 let hash = ling_crypto::Argon2idParams::default()
12759 .hash_password(pw.as_bytes())
12760 .unwrap_or_default();
12761 return Ok(Value::Str(hash));
12762 },
12763 #[cfg(not(target_arch = "wasm32"))]
12764 "password_verify" | "비밀번호확인" => {
12765 let pw = self.arg_str(&args, 0, "");
12766 let hash = self.arg_str(&args, 1, "");
12767 let ok = ling_crypto::Argon2idParams::verify_password(pw.as_bytes(), &hash).is_ok();
12768 return Ok(Value::Bool(ok));
12769 },
12770 #[cfg(not(target_arch = "wasm32"))]
12773 "random_hex" | "무작위16진수" => {
12774 use rand::RngCore;
12775 let n = self.arg_num(&args, 0, 16.0)?.max(0.0) as usize;
12776 let mut buf = vec![0u8; n];
12777 rand::rngs::OsRng.fill_bytes(&mut buf);
12778 return Ok(Value::Str(hex_encode(&buf)));
12779 },
12780 #[cfg(not(target_arch = "wasm32"))]
12784 "base64_encode" | "base64인코딩" => {
12785 use base64::Engine as _;
12786 let s = self.arg_str(&args, 0, "");
12787 return Ok(Value::Str(
12788 base64::engine::general_purpose::STANDARD.encode(s.as_bytes()),
12789 ));
12790 },
12791 #[cfg(not(target_arch = "wasm32"))]
12795 "remove_file" | "ลบไฟล์" | "删除文件" | "ファイル削除" | "파일삭제" =>
12799 {
12800 let path = self.arg_str(&args, 0, "");
12801 return Ok(Value::Bool(std::fs::remove_file(&path).is_ok()));
12802 },
12803 "rename_file" | "เปลี่ยนชื่อไฟล์" | "重命名文件" | "ファイル改名" | "파일이름변경" =>
12808 {
12809 let from = self.arg_str(&args, 0, "");
12810 let to = self.arg_str(&args, 1, "");
12811 if std::fs::rename(&from, &to).is_ok() {
12812 return Ok(Value::Bool(true));
12813 }
12814 let ok = std::fs::copy(&from, &to).is_ok();
12815 if ok {
12816 let _ = std::fs::remove_file(&from);
12817 }
12818 return Ok(Value::Bool(ok));
12819 },
12820 "base64_decode_to_file" | "base64파일로저장" => {
12821 use base64::Engine as _;
12822 let b64 = self.arg_str(&args, 0, "");
12823 let path = self.arg_str(&args, 1, "");
12824 let b64 = b64.split(',').next_back().unwrap_or(&b64); let ok = base64::engine::general_purpose::STANDARD
12826 .decode(b64)
12827 .ok()
12828 .and_then(|bytes| std::fs::write(&path, bytes).ok())
12829 .is_some();
12830 return Ok(Value::Bool(ok));
12831 },
12832 #[cfg(not(target_arch = "wasm32"))]
12837 "qr_svg" | "QR코드" => {
12838 let text = self.arg_str(&args, 0, "");
12839 let svg = qrcode::QrCode::new(text.as_bytes())
12840 .map(|code| {
12841 code.render::<qrcode::render::svg::Color>()
12842 .min_dimensions(240, 240)
12843 .dark_color(qrcode::render::svg::Color("#1a0f3d"))
12844 .light_color(qrcode::render::svg::Color("#ffffff"))
12845 .build()
12846 })
12847 .unwrap_or_default();
12848 return Ok(Value::Str(svg));
12849 },
12850 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
12853 "zip_files" | "압축파일" => {
12854 let paths = match args.first() {
12855 Some(Value::List(l)) => l.iter().map(|v| v.to_string()).collect::<Vec<_>>(),
12856 _ => Vec::new(),
12857 };
12858 let out_path = self.arg_str(&args, 1, "out.zip");
12859 let ok = (|| -> std::io::Result<()> {
12860 let file = std::fs::File::create(&out_path)?;
12861 let mut writer = zip::ZipWriter::new(file);
12862 let options: zip::write::FileOptions<'_, ()> =
12863 zip::write::FileOptions::default()
12864 .compression_method(zip::CompressionMethod::Deflated);
12865 for p in &paths {
12866 let name = std::path::Path::new(p)
12867 .file_name()
12868 .map(|n| n.to_string_lossy().into_owned())
12869 .unwrap_or_else(|| p.clone());
12870 let bytes = std::fs::read(p)?;
12871 writer.start_file(name, options)?;
12872 std::io::Write::write_all(&mut writer, &bytes)?;
12873 }
12874 writer.finish()?;
12875 Ok(())
12876 })()
12877 .is_ok();
12878 return Ok(Value::Bool(ok));
12879 },
12880 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
12887 "pdf_from_images" | "PDF来自图片" => {
12888 let paths = match args.first() {
12889 Some(Value::List(l)) => l.iter().map(|v| v.to_string()).collect::<Vec<_>>(),
12890 _ => Vec::new(),
12891 };
12892 let out_path = self.arg_str(&args, 1, "out.pdf");
12893 let ok = build_pdf_from_images(&paths, &out_path).is_ok();
12894 return Ok(Value::Bool(ok));
12895 },
12896
12897 "ease" => {
12901 let name = self.arg_str(&args, 0, "ease");
12902 let t = self.arg_num(&args, 1, 0.0)? as f32;
12903 return Ok(Value::Number(
12904 ling_ui::Easing::from_name(&name).apply(t) as f64
12905 ));
12906 },
12907
12908 "tween"
12913 | "补间"
12914 | "補間"
12915 | "트윈"
12916 | "แทรกค่า"
12917 | "میانفریم"
12918 | "تدرج_حركي"
12919 | "טווין"
12920 | "ٹوئین"
12921 | "твин" => {
12922 let a = self.arg_num(&args, 0, 0.0)?;
12923 let b = self.arg_num(&args, 1, 0.0)?;
12924 let t = self.arg_num(&args, 2, 0.0)?.clamp(0.0, 1.0);
12925 return Ok(Value::Number(a + (b - a) * t));
12926 },
12927 "tween_ease"
12928 | "缓动补间"
12929 | "緩和補間"
12930 | "이징트윈"
12931 | "แทรกนุ่ม"
12932 | "میانفریم_نرم"
12933 | "تدرج_ناعم_حركي"
12934 | "טווין_חלק"
12935 | "ٹوئین_ایز"
12936 | "tween_lisse"
12937 | "tween_glättung"
12938 | "твин_плавность" => {
12939 let a = self.arg_num(&args, 0, 0.0)? as f32;
12940 let b = self.arg_num(&args, 1, 0.0)? as f32;
12941 let t = self.arg_num(&args, 2, 0.0)? as f32;
12942 let kind = self.arg_str(&args, 3, "linear");
12943 let e = ling_animation::EaseFunction::from_name(&kind);
12944 return Ok(Value::Number(
12945 ling_animation::ease::tween_ease(&a, &b, t, e) as f64,
12946 ));
12947 },
12948 "breathe" | "呼吸" | "호흡" | "หายใจ" | "تنفس" | "נשימה" | "سانس" | "respirer"
12950 | "atmen" | "дышать" => {
12951 let t = self.arg_num(&args, 0, 0.0)? as f32;
12952 let rate = self.arg_num(&args, 1, 1.0)? as f32;
12953 let depth = self.arg_num(&args, 2, 0.1)? as f32;
12954 return Ok(Value::Number(
12955 ling_animation::scalar::breathe(t, rate, depth) as f64,
12956 ));
12957 },
12958 "wobble"
12959 | "摆动"
12960 | "揺れ"
12961 | "흔들림"
12962 | "โยก"
12963 | "نوسان"
12964 | "تذبذب"
12965 | "תנודה"
12966 | "لرزش"
12967 | "osciller"
12968 | "wackeln"
12969 | "покачивание" => {
12970 let t = self.arg_num(&args, 0, 0.0)? as f32;
12971 let freq = self.arg_num(&args, 1, 1.0)? as f32;
12972 let amp = self.arg_num(&args, 2, 1.0)? as f32;
12973 let phase = self.arg_num(&args, 3, 0.0)? as f32;
12974 return Ok(Value::Number(
12975 ling_animation::scalar::wobble(t, freq, amp, phase) as f64,
12976 ));
12977 },
12978 "gait_phase"
12979 | "步相"
12980 | "歩相"
12981 | "걸음위상"
12982 | "เฟสก้าว"
12983 | "فاز_گام"
12984 | "طور_المشية"
12985 | "שלב_הליכה"
12986 | "چال_مرحلہ"
12987 | "phase_démarche"
12988 | "gangphase"
12989 | "фаза_походки" => {
12990 let t = self.arg_num(&args, 0, 0.0)? as f32;
12991 let speed = self.arg_num(&args, 1, 1.0)? as f32;
12992 return Ok(Value::Number(
12993 ling_animation::scalar::gait_phase(t, speed) as f64
12994 ));
12995 },
12996 "gait_swing"
12997 | "步摆"
12998 | "歩振り"
12999 | "걸음흔들"
13000 | "ก้าวแกว่ง"
13001 | "نوسان_گام"
13002 | "أرجحة_المشية"
13003 | "נדנוד_הליכה"
13004 | "چال_جھولا"
13005 | "balancement_démarche"
13006 | "gangschwung"
13007 | "мах_походки" => {
13008 let t = self.arg_num(&args, 0, 0.0)? as f32;
13009 let speed = self.arg_num(&args, 1, 1.0)? as f32;
13010 let stride = self.arg_num(&args, 2, 1.0)? as f32;
13011 return Ok(Value::Number(
13012 ling_animation::scalar::gait_swing(t, speed, stride) as f64,
13013 ));
13014 },
13015 "gait_lift"
13016 | "抬脚"
13017 | "足上げ"
13018 | "발들기"
13019 | "ยกเท้า"
13020 | "بلندشدن_گام"
13021 | "رفع_المشية"
13022 | "הרמת_הליכה"
13023 | "چال_اٹھاؤ"
13024 | "levée_démarche"
13025 | "ganghub"
13026 | "подъём_походки" => {
13027 let t = self.arg_num(&args, 0, 0.0)? as f32;
13028 let speed = self.arg_num(&args, 1, 1.0)? as f32;
13029 let height = self.arg_num(&args, 2, 1.0)? as f32;
13030 return Ok(Value::Number(
13031 ling_animation::scalar::gait_lift(t, speed, height) as f64,
13032 ));
13033 },
13034 "spring_to"
13035 | "弹向"
13036 | "バネ寄せ"
13037 | "스프링이동"
13038 | "สปริงไป"
13039 | "فنر_بهسوی"
13040 | "نابض_إلى"
13041 | "קפיץ_אל"
13042 | "اسپرنگ_تک"
13043 | "ressort_vers"
13044 | "feder_zu"
13045 | "пружина_к" => {
13046 let pos = self.arg_num(&args, 0, 0.0)? as f32;
13047 let vel = self.arg_num(&args, 1, 0.0)? as f32;
13048 let target = self.arg_num(&args, 2, 0.0)? as f32;
13049 let stiffness = self.arg_num(&args, 3, 120.0)? as f32;
13050 let damping = self.arg_num(&args, 4, 14.0)? as f32;
13051 let dt = self.arg_num(&args, 5, 1.0 / 60.0)? as f32;
13052 let (np, nv) =
13053 ling_animation::scalar::spring_step(pos, vel, target, stiffness, damping, dt);
13054 return Ok(Value::List(Rc::new(vec![
13055 Value::Number(np as f64),
13056 Value::Number(nv as f64),
13057 ])));
13058 },
13059 "ik2"
13060 | "反解"
13061 | "逆運動"
13062 | "역운동"
13063 | "ไอเค2"
13064 | "سینماتیک_معکوس2"
13065 | "حركية_عكسية2"
13066 | "קינמטיקה_הפוכה2"
13067 | "آئی_کے2"
13068 | "cinématique_inverse2"
13069 | "inverse_kinematik2"
13070 | "обратная_кинематика2" => {
13071 let l1 = self.arg_num(&args, 0, 1.0)? as f32;
13072 let l2 = self.arg_num(&args, 1, 1.0)? as f32;
13073 let tx = self.arg_num(&args, 2, 0.0)? as f32;
13074 let ty = self.arg_num(&args, 3, 0.0)? as f32;
13075 let (sh, el) = ling_animation::scalar::two_bone_ik(l1, l2, tx, ty);
13076 return Ok(Value::List(Rc::new(vec![
13077 Value::Number(sh as f64),
13078 Value::Number(el as f64),
13079 ])));
13080 },
13081 "gear_couple"
13083 | "齿轮联动"
13084 | "歯車連動"
13085 | "기어연동"
13086 | "เฟืองทด"
13087 | "جفت_چرخدنده"
13088 | "اقتران_التروس"
13089 | "צימוד_גלגלי_שיניים"
13090 | "گیئر_جوڑا"
13091 | "accoupler_engrenage"
13092 | "zahnrad_koppeln"
13093 | "сцепить_шестерни" => {
13094 let angle = self.arg_num(&args, 0, 0.0)? as f32;
13095 let ti = self.arg_num(&args, 1, 1.0)? as f32;
13096 let to = self.arg_num(&args, 2, 1.0)? as f32;
13097 return Ok(Value::Number(
13098 ling_animation::scalar::gear(angle, ti, to) as f64
13099 ));
13100 },
13101 "gear_train"
13102 | "齿轮组"
13103 | "歯車列"
13104 | "기어열"
13105 | "ชุดเฟือง"
13106 | "مجموعه_چرخدنده"
13107 | "قطار_التروس"
13108 | "שרשרת_גלגלי_שיניים"
13109 | "گیئر_ٹرین"
13110 | "train_engrenages"
13111 | "zahnradgetriebe"
13112 | "передача_шестерён" => {
13113 let angle = self.arg_num(&args, 0, 0.0)? as f32;
13114 let teeth: Vec<f32> = match args.get(1) {
13115 Some(Value::List(items)) => items
13116 .iter()
13117 .filter_map(|v| {
13118 if let Value::Number(n) = v {
13119 Some(*n as f32)
13120 } else {
13121 None
13122 }
13123 })
13124 .collect(),
13125 _ => Vec::new(),
13126 };
13127 let out = ling_animation::mechanism::gear_train(angle, &teeth);
13128 return Ok(Value::List(Rc::new(
13129 out.into_iter().map(|a| Value::Number(a as f64)).collect(),
13130 )));
13131 },
13132 "cam_lift"
13133 | "凸轮升程"
13134 | "カム揚程"
13135 | "캠리프트"
13136 | "ยกลูกเบี้ยว"
13137 | "بلندشدن_بادامک"
13138 | "رفع_الكامة"
13139 | "הרמת_קאם"
13140 | "کیم_اٹھاؤ"
13141 | "levée_came"
13142 | "nockenhub"
13143 | "подъём_кулачка" => {
13144 let angle = self.arg_num(&args, 0, 0.0)? as f32;
13145 let lift = self.arg_num(&args, 1, 1.0)? as f32;
13146 return Ok(Value::Number(
13147 ling_animation::scalar::cam_lift(angle, lift) as f64
13148 ));
13149 },
13150 "piston" | "活塞" | "ピストン" | "피스톤" | "ลูกสูบ" | "پیستون" | "مكبس" | "בוכנה"
13151 | "پسٹن" | "kolben" | "поршень" => {
13152 let angle = self.arg_num(&args, 0, 0.0)? as f32;
13153 let crank = self.arg_num(&args, 1, 1.0)? as f32;
13154 let rod = self.arg_num(&args, 2, 2.0)? as f32;
13155 return Ok(Value::Number(
13156 ling_animation::scalar::piston(angle, crank, rod) as f64,
13157 ));
13158 },
13159 "rack"
13160 | "齿条"
13161 | "ラック"
13162 | "랙"
13163 | "แร็ค"
13164 | "زبانهدنده"
13165 | "سكة_مسننة"
13166 | "מוט_שיניים"
13167 | "ریک"
13168 | "crémaillère"
13169 | "zahnstange"
13170 | "рейка" => {
13171 let angle = self.arg_num(&args, 0, 0.0)? as f32;
13172 let radius = self.arg_num(&args, 1, 1.0)? as f32;
13173 return Ok(Value::Number(
13174 ling_animation::scalar::rack(angle, radius) as f64
13175 ));
13176 },
13177 #[cfg(not(target_arch = "wasm32"))]
13178 "mouse_x" => {
13179 let gfx = self.gfx.borrow();
13180 let v = gfx
13181 .window
13182 .as_ref()
13183 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
13184 .map(|p| p.0 as f64)
13185 .unwrap_or(0.0);
13186 return Ok(Value::Number(v));
13187 },
13188 #[cfg(target_arch = "wasm32")]
13189 "mouse_x" => {
13190 return Ok(Value::Number(crate::gfx::wasm_mouse_x() as f64));
13191 },
13192 #[cfg(not(target_arch = "wasm32"))]
13193 "mouse_y" => {
13194 let gfx = self.gfx.borrow();
13195 let v = gfx
13196 .window
13197 .as_ref()
13198 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
13199 .map(|p| p.1 as f64)
13200 .unwrap_or(0.0);
13201 return Ok(Value::Number(v));
13202 },
13203 #[cfg(target_arch = "wasm32")]
13204 "mouse_y" => {
13205 return Ok(Value::Number(crate::gfx::wasm_mouse_y() as f64));
13206 },
13207 #[cfg(not(target_arch = "wasm32"))]
13208 "mouse_down" => {
13209 let mut gfx = self.gfx.borrow_mut();
13210 let d = !gfx.input_suppressed()
13211 && gfx
13212 .window
13213 .as_ref()
13214 .map(|w| w.get_mouse_down(minifb::MouseButton::Left))
13215 .unwrap_or(false);
13216 return Ok(Value::Bool(d));
13217 },
13218 #[cfg(target_arch = "wasm32")]
13219 "mouse_down" => {
13220 return Ok(Value::Bool(crate::gfx::wasm_mouse_down()));
13221 },
13222 #[cfg(not(target_arch = "wasm32"))]
13223 "mouse_down_right"
13224 | "เมาส์ขวา"
13225 | "ماوس_راست_فشرده"
13226 | "الفأرة_اليمنى_مضغوطة"
13227 | "עכבר_ימני_לחוץ"
13228 | "دایاں_ماؤس_دبا_ہوا" => {
13229 let mut gfx = self.gfx.borrow_mut();
13230 let d = !gfx.input_suppressed()
13231 && gfx
13232 .window
13233 .as_ref()
13234 .map(|w| w.get_mouse_down(minifb::MouseButton::Right))
13235 .unwrap_or(false);
13236 return Ok(Value::Bool(d));
13237 },
13238 #[cfg(target_arch = "wasm32")]
13239 "mouse_down_right"
13240 | "เมาส์ขวา"
13241 | "ماوس_راست_فشرده"
13242 | "الفأرة_اليمنى_مضغوطة"
13243 | "עכבר_ימני_לחוץ"
13244 | "دایاں_ماؤس_دبا_ہوا" => {
13245 return Ok(Value::Bool(crate::gfx::wasm_mouse_down_right()));
13246 },
13247 #[cfg(not(target_arch = "wasm32"))]
13248 "mouse_down_middle"
13249 | "เมาส์กลาง"
13250 | "ماوس_وسط_فشرده"
13251 | "الفأرة_الوسطى_مضغوطة"
13252 | "עכבר_אמצעי_לחוץ"
13253 | "درمیانی_ماؤس_دبا_ہوا" => {
13254 let mut gfx = self.gfx.borrow_mut();
13255 let d = !gfx.input_suppressed()
13256 && gfx
13257 .window
13258 .as_ref()
13259 .map(|w| w.get_mouse_down(minifb::MouseButton::Middle))
13260 .unwrap_or(false);
13261 return Ok(Value::Bool(d));
13262 },
13263 #[cfg(target_arch = "wasm32")]
13264 "mouse_down_middle"
13265 | "เมาส์กลาง"
13266 | "ماوس_وسط_فشرده"
13267 | "الفأرة_الوسطى_مضغوطة"
13268 | "עכבר_אמצעי_לחוץ"
13269 | "درمیانی_ماؤس_دبا_ہوا" => {
13270 return Ok(Value::Bool(crate::gfx::wasm_mouse_down_middle()));
13271 },
13272 #[cfg(not(target_arch = "wasm32"))]
13273 "ui_hot"
13274 | "热区"
13275 | "ホットエリア"
13276 | "핫존"
13277 | "พื้นที่สัมผัส"
13278 | "ناحیه_فعال"
13279 | "منطقة_ساخنة"
13280 | "אזור_חם"
13281 | "ہاٹ_زون"
13282 | "survol_ui"
13283 | "ui_hover"
13284 | "ui_наведение" => {
13285 let x = self.arg_num(&args, 0, 0.0)? as f32;
13286 let y = self.arg_num(&args, 1, 0.0)? as f32;
13287 let w = self.arg_num(&args, 2, 0.0)? as f32;
13288 let h = self.arg_num(&args, 3, 0.0)? as f32;
13289 let gfx = self.gfx.borrow();
13290 let (mx, my) = gfx
13291 .window
13292 .as_ref()
13293 .and_then(|win| win.get_mouse_pos(minifb::MouseMode::Clamp))
13294 .unwrap_or((0.0, 0.0));
13295 return Ok(Value::Bool(ling_ui::holo::hit_rect(mx, my, x, y, w, h)));
13296 },
13297 #[cfg(target_arch = "wasm32")]
13298 "ui_hot"
13299 | "热区"
13300 | "ホットエリア"
13301 | "핫존"
13302 | "พื้นที่สัมผัส"
13303 | "ناحیه_فعال"
13304 | "منطقة_ساخنة"
13305 | "אזור_חם"
13306 | "ہاٹ_زون"
13307 | "survol_ui"
13308 | "ui_hover"
13309 | "ui_наведение" => {
13310 return Ok(Value::Bool(false));
13311 },
13312 "ui_text"
13314 | "界面文字"
13315 | "UI文字"
13316 | "UI텍스트"
13317 | "ข้อความหน้าจอ"
13318 | "متن_رابط"
13319 | "نص_الواجهة"
13320 | "טקסט_ממשק"
13321 | "یو_آئی_متن"
13322 | "texte_ui"
13323 | "ui_beschriftung"
13324 | "ui_текст" => {
13325 let x = self.arg_num(&args, 0, 0.0)? as f32;
13326 let y = self.arg_num(&args, 1, 0.0)? as f32;
13327 let scale = self.arg_num(&args, 2, 16.0)? as f32;
13328 let s = self.arg_str(&args, 3, "");
13329 let segs = ling_ui::holo::text_lines(&s, x, y, scale * 0.62, scale, scale * 0.24);
13330 let mut gfx = self.gfx.borrow_mut();
13331 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
13332 for sg in segs {
13333 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
13334 }
13335 return Ok(Value::Unit);
13336 },
13337 #[cfg(not(target_arch = "wasm32"))]
13340 "font_load"
13341 | "โหลดฟอนต์"
13342 | "加载字体"
13343 | "フォント読込"
13344 | "글꼴로드"
13345 | "بارگذاری_فونت"
13346 | "تحميل_الخط"
13347 | "טעינת_גופן"
13348 | "فونٹ_لوڈ"
13349 | "charger_police"
13350 | "schriftart_laden"
13351 | "загрузить_шрифт" => {
13352 let path = self.arg_str(&args, 0, "");
13353 let weight = match self.arg_num(&args, 1, 0.0)? {
13355 w if w > 0.0 => Some(w as f32),
13356 _ => None,
13357 };
13358 let mut loaded = ling_graphics::VectorFont::from_path_weight(&path, weight);
13360 if loaded.is_err() {
13361 if let Some(dir) = &self.source_dir {
13362 let joined = dir.join(&path);
13363 loaded = ling_graphics::VectorFont::from_path_weight(
13364 &joined.to_string_lossy(),
13365 weight,
13366 );
13367 }
13368 }
13369 match loaded {
13370 Ok(f) => {
13371 let id = self.fonts.len();
13372 self.fonts.push(f);
13373 return Ok(Value::Number(id as f64));
13374 },
13375 Err(e) => {
13376 eprintln!("font_load failed ({path}): {e}");
13377 return Ok(Value::Number(-1.0));
13378 },
13379 }
13380 },
13381 #[cfg(target_arch = "wasm32")]
13382 "font_load"
13383 | "โหลดฟอนต์"
13384 | "加载字体"
13385 | "フォント読込"
13386 | "글꼴로드"
13387 | "بارگذاری_فونت"
13388 | "تحميل_الخط"
13389 | "טעינת_גופן"
13390 | "فونٹ_لوڈ"
13391 | "charger_police"
13392 | "schriftart_laden"
13393 | "загрузить_шрифт" => {
13394 return Ok(Value::Number(-1.0));
13397 },
13398 #[cfg(not(target_arch = "wasm32"))]
13403 "image_load" => {
13404 let path = self.arg_str(&args, 0, "").replace('\\', "/");
13405 let mut loaded = image::open(&path);
13406 if loaded.is_err() {
13407 if let Some(dir) = &self.source_dir {
13408 let joined = dir.join(&path);
13409 loaded = image::open(&joined);
13410 }
13411 }
13412 match loaded {
13413 Ok(img) => {
13414 let id = self.images.len();
13415 self.images.push(img.to_rgba8());
13416 return Ok(Value::Number(id as f64));
13417 },
13418 Err(e) => {
13419 eprintln!("image_load failed ({path}): {e}");
13420 return Ok(Value::Number(-1.0));
13421 },
13422 }
13423 },
13424 #[cfg(target_arch = "wasm32")]
13425 "image_load" => {
13426 return Ok(Value::Number(-1.0));
13428 },
13429 #[cfg(not(target_arch = "wasm32"))]
13430 "image_width" => {
13431 let id = self.arg_num(&args, 0, -1.0)? as i64;
13432 if id >= 0 && (id as usize) < self.images.len() {
13433 return Ok(Value::Number(self.images[id as usize].width() as f64));
13434 }
13435 return Ok(Value::Number(0.0));
13436 },
13437 #[cfg(target_arch = "wasm32")]
13438 "image_width" => return Ok(Value::Number(0.0)),
13439 #[cfg(not(target_arch = "wasm32"))]
13440 "image_height" => {
13441 let id = self.arg_num(&args, 0, -1.0)? as i64;
13442 if id >= 0 && (id as usize) < self.images.len() {
13443 return Ok(Value::Number(self.images[id as usize].height() as f64));
13444 }
13445 return Ok(Value::Number(0.0));
13446 },
13447 #[cfg(target_arch = "wasm32")]
13448 "image_height" => return Ok(Value::Number(0.0)),
13449 #[cfg(target_arch = "wasm32")]
13453 "image_pixel_r" | "image_pixel_g" | "image_pixel_b" | "image_pixel_a" => {
13454 return Ok(Value::Number(0.0));
13455 },
13456 #[cfg(not(target_arch = "wasm32"))]
13457 "image_pixel_r" | "image_pixel_g" | "image_pixel_b" | "image_pixel_a" => {
13458 let id = self.arg_num(&args, 0, -1.0)? as i64;
13459 let px = self.arg_num(&args, 1, 0.0)? as i64;
13460 let py = self.arg_num(&args, 2, 0.0)? as i64;
13461 if id >= 0 && (id as usize) < self.images.len() {
13462 let img = &self.images[id as usize];
13463 if px >= 0 && py >= 0 && (px as u32) < img.width() && (py as u32) < img.height()
13464 {
13465 let p = img.get_pixel(px as u32, py as u32);
13466 let ch = match name {
13467 "image_pixel_r" => p[0],
13468 "image_pixel_g" => p[1],
13469 "image_pixel_b" => p[2],
13470 _ => p[3],
13471 };
13472 return Ok(Value::Number(ch as f64));
13473 }
13474 }
13475 return Ok(Value::Number(0.0));
13476 },
13477 #[cfg(not(target_arch = "wasm32"))]
13486 "image_new" => {
13487 let w = self.arg_num(&args, 0, 1.0)?.max(1.0) as u32;
13488 let h = self.arg_num(&args, 1, 1.0)?.max(1.0) as u32;
13489 let id = self.images.len();
13490 self.images.push(image::RgbaImage::new(w, h));
13491 return Ok(Value::Number(id as f64));
13492 },
13493 #[cfg(target_arch = "wasm32")]
13495 "image_new" => return Ok(Value::Number(-1.0)),
13496 #[cfg(not(target_arch = "wasm32"))]
13501 "image_set_pixel" => {
13502 let id = self.arg_num(&args, 0, -1.0)? as i64;
13503 let px = self.arg_num(&args, 1, 0.0)? as i64;
13504 let py = self.arg_num(&args, 2, 0.0)? as i64;
13505 let r = self.arg_num(&args, 3, 0.0)?.clamp(0.0, 255.0) as u8;
13506 let g = self.arg_num(&args, 4, 0.0)?.clamp(0.0, 255.0) as u8;
13507 let b = self.arg_num(&args, 5, 0.0)?.clamp(0.0, 255.0) as u8;
13508 let a = self.arg_num(&args, 6, 255.0)?.clamp(0.0, 255.0) as u8;
13509 if id >= 0 && (id as usize) < self.images.len() {
13510 let img = &mut self.images[id as usize];
13511 if px >= 0 && py >= 0 && (px as u32) < img.width() && (py as u32) < img.height()
13512 {
13513 img.put_pixel(px as u32, py as u32, image::Rgba([r, g, b, a]));
13514 }
13515 }
13516 return Ok(Value::Unit);
13517 },
13518 #[cfg(target_arch = "wasm32")]
13521 "image_set_pixel" => return Ok(Value::Unit),
13522 #[cfg(not(target_arch = "wasm32"))]
13530 "image_save" => {
13531 let id = self.arg_num(&args, 0, -1.0)? as i64;
13532 let path = self.arg_str(&args, 1, "");
13533 if id >= 0 && (id as usize) < self.images.len() {
13534 if let Some(parent) = std::path::Path::new(&path).parent() {
13535 if !parent.as_os_str().is_empty() {
13536 let _ = std::fs::create_dir_all(parent);
13537 }
13538 }
13539 if self.images[id as usize].save(&path).is_ok() {
13540 return Ok(Value::Number(1.0));
13541 }
13542 }
13543 return Ok(Value::Number(-1.0));
13544 },
13545 #[cfg(target_arch = "wasm32")]
13546 "image_save" => {
13547 return Ok(Value::Number(-1.0));
13548 },
13549 #[cfg(not(target_arch = "wasm32"))]
13559 "image_draw" => {
13560 let id = self.arg_num(&args, 0, -1.0)? as i64;
13561 let dx = self.arg_num(&args, 1, 0.0)? as i32;
13562 let dy = self.arg_num(&args, 2, 0.0)? as i32;
13563 let dw = self.arg_num(&args, 3, 0.0)?.max(0.0) as i32;
13564 let dh = self.arg_num(&args, 4, 0.0)?.max(0.0) as i32;
13565 if id >= 0 && (id as usize) < self.images.len() && dw > 0 && dh > 0 {
13566 let img = &self.images[id as usize];
13567 let sw = img.width() as i32;
13568 let sh = img.height() as i32;
13569 if sw > 0 && sh > 0 {
13570 let mut gfx = self.gfx.borrow_mut();
13571 let (fw, fh) = (gfx.width as i32, gfx.height as i32);
13572 for py in 0..dh {
13573 let ty = dy + py;
13574 if ty < 0 || ty >= fh {
13575 continue;
13576 }
13577 let sy = (py * sh / dh).clamp(0, sh - 1) as u32;
13578 for px in 0..dw {
13579 let tx = dx + px;
13580 if tx < 0 || tx >= fw {
13581 continue;
13582 }
13583 let sx = (px * sw / dw).clamp(0, sw - 1) as u32;
13584 let p = img.get_pixel(sx, sy);
13585 let a = p[3] as u32;
13586 if a == 0 {
13587 continue;
13588 }
13589 let idx = ty as usize * gfx.width + tx as usize;
13590 if a >= 255 {
13591 gfx.buffer[idx] = ((p[0] as u32) << 16)
13592 | ((p[1] as u32) << 8)
13593 | (p[2] as u32);
13594 } else {
13595 let bg = gfx.buffer[idx];
13596 let br = (bg >> 16) & 0xff;
13597 let bg_g = (bg >> 8) & 0xff;
13598 let bb = bg & 0xff;
13599 let r = (p[0] as u32 * a + br * (255 - a)) / 255;
13600 let g = (p[1] as u32 * a + bg_g * (255 - a)) / 255;
13601 let b = (p[2] as u32 * a + bb * (255 - a)) / 255;
13602 gfx.buffer[idx] = (r << 16) | (g << 8) | b;
13603 }
13604 }
13605 }
13606 }
13607 }
13608 return Ok(Value::Unit);
13609 },
13610 #[cfg(target_arch = "wasm32")]
13611 "image_draw" => {
13612 return Ok(Value::Unit);
13613 },
13614 #[cfg(not(target_arch = "wasm32"))]
13617 "font_text"
13618 | "ข้อความฟอนต์"
13619 | "字体文本"
13620 | "フォント文字"
13621 | "글꼴텍스트"
13622 | "متن_فونت"
13623 | "نص_الخط"
13624 | "טקסט_גופן"
13625 | "فونٹ_متن"
13626 | "texte_police"
13627 | "schriftart_text"
13628 | "текст_шрифт" => {
13629 let id = self.arg_num(&args, 0, 0.0)? as i64;
13630 let x = self.arg_num(&args, 1, 0.0)? as f32;
13631 let y = self.arg_num(&args, 2, 0.0)? as f32;
13632 let px = self.arg_num(&args, 3, 16.0)? as f32;
13633 let s = self.arg_str(&args, 4, "");
13634 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
13635 let strokes = self.font_layout_2d(id as usize, x, y, px, &s);
13636 let mut gfx = self.gfx.borrow_mut();
13637 let (w, h, color, add, aa) = (
13638 gfx.width,
13639 gfx.height,
13640 gfx.color,
13641 gfx.blend == 1,
13642 gfx.font_antialias,
13643 );
13644 for pl in &strokes {
13645 for seg in pl.windows(2) {
13646 if aa {
13647 crate::gfx::raster::draw_line_aa(
13648 &mut gfx.buffer,
13649 w,
13650 h,
13651 color,
13652 add,
13653 seg[0][0],
13654 seg[0][1],
13655 seg[1][0],
13656 seg[1][1],
13657 );
13658 } else {
13659 crate::gfx::raster::draw_line(
13660 &mut gfx.buffer,
13661 w,
13662 h,
13663 color,
13664 seg[0][0],
13665 seg[0][1],
13666 seg[1][0],
13667 seg[1][1],
13668 );
13669 }
13670 }
13671 }
13672 }
13673 return Ok(Value::Unit);
13674 },
13675 #[cfg(target_arch = "wasm32")]
13676 "font_text"
13677 | "ข้อความฟอนต์"
13678 | "字体文本"
13679 | "フォント文字"
13680 | "글꼴텍스트"
13681 | "متن_فونت"
13682 | "نص_الخط"
13683 | "טקסט_גופן"
13684 | "فونٹ_متن"
13685 | "texte_police"
13686 | "schriftart_text"
13687 | "текст_шрифт" => {
13688 return Ok(Value::Unit);
13689 },
13690 #[cfg(not(target_arch = "wasm32"))]
13693 "font_text_fill"
13694 | "เติมฟอนต์"
13695 | "填充字体"
13696 | "フォント塗り"
13697 | "글꼴채움"
13698 | "پرکردن_متن_فونت"
13699 | "تعبئة_نص_الخط"
13700 | "מילוי_טקסט_גופן"
13701 | "فونٹ_متن_بھرو"
13702 | "remplir_texte_police"
13703 | "schriftart_text_füllen"
13704 | "заполнить_текст_шрифт" => {
13705 let id = self.arg_num(&args, 0, 0.0)? as i64;
13706 let x = self.arg_num(&args, 1, 0.0)? as f32;
13707 let y = self.arg_num(&args, 2, 0.0)? as f32;
13708 let px = self.arg_num(&args, 3, 16.0)? as f32;
13709 let s = self.arg_str(&args, 4, "");
13710 if id >= 0 && (id as usize) < self.fonts.len() && px > 0.0 {
13711 let glyphs = self.font_layout_2d_glyphs(id as usize, x, y, px, &s);
13713 let mut gfx = self.gfx.borrow_mut();
13714 let (w, h, color, add, aa) = (
13715 gfx.width,
13716 gfx.height,
13717 gfx.color,
13718 gfx.blend == 1,
13719 gfx.font_antialias,
13720 );
13721 for contours in &glyphs {
13722 if aa {
13723 crate::gfx::raster::fill_contours_aa(
13724 &mut gfx.buffer,
13725 w,
13726 h,
13727 color,
13728 add,
13729 contours,
13730 );
13731 } else {
13732 crate::gfx::raster::fill_contours(
13733 &mut gfx.buffer,
13734 w,
13735 h,
13736 color,
13737 add,
13738 contours,
13739 );
13740 }
13741 }
13742 }
13743 return Ok(Value::Unit);
13744 },
13745 #[cfg(target_arch = "wasm32")]
13746 "font_text_fill"
13747 | "เติมฟอนต์"
13748 | "填充字体"
13749 | "フォント塗り"
13750 | "글꼴채움"
13751 | "پرکردن_متن_فونت"
13752 | "تعبئة_نص_الخط"
13753 | "מילוי_טקסט_גופן"
13754 | "فونٹ_متن_بھرو"
13755 | "remplir_texte_police"
13756 | "schriftart_text_füllen"
13757 | "заполнить_текст_шрифт" => {
13758 return Ok(Value::Unit);
13759 },
13760 #[cfg(not(target_arch = "wasm32"))]
13764 "font_text_3d"
13765 | "ข้อความฟอนต์3มิติ"
13766 | "字体3D"
13767 | "フォント3D"
13768 | "글꼴3D"
13769 | "متن_فونت_سهبعدی"
13770 | "نص_خط_ثلاثي_الأبعاد"
13771 | "טקסט_גופן_תלת_ממדי"
13772 | "تھری_ڈی_فونٹ_متن"
13773 | "texte_police_3d"
13774 | "schriftart_text_3d"
13775 | "текст_шрифт_3d" => {
13776 let id = self.arg_num(&args, 0, 0.0)? as i64;
13777 let cx = self.arg_num(&args, 1, 0.0)? as f32;
13778 let cy = self.arg_num(&args, 2, 0.0)? as f32;
13779 let cz = self.arg_num(&args, 3, 0.0)? as f32;
13780 let ux = self.arg_num(&args, 4, 1.0)? as f32;
13781 let uy = self.arg_num(&args, 5, 0.0)? as f32;
13782 let uz = self.arg_num(&args, 6, 0.0)? as f32;
13783 let vx = self.arg_num(&args, 7, 0.0)? as f32;
13784 let vy = self.arg_num(&args, 8, 1.0)? as f32;
13785 let vz = self.arg_num(&args, 9, 0.0)? as f32;
13786 let size = self.arg_num(&args, 10, 1.0)? as f32;
13787 let s = self.arg_str(&args, 11, "");
13788 let fill_rows = self.arg_num(&args, 12, 0.0)? as i32;
13792 if id >= 0 && (id as usize) < self.fonts.len() && size > 0.0 {
13793 let font = &mut self.fonts[id as usize];
13795 let asc = font.ascent();
13796 let mut pen = 0.0f32;
13797 let mut lines: Vec<[f32; 6]> = Vec::new();
13798 for ch in s.chars() {
13799 let go = font.glyph_outline(ch, 0.01);
13800 let map = |p: [f32; 2], pen: f32| {
13801 let a = pen + p[0];
13802 let b = p[1] - asc; [
13804 cx + a * size * ux + b * size * vx,
13805 cy + a * size * uy + b * size * vy,
13806 cz + a * size * uz + b * size * vz,
13807 ]
13808 };
13809 for pl in &go.polylines {
13810 for seg in pl.windows(2) {
13811 let p0 = map(seg[0], pen);
13812 let p1 = map(seg[1], pen);
13813 lines.push([p0[0], p0[1], p0[2], p1[0], p1[1], p1[2]]);
13814 }
13815 }
13816 if fill_rows > 0 {
13817 let (mut ymin, mut ymax) = (f32::MAX, f32::MIN);
13821 for pl in &go.polylines {
13822 for p in pl {
13823 ymin = ymin.min(p[1]);
13824 ymax = ymax.max(p[1]);
13825 }
13826 }
13827 if ymax > ymin {
13828 for r in 0..fill_rows {
13829 let y =
13830 ymin + (r as f32 + 0.5) * (ymax - ymin) / fill_rows as f32;
13831 let mut xs: Vec<f32> = Vec::new();
13832 for pl in &go.polylines {
13833 let n = pl.len();
13834 if n < 2 {
13835 continue;
13836 }
13837 for k in 0..n {
13838 let p0 = pl[k];
13839 let p1 = pl[(k + 1) % n];
13840 if k + 1 == n
13841 && (p1[0] - p0[0]).abs() < 1e-6
13842 && (p1[1] - p0[1]).abs() < 1e-6
13843 {
13844 continue; }
13846 let (y0, y1) = (p0[1], p1[1]);
13847 if (y0 <= y && y1 > y) || (y1 <= y && y0 > y) {
13848 let t = (y - y0) / (y1 - y0);
13849 xs.push(p0[0] + t * (p1[0] - p0[0]));
13850 }
13851 }
13852 }
13853 xs.sort_by(|a, b| {
13854 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
13855 });
13856 let mut k = 0;
13857 while k + 1 < xs.len() {
13858 let a = map([xs[k], y], pen);
13859 let b = map([xs[k + 1], y], pen);
13860 lines.push([a[0], a[1], a[2], b[0], b[1], b[2]]);
13861 k += 2;
13862 }
13863 }
13864 }
13865 }
13866 pen += go.advance;
13867 }
13868 let mut gfx = self.gfx.borrow_mut();
13869 let color = gfx.color;
13870 let near = -gfx.camera.zdist + 0.05;
13871 for l in &lines {
13872 let (mut ax, mut ay, mut az) = (l[0], l[1], l[2]);
13873 let (mut bx, mut by, mut bz) = (l[3], l[4], l[5]);
13874 let da = gfx.camera.depth(ax, ay, az);
13875 let db = gfx.camera.depth(bx, by, bz);
13876 if da <= near && db <= near {
13877 continue;
13878 }
13879 if da <= near {
13880 let t = (near - da) / (db - da);
13881 ax += t * (bx - ax);
13882 ay += t * (by - ay);
13883 az += t * (bz - az);
13884 } else if db <= near {
13885 let t = (near - da) / (db - da);
13886 bx = ax + t * (bx - ax);
13887 by = ay + t * (by - ay);
13888 bz = az + t * (bz - az);
13889 }
13890 let (sax, say, da2) = gfx.camera.project(ax, ay, az);
13891 let (sbx, sby, db2) = gfx.camera.project(bx, by, bz);
13892 let depth = (da2 + db2) / 2.0;
13893 gfx.depth_queue.push_line(depth, color, sax, say, sbx, sby);
13894 }
13895 }
13896 return Ok(Value::Unit);
13897 },
13898 #[cfg(target_arch = "wasm32")]
13899 "font_text_3d"
13900 | "ข้อความฟอนต์3มิติ"
13901 | "字体3D"
13902 | "フォント3D"
13903 | "글꼴3D"
13904 | "متن_فونت_سهبعدی"
13905 | "نص_خط_ثلاثي_الأبعاد"
13906 | "טקסט_גופן_תלת_ממדי"
13907 | "تھری_ڈی_فونٹ_متن"
13908 | "texte_police_3d"
13909 | "schriftart_text_3d"
13910 | "текст_шрифт_3d" => {
13911 return Ok(Value::Unit);
13912 },
13913 #[cfg(not(target_arch = "wasm32"))]
13915 "font_width"
13916 | "ความกว้างฟอนต์"
13917 | "字体宽度"
13918 | "フォント幅"
13919 | "글꼴너비"
13920 | "عرض_فونت"
13921 | "عرض_الخط"
13922 | "רוחב_גופן"
13923 | "فونٹ_چوڑائی"
13924 | "largeur_police"
13925 | "schriftart_breite"
13926 | "ширина_шрифта" => {
13927 let id = self.arg_num(&args, 0, 0.0)? as i64;
13928 let px = self.arg_num(&args, 1, 16.0)? as f32;
13929 let s = self.arg_str(&args, 2, "");
13930 if id >= 0 && (id as usize) < self.fonts.len() {
13931 return Ok(Value::Number(self.fonts[id as usize].measure(&s, px) as f64));
13932 }
13933 return Ok(Value::Number(0.0));
13934 },
13935 #[cfg(target_arch = "wasm32")]
13936 "font_width"
13937 | "ความกว้างฟอนต์"
13938 | "字体宽度"
13939 | "フォント幅"
13940 | "글꼴너비"
13941 | "عرض_فونت"
13942 | "عرض_الخط"
13943 | "רוחב_גופן"
13944 | "فونٹ_چوڑائی"
13945 | "largeur_police"
13946 | "schriftart_breite"
13947 | "ширина_шрифта" => {
13948 return Ok(Value::Number(0.0));
13949 },
13950 #[cfg(not(target_arch = "wasm32"))]
13955 "font_glyph_outline"
13956 | "font_outline"
13957 | "เส้นขอบฟอนต์"
13958 | "字体轮廓"
13959 | "フォント輪郭"
13960 | "글꼴윤곽"
13961 | "خطدور_نویسه_فونت"
13962 | "حدود_حرف_الخط"
13963 | "קו_מתאר_גליף"
13964 | "فونٹ_گلف_آؤٹ_لائن" => {
13965 let id = self.arg_num(&args, 0, 0.0)? as i64;
13966 let s = self.arg_str(&args, 1, "");
13967 let tol = self.arg_num(&args, 2, 0.01)? as f32;
13968 let ch = s.chars().next().unwrap_or(' ');
13969 if id >= 0 && (id as usize) < self.fonts.len() {
13970 let go = self.fonts[id as usize].glyph_outline(ch, tol.max(1e-4));
13971 let mut contours: Vec<Value> = Vec::with_capacity(go.polylines.len());
13972 for pl in &go.polylines {
13973 let mut flat: Vec<Value> = Vec::with_capacity(pl.len() * 2);
13974 for p in pl {
13975 flat.push(Value::Number(p[0] as f64));
13976 flat.push(Value::Number(p[1] as f64));
13977 }
13978 contours.push(Value::List(Rc::new(flat)));
13979 }
13980 return Ok(Value::List(Rc::new(contours)));
13981 }
13982 return Ok(Value::List(Rc::new(vec![])));
13983 },
13984 #[cfg(target_arch = "wasm32")]
13985 "font_glyph_outline"
13986 | "font_outline"
13987 | "เส้นขอบฟอนต์"
13988 | "字体轮廓"
13989 | "フォント輪郭"
13990 | "글꼴윤곽"
13991 | "خطدور_نویسه_فونت"
13992 | "حدود_حرف_الخط"
13993 | "קו_מתאר_גליף"
13994 | "فونٹ_گلف_آؤٹ_لائن" => {
13995 return Ok(Value::List(Rc::new(vec![])));
13996 },
13997 #[cfg(not(target_arch = "wasm32"))]
14000 "font_advance"
14001 | "ระยะฟอนต์"
14002 | "字体步进"
14003 | "フォント送り"
14004 | "글꼴전진"
14005 | "پیشروی_فونت"
14006 | "تقدم_الخط"
14007 | "קידום_גופן"
14008 | "فونٹ_ایڈوانس" => {
14009 let id = self.arg_num(&args, 0, 0.0)? as i64;
14010 let s = self.arg_str(&args, 1, "");
14011 let ch = s.chars().next().unwrap_or(' ');
14012 if id >= 0 && (id as usize) < self.fonts.len() {
14013 return Ok(Value::Number(self.fonts[id as usize].advance(ch) as f64));
14014 }
14015 return Ok(Value::Number(0.0));
14016 },
14017 #[cfg(target_arch = "wasm32")]
14018 "font_advance"
14019 | "ระยะฟอนต์"
14020 | "字体步进"
14021 | "フォント送り"
14022 | "글꼴전진"
14023 | "پیشروی_فونت"
14024 | "تقدم_الخط"
14025 | "קידום_גופן"
14026 | "فونٹ_ایڈوانس" => {
14027 return Ok(Value::Number(0.0));
14028 },
14029
14030 "ui_frame"
14032 | "边框"
14033 | "フレーム枠"
14034 | "프레임틀"
14035 | "กรอบ"
14036 | "قاب_رابط"
14037 | "إطار_الواجهة"
14038 | "מסגרת_ממשק"
14039 | "یو_آئی_فریم"
14040 | "cadre_ui"
14041 | "ui_rahmen"
14042 | "ui_рамка" => {
14043 let x = self.arg_num(&args, 0, 0.0)? as f32;
14044 let y = self.arg_num(&args, 1, 0.0)? as f32;
14045 let w0 = self.arg_num(&args, 2, 0.0)? as f32;
14046 let h0 = self.arg_num(&args, 3, 0.0)? as f32;
14047 let l = self.arg_num(&args, 4, 14.0)? as f32;
14048 let segs = ling_ui::holo::corner_brackets(x, y, w0, h0, l);
14049 let mut gfx = self.gfx.borrow_mut();
14050 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
14051 for sg in segs {
14052 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
14053 }
14054 return Ok(Value::Unit);
14055 },
14056 "ui_bevel"
14058 | "斜角框"
14059 | "ベベル枠"
14060 | "베벨틀"
14061 | "กรอบเฉียง"
14062 | "لبه_شیبدار"
14063 | "حافة_مشطوفة"
14064 | "מסגרת_משופעת"
14065 | "یو_آئی_بیول"
14066 | "biseau_ui"
14067 | "ui_fase"
14068 | "ui_фаска" => {
14069 let x = self.arg_num(&args, 0, 0.0)? as f32;
14070 let y = self.arg_num(&args, 1, 0.0)? as f32;
14071 let w0 = self.arg_num(&args, 2, 0.0)? as f32;
14072 let h0 = self.arg_num(&args, 3, 0.0)? as f32;
14073 let bv = self.arg_num(&args, 4, 10.0)? as f32;
14074 let segs = ling_ui::holo::beveled_rect(x, y, w0, h0, bv);
14075 let mut gfx = self.gfx.borrow_mut();
14076 let (w, h, color) = (gfx.width, gfx.height, gfx.color);
14077 for sg in segs {
14078 draw_line(&mut gfx.buffer, w, h, color, sg[0], sg[1], sg[2], sg[3]);
14079 }
14080 return Ok(Value::Unit);
14081 },
14082
14083 #[cfg(not(target_arch = "wasm32"))]
14089 "ui_theme"
14090 | "界面主题"
14091 | "UIテーマ"
14092 | "인터페이스테마"
14093 | "ธีมส่วนติดต่อ"
14094 | "پوسته_رابط"
14095 | "سمة_الواجهة"
14096 | "ערכת_נושא"
14097 | "یو_آئی_تھیم"
14098 | "thème_ui"
14099 | "ui_thema"
14100 | "ui_тема" => {
14101 let cur = self.ui_theme;
14102 let primary = self.color_at(&args, 0, cur.primary);
14103 let accent = self.color_at(&args, 3, cur.accent);
14104 let track = self.color_at(&args, 6, cur.track);
14105 let warn = self.color_at(&args, 9, cur.warn);
14106 let text = self.color_at(&args, 12, cur.text);
14107 let bg = self.color_at(&args, 15, cur.bg);
14108 self.ui_theme = UiTheme { primary, accent, track, warn, text, bg };
14109 return Ok(Value::Unit);
14110 },
14111
14112 "ui_theme_colors" | "인터페이스테마색상" => {
14118 let th = self.ui_theme;
14119 let mut out = Vec::with_capacity(18);
14120 for c in [th.primary, th.accent, th.track, th.warn, th.text, th.bg] {
14121 out.push(Value::Number(((c >> 16) & 0xFF) as f64));
14122 out.push(Value::Number(((c >> 8) & 0xFF) as f64));
14123 out.push(Value::Number((c & 0xFF) as f64));
14124 }
14125 return Ok(Value::List(Rc::new(out)));
14126 },
14127
14128 #[cfg(not(target_arch = "wasm32"))]
14130 "ui_radar"
14131 | "雷达"
14132 | "レーダー"
14133 | "레이더"
14134 | "เรดาร์"
14135 | "رادار_رابط"
14136 | "رادار_الواجهة"
14137 | "מכ״ם_ממשק"
14138 | "یو_آئی_ریڈار"
14139 | "radar_ui"
14140 | "ui_радар" => {
14141 let cx = self.arg_num(&args, 0, 0.)? as f32;
14142 let cy = self.arg_num(&args, 1, 0.)? as f32;
14143 let r = self.arg_num(&args, 2, 60.)? as f32;
14144 let sweep = self.arg_num(&args, 3, 0.)? as f32;
14145 let th = self.ui_theme;
14146 let prim = self.color_at(&args, 4, th.primary);
14147 self.draw_ui(&ling_ui::widgets::radar(
14148 cx, cy, r, sweep, prim, th.accent, th.track,
14149 ));
14150 return Ok(Value::Unit);
14151 },
14152 #[cfg(not(target_arch = "wasm32"))]
14153 "ui_compass"
14154 | "罗盘"
14155 | "コンパス"
14156 | "나침반"
14157 | "เข็มทิศ"
14158 | "قطبنمای_رابط"
14159 | "بوصلة_الواجهة"
14160 | "מצפן_ממשק"
14161 | "یو_آئی_قطب_نما"
14162 | "boussole_ui"
14163 | "ui_kompass"
14164 | "ui_компас" => {
14165 let x = self.arg_num(&args, 0, 0.)? as f32;
14166 let y = self.arg_num(&args, 1, 0.)? as f32;
14167 let w0 = self.arg_num(&args, 2, 300.)? as f32;
14168 let h0 = self.arg_num(&args, 3, 24.)? as f32;
14169 let head = self.arg_num(&args, 4, 0.)? as f32;
14170 let th = self.ui_theme;
14171 let prim = self.color_at(&args, 5, th.primary);
14172 self.draw_ui(&ling_ui::widgets::compass(
14173 x, y, w0, h0, head, prim, th.track,
14174 ));
14175 return Ok(Value::Unit);
14176 },
14177 #[cfg(not(target_arch = "wasm32"))]
14178 "ui_reticle"
14179 | "准星"
14180 | "照準"
14181 | "조준선"
14182 | "เป้าเล็ง"
14183 | "نشانه_رابط"
14184 | "علامة_تصويب"
14185 | "כוונת"
14186 | "نشانہ"
14187 | "réticule_ui"
14188 | "ui_fadenkreuz"
14189 | "ui_прицел" => {
14190 let cx = self.arg_num(&args, 0, 0.)? as f32;
14191 let cy = self.arg_num(&args, 1, 0.)? as f32;
14192 let r = self.arg_num(&args, 2, 30.)? as f32;
14193 let spread = self.arg_num(&args, 3, 0.)? as f32;
14194 let th = self.ui_theme;
14195 let prim = self.color_at(&args, 4, th.primary);
14196 self.draw_ui(&ling_ui::widgets::reticle(cx, cy, r, spread, prim));
14197 return Ok(Value::Unit);
14198 },
14199 #[cfg(not(target_arch = "wasm32"))]
14200 "ui_target"
14201 | "锁定框"
14202 | "ターゲット"
14203 | "표적"
14204 | "กรอบเป้า"
14205 | "قاب_هدف"
14206 | "إطار_الهدف"
14207 | "מסגרת_מטרה"
14208 | "ہدف_فریم"
14209 | "cible_ui"
14210 | "ui_ziel"
14211 | "ui_цель" => {
14212 let x = self.arg_num(&args, 0, 0.)? as f32;
14213 let y = self.arg_num(&args, 1, 0.)? as f32;
14214 let w0 = self.arg_num(&args, 2, 80.)? as f32;
14215 let h0 = self.arg_num(&args, 3, 80.)? as f32;
14216 let lock = self.arg_num(&args, 4, 0.)? as f32;
14217 let th = self.ui_theme;
14218 let prim = self.color_at(&args, 5, th.primary);
14219 self.draw_ui(&ling_ui::widgets::target(
14220 x, y, w0, h0, lock, prim, th.accent,
14221 ));
14222 return Ok(Value::Unit);
14223 },
14224 #[cfg(not(target_arch = "wasm32"))]
14225 "ui_panel"
14226 | "面板"
14227 | "パネル"
14228 | "패널"
14229 | "แผง"
14230 | "پنل_رابط"
14231 | "لوحة_الواجهة"
14232 | "לוח_ממשק"
14233 | "یو_آئی_پینل"
14234 | "panneau_ui"
14235 | "ui_feld"
14236 | "ui_панель" => {
14237 let x = self.arg_num(&args, 0, 0.)? as f32;
14238 let y = self.arg_num(&args, 1, 0.)? as f32;
14239 let w0 = self.arg_num(&args, 2, 200.)? as f32;
14240 let h0 = self.arg_num(&args, 3, 120.)? as f32;
14241 let bv = self.arg_num(&args, 4, 12.)? as f32;
14242 let th = self.ui_theme;
14243 let prim = self.color_at(&args, 5, th.primary);
14244 self.draw_ui(&ling_ui::widgets::panel(x, y, w0, h0, bv, prim, th.bg));
14245 return Ok(Value::Unit);
14246 },
14247 #[cfg(not(target_arch = "wasm32"))]
14248 "ui_scanlines"
14249 | "扫描线"
14250 | "走査線"
14251 | "스캔라인"
14252 | "เส้นสแกน"
14253 | "خطوط_اسکن"
14254 | "خطوط_المسح"
14255 | "קווי_סריקה"
14256 | "اسکین_لائنز"
14257 | "lignes_balayage_ui"
14258 | "ui_abtastzeilen"
14259 | "ui_линии_развёртки" => {
14260 let x = self.arg_num(&args, 0, 0.)? as f32;
14261 let y = self.arg_num(&args, 1, 0.)? as f32;
14262 let w0 = self.arg_num(&args, 2, 200.)? as f32;
14263 let h0 = self.arg_num(&args, 3, 120.)? as f32;
14264 let dens = self.arg_num(&args, 4, 24.)? as usize;
14265 let th = self.ui_theme;
14266 let line = self.color_at(&args, 5, th.track);
14267 self.draw_ui(&ling_ui::widgets::scanlines(x, y, w0, h0, dens, line));
14268 return Ok(Value::Unit);
14269 },
14270
14271 #[cfg(not(target_arch = "wasm32"))]
14273 "ui_bar"
14274 | "进度条"
14275 | "バー"
14276 | "막대"
14277 | "แถบ"
14278 | "نوار_رابط"
14279 | "شريط_الواجهة"
14280 | "סרגל_ממשק"
14281 | "یو_آئی_بار"
14282 | "barre_ui"
14283 | "ui_leiste"
14284 | "ui_полоса" => {
14285 let x = self.arg_num(&args, 0, 0.)? as f32;
14286 let y = self.arg_num(&args, 1, 0.)? as f32;
14287 let w0 = self.arg_num(&args, 2, 160.)? as f32;
14288 let h0 = self.arg_num(&args, 3, 16.)? as f32;
14289 let val = self.arg_num(&args, 4, 0.)? as f32;
14290 let max = self.arg_num(&args, 5, 1.)? as f32;
14291 let th = self.ui_theme;
14292 let fill = self.color_at(&args, 6, th.primary);
14293 self.draw_ui(&ling_ui::widgets::bar(
14294 x,
14295 y,
14296 w0,
14297 h0,
14298 val / max.max(1e-6),
14299 fill,
14300 th.track,
14301 ));
14302 return Ok(Value::Unit);
14303 },
14304 #[cfg(not(target_arch = "wasm32"))]
14305 "ui_segbar"
14306 | "分段条"
14307 | "分割バー"
14308 | "분할막대"
14309 | "แถบแบ่ง"
14310 | "نوار_قطعهای"
14311 | "شريط_مقسم"
14312 | "סרגל_מקוטע"
14313 | "سیگمنٹ_بار"
14314 | "barre_segmentée_ui"
14315 | "ui_segmentleiste"
14316 | "ui_сегментная_полоса" => {
14317 let x = self.arg_num(&args, 0, 0.)? as f32;
14318 let y = self.arg_num(&args, 1, 0.)? as f32;
14319 let w0 = self.arg_num(&args, 2, 160.)? as f32;
14320 let h0 = self.arg_num(&args, 3, 16.)? as f32;
14321 let val = self.arg_num(&args, 4, 0.)? as f32;
14322 let max = self.arg_num(&args, 5, 1.)? as f32;
14323 let segs = self.arg_num(&args, 6, 10.)? as usize;
14324 let th = self.ui_theme;
14325 let fill = self.color_at(&args, 7, th.primary);
14326 self.draw_ui(&ling_ui::widgets::segbar(
14327 x,
14328 y,
14329 w0,
14330 h0,
14331 val / max.max(1e-6),
14332 segs,
14333 fill,
14334 th.track,
14335 ));
14336 return Ok(Value::Unit);
14337 },
14338 #[cfg(not(target_arch = "wasm32"))]
14339 "ui_gauge"
14340 | "仪表"
14341 | "ゲージ"
14342 | "게이지"
14343 | "มาตรวัด"
14344 | "گیج_رابط"
14345 | "مقياس_الواجهة"
14346 | "מד_ממשק"
14347 | "یو_آئی_گیج"
14348 | "jauge_ui"
14349 | "ui_anzeige"
14350 | "ui_индикатор" => {
14351 let cx = self.arg_num(&args, 0, 0.)? as f32;
14352 let cy = self.arg_num(&args, 1, 0.)? as f32;
14353 let r = self.arg_num(&args, 2, 50.)? as f32;
14354 let val = self.arg_num(&args, 3, 0.)? as f32;
14355 let max = self.arg_num(&args, 4, 1.)? as f32;
14356 let th = self.ui_theme;
14357 let needle = self.color_at(&args, 5, th.warn);
14358 self.draw_ui(&ling_ui::widgets::gauge(
14359 cx,
14360 cy,
14361 r,
14362 val / max.max(1e-6),
14363 needle,
14364 th.accent,
14365 th.track,
14366 ));
14367 return Ok(Value::Unit);
14368 },
14369 #[cfg(not(target_arch = "wasm32"))]
14370 "ui_ring"
14371 | "环表"
14372 | "リングメーター"
14373 | "링미터"
14374 | "วงแหวนวัด"
14375 | "حلقه_گیج"
14376 | "حلقة_قياس"
14377 | "טבעת_מד"
14378 | "رنگ_گیج"
14379 | "anneau_ui"
14380 | "ui_кольцо" => {
14381 let cx = self.arg_num(&args, 0, 0.)? as f32;
14382 let cy = self.arg_num(&args, 1, 0.)? as f32;
14383 let r = self.arg_num(&args, 2, 40.)? as f32;
14384 let val = self.arg_num(&args, 3, 0.)? as f32;
14385 let max = self.arg_num(&args, 4, 1.)? as f32;
14386 let th = self.ui_theme;
14387 let fill = self.color_at(&args, 5, th.primary);
14388 self.draw_ui(&ling_ui::widgets::ring(
14389 cx,
14390 cy,
14391 r,
14392 val / max.max(1e-6),
14393 fill,
14394 th.track,
14395 ));
14396 return Ok(Value::Unit);
14397 },
14398 #[cfg(not(target_arch = "wasm32"))]
14399 "ui_vu"
14400 | "音量条"
14401 | "VUメーター"
14402 | "음량막대"
14403 | "มาตรเสียง"
14404 | "گیج_صدا"
14405 | "مقياس_مستوى_الصوت"
14406 | "מד_עוצמה"
14407 | "وی_یو_میٹر"
14408 | "vumètre_ui"
14409 | "ui_vumeter"
14410 | "ui_вю_метр" => {
14411 let x = self.arg_num(&args, 0, 0.)? as f32;
14412 let y = self.arg_num(&args, 1, 0.)? as f32;
14413 let w0 = self.arg_num(&args, 2, 160.)? as f32;
14414 let h0 = self.arg_num(&args, 3, 60.)? as f32;
14415 let levels = self.arg_list_f32(&args, 4);
14416 let th = self.ui_theme;
14417 let fill = self.color_at(&args, 5, th.primary);
14418 self.draw_ui(&ling_ui::widgets::vu(x, y, w0, h0, &levels, fill, th.warn));
14419 return Ok(Value::Unit);
14420 },
14421 #[cfg(not(target_arch = "wasm32"))]
14422 "ui_spark"
14423 | "迷你图"
14424 | "スパークライン"
14425 | "스파크라인"
14426 | "กราฟจิ๋ว"
14427 | "نمودار_ریز"
14428 | "رسم_مصغر"
14429 | "גרף_זעיר"
14430 | "اسپارک_لائن"
14431 | "mini_graphe_ui"
14432 | "ui_sparkline"
14433 | "ui_мини_график" => {
14434 let x = self.arg_num(&args, 0, 0.)? as f32;
14435 let y = self.arg_num(&args, 1, 0.)? as f32;
14436 let w0 = self.arg_num(&args, 2, 160.)? as f32;
14437 let h0 = self.arg_num(&args, 3, 40.)? as f32;
14438 let vals = self.arg_list_f32(&args, 4);
14439 let th = self.ui_theme;
14440 let line = self.color_at(&args, 5, th.accent);
14441 self.draw_ui(&ling_ui::widgets::spark(x, y, w0, h0, &vals, line));
14442 return Ok(Value::Unit);
14443 },
14444 #[cfg(not(target_arch = "wasm32"))]
14445 "ui_battery"
14446 | "电池"
14447 | "バッテリー"
14448 | "배터리"
14449 | "แบตเตอรี่"
14450 | "نشانگر_باتری"
14451 | "مؤشر_البطارية"
14452 | "מחוון_סוללה"
14453 | "بیٹری_انڈیکیٹر"
14454 | "batterie_ui"
14455 | "ui_batterie"
14456 | "ui_батарея" => {
14457 let x = self.arg_num(&args, 0, 0.)? as f32;
14458 let y = self.arg_num(&args, 1, 0.)? as f32;
14459 let w0 = self.arg_num(&args, 2, 50.)? as f32;
14460 let h0 = self.arg_num(&args, 3, 22.)? as f32;
14461 let val = self.arg_num(&args, 4, 1.)? as f32;
14462 let max = self.arg_num(&args, 5, 1.)? as f32;
14463 let th = self.ui_theme;
14464 let fill = self.color_at(&args, 6, th.accent);
14465 self.draw_ui(&ling_ui::widgets::battery(
14466 x,
14467 y,
14468 w0,
14469 h0,
14470 val / max.max(1e-6),
14471 fill,
14472 th.track,
14473 th.warn,
14474 ));
14475 return Ok(Value::Unit);
14476 },
14477
14478 #[cfg(not(target_arch = "wasm32"))]
14480 "ui_button"
14481 | "按钮"
14482 | "ボタン"
14483 | "버튼"
14484 | "ปุ่ม"
14485 | "دکمه_رابط"
14486 | "زر_الواجهة"
14487 | "כפתור_ממשק"
14488 | "یو_آئی_بٹن"
14489 | "bouton_ui"
14490 | "ui_knopf"
14491 | "ui_кнопка" => {
14492 let x = self.arg_num(&args, 0, 0.)? as f32;
14493 let y = self.arg_num(&args, 1, 0.)? as f32;
14494 let w0 = self.arg_num(&args, 2, 120.)? as f32;
14495 let h0 = self.arg_num(&args, 3, 40.)? as f32;
14496 let (mx, my, down) = self.mouse_now();
14497 let hover = ling_ui::holo::hit_rect(mx, my, x, y, w0, h0);
14498 let clicked = hover && down && !self.mouse_was_down;
14499 let th = self.ui_theme;
14500 let prim = self.color_at(&args, 4, th.primary);
14501 self.draw_ui(&ling_ui::widgets::button(
14502 x,
14503 y,
14504 w0,
14505 h0,
14506 hover,
14507 down && hover,
14508 prim,
14509 th.bg,
14510 ));
14511 return Ok(Value::Number(if clicked { 1.0 } else { 0.0 }));
14512 },
14513 #[cfg(not(target_arch = "wasm32"))]
14514 "ui_toggle"
14515 | "开关"
14516 | "トグル"
14517 | "토글"
14518 | "สวิตช์"
14519 | "کلید_ضامن"
14520 | "مفتاح_تبديل"
14521 | "מתג"
14522 | "ٹوگل"
14523 | "bascule_ui"
14524 | "ui_schalter"
14525 | "ui_переключатель" => {
14526 let x = self.arg_num(&args, 0, 0.)? as f32;
14527 let y = self.arg_num(&args, 1, 0.)? as f32;
14528 let w0 = self.arg_num(&args, 2, 52.)? as f32;
14529 let h0 = self.arg_num(&args, 3, 24.)? as f32;
14530 let mut state = self.arg_num(&args, 4, 0.)? > 0.5;
14531 let (mx, my, down) = self.mouse_now();
14532 let hover = ling_ui::holo::hit_rect(mx, my, x, y, w0, h0);
14533 if hover && down && !self.mouse_was_down {
14534 state = !state;
14535 }
14536 let th = self.ui_theme;
14537 let on = self.color_at(&args, 5, th.accent);
14538 self.draw_ui(&ling_ui::widgets::toggle(x, y, w0, h0, state, on, th.track));
14539 return Ok(Value::Number(if state { 1.0 } else { 0.0 }));
14540 },
14541 #[cfg(not(target_arch = "wasm32"))]
14542 "ui_slider"
14543 | "滑块"
14544 | "スライダー"
14545 | "슬라이더"
14546 | "แถบเลื่อน"
14547 | "لغزنده"
14548 | "شريط_انزلاق"
14549 | "מחוון_החלקה"
14550 | "سلائیڈر"
14551 | "curseur_ui"
14552 | "ui_schieberegler"
14553 | "ui_ползунок" => {
14554 let x = self.arg_num(&args, 0, 0.)? as f32;
14555 let y = self.arg_num(&args, 1, 0.)? as f32;
14556 let w0 = self.arg_num(&args, 2, 160.)? as f32;
14557 let mut val = self.arg_num(&args, 3, 0.)? as f32;
14558 let mn = self.arg_num(&args, 4, 0.)? as f32;
14559 let mx_ = self.arg_num(&args, 5, 1.)? as f32;
14560 let (mx, my, down) = self.mouse_now();
14561 let hover = ling_ui::holo::hit_rect(mx, my, x - 8.0, y - 10.0, w0 + 16.0, 20.0);
14562 if hover && down {
14563 let frac = ((mx - x) / w0).clamp(0.0, 1.0);
14564 val = mn + (mx_ - mn) * frac;
14565 }
14566 let frac = ((val - mn) / (mx_ - mn).abs().max(1e-6)).clamp(0.0, 1.0);
14567 let th = self.ui_theme;
14568 let fill = self.color_at(&args, 6, th.primary);
14569 self.draw_ui(&ling_ui::widgets::slider(
14570 x, y, w0, frac, hover, fill, th.track,
14571 ));
14572 return Ok(Value::Number(val as f64));
14573 },
14574 #[cfg(not(target_arch = "wasm32"))]
14575 "ui_checkbox"
14576 | "复选框"
14577 | "チェックボックス"
14578 | "체크박스"
14579 | "ช่องเลือก"
14580 | "جعبه_علامت"
14581 | "مربع_اختيار"
14582 | "תיבת_סימון"
14583 | "چیک_باکس"
14584 | "case_cocher_ui"
14585 | "ui_kontrollkästchen"
14586 | "ui_флажок" => {
14587 let x = self.arg_num(&args, 0, 0.)? as f32;
14588 let y = self.arg_num(&args, 1, 0.)? as f32;
14589 let s = self.arg_num(&args, 2, 20.)? as f32;
14590 let mut checked = self.arg_num(&args, 3, 0.)? > 0.5;
14591 let (mx, my, down) = self.mouse_now();
14592 let hover = ling_ui::holo::hit_rect(mx, my, x, y, s, s);
14593 if hover && down && !self.mouse_was_down {
14594 checked = !checked;
14595 }
14596 let th = self.ui_theme;
14597 let prim = self.color_at(&args, 4, th.primary);
14598 self.draw_ui(&ling_ui::widgets::checkbox(
14599 x, y, s, checked, hover, prim, th.track,
14600 ));
14601 return Ok(Value::Number(if checked { 1.0 } else { 0.0 }));
14602 },
14603 #[cfg(not(target_arch = "wasm32"))]
14604 "ui_tabs"
14605 | "标签页"
14606 | "タブ"
14607 | "탭"
14608 | "แท็บ"
14609 | "برگهها"
14610 | "ألسنة_الواجهة"
14611 | "לשוניות"
14612 | "ٹیبز"
14613 | "onglets_ui"
14614 | "ui_reiter"
14615 | "ui_вкладки" => {
14616 let x = self.arg_num(&args, 0, 0.)? as f32;
14617 let y = self.arg_num(&args, 1, 0.)? as f32;
14618 let w0 = self.arg_num(&args, 2, 240.)? as f32;
14619 let h0 = self.arg_num(&args, 3, 28.)? as f32;
14620 let count = self.arg_num(&args, 4, 3.)? as usize;
14621 let mut active = self.arg_num(&args, 5, 0.)? as i32;
14622 let (mx, my, down) = self.mouse_now();
14623 let mut hover = -1;
14624 if my >= y && my <= y + h0 && mx >= x && mx <= x + w0 && count > 0 {
14625 hover = (((mx - x) / (w0 / count as f32)) as i32)
14626 .max(0)
14627 .min(count as i32 - 1);
14628 if down && !self.mouse_was_down {
14629 active = hover;
14630 }
14631 }
14632 let th = self.ui_theme;
14633 let prim = self.color_at(&args, 6, th.primary);
14634 self.draw_ui(&ling_ui::widgets::tabs(
14635 x,
14636 y,
14637 w0,
14638 h0,
14639 count,
14640 active as usize,
14641 hover,
14642 prim,
14643 th.track,
14644 ));
14645 return Ok(Value::Number(active as f64));
14646 },
14647 #[cfg(not(target_arch = "wasm32"))]
14648 "ui_progress"
14649 | "进度"
14650 | "プログレス"
14651 | "진행바"
14652 | "ความคืบหน้า"
14653 | "نوار_پیشرفت"
14654 | "شريط_التقدم"
14655 | "פס_התקדמות"
14656 | "پیش_رفت_بار"
14657 | "progression_ui"
14658 | "ui_fortschritt"
14659 | "ui_прогресс" => {
14660 let x = self.arg_num(&args, 0, 0.)? as f32;
14661 let y = self.arg_num(&args, 1, 0.)? as f32;
14662 let w0 = self.arg_num(&args, 2, 200.)? as f32;
14663 let h0 = self.arg_num(&args, 3, 12.)? as f32;
14664 let frac = self.arg_num(&args, 4, 0.)? as f32;
14665 let th = self.ui_theme;
14666 let fill = self.color_at(&args, 5, th.accent);
14667 self.draw_ui(&ling_ui::widgets::progress(
14668 x, y, w0, h0, frac, fill, th.track,
14669 ));
14670 return Ok(Value::Unit);
14671 },
14672 #[cfg(not(target_arch = "wasm32"))]
14673 "ui_tooltip"
14674 | "提示框"
14675 | "ツールチップ"
14676 | "툴팁"
14677 | "คำแนะนำ"
14678 | "راهنمای_شناور"
14679 | "تلميح_الواجهة"
14680 | "חלונית_עזרה"
14681 | "ٹول_ٹپ"
14682 | "infobulle_ui"
14683 | "ui_подсказка" => {
14684 let x = self.arg_num(&args, 0, 0.)? as f32;
14685 let y = self.arg_num(&args, 1, 0.)? as f32;
14686 let w0 = self.arg_num(&args, 2, 120.)? as f32;
14687 let h0 = self.arg_num(&args, 3, 28.)? as f32;
14688 let th = self.ui_theme;
14689 let prim = self.color_at(&args, 4, th.primary);
14690 self.draw_ui(&ling_ui::widgets::tooltip(x, y, w0, h0, prim, th.bg));
14691 return Ok(Value::Unit);
14692 },
14693 #[cfg(not(target_arch = "wasm32"))]
14694 "ui_stepper"
14695 | "步进器"
14696 | "ステッパー"
14697 | "스테퍼"
14698 | "ตัวปรับค่า"
14699 | "پلهگر"
14700 | "زر_خطوات"
14701 | "בורר_מדורג"
14702 | "اسٹیپر"
14703 | "pas_à_pas_ui"
14704 | "ui_schrittsteuerung"
14705 | "ui_степпер" => {
14706 let x = self.arg_num(&args, 0, 0.)? as f32;
14707 let y = self.arg_num(&args, 1, 0.)? as f32;
14708 let w0 = self.arg_num(&args, 2, 120.)? as f32;
14709 let h0 = self.arg_num(&args, 3, 28.)? as f32;
14710 let mut val = self.arg_num(&args, 4, 0.)? as f32;
14711 let step = self.arg_num(&args, 5, 1.)? as f32;
14712 let (mx, my, down) = self.mouse_now();
14713 let hm = ling_ui::holo::hit_rect(mx, my, x, y, h0, h0);
14714 let hp = ling_ui::holo::hit_rect(mx, my, x + w0 - h0, y, h0, h0);
14715 if down && !self.mouse_was_down {
14716 if hm {
14717 val -= step;
14718 }
14719 if hp {
14720 val += step;
14721 }
14722 }
14723 let th = self.ui_theme;
14724 let prim = self.color_at(&args, 6, th.primary);
14725 self.draw_ui(&ling_ui::widgets::stepper(
14726 x, y, w0, h0, hm, hp, prim, th.track,
14727 ));
14728 return Ok(Value::Number(val as f64));
14729 },
14730
14731 #[cfg(not(target_arch = "wasm32"))]
14733 "ui_healthbar"
14734 | "血条"
14735 | "体力バー"
14736 | "체력바"
14737 | "แถบพลังชีวิต"
14738 | "نوار_سلامتی"
14739 | "شريط_الصحة"
14740 | "פס_בריאות"
14741 | "ہیلتھ_بار"
14742 | "barre_vie_ui"
14743 | "ui_lebensbalken"
14744 | "ui_полоса_здоровья" => {
14745 let x = self.arg_num(&args, 0, 0.)? as f32;
14746 let y = self.arg_num(&args, 1, 0.)? as f32;
14747 let w0 = self.arg_num(&args, 2, 180.)? as f32;
14748 let h0 = self.arg_num(&args, 3, 16.)? as f32;
14749 let val = self.arg_num(&args, 4, 1.)? as f32;
14750 let max = self.arg_num(&args, 5, 1.)? as f32;
14751 let pulse = self.arg_num(&args, 6, 0.)? as f32;
14752 let th = self.ui_theme;
14753 let full = self.color_at(&args, 7, th.accent);
14754 self.draw_ui(&ling_ui::widgets::healthbar(
14755 x,
14756 y,
14757 w0,
14758 h0,
14759 val / max.max(1e-6),
14760 pulse,
14761 full,
14762 th.warn,
14763 th.track,
14764 ));
14765 return Ok(Value::Unit);
14766 },
14767 #[cfg(not(target_arch = "wasm32"))]
14768 "ui_cooldown"
14769 | "冷却"
14770 | "クールダウン"
14771 | "쿨다운"
14772 | "คูลดาวน์"
14773 | "زمان_خنکسازی"
14774 | "مؤقت_التهدئة"
14775 | "זמן_קירור"
14776 | "کول_ڈاؤن"
14777 | "recharge_ui"
14778 | "ui_abklingzeit"
14779 | "ui_перезарядка" => {
14780 let cx = self.arg_num(&args, 0, 0.)? as f32;
14781 let cy = self.arg_num(&args, 1, 0.)? as f32;
14782 let r = self.arg_num(&args, 2, 28.)? as f32;
14783 let frac = self.arg_num(&args, 3, 0.)? as f32;
14784 let th = self.ui_theme;
14785 let fill = self.color_at(&args, 4, th.primary);
14786 self.draw_ui(&ling_ui::widgets::cooldown(cx, cy, r, frac, fill, th.track));
14787 return Ok(Value::Unit);
14788 },
14789 #[cfg(not(target_arch = "wasm32"))]
14790 "ui_counter"
14791 | "计数器"
14792 | "カウンター"
14793 | "카운터"
14794 | "ตัวนับ"
14795 | "شمارشگر"
14796 | "عداد_الواجهة"
14797 | "מונה_ממשק"
14798 | "کاؤنٹر"
14799 | "compteur_ui"
14800 | "ui_zähler"
14801 | "ui_счётчик" => {
14802 let x = self.arg_num(&args, 0, 0.)? as f32;
14803 let y = self.arg_num(&args, 1, 0.)? as f32;
14804 let dw = self.arg_num(&args, 2, 14.)? as f32;
14805 let dh = self.arg_num(&args, 3, 24.)? as f32;
14806 let val = self.arg_num(&args, 4, 0.)? as i64;
14807 let digits = self.arg_num(&args, 5, 4.)? as usize;
14808 let th = self.ui_theme;
14809 let on = self.color_at(&args, 6, th.primary);
14810 let off = ling_ui::widgets::shade(th.track, 0.5);
14811 self.draw_ui(&ling_ui::widgets::counter(
14812 x, y, dw, dh, val, digits, on, off,
14813 ));
14814 return Ok(Value::Unit);
14815 },
14816 #[cfg(not(target_arch = "wasm32"))]
14817 "ui_minimap"
14818 | "小地图"
14819 | "ミニマップ"
14820 | "미니맵"
14821 | "แผนที่ย่อ"
14822 | "نقشه_کوچک"
14823 | "خريطة_مصغرة"
14824 | "מפה_מוקטנת"
14825 | "منی_میپ"
14826 | "minicarte_ui"
14827 | "ui_minikarte"
14828 | "ui_миникарта" => {
14829 let x = self.arg_num(&args, 0, 0.)? as f32;
14830 let y = self.arg_num(&args, 1, 0.)? as f32;
14831 let w0 = self.arg_num(&args, 2, 140.)? as f32;
14832 let h0 = self.arg_num(&args, 3, 140.)? as f32;
14833 let th = self.ui_theme;
14834 let prim = self.color_at(&args, 4, th.primary);
14835 self.draw_ui(&ling_ui::widgets::minimap(x, y, w0, h0, prim, th.bg));
14836 return Ok(Value::Unit);
14837 },
14838 #[cfg(not(target_arch = "wasm32"))]
14839 "ui_dpad"
14840 | "方向键"
14841 | "方向パッド"
14842 | "방향패드"
14843 | "ปุ่มทิศทาง"
14844 | "دسته_جهتدار"
14845 | "لوحة_الاتجاهات"
14846 | "לוח_כיוונים"
14847 | "ڈی_پیڈ"
14848 | "croix_direction_ui"
14849 | "ui_steuerkreuz"
14850 | "ui_крестовина" => {
14851 let cx = self.arg_num(&args, 0, 0.)? as f32;
14852 let cy = self.arg_num(&args, 1, 0.)? as f32;
14853 let r = self.arg_num(&args, 2, 50.)? as f32;
14854 let (mx, my, down) = self.mouse_now();
14855 let mut dir = 0;
14856 if down {
14857 let (dx, dy) = (mx - cx, my - cy);
14858 if dx * dx + dy * dy <= r * r {
14859 if dx.abs() > dy.abs() {
14860 dir = if dx > 0.0 { 2 } else { 4 };
14861 } else {
14862 dir = if dy > 0.0 { 3 } else { 1 };
14863 }
14864 }
14865 }
14866 let th = self.ui_theme;
14867 let prim = self.color_at(&args, 3, th.primary);
14868 self.draw_ui(&ling_ui::widgets::dpad(cx, cy, r, dir, prim, th.track));
14869 return Ok(Value::Number(dir as f64));
14870 },
14871 #[cfg(not(target_arch = "wasm32"))]
14872 "ui_slotgrid"
14873 | "物品格"
14874 | "スロットグリッド"
14875 | "슬롯격자"
14876 | "ช่องไอเทม"
14877 | "شبکه_شیار"
14878 | "شبكة_الفتحات"
14879 | "רשת_חריצים"
14880 | "سلاٹ_گرڈ"
14881 | "grille_emplacements_ui"
14882 | "ui_slotraster"
14883 | "ui_сетка_слотов" => {
14884 let x = self.arg_num(&args, 0, 0.)? as f32;
14885 let y = self.arg_num(&args, 1, 0.)? as f32;
14886 let cols = self.arg_num(&args, 2, 4.)? as usize;
14887 let rows = self.arg_num(&args, 3, 1.)? as usize;
14888 let cell = self.arg_num(&args, 4, 36.)? as f32;
14889 let sel = self.arg_num(&args, 5, -1.)? as i32;
14890 let th = self.ui_theme;
14891 let prim = self.color_at(&args, 6, th.primary);
14892 self.draw_ui(&ling_ui::widgets::slotgrid(
14893 x, y, cols, rows, cell, sel, prim, th.track,
14894 ));
14895 return Ok(Value::Unit);
14896 },
14897 #[cfg(not(target_arch = "wasm32"))]
14898 "ui_vignette"
14899 | "暗角"
14900 | "ビネット"
14901 | "비네트"
14902 | "ขอบมืด"
14903 | "سایهگرد_کادر"
14904 | "تظليل_الحواف"
14905 | "הצללת_מסגרת"
14906 | "ویگنیٹ"
14907 | "vignette_ui"
14908 | "ui_виньетка" => {
14909 let intensity = self.arg_num(&args, 0, 0.5)? as f32;
14910 let (w, h) = {
14911 let g = self.gfx.borrow();
14912 (g.width as f32, g.height as f32)
14913 };
14914 let th = self.ui_theme;
14915 let col = self.color_at(&args, 1, th.warn);
14916 self.draw_ui(&ling_ui::widgets::vignette(w, h, intensity, col));
14917 return Ok(Value::Unit);
14918 },
14919
14920 #[cfg(not(target_arch = "wasm32"))]
14922 "ui_gauge3d"
14923 | "立体仪表"
14924 | "立体ゲージ"
14925 | "입체게이지"
14926 | "มาตรวัด3มิติ"
14927 | "گیج_سهبعدی"
14928 | "مقياس_ثلاثي_الأبعاد"
14929 | "מד_תלת_ממדי"
14930 | "تھری_ڈی_گیج"
14931 | "jauge_3d_ui"
14932 | "ui_anzeige_3d"
14933 | "ui_индикатор_3d" => {
14934 let cx = self.arg_num(&args, 0, 0.)? as f32;
14935 let cy = self.arg_num(&args, 1, 0.)? as f32;
14936 let r = self.arg_num(&args, 2, 50.)? as f32;
14937 let val = self.arg_num(&args, 3, 0.)? as f32;
14938 let max = self.arg_num(&args, 4, 1.)? as f32;
14939 let spin = self.arg_num(&args, 5, 0.)? as f32;
14940 let th = self.ui_theme;
14941 let fill = self.color_at(&args, 6, th.primary);
14942 self.draw_ui(&ling_ui::widgets::gauge3d(
14943 cx,
14944 cy,
14945 r,
14946 val / max.max(1e-6),
14947 spin,
14948 fill,
14949 th.track,
14950 ));
14951 return Ok(Value::Unit);
14952 },
14953 #[cfg(not(target_arch = "wasm32"))]
14954 "ui_panel3d"
14955 | "立体面板"
14956 | "立体パネル"
14957 | "입체패널"
14958 | "แผง3มิติ"
14959 | "پنل_سهبعدی"
14960 | "لوحة_ثلاثية_الأبعاد"
14961 | "לוח_תלת_ממדי"
14962 | "تھری_ڈی_پینل"
14963 | "panneau_3d_ui"
14964 | "ui_feld_3d"
14965 | "ui_панель_3d" => {
14966 let x = self.arg_num(&args, 0, 0.)? as f32;
14967 let y = self.arg_num(&args, 1, 0.)? as f32;
14968 let w0 = self.arg_num(&args, 2, 200.)? as f32;
14969 let h0 = self.arg_num(&args, 3, 120.)? as f32;
14970 let depth = self.arg_num(&args, 4, 14.)? as f32;
14971 let th = self.ui_theme;
14972 let prim = self.color_at(&args, 5, th.primary);
14973 self.draw_ui(&ling_ui::widgets::panel3d(x, y, w0, h0, depth, prim, th.bg));
14974 return Ok(Value::Unit);
14975 },
14976 #[cfg(not(target_arch = "wasm32"))]
14977 "ui_radar3d"
14978 | "立体雷达"
14979 | "立体レーダー"
14980 | "입체레이더"
14981 | "เรดาร์3มิติ"
14982 | "رادار_سهبعدی"
14983 | "رادار_ثلاثي_الأبعاد"
14984 | "מכ״ם_תלת_ממדי"
14985 | "تھری_ڈی_ریڈار"
14986 | "radar_3d_ui"
14987 | "ui_radar_3d"
14988 | "ui_радар_3d" => {
14989 let cx = self.arg_num(&args, 0, 0.)? as f32;
14990 let cy = self.arg_num(&args, 1, 0.)? as f32;
14991 let r = self.arg_num(&args, 2, 60.)? as f32;
14992 let tilt = self.arg_num(&args, 3, 0.9)? as f32;
14993 let sweep = self.arg_num(&args, 4, 0.)? as f32;
14994 let th = self.ui_theme;
14995 let prim = self.color_at(&args, 5, th.primary);
14996 self.draw_ui(&ling_ui::widgets::radar3d(
14997 cx, cy, r, tilt, sweep, prim, th.track,
14998 ));
14999 return Ok(Value::Unit);
15000 },
15001
15002 #[cfg(not(target_arch = "wasm32"))]
15004 "audio_blip"
15005 | "提示音"
15006 | "ビープ音"
15007 | "효과음"
15008 | "เสียงบี๊บ"
15009 | "بوق_کوتاه"
15010 | "نغمة_قصيرة"
15011 | "ביפ"
15012 | "بلپ_آواز"
15013 | "bip_audio"
15014 | "звук_бип" => {
15015 let freq = self.arg_num(&args, 0, 660.)? as f32;
15016 let dur = self.arg_num(&args, 1, 0.08)? as f32;
15017 let wave = Wave::from_name(&self.arg_str(&args, 2, "sine"));
15018 let amp = self.arg_num(&args, 3, 0.25)? as f32;
15019 if let Some(audio) = &self.audio {
15020 audio.blip(freq, amp, dur, wave);
15021 }
15022 return Ok(Value::Unit);
15023 },
15024 #[cfg(not(target_arch = "wasm32"))]
15025 "ui_sound"
15026 | "界面音"
15027 | "UI音"
15028 | "인터페이스음"
15029 | "เสียงปุ่ม"
15030 | "صدای_رابط"
15031 | "صوت_الواجهة"
15032 | "צליל_ממשק"
15033 | "یو_آئی_آواز"
15034 | "son_ui"
15035 | "ui_klang"
15036 | "ui_звук" => {
15037 let name = self.arg_str(&args, 0, "click");
15038 if let Some(audio) = &self.audio {
15039 match name.as_str() {
15040 "hover" => audio.blip(880.0, 0.10, 0.04, Wave::Sine),
15041 "confirm" => {
15042 audio.blip(660.0, 0.22, 0.07, Wave::Square);
15043 audio.blip(990.0, 0.18, 0.10, Wave::Square);
15044 },
15045 "error" => {
15046 audio.blip(180.0, 0.30, 0.16, Wave::Saw);
15047 audio.blip(140.0, 0.30, 0.18, Wave::Saw);
15048 },
15049 "toggle" => audio.blip(520.0, 0.22, 0.05, Wave::Triangle),
15050 "tick" => audio.blip(1500.0, 0.12, 0.02, Wave::Square),
15051 _ => audio.blip(720.0, 0.26, 0.05, Wave::Square), }
15053 }
15054 return Ok(Value::Unit);
15055 },
15056
15057 #[cfg(not(target_arch = "wasm32"))]
15065 "music_load"
15066 | "载入音乐"
15067 | "音楽読込"
15068 | "음악로드"
15069 | "โหลดเพลง"
15070 | "بارگذاری_موسیقی"
15071 | "تحميل_الموسيقى"
15072 | "טעינת_מוזיקה"
15073 | "موسیقی_لوڈ"
15074 | "charger_musique"
15075 | "musik_laden"
15076 | "загрузить_музыку" => {
15077 let path = self.arg_str(&args, 0, "");
15078 let resolved = if std::path::Path::new(&path).exists() {
15079 path.clone()
15080 } else if let Some(d) = &self.source_dir {
15081 d.join(&path).to_string_lossy().into_owned()
15082 } else {
15083 path.clone()
15084 };
15085 match ling_music::load(&resolved) {
15086 Ok(t) => {
15087 let id = self.tracks.len();
15088 self.tracks.push(t);
15089 return Ok(Value::Number(id as f64));
15090 },
15091 Err(e) => {
15092 eprintln!("music_load failed ({path}): {e}");
15093 return Ok(Value::Number(-1.0));
15094 },
15095 }
15096 },
15097 #[cfg(not(target_arch = "wasm32"))]
15098 "music_duration"
15099 | "音乐时长"
15100 | "音楽長さ"
15101 | "음악길이"
15102 | "ความยาวเพลง"
15103 | "مدت_موسیقی"
15104 | "مدة_الموسيقى"
15105 | "משך_מוזיקה"
15106 | "موسیقی_دورانیہ"
15107 | "durée_musique"
15108 | "musik_dauer"
15109 | "длительность_музыки" => {
15110 let id = self.arg_num(&args, 0, 0.0)? as i64;
15111 let d = self
15112 .tracks
15113 .get(id as usize)
15114 .map(|t| t.duration)
15115 .unwrap_or(0.0);
15116 return Ok(Value::Number(d as f64));
15117 },
15118 #[cfg(not(target_arch = "wasm32"))]
15119 "music_bpm"
15120 | "节拍速度"
15121 | "テンポ"
15122 | "템포"
15123 | "จังหวะต่อนาที"
15124 | "ضربان_در_دقیقه"
15125 | "نبضات_بالدقيقة"
15126 | "פעימות_לדקה"
15127 | "بی_پی_ایم"
15128 | "bpm_musique"
15129 | "musik_bpm"
15130 | "музыка_bpm" => {
15131 let id = self.arg_num(&args, 0, 0.0)? as i64;
15132 let b = self
15133 .tracks
15134 .get(id as usize)
15135 .map(|t| ling_music::analysis::bpm(&t.mono, t.rate))
15136 .unwrap_or(0.0);
15137 return Ok(Value::Number(b as f64));
15138 },
15139 #[cfg(not(target_arch = "wasm32"))]
15140 "music_key"
15141 | "调性"
15142 | "調性"
15143 | "조성"
15144 | "คีย์เพลง"
15145 | "گام_موسیقی"
15146 | "مقام_الموسيقى"
15147 | "סולם_מוזיקלי"
15148 | "موسیقی_کلید"
15149 | "tonalité_musique"
15150 | "musik_tonart"
15151 | "тональность_музыки" => {
15152 let id = self.arg_num(&args, 0, 0.0)? as i64;
15153 let k = self
15154 .tracks
15155 .get(id as usize)
15156 .map(|t| ling_music::analysis::key_name(&t.mono, t.rate))
15157 .unwrap_or_default();
15158 return Ok(Value::Str(k));
15159 },
15160 #[cfg(not(target_arch = "wasm32"))]
15161 "music_onsets"
15162 | "音符起点"
15163 | "オンセット"
15164 | "온셋"
15165 | "จุดเริ่มเสียง"
15166 | "آغازهای_نت"
15167 | "بدايات_النغمات"
15168 | "התחלות_תווים"
15169 | "نوٹ_شروعات"
15170 | "attaques_musique"
15171 | "musik_einsätze"
15172 | "атаки_музыки" => {
15173 let id = self.arg_num(&args, 0, 0.0)? as i64;
15174 let v = self
15175 .tracks
15176 .get(id as usize)
15177 .map(|t| ling_music::analysis::onsets(&t.mono, t.rate))
15178 .unwrap_or_default();
15179 return Ok(Value::List(Rc::new(
15180 v.into_iter().map(|x| Value::Number(x as f64)).collect(),
15181 )));
15182 },
15183 #[cfg(not(target_arch = "wasm32"))]
15184 "music_beat_grid"
15185 | "节拍网格"
15186 | "ビートグリッド"
15187 | "비트그리드"
15188 | "กริดจังหวะ"
15189 | "شبکه_ضرب"
15190 | "شبكة_الإيقاع"
15191 | "רשת_פעימות"
15192 | "بیٹ_گرڈ"
15193 | "grille_temps_musique"
15194 | "musik_taktraster"
15195 | "сетка_ритма_музыки" => {
15196 let id = self.arg_num(&args, 0, 0.0)? as i64;
15197 let beats = self
15198 .tracks
15199 .get(id as usize)
15200 .map(|t| {
15201 let b = ling_music::analysis::bpm(&t.mono, t.rate);
15202 ling_music::analysis::beat_grid(&t.mono, t.rate, b)
15203 })
15204 .unwrap_or_default();
15205 return Ok(Value::List(Rc::new(
15206 beats.into_iter().map(|x| Value::Number(x as f64)).collect(),
15207 )));
15208 },
15209
15210 #[cfg(not(target_arch = "wasm32"))]
15212 "music_play"
15213 | "播放音乐"
15214 | "音楽再生"
15215 | "음악재생"
15216 | "เล่นเพลง"
15217 | "پخش_موسیقی"
15218 | "شغّل_الموسيقى"
15219 | "נגן_מוזיקה"
15220 | "موسیقی_چلاؤ"
15221 | "jouer_musique"
15222 | "musik_abspielen"
15223 | "играть_музыку" => {
15224 let id = self.arg_num(&args, 0, 0.0)? as i64;
15225 if self.ensure_music() {
15226 let track = self
15227 .tracks
15228 .get(id as usize)
15229 .map(|t| (t.stereo.clone(), t.rate));
15230 if let (Some((st, rate)), Some(m)) = (track, &self.music) {
15231 m.set_track(st, rate);
15232 m.play();
15233 } else if let Some(m) = &self.music {
15234 m.play();
15235 }
15236 }
15237 return Ok(Value::Unit);
15238 },
15239 #[cfg(not(target_arch = "wasm32"))]
15240 "music_pause"
15241 | "暂停音乐"
15242 | "音楽一時停止"
15243 | "음악일시정지"
15244 | "หยุดเพลงชั่วคราว"
15245 | "مکث_موسیقی"
15246 | "ألبث_الموسيقى"
15247 | "השהה_מוזיקה"
15248 | "موسیقی_روکو_مؤقت"
15249 | "pause_musique"
15250 | "musik_pausieren"
15251 | "пауза_музыки" => {
15252 if let Some(m) = &self.music {
15253 m.pause();
15254 }
15255 return Ok(Value::Unit);
15256 },
15257 #[cfg(not(target_arch = "wasm32"))]
15258 "music_stop"
15259 | "停止音乐"
15260 | "音楽停止"
15261 | "음악정지"
15262 | "หยุดเพลง"
15263 | "توقف_موسیقی"
15264 | "أوقف_الموسيقى"
15265 | "עצור_מוזיקה"
15266 | "موسیقی_روکو"
15267 | "arrêter_musique"
15268 | "musik_stoppen"
15269 | "остановить_музыку" => {
15270 if let Some(m) = &self.music {
15271 m.stop();
15272 }
15273 return Ok(Value::Unit);
15274 },
15275 #[cfg(not(target_arch = "wasm32"))]
15276 "music_seek"
15277 | "定位音乐"
15278 | "音楽シーク"
15279 | "음악탐색"
15280 | "ค้นหาเพลง"
15281 | "جستجوی_موسیقی"
15282 | "ابحث_في_الموسيقى"
15283 | "חפש_במוזיקה"
15284 | "موسیقی_تلاش"
15285 | "chercher_musique"
15286 | "musik_suchen"
15287 | "перемотать_музыку" => {
15288 let sec = self.arg_num(&args, 0, 0.0)? as f32;
15289 if let Some(m) = &self.music {
15290 m.seek(sec);
15291 }
15292 return Ok(Value::Unit);
15293 },
15294 #[cfg(not(target_arch = "wasm32"))]
15295 "music_pos"
15296 | "音乐位置"
15297 | "音楽位置"
15298 | "음악위치"
15299 | "ตำแหน่งเพลง"
15300 | "موقعیت_موسیقی"
15301 | "موضع_الموسيقى"
15302 | "מיקום_מוזיקה"
15303 | "موسیقی_مقام"
15304 | "position_musique"
15305 | "musik_position"
15306 | "позиция_музыки" => {
15307 let p = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
15308 return Ok(Value::Number(p as f64));
15309 },
15310 #[cfg(not(target_arch = "wasm32"))]
15311 "music_volume"
15312 | "音乐音量"
15313 | "音楽音量"
15314 | "음악음량"
15315 | "ระดับเพลง"
15316 | "بلندی_موسیقی"
15317 | "مستوى_الموسيقى"
15318 | "עוצמת_מוזיקה"
15319 | "موسیقی_شدت"
15320 | "volume_musique"
15321 | "musik_lautstärke"
15322 | "громкость_музыки" => {
15323 let v = self.arg_num(&args, 0, 0.8)? as f32;
15324 if self.ensure_music() {
15325 if let Some(m) = &self.music {
15326 m.set_volume(v);
15327 }
15328 }
15329 return Ok(Value::Unit);
15330 },
15331
15332 #[cfg(not(target_arch = "wasm32"))]
15334 "music_patch"
15335 | "乐器音色"
15336 | "音色読込"
15337 | "악기패치"
15338 | "แพตช์เครื่องดนตรี"
15339 | "پچ_موسیقی"
15340 | "آلة_الموسيقى"
15341 | "תיקון_כלי_נגינה"
15342 | "میوزک_پیچ"
15343 | "patch_musique"
15344 | "musik_patch"
15345 | "патч_музыки" => {
15346 let path = self.arg_str(&args, 0, "");
15347 let resolved = if std::path::Path::new(&path).exists() {
15348 path.clone()
15349 } else if let Some(d) = &self.source_dir {
15350 d.join(&path).to_string_lossy().into_owned()
15351 } else {
15352 path.clone()
15353 };
15354 if !self.ensure_music() {
15355 return Ok(Value::Number(-1.0));
15356 }
15357 match ling_music::patch::from_path(&resolved) {
15358 Ok(p) => {
15359 let id = self.music.as_ref().unwrap().add_patch(p);
15360 return Ok(Value::Number(id as f64));
15361 },
15362 Err(e) => {
15363 eprintln!("music_patch failed ({path}): {e}");
15364 return Ok(Value::Number(-1.0));
15365 },
15366 }
15367 },
15368 #[cfg(not(target_arch = "wasm32"))]
15369 "music_note"
15370 | "弹音符"
15371 | "音符演奏"
15372 | "음표연주"
15373 | "เล่นโน้ต"
15374 | "نواختن_نت"
15375 | "عزف_نغمة"
15376 | "נגן_תו"
15377 | "نوٹ_بجاؤ"
15378 | "note_musique"
15379 | "musik_note"
15380 | "нота_музыки" => {
15381 let inst = self.arg_num(&args, 0, 0.0)? as usize;
15382 let midi = self.pitch_arg(&args, 1, 60);
15383 let dur = self.arg_num(&args, 2, 0.5)? as f32;
15384 let vel = self.arg_num(&args, 3, 0.9)? as f32;
15385 if self.ensure_music() {
15386 if let Some(m) = &self.music {
15387 m.note(inst, midi, vel, dur);
15388 }
15389 }
15390 return Ok(Value::Unit);
15391 },
15392 #[cfg(not(target_arch = "wasm32"))]
15393 "music_note_on"
15394 | "音符开始"
15395 | "音符オン"
15396 | "음표켜기"
15397 | "โน้ตเริ่ม"
15398 | "شروع_نت"
15399 | "بدء_النغمة"
15400 | "התחלת_תו"
15401 | "نوٹ_شروع"
15402 | "note_musique_on"
15403 | "musik_note_an"
15404 | "нота_музыки_вкл" => {
15405 let inst = self.arg_num(&args, 0, 0.0)? as usize;
15406 let midi = self.pitch_arg(&args, 1, 60);
15407 let vel = self.arg_num(&args, 2, 0.9)? as f32;
15408 if self.ensure_music() {
15409 if let Some(m) = &self.music {
15410 m.note_on(inst, midi, vel);
15411 }
15412 }
15413 return Ok(Value::Unit);
15414 },
15415 #[cfg(not(target_arch = "wasm32"))]
15416 "music_note_off"
15417 | "音符结束"
15418 | "音符オフ"
15419 | "음표끄기"
15420 | "โน้ตจบ"
15421 | "پایان_نت"
15422 | "إيقاف_النغمة"
15423 | "סיום_תו"
15424 | "نوٹ_ختم"
15425 | "note_musique_off"
15426 | "musik_note_aus"
15427 | "нота_музыки_выкл" => {
15428 let inst = self.arg_num(&args, 0, 0.0)? as usize;
15429 let midi = self.pitch_arg(&args, 1, 60);
15430 if let Some(m) = &self.music {
15431 m.note_off(inst, midi);
15432 }
15433 return Ok(Value::Unit);
15434 },
15435
15436 #[cfg(not(target_arch = "wasm32"))]
15438 "music_judge"
15439 | "判定"
15440 | "判定する"
15441 | "판정"
15442 | "ตัดสินจังหวะ"
15443 | "داوری_ضرب"
15444 | "حكم_الإيقاع"
15445 | "שיפוט_קצב"
15446 | "بیٹ_فیصلہ"
15447 | "juger_musique"
15448 | "musik_bewerten"
15449 | "оценить_музыку" => {
15450 let delta_ms = self.arg_num(&args, 0, 9999.0)? as f32;
15451 return Ok(Value::Number(
15452 ling_music::Grade::judge(delta_ms).index() as f64
15453 ));
15454 },
15455 #[cfg(not(target_arch = "wasm32"))]
15456 "music_grade_name"
15457 | "判定名"
15458 | "判定名称"
15459 | "판정이름"
15460 | "ชื่อการตัดสิน"
15461 | "نام_رتبه"
15462 | "اسم_التقييم"
15463 | "שם_דירוג"
15464 | "گریڈ_نام"
15465 | "nom_grade_musique"
15466 | "musik_bewertungsname"
15467 | "имя_оценки_музыки" => {
15468 let idx = self.arg_num(&args, 0, 4.0)? as i32;
15469 return Ok(Value::Str(
15470 ling_music::Grade::from_index(idx).name().to_string(),
15471 ));
15472 },
15473
15474 #[cfg(not(target_arch = "wasm32"))]
15476 "music_lrc"
15477 | "载入歌词"
15478 | "歌詞読込"
15479 | "가사로드"
15480 | "โหลดเนื้อเพลง"
15481 | "بارگذاری_متن_ترانه"
15482 | "تحميل_كلمات_الأغنية"
15483 | "טעינת_מילות_שיר"
15484 | "گیت_متن_لوڈ"
15485 | "lrc_musique"
15486 | "musik_lrc"
15487 | "lrc_музыки" => {
15488 let path = self.arg_str(&args, 0, "");
15489 let resolved = if std::path::Path::new(&path).exists() {
15490 path.clone()
15491 } else if let Some(d) = &self.source_dir {
15492 d.join(&path).to_string_lossy().into_owned()
15493 } else {
15494 path.clone()
15495 };
15496 match std::fs::read_to_string(&resolved) {
15497 Ok(text) => {
15498 let id = self.lyrics.len();
15499 self.lyrics.push(ling_music::Lyrics::parse(&text));
15500 return Ok(Value::Number(id as f64));
15501 },
15502 Err(e) => {
15503 eprintln!("music_lrc failed ({path}): {e}");
15504 return Ok(Value::Number(-1.0));
15505 },
15506 }
15507 },
15508 #[cfg(not(target_arch = "wasm32"))]
15509 "music_lyric"
15510 | "当前歌词"
15511 | "現在歌詞"
15512 | "현재가사"
15513 | "เนื้อเพลงปัจจุบัน"
15514 | "متن_ترانه_فعلی"
15515 | "كلمات_الأغنية_الحالية"
15516 | "מילות_שיר_נוכחיות"
15517 | "موجودہ_گیت_متن"
15518 | "paroles_musique"
15519 | "musik_liedtext"
15520 | "текст_песни" => {
15521 let id = self.arg_num(&args, 0, 0.0)? as i64;
15522 let t = self.arg_num(&args, 1, 0.0)? as f32;
15523 let line = self
15524 .lyrics
15525 .get(id as usize)
15526 .map(|l| l.line_at(t).to_string())
15527 .unwrap_or_default();
15528 return Ok(Value::Str(line));
15529 },
15530 #[cfg(not(target_arch = "wasm32"))]
15531 "music_mic_pitch"
15532 | "麦克风音高"
15533 | "マイク音程"
15534 | "마이크음정"
15535 | "ระดับเสียงไมค์"
15536 | "زیروبمی_میکروفون"
15537 | "طبقة_صوت_الميكروفون"
15538 | "גובה_צליל_מיקרופון"
15539 | "مائیکروفون_پچ"
15540 | "hauteur_micro_musique"
15541 | "musik_mikrofon_tonhöhe"
15542 | "высота_тона_микрофона" => {
15543 let hz = if let Some(mic) = self.mic.as_ref() {
15544 let s = mic.latest_samples();
15545 let rate = mic.sample_rate();
15546 ling_music::pitch::detect(&s, rate).unwrap_or(0.0)
15547 } else {
15548 0.0
15549 };
15550 return Ok(Value::Number(hz as f64));
15551 },
15552 #[cfg(not(target_arch = "wasm32"))]
15553 "music_note_name"
15554 | "音名"
15555 | "音名称"
15556 | "음이름"
15557 | "ชื่อโน้ต"
15558 | "نام_نت"
15559 | "اسم_النغمة"
15560 | "שם_תו"
15561 | "نوٹ_نام"
15562 | "nom_note_musique"
15563 | "musik_notenname"
15564 | "имя_ноты_музыки" => {
15565 let hz = self.arg_num(&args, 0, 0.0)? as f32;
15566 return Ok(Value::Str(ling_music::note::hz_to_name(hz)));
15567 },
15568 #[cfg(not(target_arch = "wasm32"))]
15569 "music_hz"
15570 | "音符频率"
15571 | "音符周波数"
15572 | "음표주파수"
15573 | "ความถี่โน้ต"
15574 | "فرکانس_نت"
15575 | "تردد_النغمة"
15576 | "תדר_תו"
15577 | "نوٹ_ہرٹز"
15578 | "hz_musique"
15579 | "musik_hz"
15580 | "музыка_гц" => {
15581 let midi = self.pitch_arg(&args, 0, 69);
15582 return Ok(Value::Number(
15583 ling_music::note::midi_to_hz(midi as f32) as f64
15584 ));
15585 },
15586 #[cfg(not(target_arch = "wasm32"))]
15587 "music_pitch_score"
15588 | "音准评分"
15589 | "音程スコア"
15590 | "음정점수"
15591 | "คะแนนเสียง"
15592 | "امتیاز_زیروبمی"
15593 | "درجة_طبقة_الصوت"
15594 | "ציון_גובה_צליל"
15595 | "پچ_اسکور"
15596 | "score_hauteur_musique"
15597 | "musik_tonhöhen_punktzahl"
15598 | "счёт_высоты_тона" => {
15599 let hz = self.arg_num(&args, 0, 0.0)? as f32;
15600 let target = self.arg_num(&args, 1, 0.0)? as f32;
15601 return Ok(Value::Number(
15602 ling_music::karaoke::pitch_score(hz, target) as f64
15603 ));
15604 },
15605
15606 #[cfg(not(target_arch = "wasm32"))]
15608 "music_midi_load"
15609 | "载入MIDI"
15610 | "MIDI読込"
15611 | "미디로드"
15612 | "โหลดมิดี"
15613 | "بارگذاری_MIDI"
15614 | "تحميل_MIDI"
15615 | "טעינת_MIDI"
15616 | "MIDI_لوڈ"
15617 | "charger_midi_musique"
15618 | "musik_midi_laden"
15619 | "загрузить_midi_музыки" => {
15620 let path = self.arg_str(&args, 0, "");
15621 let resolved = if std::path::Path::new(&path).exists() {
15622 path.clone()
15623 } else if let Some(d) = &self.source_dir {
15624 d.join(&path).to_string_lossy().into_owned()
15625 } else {
15626 path.clone()
15627 };
15628 match ling_music::midi::load(&resolved) {
15629 Ok(m) => {
15630 let id = self.midis.len();
15631 self.midis.push(m);
15632 return Ok(Value::Number(id as f64));
15633 },
15634 Err(e) => {
15635 eprintln!("music_midi_load failed ({path}): {e}");
15636 return Ok(Value::Number(-1.0));
15637 },
15638 }
15639 },
15640 #[cfg(not(target_arch = "wasm32"))]
15641 "music_midi_count"
15642 | "MIDI数量"
15643 | "MIDI数"
15644 | "미디수"
15645 | "จำนวนมิดี"
15646 | "تعداد_MIDI"
15647 | "عدد_MIDI"
15648 | "מספר_MIDI"
15649 | "MIDI_تعداد"
15650 | "nombre_midi_musique"
15651 | "musik_midi_anzahl"
15652 | "число_midi_музыки" => {
15653 let id = self.arg_num(&args, 0, 0.0)? as i64;
15654 let n = self
15655 .midis
15656 .get(id as usize)
15657 .map(|m| m.notes.len())
15658 .unwrap_or(0);
15659 return Ok(Value::Number(n as f64));
15660 },
15661 #[cfg(not(target_arch = "wasm32"))]
15663 "music_midi_notes"
15664 | "MIDI音符"
15665 | "MIDIノート"
15666 | "미디음표"
15667 | "โน้ตมิดี"
15668 | "نتهای_MIDI"
15669 | "نغمات_MIDI"
15670 | "תווי_MIDI"
15671 | "MIDI_نوٹس"
15672 | "notes_midi_musique"
15673 | "musik_midi_noten"
15674 | "ноты_midi_музыки" => {
15675 let id = self.arg_num(&args, 0, 0.0)? as i64;
15676 let mut out = Vec::new();
15677 if let Some(m) = self.midis.get(id as usize) {
15678 for n in &m.notes {
15679 out.push(Value::Number(n.time as f64));
15680 out.push(Value::Number(n.midi as f64));
15681 }
15682 }
15683 return Ok(Value::List(Rc::new(out)));
15684 },
15685 #[cfg(not(target_arch = "wasm32"))]
15687 "music_midi_bars"
15688 | "MIDI音条"
15689 | "MIDIバー"
15690 | "미디바"
15691 | "แท่งมิดี"
15692 | "میلههای_MIDI"
15693 | "أعمدة_MIDI"
15694 | "עמודות_MIDI"
15695 | "MIDI_بارز"
15696 | "mesures_midi_musique"
15697 | "musik_midi_takte"
15698 | "такты_midi_музыки" => {
15699 let id = self.arg_num(&args, 0, 0.0)? as i64;
15700 let mut out = Vec::new();
15701 if let Some(m) = self.midis.get(id as usize) {
15702 for n in &m.notes {
15703 out.push(Value::Number(n.time as f64));
15704 out.push(Value::Number(n.midi as f64));
15705 out.push(Value::Number(n.dur as f64));
15706 }
15707 }
15708 return Ok(Value::List(Rc::new(out)));
15709 },
15710
15711 #[cfg(not(target_arch = "wasm32"))]
15713 "music_fft"
15714 | "音乐频谱"
15715 | "音楽スペクトル"
15716 | "음악스펙트럼"
15717 | "สเปกตรัมเพลง"
15718 | "طیف_موسیقی"
15719 | "طيف_الموسيقى"
15720 | "ספקטרום_מוזיקה"
15721 | "میوزک_اسپیکٹرم"
15722 | "fft_musique"
15723 | "musik_fft"
15724 | "fft_музыки" => {
15725 let id = self.arg_num(&args, 0, 0.0)? as i64;
15726 let nbands = self.arg_num(&args, 1, 16.0)? as usize;
15727 let pos = self.music.as_ref().map(|m| m.position()).unwrap_or(0.0);
15728 if let Some(t) = self.tracks.get(id as usize) {
15729 let idx = (pos * t.rate as f32) as usize;
15730 let end = (idx + 2048).min(t.mono.len());
15731 if end > idx + 64 {
15732 self.fft.borrow_mut().push_samples(&t.mono[idx..end]);
15733 }
15734 }
15735 let bands = self.fft.borrow().freq_bands(nbands);
15736 return Ok(Value::List(Rc::new(
15737 bands.into_iter().map(|x| Value::Number(x as f64)).collect(),
15738 )));
15739 },
15740
15741 #[cfg(not(target_arch = "wasm32"))]
15743 "audio_stop_sfx"
15744 | "停止音效"
15745 | "効果音停止"
15746 | "효과음정지"
15747 | "หยุดเอฟเฟกต์ทั้งหมด"
15748 | "توقف_همه_جلوهها"
15749 | "أوقف_كل_المؤثرات"
15750 | "עצור_כל_האפקטים"
15751 | "تمام_ایفیکٹ_روکو" => {
15752 if let Some(a) = &self.audio {
15753 a.stop_all_sfx();
15754 }
15755 return Ok(Value::Unit);
15756 },
15757 #[cfg(not(target_arch = "wasm32"))]
15759 "audio_sfx"
15760 | "音效"
15761 | "空間効果音"
15762 | "공간효과음"
15763 | "เสียงเอฟเฟกต์"
15764 | "جلوه_صوتی"
15765 | "مؤثرات_صوتية"
15766 | "אפקט_קול"
15767 | "آواز_ایفیکٹ"
15768 | "effet_sonore"
15769 | "klangeffekt"
15770 | "звуковой_эффект" => {
15771 let x = self.arg_num(&args, 0, 0.0)? as f32;
15772 let y = self.arg_num(&args, 1, 0.0)? as f32;
15773 let z = self.arg_num(&args, 2, 0.0)? as f32;
15774 let w = self.arg_num(&args, 3, 1.0)? as f32;
15775 let freq = self.arg_num(&args, 4, 440.0)? as f32;
15776 let amp = self.arg_num(&args, 5, 0.3)? as f32;
15777 let dur = self.arg_num(&args, 6, 0.15)? as f32;
15778 let wave = Wave::from_name(&self.arg_str(&args, 7, "sine"));
15779 if let Some(a) = &self.audio {
15780 a.sfx(x, y, z, w, freq, amp, dur, wave);
15781 }
15782 return Ok(Value::Unit);
15783 },
15784 #[cfg(not(target_arch = "wasm32"))]
15789 "morph_note"
15790 | "โน้ตมอร์ฟ"
15791 | "变形音"
15792 | "モーフ音"
15793 | "모프음"
15794 | "نت_مورف"
15795 | "نغمة_متحولة"
15796 | "תו_מורף"
15797 | "مورف_نوٹ" => {
15798 let x = self.arg_num(&args, 0, 0.0)? as f32;
15799 let y = self.arg_num(&args, 1, 0.0)? as f32;
15800 let z = self.arg_num(&args, 2, 0.0)? as f32;
15801 let w = self.arg_num(&args, 3, 1.0)? as f32;
15802 let freq = self.arg_num(&args, 4, 220.0)? as f32;
15803 let amp = self.arg_num(&args, 5, 0.3)? as f32;
15804 let dur = self.arg_num(&args, 6, 0.6)? as f32;
15805 let material = self.arg_num(&args, 7, 0.0)?.clamp(0.0, 3.0) as u8;
15806 let morph = self.arg_num(&args, 8, 0.0)? as f32;
15807 if let Some(a) = &self.audio {
15808 a.morph_note(x, y, z, w, freq, amp, dur, material, morph);
15809 }
15810 return Ok(Value::Unit);
15811 },
15812 #[cfg(not(target_arch = "wasm32"))]
15814 "audio_sample_load"
15815 | "载入采样"
15816 | "サンプル読込"
15817 | "샘플로드"
15818 | "โหลดตัวอย่างเสียง"
15819 | "بارگذاری_نمونه_صدا"
15820 | "تحميل_عينة_صوتية"
15821 | "טעינת_דגימת_קול"
15822 | "آواز_نمونہ_لوڈ"
15823 | "charger_échantillon"
15824 | "sample_laden"
15825 | "загрузить_семпл" => {
15826 let path = self.arg_str(&args, 0, "");
15827 let resolved = if std::path::Path::new(&path).exists() {
15828 path.clone()
15829 } else if let Some(d) = &self.source_dir {
15830 d.join(&path).to_string_lossy().into_owned()
15831 } else {
15832 path.clone()
15833 };
15834 match ling_music::load(&resolved) {
15835 Ok(t) => {
15836 if let Some(a) = &self.audio {
15837 return Ok(Value::Number(a.add_sample(t.mono, t.rate) as f64));
15838 }
15839 return Ok(Value::Number(-1.0));
15840 },
15841 Err(e) => {
15842 eprintln!("audio_sample_load failed ({path}): {e}");
15843 return Ok(Value::Number(-1.0));
15844 },
15845 }
15846 },
15847 #[cfg(not(target_arch = "wasm32"))]
15848 "audio_sample_play"
15849 | "播放采样"
15850 | "サンプル再生"
15851 | "샘플재생"
15852 | "เล่นตัวอย่างเสียง"
15853 | "پخش_نمونه_صدا"
15854 | "تشغيل_عينة_صوتية"
15855 | "נגינת_דגימת_קול"
15856 | "آواز_نمونہ_چلاؤ"
15857 | "jouer_échantillon"
15858 | "sample_abspielen"
15859 | "играть_семпл" => {
15860 let id = self.arg_num(&args, 0, 0.0)? as usize;
15861 let x = self.arg_num(&args, 1, 0.0)? as f32;
15862 let y = self.arg_num(&args, 2, 0.0)? as f32;
15863 let z = self.arg_num(&args, 3, 0.0)? as f32;
15864 let w = self.arg_num(&args, 4, 1.0)? as f32;
15865 let vol = self.arg_num(&args, 5, 1.0)? as f32;
15866 let looping = self.arg_num(&args, 6, 0.0)? > 0.5;
15867 let v = self
15868 .audio
15869 .as_ref()
15870 .map(|a| a.play_sample(id, x, y, z, w, vol, looping))
15871 .unwrap_or(0);
15872 return Ok(Value::Number(v as f64));
15873 },
15874 #[cfg(not(target_arch = "wasm32"))]
15879 "audio_sample_play_flat"
15880 | "播放采样平"
15881 | "サンプル再生平"
15882 | "샘플재생평"
15883 | "เล่นตัวอย่างเสียงเรียบ" => {
15884 let id = self.arg_num(&args, 0, 0.0)? as usize;
15885 let vol = self.arg_num(&args, 1, 1.0)? as f32;
15886 let looping = self.arg_num(&args, 2, 0.0)? > 0.5;
15887 let v = self
15888 .audio
15889 .as_ref()
15890 .map(|a| a.play_sample_flat(id, vol, looping))
15891 .unwrap_or(0);
15892 return Ok(Value::Number(v as f64));
15893 },
15894 #[cfg(not(target_arch = "wasm32"))]
15895 "audio_sample_stop"
15896 | "停止采样"
15897 | "サンプル停止"
15898 | "샘플정지"
15899 | "หยุดตัวอย่างเสียง"
15900 | "توقف_نمونه_صدا"
15901 | "إيقاف_عينة_صوتية"
15902 | "עצירת_דגימת_קול"
15903 | "آواز_نمونہ_روکو"
15904 | "arrêter_échantillon"
15905 | "sample_stoppen"
15906 | "остановить_семпл" => {
15907 let v = self.arg_num(&args, 0, 0.0)? as u32;
15908 if let Some(a) = &self.audio {
15909 a.stop_sample(v);
15910 }
15911 return Ok(Value::Unit);
15912 },
15913 #[cfg(not(target_arch = "wasm32"))]
15918 "audio_sample_duration"
15919 | "采样时长"
15920 | "サンプル長さ"
15921 | "샘플길이"
15922 | "ความยาวตัวอย่างเสียง" => {
15923 let id = self.arg_num(&args, 0, 0.0)? as usize;
15924 let dur = self
15925 .audio
15926 .as_ref()
15927 .map(|a| a.sample_duration(id))
15928 .unwrap_or(0.0);
15929 return Ok(Value::Number(dur as f64));
15930 },
15931 #[cfg(not(target_arch = "wasm32"))]
15933 "audio_fx_delay"
15934 | "回声"
15935 | "ディレイ効果"
15936 | "딜레이"
15937 | "เสียงสะท้อน"
15938 | "افکت_تاخیر"
15939 | "صدى_تأخير"
15940 | "אפקט_עיכוב"
15941 | "تاخیر_ایفیکٹ"
15942 | "délai_audio"
15943 | "audio_verzögerung"
15944 | "звук_задержка" => {
15945 let time = self.arg_num(&args, 0, 0.3)? as f32;
15946 let fb = self.arg_num(&args, 1, 0.3)? as f32;
15947 let mix = self.arg_num(&args, 2, 0.3)? as f32;
15948 if let Some(a) = &self.audio {
15949 a.fx_delay(time, fb, mix);
15950 }
15951 return Ok(Value::Unit);
15952 },
15953 #[cfg(not(target_arch = "wasm32"))]
15954 "audio_fx_reverb"
15955 | "混响"
15956 | "リバーブ"
15957 | "리버브"
15958 | "เสียงก้อง"
15959 | "افکت_پژواک"
15960 | "صدى_ارتداد"
15961 | "אפקט_הדהוד"
15962 | "بازگشت_آواز_ایفیکٹ"
15963 | "réverbération_audio"
15964 | "audio_nachhall"
15965 | "звук_реверберация" => {
15966 let mix = self.arg_num(&args, 0, 0.3)? as f32;
15967 if let Some(a) = &self.audio {
15968 a.fx_reverb(mix);
15969 }
15970 return Ok(Value::Unit);
15971 },
15972 #[cfg(not(target_arch = "wasm32"))]
15973 "audio_fx_lowpass"
15974 | "低通滤波"
15975 | "ローパス"
15976 | "저역통과"
15977 | "กรองความถี่ต่ำ"
15978 | "فیلتر_پایینگذر"
15979 | "مرشح_تمرير_منخفض"
15980 | "מסנן_תדר_נמוך"
15981 | "لو_پاس_فلٹر"
15982 | "passe_bas_audio"
15983 | "audio_tiefpass"
15984 | "звук_фнч" => {
15985 let cutoff = self.arg_num(&args, 0, 1.0)? as f32;
15986 if let Some(a) = &self.audio {
15987 a.fx_lowpass(cutoff);
15988 }
15989 return Ok(Value::Unit);
15990 },
15991
15992 #[cfg(not(target_arch = "wasm32"))]
15999 "soft_ball"
16000 | "软球"
16001 | "ソフトボール"
16002 | "소프트볼"
16003 | "ลูกบอลนุ่ม"
16004 | "توپ_نرم"
16005 | "كرة_ناعمة"
16006 | "כדור_רך"
16007 | "نرم_گیند"
16008 | "balle_molle"
16009 | "weicher_ball"
16010 | "мягкий_шар" => {
16011 let x = self.arg_num(&args, 0, 0.)? as f32;
16012 let y = self.arg_num(&args, 1, 0.)? as f32;
16013 let z = self.arg_num(&args, 2, 0.)? as f32;
16014 let r = self.arg_num(&args, 3, 1.0)? as f32;
16015 let b = ling_physics::soft::SoftBody::sphere(
16016 ling_physics::Vec3::new(x, y, z),
16017 r,
16018 8,
16019 12,
16020 1.0,
16021 );
16022 let id = self.soft_bodies.len();
16023 self.soft_bodies.push(b);
16024 return Ok(Value::Number(id as f64));
16025 },
16026 #[cfg(not(target_arch = "wasm32"))]
16027 "soft_step"
16028 | "软体步进"
16029 | "ソフト更新"
16030 | "소프트스텝"
16031 | "ก้าวนุ่ม"
16032 | "گام_نرم_جسم"
16033 | "خطوة_ناعمة"
16034 | "צעד_רך"
16035 | "نرم_قدم"
16036 | "pas_mou"
16037 | "weicher_schritt"
16038 | "мягкий_шаг" => {
16039 let id = self.arg_num(&args, 0, 0.)? as usize;
16040 let dt = self.arg_num(&args, 1, 0.016)? as f32;
16041 let gy = self.arg_num(&args, 2, 15.0)? as f32;
16042 if let Some(b) = self.soft_bodies.get_mut(id) {
16043 b.integrate(dt, ling_physics::Vec3::new(0.0, gy, 0.0), 4);
16044 }
16045 return Ok(Value::Unit);
16046 },
16047 #[cfg(not(target_arch = "wasm32"))]
16048 "soft_bounce"
16049 | "软体落地"
16050 | "ソフト着地"
16051 | "소프트바운스"
16052 | "เด้งนุ่ม"
16053 | "جهش_نرم"
16054 | "ارتداد_ناعم"
16055 | "קפיצה_רכה"
16056 | "نرم_اچھال"
16057 | "rebond_mou"
16058 | "weicher_abprall"
16059 | "мягкий_отскок" => {
16060 let id = self.arg_num(&args, 0, 0.)? as usize;
16061 let fy = self.arg_num(&args, 1, 0.)? as f32;
16062 let rest = self.arg_num(&args, 2, 0.5)? as f32;
16063 if let Some(b) = self.soft_bodies.get_mut(id) {
16064 b.floor_collision(fy, rest);
16065 }
16066 return Ok(Value::Unit);
16067 },
16068 #[cfg(not(target_arch = "wasm32"))]
16069 "soft_contain"
16070 | "软体边界"
16071 | "ソフト箱"
16072 | "소프트경계"
16073 | "กล่องนุ่ม"
16074 | "محفظه_نرم"
16075 | "احتواء_ناعم"
16076 | "הכלה_רכה"
16077 | "نرم_احاطہ"
16078 | "contenir_mou"
16079 | "weiche_eindämmung"
16080 | "мягкое_сдерживание" => {
16081 let id = self.arg_num(&args, 0, 0.)? as usize;
16082 let nx = self.arg_num(&args, 1, -5.)? as f32;
16083 let ny = self.arg_num(&args, 2, -5.)? as f32;
16084 let nz = self.arg_num(&args, 3, -5.)? as f32;
16085 let mx = self.arg_num(&args, 4, 5.)? as f32;
16086 let my = self.arg_num(&args, 5, 5.)? as f32;
16087 let mz = self.arg_num(&args, 6, 5.)? as f32;
16088 let rest = self.arg_num(&args, 7, 0.6)? as f32;
16089 if let Some(b) = self.soft_bodies.get_mut(id) {
16090 b.contain(
16091 ling_physics::Vec3::new(nx, ny, nz),
16092 ling_physics::Vec3::new(mx, my, mz),
16093 rest,
16094 );
16095 }
16096 return Ok(Value::Unit);
16097 },
16098 #[cfg(not(target_arch = "wasm32"))]
16099 "soft_kick"
16100 | "软体踢"
16101 | "ソフト衝撃"
16102 | "소프트킥"
16103 | "เตะนุ่ม"
16104 | "ضربه_نرم"
16105 | "ركلة_ناعمة"
16106 | "בעיטה_רכה"
16107 | "نرم_ٹھوکر"
16108 | "coup_mou"
16109 | "weicher_stoß"
16110 | "мягкий_удар" => {
16111 let id = self.arg_num(&args, 0, 0.)? as usize;
16112 let dx = self.arg_num(&args, 1, 0.)? as f32;
16113 let dy = self.arg_num(&args, 2, 0.)? as f32;
16114 let dz = self.arg_num(&args, 3, 0.)? as f32;
16115 let s = self.arg_num(&args, 4, 0.1)? as f32;
16116 if let Some(b) = self.soft_bodies.get_mut(id) {
16117 b.kick(ling_physics::Vec3::new(dx, dy, dz), s);
16118 }
16119 return Ok(Value::Unit);
16120 },
16121 #[cfg(not(target_arch = "wasm32"))]
16124 "soft_spin"
16125 | "软体自旋"
16126 | "ソフト回転"
16127 | "소프트회전"
16128 | "หมุนนุ่ม"
16129 | "چرخش_نرم"
16130 | "دوران_ناعم"
16131 | "סיבוב_רך"
16132 | "نرم_گھماؤ"
16133 | "rotation_molle"
16134 | "weicher_spin"
16135 | "мягкое_вращение" => {
16136 let id = self.arg_num(&args, 0, 0.)? as usize;
16137 let ax = self.arg_num(&args, 1, 0.)? as f32;
16138 let ay = self.arg_num(&args, 2, 0.)? as f32;
16139 let az = self.arg_num(&args, 3, 0.)? as f32;
16140 let rate = self.arg_num(&args, 4, 0.1)? as f32;
16141 if let Some(b) = self.soft_bodies.get_mut(id) {
16142 b.spin(ling_physics::Vec3::new(ax, ay, az), rate);
16143 }
16144 return Ok(Value::Unit);
16145 },
16146 #[cfg(not(target_arch = "wasm32"))]
16147 "soft_deform"
16148 | "形变量"
16149 | "変形量"
16150 | "변형량"
16151 | "ความบิดเบี้ยว"
16152 | "تغییرشکل_نرم"
16153 | "تشوه_ناعم"
16154 | "עיוות_רך"
16155 | "نرم_بگاڑ"
16156 | "déformer_mou"
16157 | "weiches_verformen"
16158 | "мягкая_деформация" => {
16159 let id = self.arg_num(&args, 0, 0.)? as usize;
16160 let d = self
16161 .soft_bodies
16162 .get(id)
16163 .map(|b| b.deformation())
16164 .unwrap_or(0.0);
16165 return Ok(Value::Number(d as f64));
16166 },
16167 #[cfg(not(target_arch = "wasm32"))]
16170 "soft_angular_speed"
16171 | "软体角速"
16172 | "ソフト角速度"
16173 | "소프트각속도"
16174 | "ความเร็วเชิงมุมนุ่ม"
16175 | "سرعت_زاویهای_نرم"
16176 | "سرعة_زاوية_ناعمة"
16177 | "מהירות_זוויתית_רכה"
16178 | "نرم_زاویائی_رفتار"
16179 | "vitesse_angulaire_molle"
16180 | "weiche_winkelgeschwindigkeit"
16181 | "мягкая_угловая_скорость" => {
16182 let id = self.arg_num(&args, 0, 0.)? as usize;
16183 let w = self
16184 .soft_bodies
16185 .get(id)
16186 .map(|b| b.angular_speed())
16187 .unwrap_or(0.0);
16188 return Ok(Value::Number(w as f64));
16189 },
16190 #[cfg(not(target_arch = "wasm32"))]
16191 "soft_centroid"
16192 | "软体质心"
16193 | "ソフト重心"
16194 | "소프트중심"
16195 | "จุดศูนย์กลางนุ่ม"
16196 | "مرکز_جرم_نرم"
16197 | "مركز_ثقل_ناعم"
16198 | "מרכז_כובד_רך"
16199 | "نرم_مرکز_ثقل"
16200 | "centroïde_mou"
16201 | "weicher_schwerpunkt"
16202 | "мягкий_центроид" => {
16203 let id = self.arg_num(&args, 0, 0.)? as usize;
16204 let c = self
16205 .soft_bodies
16206 .get(id)
16207 .map(|b| b.centroid())
16208 .unwrap_or(ling_physics::Vec3::ZERO);
16209 return Ok(Value::List(Rc::new(vec![
16210 Value::Number(c.x as f64),
16211 Value::Number(c.y as f64),
16212 Value::Number(c.z as f64),
16213 ])));
16214 },
16215 #[cfg(not(target_arch = "wasm32"))]
16217 "soft_nodes"
16218 | "软体节点"
16219 | "ソフト節点"
16220 | "소프트노드"
16221 | "จุดนุ่ม"
16222 | "گرههای_نرم"
16223 | "عقد_ناعمة"
16224 | "צמתי_רך"
16225 | "نرم_نوڈز"
16226 | "nœuds_mous"
16227 | "weiche_knoten"
16228 | "мягкие_узлы" => {
16229 let id = self.arg_num(&args, 0, 0.)? as usize;
16230 let mut out = Vec::new();
16231 if let Some(b) = self.soft_bodies.get(id) {
16232 for n in &b.nodes {
16233 out.push(Value::Number(n.pos.x as f64));
16234 out.push(Value::Number(n.pos.y as f64));
16235 out.push(Value::Number(n.pos.z as f64));
16236 }
16237 }
16238 return Ok(Value::List(Rc::new(out)));
16239 },
16240
16241 #[cfg(not(target_arch = "wasm32"))]
16243 "rb_add"
16244 | "刚体添加"
16245 | "剛体追加"
16246 | "강체추가"
16247 | "เพิ่มวัตถุแข็ง"
16248 | "افزودن_جسم_صلب"
16249 | "أضف_جسما_صلبا"
16250 | "הוסף_גוף_קשיח"
16251 | "سخت_جسم_شامل_کرو"
16252 | "ajouter_corps_rigide"
16253 | "starrkörper_hinzufügen"
16254 | "добавить_твёрдое_тело" => {
16255 let x = self.arg_num(&args, 0, 0.)? as f32;
16256 let y = self.arg_num(&args, 1, 0.)? as f32;
16257 let z = self.arg_num(&args, 2, 0.)? as f32;
16258 let mass = self.arg_num(&args, 3, 1.0)? as f32;
16259 let mut b =
16260 ling_physics::rigid::RigidBody::new(ling_physics::Vec3::new(x, y, z), mass);
16261 b.restitution = 0.6;
16262 return Ok(Value::Number(self.rigid_world.add(b) as f64));
16263 },
16264 #[cfg(not(target_arch = "wasm32"))]
16265 "rb_torque"
16266 | "扭矩"
16267 | "トルク"
16268 | "토크"
16269 | "แรงบิด"
16270 | "گشتاور"
16271 | "عزم_دوران"
16272 | "מומנט"
16273 | "ٹارک"
16274 | "couple_corps_rigide"
16275 | "starrkörper_drehmoment"
16276 | "крутящий_момент_твёрдого_тела" => {
16277 let i = self.arg_num(&args, 0, 0.)? as usize;
16278 let tx = self.arg_num(&args, 1, 0.)? as f32;
16279 let ty = self.arg_num(&args, 2, 0.)? as f32;
16280 let tz = self.arg_num(&args, 3, 0.)? as f32;
16281 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
16282 b.apply_torque(ling_physics::Vec3::new(tx, ty, tz));
16283 }
16284 return Ok(Value::Unit);
16285 },
16286 #[cfg(not(target_arch = "wasm32"))]
16287 "rb_spin"
16288 | "自旋"
16289 | "スピン"
16290 | "스핀"
16291 | "หมุน"
16292 | "چرخش_جسم_صلب"
16293 | "دوران_جسم_صلب"
16294 | "סיבוב_גוף_קשיח"
16295 | "سخت_جسم_گھماؤ"
16296 | "spin_corps_rigide"
16297 | "starrkörper_spin"
16298 | "вращение_твёрдого_тела" => {
16299 let i = self.arg_num(&args, 0, 0.)? as usize;
16300 let wx = self.arg_num(&args, 1, 0.)? as f32;
16301 let wy = self.arg_num(&args, 2, 0.)? as f32;
16302 let wz = self.arg_num(&args, 3, 0.)? as f32;
16303 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
16304 b.apply_spin(ling_physics::Vec3::new(wx, wy, wz));
16305 }
16306 return Ok(Value::Unit);
16307 },
16308 #[cfg(not(target_arch = "wasm32"))]
16309 "rb_impulse"
16310 | "刚体冲量"
16311 | "剛体インパルス"
16312 | "강체충격"
16313 | "แรงดลแข็ง"
16314 | "ضربه_جسم_صلب"
16315 | "دفعة_جسم_صلب"
16316 | "דחף_גוף_קשיח"
16317 | "سخت_جسم_دھکا"
16318 | "impulsion_corps_rigide"
16319 | "starrkörper_impuls"
16320 | "импульс_твёрдого_тела" => {
16321 let i = self.arg_num(&args, 0, 0.)? as usize;
16322 let ix = self.arg_num(&args, 1, 0.)? as f32;
16323 let iy = self.arg_num(&args, 2, 0.)? as f32;
16324 let iz = self.arg_num(&args, 3, 0.)? as f32;
16325 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
16326 b.apply_impulse(ling_physics::Vec3::new(ix, iy, iz));
16327 }
16328 return Ok(Value::Unit);
16329 },
16330 #[cfg(not(target_arch = "wasm32"))]
16331 "rb_floor"
16332 | "刚体落地"
16333 | "剛体着地"
16334 | "강체바닥"
16335 | "พื้นแข็ง"
16336 | "کف_جسم_صلب"
16337 | "أرضية_جسم_صلب"
16338 | "רצפת_גוף_קשיח"
16339 | "سخت_جسم_فرش"
16340 | "sol_corps_rigide"
16341 | "starrkörper_boden"
16342 | "пол_твёрдого_тела" => {
16343 let i = self.arg_num(&args, 0, 0.)? as usize;
16344 let fy = self.arg_num(&args, 1, 0.)? as f32;
16345 let rest = self.arg_num(&args, 2, 0.6)? as f32;
16346 let fric = self.arg_num(&args, 3, 0.6)? as f32;
16347 if let Some(b) = self.rigid_world.bodies.get_mut(i) {
16348 b.bounce_floor(fy, rest, fric);
16349 }
16350 return Ok(Value::Unit);
16351 },
16352 #[cfg(not(target_arch = "wasm32"))]
16353 "rb_gravity"
16354 | "刚体重力"
16355 | "剛体重力"
16356 | "강체중력"
16357 | "แรงโน้มถ่วงแข็ง"
16358 | "گرانش_جسم_صلب"
16359 | "جاذبية_جسم_صلب"
16360 | "כבידת_גוף_קשיח"
16361 | "سخت_جسم_کشش_ثقل"
16362 | "gravité_corps_rigide"
16363 | "starrkörper_schwerkraft"
16364 | "гравитация_твёрдого_тела" => {
16365 let gx = self.arg_num(&args, 0, 0.)? as f32;
16366 let gy = self.arg_num(&args, 1, 9.81)? as f32;
16367 let gz = self.arg_num(&args, 2, 0.)? as f32;
16368 self.rigid_world.gravity = ling_physics::Vec3::new(gx, gy, gz);
16369 return Ok(Value::Unit);
16370 },
16371 #[cfg(not(target_arch = "wasm32"))]
16372 "rb_step"
16373 | "刚体步进"
16374 | "剛体更新"
16375 | "강체스텝"
16376 | "ก้าวแข็ง"
16377 | "گام_جسم_صلب"
16378 | "خطوة_جسم_صلب"
16379 | "צעד_גוף_קשיח"
16380 | "سخت_جسم_قدم"
16381 | "pas_corps_rigide"
16382 | "starrkörper_schritt"
16383 | "шаг_твёрдого_тела" => {
16384 let dt = self.arg_num(&args, 0, 0.016)? as f32;
16385 self.rigid_world.step(dt);
16386 return Ok(Value::Unit);
16387 },
16388 #[cfg(not(target_arch = "wasm32"))]
16389 "rb_pos"
16390 | "刚体位置"
16391 | "剛体位置"
16392 | "강체위치"
16393 | "ตำแหน่งแข็ง"
16394 | "موقعیت_جسم_صلب"
16395 | "موضع_جسم_صلب"
16396 | "מיקום_גוף_קשיח"
16397 | "سخت_جسم_مقام"
16398 | "position_corps_rigide"
16399 | "starrkörper_position"
16400 | "позиция_твёрдого_тела" => {
16401 let i = self.arg_num(&args, 0, 0.)? as usize;
16402 let p = self
16403 .rigid_world
16404 .bodies
16405 .get(i)
16406 .map(|b| b.pos)
16407 .unwrap_or(ling_physics::Vec3::ZERO);
16408 return Ok(Value::List(Rc::new(vec![
16409 Value::Number(p.x as f64),
16410 Value::Number(p.y as f64),
16411 Value::Number(p.z as f64),
16412 ])));
16413 },
16414 #[cfg(not(target_arch = "wasm32"))]
16415 "rb_rot"
16416 | "刚体旋转"
16417 | "剛体回転"
16418 | "강체회전"
16419 | "การหมุนแข็ง"
16420 | "چرخش_وضعية_جسم_صلب"
16421 | "دوران_وضعية_جسم_صلب"
16422 | "סיבוב_זווית_גוף_קשיח"
16423 | "سخت_جسم_گردش"
16424 | "rotation_corps_rigide"
16425 | "starrkörper_rotation"
16426 | "поворот_твёрдого_тела" => {
16427 let i = self.arg_num(&args, 0, 0.)? as usize;
16428 let q = self
16429 .rigid_world
16430 .bodies
16431 .get(i)
16432 .map(|b| b.orientation)
16433 .unwrap_or(ling_physics::Quat::IDENTITY);
16434 return Ok(Value::List(Rc::new(vec![
16435 Value::Number(q.x as f64),
16436 Value::Number(q.y as f64),
16437 Value::Number(q.z as f64),
16438 Value::Number(q.w as f64),
16439 ])));
16440 },
16441
16442 #[cfg(not(target_arch = "wasm32"))]
16444 "mesh_load"
16445 | "โหลดเมช"
16446 | "载入网格"
16447 | "メッシュ読込"
16448 | "메시로드"
16449 | "بارگذاری_مش"
16450 | "حمّل_شبكة"
16451 | "טען_מש"
16452 | "میش_لوڈ" => {
16453 let path = self.arg_str(&args, 0, "");
16454 let resolved = if std::path::Path::new(&path).exists() {
16455 path.clone()
16456 } else if let Some(d) = &self.source_dir {
16457 d.join(&path).to_string_lossy().into_owned()
16458 } else {
16459 path.clone()
16460 };
16461 let bytes = match std::fs::read(&resolved) {
16462 Ok(b) => b,
16463 Err(e) => {
16464 eprintln!("mesh_load failed ({path}): {e}");
16465 return Ok(Value::Number(-1.0));
16466 },
16467 };
16468 if bytes.len() < 16 || &bytes[0..4] != b"LMSH" {
16469 eprintln!("mesh_load: bad header ({path})");
16470 return Ok(Value::Number(-1.0));
16471 }
16472 let rd4 =
16473 |o: usize| -> [u8; 4] { [bytes[o], bytes[o + 1], bytes[o + 2], bytes[o + 3]] };
16474 let height = f32::from_le_bytes(rd4(8));
16475 let ntri = u32::from_le_bytes(rd4(12)) as usize;
16476 let need = 16usize.saturating_add(ntri.saturating_mul(9 * 4 + 3));
16477 if bytes.len() < need {
16478 eprintln!("mesh_load: truncated ({path})");
16479 return Ok(Value::Number(-1.0));
16480 }
16481 let mut pos = Vec::with_capacity(ntri * 3);
16482 let mut col = Vec::with_capacity(ntri);
16483 let mut off = 16usize;
16484 for _ in 0..ntri {
16485 for _k in 0..3 {
16486 let x = f32::from_le_bytes(rd4(off));
16487 let y = f32::from_le_bytes(rd4(off + 4));
16488 let z = f32::from_le_bytes(rd4(off + 8));
16489 off += 12;
16490 pos.push([x, y, z]);
16491 }
16492 col.push([bytes[off], bytes[off + 1], bytes[off + 2]]);
16493 off += 3;
16494 }
16495 eprintln!("mesh_load: {} ({} tris, h={:.2})", path, ntri, height);
16496 let id = self.meshes.len();
16497 self.meshes
16498 .push(crate::gfx::shapes::ColorMesh { pos, col, height });
16499 return Ok(Value::Number(id as f64));
16500 },
16501 #[cfg(target_arch = "wasm32")]
16502 "mesh_load"
16503 | "โหลดเมช"
16504 | "载入网格"
16505 | "メッシュ読込"
16506 | "메시로드"
16507 | "بارگذاری_مش"
16508 | "حمّل_شبكة"
16509 | "טען_רשת"
16510 | "میش_لوڈ" => {
16511 return Ok(Value::Number(-1.0));
16514 },
16515 #[cfg(not(target_arch = "wasm32"))]
16516 "mesh_draw"
16517 | "วาดเมชสี"
16518 | "绘制网格"
16519 | "メッシュ描画"
16520 | "메시그리기"
16521 | "رسم_مش_رنگی"
16522 | "ارسم_شبكة_ملونة"
16523 | "צייר_רשת_צבעונית"
16524 | "رنگین_میش_کھینچو" => {
16525 let id = self.arg_num(&args, 0, 0.)? as usize;
16527 let cx = self.arg_num(&args, 1, 0.)? as f32;
16528 let cy = self.arg_num(&args, 2, 0.)? as f32;
16529 let cz = self.arg_num(&args, 3, 0.)? as f32;
16530 let sc = self.arg_num(&args, 4, 1.)? as f32;
16531 let yaw = self.arg_num(&args, 5, 0.)? as f32;
16532 let sway = self.arg_num(&args, 6, 0.)? as f32;
16533 let arm = self.arg_num(&args, 7, 0.)? as f32;
16534 let lean = self.arg_num(&args, 8, 0.)? as f32;
16535 let leg = self.arg_num(&args, 9, 0.)? as f32;
16536 let tuck = self.arg_num(&args, 10, 0.)? as f32;
16537 if id < self.meshes.len() {
16538 let m = &self.meshes[id];
16539 let mut gfx = self.gfx.borrow_mut();
16540 gfx.draw_color_mesh(m, cx, cy, cz, sc, yaw, sway, arm, lean, leg, tuck);
16541 }
16542 return Ok(Value::Unit);
16543 },
16544 #[cfg(target_arch = "wasm32")]
16545 "mesh_draw"
16546 | "วาดเมชสี"
16547 | "绘制网格"
16548 | "メッシュ描画"
16549 | "메시그리기"
16550 | "رسم_مش_رنگی"
16551 | "ارسم_شبكة_ملونة"
16552 | "צייר_רשת_צבעונית"
16553 | "رنگین_میش_کھینچو" => {
16554 return Ok(Value::Unit);
16555 },
16556
16557 "liquid_new"
16559 | "新建液体"
16560 | "液体新規"
16561 | "액체생성"
16562 | "สร้างของเหลว"
16563 | "مایع_جدید"
16564 | "سائل_جديد"
16565 | "נוזל_חדש"
16566 | "نیا_مائع"
16567 | "nouveau_liquide"
16568 | "neue_flüssigkeit"
16569 | "новая_жидкость" => {
16570 let w = self.arg_num(&args, 0, 64.)? as usize;
16571 let h = self.arg_num(&args, 1, 64.)? as usize;
16572 let id = self.liquids.len();
16573 self.liquids
16574 .push(ling_physics::liquid::LiquidGrid::new(w, h));
16575 return Ok(Value::Number(id as f64));
16576 },
16577 "liquid_set_colors"
16578 | "液体颜色"
16579 | "液体配色"
16580 | "액체색상"
16581 | "สีของเหลว"
16582 | "تنظیم_رنگ_مایع"
16583 | "عيّن_ألوان_السائل"
16584 | "קבע_צבעי_נוזל"
16585 | "مائع_رنگ_مقرر_کرو" => {
16586 let id = self.arg_num(&args, 0, 0.)? as usize;
16587 let wr = self.arg_num(&args, 1, 40.)? as f32;
16588 let wg = self.arg_num(&args, 2, 110.)? as f32;
16589 let wb = self.arg_num(&args, 3, 235.)? as f32;
16590 let or_ = self.arg_num(&args, 4, 240.)? as f32;
16591 let og = self.arg_num(&args, 5, 175.)? as f32;
16592 let ob = self.arg_num(&args, 6, 45.)? as f32;
16593 if let Some(g) = self.liquids.get_mut(id) {
16594 g.set_colors(wr, wg, wb, or_, og, ob);
16595 }
16596 return Ok(Value::Unit);
16597 },
16598 "liquid_splat"
16599 | "液体注入"
16600 | "液体追加"
16601 | "액체분사"
16602 | "หยดของเหลว"
16603 | "پاشش_مایع"
16604 | "بقعة_سائل"
16605 | "התזת_נוזל"
16606 | "مائع_چھینٹا"
16607 | "éclaboussure_liquide"
16608 | "flüssigkeit_spritzer"
16609 | "брызги_жидкости" => {
16610 let id = self.arg_num(&args, 0, 0.)? as usize;
16611 let x = self.arg_num(&args, 1, 0.)? as f32;
16612 let y = self.arg_num(&args, 2, 0.)? as f32;
16613 let kind = self.arg_num(&args, 3, 0.)? as i32;
16614 let amt = self.arg_num(&args, 4, 1.0)? as f32;
16615 let rad = self.arg_num(&args, 5, 4.0)? as f32;
16616 if let Some(g) = self.liquids.get_mut(id) {
16617 g.splat(x, y, kind, amt, rad);
16618 }
16619 return Ok(Value::Unit);
16620 },
16621 "liquid_gravity"
16622 | "液体重力"
16623 | "液体重力ベクトル"
16624 | "액체중력"
16625 | "แรงโน้มถ่วงเหลว"
16626 | "گرانش_مایع"
16627 | "جاذبية_السائل"
16628 | "כבידת_נוזל"
16629 | "مائع_کشش_ثقل"
16630 | "gravité_liquide"
16631 | "flüssigkeit_schwerkraft"
16632 | "гравитация_жидкости" => {
16633 let id = self.arg_num(&args, 0, 0.)? as usize;
16634 let gx = self.arg_num(&args, 1, 0.)? as f32;
16635 let gy = self.arg_num(&args, 2, 60.)? as f32;
16636 if let Some(g) = self.liquids.get_mut(id) {
16637 g.set_gravity(gx, gy);
16638 }
16639 return Ok(Value::Unit);
16640 },
16641 "liquid_step"
16642 | "液体步进"
16643 | "液体更新"
16644 | "액체스텝"
16645 | "ก้าวของเหลว"
16646 | "گام_مایع"
16647 | "خطوة_السائل"
16648 | "צעד_נוזל"
16649 | "مائع_قدم"
16650 | "pas_liquide"
16651 | "flüssigkeit_schritt"
16652 | "шаг_жидкости" => {
16653 let id = self.arg_num(&args, 0, 0.)? as usize;
16654 let dt = self.arg_num(&args, 1, 0.016)? as f32;
16655 if let Some(g) = self.liquids.get_mut(id) {
16656 g.step(dt);
16657 }
16658 return Ok(Value::Unit);
16659 },
16660 "liquid_step_all"
16666 | "液体全步进"
16667 | "液体全更新"
16668 | "전체액체스텝"
16669 | "ก้าวของเหลวทั้งหมด"
16670 | "گام_همه_مایعات"
16671 | "خطوة_كل_السوائل"
16672 | "צעד_כל_הנוזלים"
16673 | "تمام_مائع_قدم" => {
16674 let dt = self.arg_num(&args, 0, 0.016)? as f32;
16675 ling_physics::liquid::step_all(&mut self.liquids, dt);
16676 return Ok(Value::Unit);
16677 },
16678 "liquid_rainbow"
16680 | "液体彩虹"
16681 | "液体虹"
16682 | "액체무지개"
16683 | "ของเหลวสายรุ้ง"
16684 | "مایع_رنگینکمان"
16685 | "سائل_قوس_قزح"
16686 | "נוזל_קשת"
16687 | "قوس_قزح_مائع"
16688 | "arc_en_ciel_liquide"
16689 | "flüssigkeit_regenbogen"
16690 | "радуга_жидкости" => {
16691 let id = self.arg_num(&args, 0, 0.)? as usize;
16692 let on = self.arg_num(&args, 1, 1.0)? > 0.5;
16693 if let Some(g) = self.liquids.get_mut(id) {
16694 g.rainbow = on;
16695 }
16696 return Ok(Value::Unit);
16697 },
16698 "liquid_mix"
16700 | "液体混合"
16701 | "液体混合度"
16702 | "액체혼합"
16703 | "การผสมของเหลว"
16704 | "ترکیب_مایع"
16705 | "مزج_سائل"
16706 | "ערבוב_נוזל"
16707 | "مائع_ملاؤ"
16708 | "mélanger_liquide"
16709 | "flüssigkeit_mischen"
16710 | "смешать_жидкость" => {
16711 let id = self.arg_num(&args, 0, 0.)? as usize;
16712 let m = self.liquids.get(id).map(|g| g.mix_amount()).unwrap_or(0.0);
16713 return Ok(Value::Number(m as f64));
16714 },
16715 #[cfg(not(target_arch = "wasm32"))]
16717 "liquid_draw"
16718 | "绘制液体"
16719 | "液体描画"
16720 | "액체그리기"
16721 | "วาดของเหลว"
16722 | "رسم_مایع"
16723 | "ارسم_سائلا"
16724 | "צייר_נוזל"
16725 | "مائع_کھینچو"
16726 | "dessiner_liquide"
16727 | "flüssigkeit_zeichnen"
16728 | "рисовать_жидкость" => {
16729 let id = self.arg_num(&args, 0, 0.)? as usize;
16730 let sx = self.arg_num(&args, 1, 0.)? as i32;
16731 let sy = self.arg_num(&args, 2, 0.)? as i32;
16732 let scale = (self.arg_num(&args, 3, 4.)? as i32).max(1);
16733 if id < self.liquids.len() {
16734 let (gw, gh) = {
16735 let g = &self.liquids[id];
16736 (g.w, g.h)
16737 };
16738 let mut gfx = self.gfx.borrow_mut();
16739 let (w, h) = (gfx.width as i32, gfx.height as i32);
16740 let g = &self.liquids[id];
16741 for cy in 0..gh {
16742 for cx in 0..gw {
16743 let col = g.sample_rgb(cx, cy);
16744 let bx = sx + cx as i32 * scale;
16745 let by = sy + cy as i32 * scale;
16746 for dy in 0..scale {
16747 for dx in 0..scale {
16748 let px = bx + dx;
16749 let py = by + dy;
16750 if px >= 0 && py >= 0 && px < w && py < h {
16751 gfx.buffer[(py * w + px) as usize] = col;
16752 }
16753 }
16754 }
16755 }
16756 }
16757 }
16758 return Ok(Value::Unit);
16759 },
16760 "liquid_draw_surface"
16763 | "液体贴面"
16764 | "液体曲面"
16765 | "액체곡면"
16766 | "ของเหลวบนพื้นผิว"
16767 | "رسم_سطح_مایع"
16768 | "ارسم_سطح_السائل"
16769 | "צייר_משטח_נוזל"
16770 | "مائع_سطح_کھینچو"
16771 | "dessiner_surface_liquide"
16772 | "flüssigkeit_oberfläche_zeichnen"
16773 | "рисовать_поверхность_жидкости" => {
16774 #[cfg(not(target_arch = "wasm32"))]
16775 {
16776 let id = self.arg_num(&args, 0, 0.)? as usize;
16777 let kind = self.arg_num(&args, 1, 1.)? as i32;
16778 let cx = self.arg_num(&args, 2, 0.)? as f32;
16779 let cy = self.arg_num(&args, 3, 0.)? as f32;
16780 let cz = self.arg_num(&args, 4, 0.)? as f32;
16781 let radius = self.arg_num(&args, 5, 2.0)? as f32;
16782 let height = self.arg_num(&args, 6, 3.0)? as f32;
16783 if id < self.liquids.len() {
16784 let (gw, gh) = {
16785 let g = &self.liquids[id];
16786 (g.w, g.h)
16787 };
16788 let mut gfx = self.gfx.borrow_mut();
16789 let (w, h, add) = (gfx.width, gfx.height, gfx.blend == 1);
16790 let cam = gfx.camera.clone();
16791 let near = -cam.zdist + 0.05;
16792 let g = &self.liquids[id];
16793 let tau = std::f32::consts::TAU;
16794 let pi = std::f32::consts::PI;
16795 let sp = |u: f32, v: f32| -> [f32; 3] {
16797 if kind == 0 {
16798 [
16799 cx + (u - 0.5) * 2.0 * radius,
16800 cy,
16801 cz + (v - 0.5) * 2.0 * radius,
16802 ]
16803 } else if kind == 2 {
16804 let th = u * tau;
16805 [
16806 cx + th.cos() * radius,
16807 cy + (v - 0.5) * height,
16808 cz + th.sin() * radius,
16809 ]
16810 } else if kind == 3 {
16811 let th = u * tau;
16812 let rr = radius * (1.0 - v);
16813 [
16814 cx + th.cos() * rr,
16815 cy + (v - 0.5) * height,
16816 cz + th.sin() * rr,
16817 ]
16818 } else if kind == 4 {
16819 let th = u * tau;
16820 let ph = v * pi * 0.5;
16821 [
16822 cx + ph.sin() * th.cos() * radius,
16823 cy - ph.cos() * radius,
16824 cz + ph.sin() * th.sin() * radius,
16825 ]
16826 } else {
16827 let th = u * tau;
16828 let ph = v * pi;
16829 [
16830 cx + ph.sin() * th.cos() * radius,
16831 cy + ph.cos() * radius,
16832 cz + ph.sin() * th.sin() * radius,
16833 ]
16834 }
16835 };
16836 let nrm = |u: f32, v: f32| -> [f32; 3] {
16837 if kind == 0 {
16838 [0.0, -1.0, 0.0]
16839 } else if kind == 2 {
16840 let th = u * tau;
16841 [th.cos(), 0.0, th.sin()]
16842 } else if kind == 3 {
16843 let th = u * tau;
16844 let s = (radius / height.max(0.01)).atan();
16845 [th.cos() * s.cos(), s.sin(), th.sin() * s.cos()]
16846 } else if kind == 4 {
16847 let th = u * tau;
16848 let ph = v * pi * 0.5;
16849 [ph.sin() * th.cos(), -ph.cos(), ph.sin() * th.sin()]
16850 } else {
16851 let th = u * tau;
16852 let ph = v * pi;
16853 [ph.sin() * th.cos(), ph.cos(), ph.sin() * th.sin()]
16854 }
16855 };
16856 let gwf = gw as f32;
16857 let ghf = gh as f32;
16858 let mut cyc = 0usize;
16859 while cyc < gh {
16860 let mut cxc = 0usize;
16861 while cxc < gw {
16862 let uc = (cxc as f32 + 0.5) / gwf;
16864 let vc = (cyc as f32 + 0.5) / ghf;
16865 let c = sp(uc, vc);
16866 let n = nrm(uc, vc);
16867 let dc = cam.depth(c[0], c[1], c[2]);
16868 if dc > near {
16869 let cull = kind != 0
16870 && cam.depth(
16871 c[0] + n[0] * 0.06,
16872 c[1] + n[1] * 0.06,
16873 c[2] + n[2] * 0.06,
16874 ) > dc;
16875 if !cull {
16876 let u0 = cxc as f32 / gwf;
16878 let u1 = (cxc + 1) as f32 / gwf;
16879 let v0 = cyc as f32 / ghf;
16880 let v1 = (cyc + 1) as f32 / ghf;
16881 let q = [sp(u0, v0), sp(u1, v0), sp(u1, v1), sp(u0, v1)];
16882 let mut poly: Vec<[f32; 2]> = Vec::with_capacity(5);
16883 let mut ok = true;
16884 for p in &q {
16885 if cam.depth(p[0], p[1], p[2]) <= near {
16886 ok = false;
16887 break;
16888 }
16889 let (sx, sy, _) = cam.project(p[0], p[1], p[2]);
16890 poly.push([sx, sy]);
16891 }
16892 if ok {
16893 let p0 = poly[0];
16894 poly.push(p0);
16895 let col = g.sample_rgb(cxc, cyc);
16896 crate::gfx::raster::fill_contours_aa(
16897 &mut gfx.buffer,
16898 w,
16899 h,
16900 col,
16901 add,
16902 std::slice::from_ref(&poly),
16903 );
16904 }
16905 }
16906 }
16907 cxc += 1;
16908 }
16909 cyc += 1;
16910 }
16911 }
16912 }
16913 #[cfg(target_arch = "wasm32")]
16914 {
16915 }
16918 return Ok(Value::Unit);
16919 },
16920 #[cfg(not(target_arch = "wasm32"))]
16923 "sparkle" | "闪光" | "きらめき" | "반짝임" | "ประกาย" | "درخشش" | "بريق" | "נצנוץ"
16924 | "چمک" | "scintillement" | "funkeln" | "искриться" => {
16925 let x = self.arg_num(&args, 0, 0.)? as f32;
16926 let y = self.arg_num(&args, 1, 0.)? as f32;
16927 let ww = self.arg_num(&args, 2, 200.)? as f32;
16928 let hh = self.arg_num(&args, 3, 200.)? as f32;
16929 let count = self.arg_num(&args, 4, 40.)? as i32;
16930 let t = self.arg_num(&args, 5, 0.)? as f32;
16931 let mut gfx = self.gfx.borrow_mut();
16932 let (w, h, add, color) = (gfx.width, gfx.height, gfx.blend == 1, gfx.color);
16933 let (cr, cg, cb) = (
16934 (color >> 16 & 0xFF) as f32,
16935 (color >> 8 & 0xFF) as f32,
16936 (color & 0xFF) as f32,
16937 );
16938 let mut n = 0i32;
16939 while n < count {
16940 let hsh = (n as u32).wrapping_mul(2654435761).wrapping_add(0x9E3779B9);
16941 let u = ((hsh >> 8) & 1023) as f32 / 1023.0;
16942 let v = ((hsh >> 18) & 1023) as f32 / 1023.0;
16943 let phase = (hsh & 255) as f32 / 255.0;
16944 let tw = (t * 3.0 + phase * std::f32::consts::TAU + n as f32).sin() * 0.5 + 0.5;
16945 let sz = 1.5 + tw * 5.0;
16946 let px = x + u * ww;
16947 let py = y + v * hh;
16948 let b = tw * tw; let col =
16950 (((cr * b) as u32) << 16) | (((cg * b) as u32) << 8) | ((cb * b) as u32);
16951 crate::gfx::raster::draw_line_aa(
16952 &mut gfx.buffer,
16953 w,
16954 h,
16955 col,
16956 add,
16957 px - sz,
16958 py,
16959 px + sz,
16960 py,
16961 );
16962 crate::gfx::raster::draw_line_aa(
16963 &mut gfx.buffer,
16964 w,
16965 h,
16966 col,
16967 add,
16968 px,
16969 py - sz,
16970 px,
16971 py + sz,
16972 );
16973 let d = sz * 0.55;
16974 crate::gfx::raster::draw_line_aa(
16975 &mut gfx.buffer,
16976 w,
16977 h,
16978 col,
16979 add,
16980 px - d,
16981 py - d,
16982 px + d,
16983 py + d,
16984 );
16985 crate::gfx::raster::draw_line_aa(
16986 &mut gfx.buffer,
16987 w,
16988 h,
16989 col,
16990 add,
16991 px - d,
16992 py + d,
16993 px + d,
16994 py - d,
16995 );
16996 n += 1;
16997 }
16998 return Ok(Value::Unit);
16999 },
17000
17001 #[cfg(not(target_arch = "wasm32"))]
17007 "dialog_show"
17008 | "对话显示"
17009 | "会話表示"
17010 | "대화표시"
17011 | "แสดงบทสนทนา"
17012 | "نمایش_گفتگو"
17013 | "اعرض_الحوار"
17014 | "הצג_דיאלוג"
17015 | "مکالمہ_دکھاؤ"
17016 | "afficher_dialogue"
17017 | "dialog_anzeigen"
17018 | "показать_диалог" => {
17019 let text = self.arg_str(&args, 0, "");
17020 let cps = self.arg_num(&args, 1, 32.0)? as f32;
17021 self.dialog = Some(ling_game::dialog::Dialog::new(&text, cps));
17022 return Ok(Value::Unit);
17023 },
17024 #[cfg(not(target_arch = "wasm32"))]
17025 "dialog_step"
17026 | "对话步进"
17027 | "会話更新"
17028 | "대화스텝"
17029 | "ก้าวบทสนทนา"
17030 | "گام_گفتگو"
17031 | "خطوة_الحوار"
17032 | "צעד_דיאלוג"
17033 | "مکالمہ_قدم"
17034 | "pas_dialogue"
17035 | "dialog_schritt"
17036 | "шаг_диалога" => {
17037 let dt = self.arg_num(&args, 0, 0.016)? as f32;
17038 if let Some(d) = self.dialog.as_mut() {
17039 d.update(dt);
17040 }
17041 return Ok(Value::Unit);
17042 },
17043 #[cfg(not(target_arch = "wasm32"))]
17044 "dialog_advance"
17045 | "对话推进"
17046 | "会話送り"
17047 | "대화진행"
17048 | "เลื่อนบทสนทนา"
17049 | "پیشروی_گفتگو"
17050 | "تقدّم_الحوار"
17051 | "קדם_דיאלוג"
17052 | "مکالمہ_آگے_بڑھاؤ"
17053 | "avancer_dialogue"
17054 | "dialog_weiter"
17055 | "продолжить_диалог" => {
17056 if let Some(d) = self.dialog.as_mut() {
17057 d.advance();
17058 }
17059 return Ok(Value::Unit);
17060 },
17061 #[cfg(not(target_arch = "wasm32"))]
17062 "dialog_active"
17063 | "对话激活"
17064 | "会話中"
17065 | "대화중"
17066 | "บทสนทนาทำงาน"
17067 | "گفتگو_فعال"
17068 | "الحوار_نشط"
17069 | "דיאלוג_פעיל"
17070 | "مکالمہ_فعال"
17071 | "dialogue_actif"
17072 | "dialog_aktiv"
17073 | "диалог_активен" => {
17074 let a = self
17075 .dialog
17076 .as_ref()
17077 .map(|d| !d.is_closed())
17078 .unwrap_or(false);
17079 return Ok(Value::Bool(a));
17080 },
17081 #[cfg(not(target_arch = "wasm32"))]
17082 "dialog_typing"
17083 | "对话打字"
17084 | "会話タイプ中"
17085 | "대화타이핑"
17086 | "กำลังพิมพ์บทสนทนา"
17087 | "گفتگو_در_حال_تایپ"
17088 | "الحوار_يكتب"
17089 | "דיאלוג_מקליד"
17090 | "مکالمہ_ٹائپنگ"
17091 | "dialogue_frappe"
17092 | "dialog_tippen"
17093 | "диалог_печатает" => {
17094 use ling_game::dialog::Dialog;
17095
17096 let a = self
17097 .dialog
17098 .as_ref()
17099 .map(|d: &Dialog| !d.is_closed() && d.is_typing())
17100 .unwrap_or(false);
17101 return Ok(Value::Bool(a));
17102 },
17103 #[cfg(not(target_arch = "wasm32"))]
17104 "dialog_close"
17105 | "对话关闭"
17106 | "会話閉じる"
17107 | "대화닫기"
17108 | "ปิดบทสนทนา"
17109 | "بستن_گفتگو"
17110 | "أغلق_الحوار"
17111 | "סגור_דיאלוג"
17112 | "مکالمہ_بند"
17113 | "fermer_dialogue"
17114 | "dialog_schließen"
17115 | "закрыть_диалог" => {
17116 self.dialog = None;
17117 return Ok(Value::Unit);
17118 },
17119 #[cfg(not(target_arch = "wasm32"))]
17121 "dialog_color"
17122 | "对话颜色"
17123 | "会話色"
17124 | "대화색"
17125 | "สีบทสนทนา"
17126 | "رنگ_گفتگو"
17127 | "لون_الحوار"
17128 | "צבע_דיאלוג"
17129 | "مکالمہ_رنگ"
17130 | "couleur_dialogue"
17131 | "dialog_farbe"
17132 | "цвет_диалога" => {
17133 let role = (self.arg_num(&args, 0, 0.0)? as usize).min(3);
17134 let r = self.arg_num(&args, 1, 255.0)? as u32 & 0xFF;
17135 let g = self.arg_num(&args, 2, 255.0)? as u32 & 0xFF;
17136 let b = self.arg_num(&args, 3, 255.0)? as u32 & 0xFF;
17137 self.dialog_colors[role] = (r << 16) | (g << 8) | b;
17138 return Ok(Value::Unit);
17139 },
17140 #[cfg(not(target_arch = "wasm32"))]
17142 "dialog_draw"
17143 | "对话绘制"
17144 | "会話描画"
17145 | "대화그리기"
17146 | "วาดบทสนทนา"
17147 | "رسم_گفتگو"
17148 | "ارسم_الحوار"
17149 | "צייר_דיאלוג"
17150 | "مکالمہ_کھینچو"
17151 | "dessiner_dialogue"
17152 | "dialog_zeichnen"
17153 | "рисовать_диалог" => {
17154 let x = self.arg_num(&args, 0, 40.0)? as f32;
17155 let y = self.arg_num(&args, 1, 0.0)? as f32;
17156 let ww = self.arg_num(&args, 2, 720.0)? as f32;
17157 let hh = self.arg_num(&args, 3, 150.0)? as f32;
17158 let font = self.arg_num(&args, 4, -1.0)? as i64;
17159 let t = (crate::runtime::now_secs() - self.start_time_secs) as f32;
17160 self.render_dialog(x, y, ww, hh, font, t);
17161 return Ok(Value::Unit);
17162 },
17163
17164 #[cfg(not(target_arch = "wasm32"))]
17169 "text_poll" => {
17170 const TEXT_BUFFER_MAX: usize = 240;
17171 #[cfg(windows)]
17180 {
17181 let topmost = self.gfx.borrow().topmost_window;
17182 if topmost {
17183 if !window_is_foreground(self.gfx.borrow().hwnd) {
17184 return Ok(Value::Str(self.text_buffer.clone()));
17185 }
17186 let shift = os_key_down(VK_SHIFT);
17187 let now = crate::runtime::now_secs();
17188 let mut gfx = self.gfx.borrow_mut();
17189 let back_idx = VK_BACK as usize;
17190 let back_down = os_key_down(VK_BACK);
17191 let back_was = gfx.raw_keys_prev[back_idx];
17192 let (mut back_since, mut back_fire) = (
17193 gfx.raw_keys_down_since[back_idx],
17194 gfx.raw_keys_last_fire[back_idx],
17195 );
17196 if key_repeat_fire(
17197 now,
17198 back_down,
17199 back_was,
17200 &mut back_since,
17201 &mut back_fire,
17202 ) {
17203 self.text_buffer.pop();
17204 }
17205 gfx.raw_keys_down_since[back_idx] = back_since;
17206 gfx.raw_keys_last_fire[back_idx] = back_fire;
17207 gfx.raw_keys_prev[back_idx] = back_down;
17208 for &vk in TEXT_POLL_VKS {
17209 let idx = (vk as usize) & 0xFF;
17210 let down = os_key_down(vk);
17211 let was = gfx.raw_keys_prev[idx];
17212 let (mut since, mut fire) =
17213 (gfx.raw_keys_down_since[idx], gfx.raw_keys_last_fire[idx]);
17214 if key_repeat_fire(now, down, was, &mut since, &mut fire) {
17215 if let Some(c) = vk_char(vk, shift) {
17216 if self.text_buffer.chars().count() < TEXT_BUFFER_MAX {
17217 self.text_buffer.push(c);
17218 }
17219 }
17220 }
17221 gfx.raw_keys_down_since[idx] = since;
17222 gfx.raw_keys_last_fire[idx] = fire;
17223 gfx.raw_keys_prev[idx] = down;
17224 }
17225 return Ok(Value::Str(self.text_buffer.clone()));
17226 }
17227 }
17228 let (keys, shift) = {
17229 let gfx = self.gfx.borrow();
17230 match gfx.window.as_ref() {
17231 Some(w) => (
17232 w.get_keys_pressed(minifb::KeyRepeat::Yes),
17233 w.is_key_down(minifb::Key::LeftShift)
17234 || w.is_key_down(minifb::Key::RightShift),
17235 ),
17236 None => (Vec::new(), false),
17237 }
17238 };
17239 for k in keys {
17240 if k == minifb::Key::Backspace {
17241 self.text_buffer.pop();
17242 } else if let Some(c) = key_char(k, shift) {
17243 if self.text_buffer.chars().count() < TEXT_BUFFER_MAX {
17244 self.text_buffer.push(c);
17245 }
17246 }
17247 }
17248 return Ok(Value::Str(self.text_buffer.clone()));
17249 },
17250 #[cfg(target_arch = "wasm32")]
17251 "text_poll" => {
17252 return Ok(Value::Str(self.text_buffer.clone()));
17253 },
17254 "text_get" => return Ok(Value::Str(self.text_buffer.clone())),
17255 "text_set" => {
17256 self.text_buffer = self.arg_str(&args, 0, "");
17257 return Ok(Value::Unit);
17258 },
17259 "text_clear" => {
17260 self.text_buffer.clear();
17261 return Ok(Value::Unit);
17262 },
17263 #[cfg(not(target_arch = "wasm32"))]
17265 "record_frame" => {
17266 let n = self.record_n;
17267 let (buf, w, h) = {
17268 let gfx = self.gfx.borrow();
17269 (gfx.buffer.clone(), gfx.width, gfx.height)
17270 };
17271 let _ = std::fs::create_dir_all("recordings");
17272 let mut out = Vec::with_capacity(w * h * 3 + 32);
17273 out.extend_from_slice(format!("P6\n{w} {h}\n255\n").as_bytes());
17274 for px in &buf {
17275 let p = *px;
17276 out.push((p >> 16) as u8);
17277 out.push((p >> 8) as u8);
17278 out.push(p as u8);
17279 }
17280 let _ = std::fs::write(format!("recordings/frame_{n:05}.ppm"), out);
17281 self.record_n += 1;
17282 return Ok(Value::Number(n as f64));
17283 },
17284 "record_count" => return Ok(Value::Number(self.record_n as f64)),
17285 #[cfg(not(target_arch = "wasm32"))]
17287 "screenshot" | "บันทึกภาพ" | "عکسصفحه" | "لقطة_شاشة" | "צילום_מסך" | "اسکرین_شاٹ" =>
17288 {
17289 let mode = self.arg_str(&args, 0, "game");
17290 let (buf, w, h) = {
17291 let gfx = self.gfx.borrow();
17292 (gfx.buffer.clone(), gfx.width, gfx.height)
17293 };
17294 let _ = std::fs::create_dir_all("screenshots");
17295 let ts = std::time::SystemTime::now()
17296 .duration_since(std::time::UNIX_EPOCH)
17297 .map(|d| d.as_secs())
17298 .unwrap_or(0);
17299 let safe: String = mode
17300 .chars()
17301 .map(|c| if c.is_alphanumeric() { c } else { '_' })
17302 .collect();
17303 let path = format!("screenshots/ss_{ts}_{safe}_{w}x{h}.png");
17304 let mut rgb = Vec::with_capacity(w * h * 3);
17305 for px in &buf {
17306 let p = *px;
17307 rgb.push((p >> 16) as u8);
17308 rgb.push((p >> 8) as u8);
17309 rgb.push(p as u8);
17310 }
17311 if let Some(img) = image::RgbImage::from_raw(w as u32, h as u32, rgb) {
17312 let _ = img.save(&path);
17313 }
17314 return Ok(Value::Str(path));
17315 },
17316 #[cfg(not(target_arch = "wasm32"))]
17320 "mic_capture" => {
17321 if let Some(mic) = self.mic.as_ref() {
17322 let s = mic.latest_samples();
17323 self.mic_buffer.extend_from_slice(&s);
17324 let cap = 96_000usize; if self.mic_buffer.len() > cap {
17326 let drop = self.mic_buffer.len() - cap;
17327 self.mic_buffer.drain(0..drop);
17328 }
17329 }
17330 return Ok(Value::Number(self.mic_buffer.len() as f64));
17331 },
17332 #[cfg(not(target_arch = "wasm32"))]
17334 "mic_seed" => {
17335 let mut bytes = Vec::with_capacity(self.mic_buffer.len() * 4);
17336 for f in &self.mic_buffer {
17337 bytes.extend_from_slice(&f.to_le_bytes());
17338 }
17339 return Ok(Value::Str(hex_encode(&ling_crypto::geo::holo_hash(&bytes))));
17340 },
17341 #[cfg(not(target_arch = "wasm32"))]
17342 "mic_clear" => {
17343 self.mic_buffer.clear();
17344 return Ok(Value::Number(0.0));
17345 },
17346 #[cfg(not(target_arch = "wasm32"))]
17349 "flush_3d" | "render_3d" => {
17350 let mut gfx = self.gfx.borrow_mut();
17351 if !gfx.depth_queue.is_empty() {
17352 let w = gfx.width;
17353 let h = gfx.height;
17354 let dt = gfx.depth_test;
17355 let reset_z = gfx.zbuf_needs_clear;
17356 let (bm, ba) = (gfx.blend, gfx.alpha);
17357 let aa = gfx.antialias;
17358 let queue = std::mem::take(&mut gfx.depth_queue);
17359 {
17360 let g = &mut *gfx;
17361 let z = if dt { Some(&mut g.depth_buf) } else { None };
17362 queue.flush(&mut g.buffer, z, reset_z, w, h, aa);
17363 }
17364 gfx.zbuf_needs_clear = false;
17365 gfx.depth_queue.set_state(bm, ba); }
17367 return Ok(Value::Unit);
17368 },
17369 #[cfg(target_arch = "wasm32")]
17370 "flush_3d" | "render_3d" => {
17371 let mut gfx = self.gfx.borrow_mut();
17372 if !gfx.depth_queue.is_empty() {
17373 let w = gfx.width;
17374 let h = gfx.height;
17375 let dt = gfx.depth_test;
17376 let reset_z = gfx.zbuf_needs_clear;
17377 let (bm, ba) = (gfx.blend, gfx.alpha);
17378 let aa = gfx.antialias;
17379 let queue = std::mem::take(&mut gfx.depth_queue);
17380 {
17381 let g = &mut *gfx;
17382 let z = if dt { Some(&mut g.depth_buf) } else { None };
17383 queue.flush(&mut g.buffer, z, reset_z, w, h, aa);
17384 }
17385 gfx.zbuf_needs_clear = false;
17386 gfx.depth_queue.set_state(bm, ba);
17387 }
17388 return Ok(Value::Unit);
17389 },
17390
17391 "flush_post" | "post_now" | "포스트플러시" | "后期冲刷" => {
17396 let mut gfx = self.gfx.borrow_mut();
17397 if !gfx.depth_queue.is_empty() {
17398 let w = gfx.width;
17399 let h = gfx.height;
17400 let dt = gfx.depth_test;
17401 let reset_z = gfx.zbuf_needs_clear;
17402 let (bm, ba) = (gfx.blend, gfx.alpha);
17403 let aa = gfx.antialias;
17404 let queue = std::mem::take(&mut gfx.depth_queue);
17405 {
17406 let g = &mut *gfx;
17407 let z = if dt { Some(&mut g.depth_buf) } else { None };
17408 queue.flush(&mut g.buffer, z, reset_z, w, h, aa);
17409 }
17410 gfx.zbuf_needs_clear = false;
17411 gfx.depth_queue.set_state(bm, ba);
17412 }
17413 gfx.toon_post_process();
17414 gfx.post_done = true;
17415 return Ok(Value::Unit);
17416 },
17417
17418 #[cfg(not(target_arch = "wasm32"))]
17421 "screen_distort"
17422 | "บิดจอ"
17423 | "屏幕扭曲"
17424 | "画面歪み"
17425 | "화면왜곡"
17426 | "اعوجاج_صفحه"
17427 | "شوّه_الشاشة"
17428 | "עוות_מסך"
17429 | "اسکرین_ڈسٹورٹ" => {
17430 let amount = self.arg_num(&args, 0, 8.0)? as f32;
17431 let t = self.arg_num(&args, 1, 0.0)? as f32;
17432 let step = self.arg_num(&args, 2, 1.0)?.max(1.0) as usize;
17435 let _d = std::time::Instant::now();
17436 self.gfx.borrow_mut().distort(amount, t, step);
17437 ling_phase_add(phase::DISTORT, _d.elapsed().as_nanos());
17438 return Ok(Value::Unit);
17439 },
17440
17441 "set_rim"
17442 | "设置边缘光"
17443 | "リム設定"
17444 | "림라이트"
17445 | "ตั้งขอบเรือง"
17446 | "تنظیم_نور_لبه"
17447 | "عيّن_إضاءة_الحافة"
17448 | "קבע_תאורת_קצה"
17449 | "رم_لائٹ_مقرر_کرو"
17450 | "définir_contour_lumineux"
17451 | "rimlicht_setzen"
17452 | "задать_контурный_свет" => {
17453 let s = self.arg_num(&args, 0, 0.6)? as f32;
17454 let r = self.arg_num(&args, 1, 115.)? as f32 / 255.0;
17455 let g = self.arg_num(&args, 2, 217.)? as f32 / 255.0;
17456 let b = self.arg_num(&args, 3, 255.)? as f32 / 255.0;
17457 let mut gfx = self.gfx.borrow_mut();
17458 gfx.shade.rim = s;
17459 gfx.shade.rim_color = [r, g, b];
17460 return Ok(Value::Unit);
17461 },
17462
17463 n if crate::gfx::shapes::canon(n).is_some() => {
17472 let kind = crate::gfx::shapes::canon(n).unwrap();
17473 let cx = self.arg_num(&args, 0, 0.)? as f32;
17474 let cy = self.arg_num(&args, 1, 0.)? as f32;
17475 let cz = self.arg_num(&args, 2, 0.)? as f32;
17476 let sx = self.arg_num(&args, 3, 1.)? as f32;
17477 let sy = self.arg_num(&args, 4, 1.)? as f32;
17478 let sz = self.arg_num(&args, 5, 1.)? as f32;
17479 let rx = self.arg_num(&args, 6, 0.)? as f32;
17480 let ry = self.arg_num(&args, 7, 0.)? as f32;
17481 let rz = self.arg_num(&args, 8, 0.)? as f32;
17482 let mode = self.arg_num(&args, 9, 0.)? as i32;
17483 let e0 = self.arg_num(&args, 10, 0.)? as f32;
17484 let e1 = self.arg_num(&args, 11, 0.)? as f32;
17485 let e2 = self.arg_num(&args, 12, 0.)? as f32;
17486 if let Some(mesh) = crate::gfx::shapes::build(
17487 kind,
17488 [cx, cy, cz, sx, sy, sz, rx, ry, rz],
17489 e0,
17490 e1,
17491 e2,
17492 ) {
17493 let mut gfx = self.gfx.borrow_mut();
17494 gfx.emit_mesh(&mesh, mode);
17495 }
17496 return Ok(Value::Unit);
17497 },
17498
17499 _ => {},
17500 }
17501
17502 if let Some(field_names) = self.structs.get(name).cloned() {
17504 if args.len() != field_names.len() {
17505 return Err(EvalErr::from(format!(
17506 "{name} expects {} field(s), got {}",
17507 field_names.len(),
17508 args.len()
17509 )));
17510 }
17511 let fields = field_names.into_iter().zip(args).collect();
17512 return Ok(Value::Struct { name: name.to_string(), fields });
17513 }
17514
17515 if let Some((enum_name, arity)) = self.enum_variants.get(name).cloned() {
17517 if args.len() != arity {
17518 return Err(EvalErr::from(format!(
17519 "{name} expects {arity} value(s), got {}",
17520 args.len()
17521 )));
17522 }
17523 let variant = name.rsplit("::").next().unwrap_or(name).to_string();
17524 return Ok(Value::Variant { enum_name, variant, payload: args });
17525 }
17526
17527 #[cfg(target_arch = "wasm32")]
17528 if let Some(v) = wasm_unsupported_builtin(name) {
17529 return Ok(v);
17530 }
17531
17532 Err(EvalErr::from(format!("unknown function '{name}'")))
17533 }
17534
17535 fn call_value(&mut self, v: Value, args: Vec<Value>) -> EvalResult {
17536 match v {
17537 Value::Fn(params, body, mut captured) => {
17538 for (p, a) in params.iter().zip(args) {
17539 captured.insert(p.clone(), a);
17540 }
17541 match self.framed("<closure>", |me| me.exec_block(&body, &mut captured)) {
17542 Ok(v) => Ok(v.unwrap_or(Value::Unit)),
17543 Err(EvalErr::Return(v)) => Ok(v),
17544 Err(e) => Err(e),
17545 }
17546 },
17547 other => Err(EvalErr::from(format!("cannot call {:?}", other))),
17548 }
17549 }
17550
17551 fn call_method(&self, recv: Value, method: &str, args: Vec<Value>) -> EvalResult {
17552 match (&recv, method) {
17553 (Value::Str(s), "is_empty" | "是空") => Ok(Value::Bool(s.is_empty())),
17554 (Value::Str(s), "len" | "长" | "长度" | "長さ" | "길이" | "ความยาว") => {
17561 Ok(Value::Number(s.len() as f64))
17562 },
17563 (Value::Str(s), "to_string" | "转文") => Ok(Value::Str(s.clone())),
17564 (Value::Str(s), "contains" | "包含") => {
17565 if let Some(Value::Str(sub)) = args.first() {
17566 Ok(Value::Bool(s.contains(sub.as_str())))
17567 } else {
17568 Ok(Value::Bool(false))
17569 }
17570 },
17571 (Value::Str(s), "push_str" | "推_文") => {
17572 let mut s2 = s.clone();
17573 if let Some(Value::Str(a)) = args.first() {
17574 s2.push_str(a);
17575 }
17576 Ok(Value::Str(s2))
17577 },
17578 (Value::List(v), "len" | "长" | "长度" | "長さ" | "길이" | "ความยาว") => {
17579 Ok(Value::Number(v.len() as f64))
17580 },
17581 (Value::List(v), "push" | "推" | "添加" | "追加" | "추가" | "เพิ่ม") =>
17582 {
17583 let mut v2: Vec<Value> = (**v).clone();
17584 if let Some(a) = args.first() {
17585 v2.push(a.clone());
17586 }
17587 Ok(Value::List(Rc::new(v2)))
17588 },
17589 (Value::Struct { fields, .. }, _) if args.is_empty() => fields
17591 .iter()
17592 .find(|(k, _)| k == method)
17593 .map(|(_, v)| v.clone())
17594 .ok_or_else(|| EvalErr::from(format!("no field '{method}' on {recv}"))),
17595 (Value::Variant { variant, .. }, "tag" | "标签" | "タグ" | "태그" | "ป้าย")
17597 if args.is_empty() =>
17598 {
17599 Ok(Value::Str(variant.clone()))
17600 },
17601 (Value::Ok(inner), _) | (Value::Err(inner), _) => Ok(*inner.clone()),
17602 _ => Err(EvalErr::from(format!("no method '{method}' on {recv}"))),
17603 }
17604 }
17605
17606 fn match_pattern(&self, pat: &Pattern, val: &Value) -> Option<Env> {
17609 match (pat, val) {
17610 (Pattern::Wildcard, _) => Some(new_env()),
17611 (Pattern::Str(s), Value::Str(v)) if s == v => Some(new_env()),
17612 (Pattern::Number(n), Value::Number(v)) if (n - v).abs() < 1e-12 => Some(new_env()),
17613 (Pattern::Bool(b), Value::Bool(v)) if b == v => Some(new_env()),
17614 (Pattern::Ident(name), _) => {
17615 let mut e = new_env();
17616 e.insert(name.clone(), val.clone());
17617 Some(e)
17618 },
17619 (Pattern::Constructor(ctor, inner_pat), _) => {
17620 let (matches, inner_val) = match (ctor.as_str(), val) {
17621 ("ok" | "好", Value::Ok(v)) => (true, Some(v.as_ref().clone())),
17622 ("bad" | "坏", Value::Err(v)) => (true, Some(v.as_ref().clone())),
17623 ("ok" | "好", v) if !matches!(v, Value::Err(_)) => (true, Some(v.clone())),
17624 _ => (false, None),
17625 };
17626 if !matches {
17627 return None;
17628 }
17629 match (inner_pat, inner_val) {
17630 (Some(p), Some(v)) => self.match_pattern(p, &v),
17631 (None, _) => Some(new_env()),
17632 (Some(p), None) => self.match_pattern(p, &Value::Unit),
17633 }
17634 },
17635 (Pattern::Variant(vname, sub_pats), Value::Variant { variant, payload, .. }) => {
17637 if vname != variant || sub_pats.len() != payload.len() {
17638 return None;
17639 }
17640 let mut bindings = new_env();
17641 for (p, v) in sub_pats.iter().zip(payload.iter()) {
17642 bindings.extend(self.match_pattern(p, v)?);
17643 }
17644 Some(bindings)
17645 },
17646 (Pattern::Variant(vname, sub), Value::Ok(v)) if (vname == "ok" || vname == "好") => {
17649 match sub.as_slice() {
17650 [] => Some(new_env()),
17651 [p] => self.match_pattern(p, v),
17652 _ => None,
17653 }
17654 },
17655 (Pattern::Variant(vname, sub), Value::Err(v))
17656 if (vname == "bad" || vname == "坏" || vname == "err") =>
17657 {
17658 match sub.as_slice() {
17659 [] => Some(new_env()),
17660 [p] => self.match_pattern(p, v),
17661 _ => None,
17662 }
17663 },
17664 _ => None,
17665 }
17666 }
17667
17668 fn value_to_iter(&self, val: Value) -> Result<Vec<Value>, EvalErr> {
17671 match val {
17672 Value::List(v) => Ok(Rc::try_unwrap(v).unwrap_or_else(|rc| (*rc).clone())),
17673 Value::Str(s) => Ok(s.chars().map(|c| Value::Str(c.to_string())).collect()),
17674 Value::Number(n) => Ok((0..n as i64).map(|i| Value::Number(i as f64)).collect()),
17675 other => Err(EvalErr::from(format!("cannot iterate over {:?}", other))),
17676 }
17677 }
17678
17679 pub(crate) fn is_truthy(&self, val: &Value) -> bool {
17680 match val {
17681 Value::Bool(b) => *b,
17682 Value::Unit => false,
17683 Value::Number(n) => *n != 0.0,
17684 Value::Str(s) => !s.is_empty(),
17685 Value::List(v) => !v.is_empty(),
17686 Value::Ok(_) => true,
17687 Value::Err(_) => false,
17688 Value::Fn(_, _, _) => true,
17689 Value::Struct { .. } => true,
17690 Value::Variant { .. } => true,
17691 }
17692 }
17693
17694 fn to_number(&self, val: &Value) -> Result<f64, EvalErr> {
17695 match val {
17696 Value::Number(n) => Ok(*n),
17697 Value::Str(s) => s
17698 .parse()
17699 .map_err(|_| EvalErr::from(format!("cannot convert '{s}' to number"))),
17700 other => Err(EvalErr::from(format!("expected number, got {:?}", other))),
17701 }
17702 }
17703
17704 fn arg_num(&self, args: &[Value], n: usize, default: f64) -> Result<f64, EvalErr> {
17706 match args.get(n) {
17707 Some(v) => self.to_number(v),
17708 None => Ok(default),
17709 }
17710 }
17711
17712 fn arg_str(&self, args: &[Value], n: usize, default: &str) -> String {
17713 args.get(n)
17714 .map(|v| v.to_string())
17715 .unwrap_or_else(|| default.to_string())
17716 }
17717
17718 #[cfg(all(not(target_arch = "wasm32"), feature = "web"))]
17725 fn drain_http_requests(
17726 &mut self,
17727 rx: std::sync::mpsc::Receiver<web::PendingRequest>,
17728 routes: Vec<(String, String, Value)>,
17729 ) -> Result<Value, EvalErr> {
17730 for pending in rx {
17731 let matched = routes.iter().find(|(m, p, _)| m == &pending.method && p == &pending.path);
17732 let response = match matched {
17733 Some((_, _, handler)) => {
17734 let req_value = Value::Struct {
17735 name: "Request".to_string(),
17736 fields: vec![
17737 ("method".to_string(), Value::Str(pending.method.clone())),
17738 ("path".to_string(), Value::Str(pending.path.clone())),
17739 ("query".to_string(), Value::Str(pending.query.clone())),
17740 ("body".to_string(), Value::Str(pending.body.clone())),
17741 ("cookie".to_string(), Value::Str(pending.cookie.clone())),
17742 ("authorization".to_string(), Value::Str(pending.authorization.clone())),
17743 ("client_ip".to_string(), Value::Str(pending.client_ip.clone())),
17744 ],
17745 };
17746 match self.call_value(handler.clone(), vec![req_value]) {
17747 Ok(v) => web::value_to_response(&v),
17748 Err(e) => web::HttpResponse {
17749 status: 500,
17750 content_type: "text/plain; charset=utf-8".to_string(),
17751 body: format!("handler error: {e:?}"),
17752 set_cookie: None,
17753 location: None,
17754 },
17755 }
17756 },
17757 None => web::HttpResponse {
17758 status: 404,
17759 content_type: "text/plain; charset=utf-8".to_string(),
17760 body: "not found".to_string(),
17761 set_cookie: None,
17762 location: None,
17763 },
17764 };
17765 let _ = pending.respond_to.send(response);
17766 }
17767 Ok(Value::Unit)
17768 }
17769
17770 fn arg_list_str(&self, args: &[Value], n: usize) -> Vec<String> {
17773 match args.get(n) {
17774 Some(Value::List(v)) => v.iter().map(|x| x.to_string()).collect(),
17775 _ => Vec::new(),
17776 }
17777 }
17778
17779 #[allow(dead_code)]
17781 fn arg_list_f32(&self, args: &[Value], n: usize) -> Vec<f32> {
17782 match args.get(n) {
17783 Some(Value::List(v)) => v
17784 .iter()
17785 .filter_map(|x| match x {
17786 Value::Number(n) => Some(*n as f32),
17787 _ => None,
17788 })
17789 .collect(),
17790 _ => Vec::new(),
17791 }
17792 }
17793
17794 #[cfg(not(target_arch = "wasm32"))]
17797 fn color_at(&self, args: &[Value], i: usize, default: u32) -> u32 {
17798 match (args.get(i), args.get(i + 1), args.get(i + 2)) {
17799 (Some(a), Some(b), Some(c)) => {
17800 match (self.to_number(a), self.to_number(b), self.to_number(c)) {
17801 (Ok(r), Ok(g), Ok(bl)) => {
17802 ((r as u32 & 0xFF) << 16) | ((g as u32 & 0xFF) << 8) | (bl as u32 & 0xFF)
17803 },
17804 _ => default,
17805 }
17806 },
17807 _ => default,
17808 }
17809 }
17810
17811 #[cfg(not(target_arch = "wasm32"))]
17813 fn pitch_arg(&self, args: &[Value], i: usize, default: i32) -> i32 {
17814 match args.get(i) {
17815 Some(Value::Str(s)) => ling_music::note::parse_pitch(s).unwrap_or(default),
17816 Some(Value::Number(n)) => *n as i32,
17817 _ => default,
17818 }
17819 }
17820
17821 #[cfg(not(target_arch = "wasm32"))]
17823 fn mouse_now(&self) -> (f32, f32, bool) {
17824 let gfx = self.gfx.borrow();
17825 let (mx, my) = gfx
17826 .window
17827 .as_ref()
17828 .and_then(|w| w.get_mouse_pos(minifb::MouseMode::Clamp))
17829 .unwrap_or((0.0, 0.0));
17830 let down = gfx
17831 .window
17832 .as_ref()
17833 .map(|w| w.get_mouse_down(minifb::MouseButton::Left))
17834 .unwrap_or(false);
17835 (mx, my, down)
17836 }
17837
17838 #[cfg(not(target_arch = "wasm32"))]
17842 fn draw_ui(&self, d: &ling_ui::widgets::Draw) {
17843 let mut gfx = self.gfx.borrow_mut();
17844 let (w, h, add) = (gfx.width, gfx.height, gfx.blend == 1);
17845 for (c, poly) in &d.fills {
17846 crate::gfx::raster::fill_contours_aa(
17847 &mut gfx.buffer,
17848 w,
17849 h,
17850 *c,
17851 add,
17852 std::slice::from_ref(poly),
17853 );
17854 }
17855 for (c, pl) in &d.strokes {
17856 for s in pl.windows(2) {
17857 crate::gfx::raster::draw_line_aa(
17858 &mut gfx.buffer,
17859 w,
17860 h,
17861 *c,
17862 add,
17863 s[0][0],
17864 s[0][1],
17865 s[1][0],
17866 s[1][1],
17867 );
17868 }
17869 }
17870 }
17871
17872 fn tex_rect(&self, args: &[Value]) -> Result<(usize, usize, usize, usize), EvalErr> {
17874 let tx = self.arg_num(args, 0, 0.0)? as usize;
17875 let ty = self.arg_num(args, 1, 0.0)? as usize;
17876 let tw = self.arg_num(args, 2, 256.0)? as usize;
17877 let th = self.arg_num(args, 3, 256.0)? as usize;
17878 Ok((tx, ty, tw.max(1), th.max(1)))
17879 }
17880
17881 pub(crate) fn apply_binop(&self, op: &BinOp, l: Value, r: Value) -> EvalResult {
17882 match op {
17883 BinOp::Add => match (l, r) {
17884 (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
17885 (Value::Str(a), Value::Str(b)) => Ok(Value::Str(a + &b)),
17886 (Value::Str(a), b) => Ok(Value::Str(a + &b.to_string())),
17887 (a, Value::Str(b)) => Ok(Value::Str(a.to_string() + &b)),
17888 (a, b) => Err(EvalErr::from(format!("cannot add {:?} and {:?}", a, b))),
17889 },
17890 BinOp::Sub => Ok(Value::Number(self.to_number(&l)? - self.to_number(&r)?)),
17891 BinOp::Mul => Ok(Value::Number(self.to_number(&l)? * self.to_number(&r)?)),
17892 BinOp::Div => Ok(Value::Number(self.to_number(&l)? / self.to_number(&r)?)),
17893 BinOp::Rem => Ok(Value::Number(self.to_number(&l)? % self.to_number(&r)?)),
17894 BinOp::Eq => Ok(Value::Bool(values_equal(&l, &r))),
17895 BinOp::Ne => Ok(Value::Bool(!values_equal(&l, &r))),
17896 BinOp::Lt => Ok(Value::Bool(self.to_number(&l)? < self.to_number(&r)?)),
17897 BinOp::Gt => Ok(Value::Bool(self.to_number(&l)? > self.to_number(&r)?)),
17898 BinOp::Le => Ok(Value::Bool(self.to_number(&l)? <= self.to_number(&r)?)),
17899 BinOp::Ge => Ok(Value::Bool(self.to_number(&l)? >= self.to_number(&r)?)),
17900 BinOp::And => Ok(Value::Bool(self.is_truthy(&l) && self.is_truthy(&r))),
17901 BinOp::Or => Ok(Value::Bool(self.is_truthy(&l) || self.is_truthy(&r))),
17902 }
17903 }
17904
17905 fn builtin_format(&self, args: &[Value]) -> Result<String, EvalErr> {
17906 if args.is_empty() {
17907 return Ok(String::new());
17908 }
17909 let fmt = match &args[0] {
17910 Value::Str(s) => s.clone(),
17911 other => return Ok(other.to_string()),
17912 };
17913
17914 let mut result = String::new();
17915 let mut arg_idx = 1usize;
17916 let mut chars = fmt.chars().peekable();
17917 while let Some(c) = chars.next() {
17918 if c == '{' {
17919 if chars.peek() == Some(&'}') {
17920 chars.next();
17921 if arg_idx < args.len() {
17922 result.push_str(&args[arg_idx].to_string());
17923 arg_idx += 1;
17924 }
17925 } else {
17926 let mut spec = String::new();
17927 for ch in chars.by_ref() {
17928 if ch == '}' {
17929 break;
17930 }
17931 spec.push(ch);
17932 }
17933 if arg_idx < args.len() {
17934 if let Some(suffix) = spec.strip_prefix(":.") {
17935 if let Value::Number(n) = &args[arg_idx] {
17936 let prec: usize = suffix.trim_end_matches('f').parse().unwrap_or(2);
17937 result.push_str(&format!("{:.prec$}", n));
17938 arg_idx += 1;
17939 continue;
17940 }
17941 }
17942 result.push_str(&args[arg_idx].to_string());
17943 arg_idx += 1;
17944 }
17945 }
17946 } else {
17947 result.push(c);
17948 }
17949 }
17950 Ok(result)
17951 }
17952}
17953
17954#[cfg(not(target_arch = "wasm32"))]
17955#[cfg(not(target_arch = "wasm32"))]
17957fn parse_pad_button(name: &str) -> Option<ling_input::GamepadButton> {
17958 use ling_input::GamepadButton as B;
17959 Some(match name.to_ascii_lowercase().as_str() {
17960 "a" | "south" | "cross" => B::South,
17961 "b" | "east" | "circle" => B::East,
17962 "x" | "west" | "square" => B::West,
17963 "y" | "north" | "triangle" => B::North,
17964 "lb" | "l1" | "left_shoulder" => B::LeftShoulder,
17965 "rb" | "r1" | "right_shoulder" => B::RightShoulder,
17966 "lt" | "l2" | "left_trigger" => B::LeftTrigger,
17967 "rt" | "r2" | "right_trigger" => B::RightTrigger,
17968 "start" | "menu" | "options" | "démarrer" | "начать" => B::Start,
17969 "select" | "back" | "share" | "view" => B::Select,
17970 "guide" | "home" => B::Guide,
17971 "l3" | "left_stick" => B::LeftStick,
17972 "r3" | "right_stick" => B::RightStick,
17973 "up" | "dpad_up" => B::DpadUp,
17974 "down" | "dpad_down" => B::DpadDown,
17975 "left" | "dpad_left" => B::DpadLeft,
17976 "right" | "dpad_right" => B::DpadRight,
17977 _ => return None,
17978 })
17979}
17980
17981#[cfg(not(target_arch = "wasm32"))]
17982fn str_to_minifb_key(name: &str) -> Option<minifb::Key> {
17983 use minifb::Key;
17984 Some(match name {
17985 "numpad0" | "kp0" => Key::NumPad0,
17986 "numpad1" | "kp1" => Key::NumPad1,
17987 "numpad2" | "kp2" => Key::NumPad2,
17988 "numpad3" | "kp3" => Key::NumPad3,
17989 "numpad4" | "kp4" => Key::NumPad4,
17990 "numpad5" | "kp5" => Key::NumPad5,
17991 "numpad6" | "kp6" => Key::NumPad6,
17992 "numpad7" | "kp7" => Key::NumPad7,
17993 "numpad8" | "kp8" => Key::NumPad8,
17994 "numpad9" | "kp9" => Key::NumPad9,
17995 "numpad+" | "kp+" => Key::NumPadPlus,
17996 "numpad-" | "kp-" => Key::NumPadMinus,
17997 "numpad*" | "kp*" => Key::NumPadAsterisk,
17998 "numpad/" | "kp/" => Key::NumPadSlash,
17999 "left" => Key::Left,
18000 "right" => Key::Right,
18001 "up" => Key::Up,
18002 "down" => Key::Down,
18003 "space" => Key::Space,
18004 "enter" => Key::Enter,
18005 "escape" => Key::Escape,
18006 "pageup" => Key::PageUp,
18007 "pagedown" => Key::PageDown,
18008 "lshift" | "leftshift" => Key::LeftShift,
18009 "rshift" | "rightshift" => Key::RightShift,
18010 "lctrl" | "leftctrl" => Key::LeftCtrl,
18011 "rctrl" | "rightctrl" => Key::RightCtrl,
18012 "lalt" | "leftalt" => Key::LeftAlt,
18013 "ralt" | "rightalt" => Key::RightAlt,
18014 "tab" => Key::Tab,
18015 "backspace" => Key::Backspace,
18016 "delete" => Key::Delete,
18017 "insert" => Key::Insert,
18018 "home" => Key::Home,
18019 "end" => Key::End,
18020 "a" => Key::A,
18021 "b" => Key::B,
18022 "c" => Key::C,
18023 "d" => Key::D,
18024 "e" => Key::E,
18025 "f" => Key::F,
18026 "g" => Key::G,
18027 "h" => Key::H,
18028 "i" => Key::I,
18029 "j" => Key::J,
18030 "k" => Key::K,
18031 "l" => Key::L,
18032 "m" => Key::M,
18033 "n" => Key::N,
18034 "o" => Key::O,
18035 "p" => Key::P,
18036 "q" => Key::Q,
18037 "r" => Key::R,
18038 "s" => Key::S,
18039 "t" => Key::T,
18040 "u" => Key::U,
18041 "v" => Key::V,
18042 "w" => Key::W,
18043 "x" => Key::X,
18044 "y" => Key::Y,
18045 "z" => Key::Z,
18046 "0" => Key::Key0,
18047 "1" => Key::Key1,
18048 "2" => Key::Key2,
18049 "3" => Key::Key3,
18050 "4" => Key::Key4,
18051 "5" => Key::Key5,
18052 "6" => Key::Key6,
18053 "7" => Key::Key7,
18054 "8" => Key::Key8,
18055 "9" => Key::Key9,
18056 _ => return None,
18057 })
18058}
18059
18060pub(crate) fn values_equal(a: &Value, b: &Value) -> bool {
18061 match (a, b) {
18062 (Value::Number(x), Value::Number(y)) => (x - y).abs() < 1e-12,
18063 (Value::Str(x), Value::Str(y)) => x == y,
18064 (Value::Bool(x), Value::Bool(y)) => x == y,
18065 (Value::Unit, Value::Unit) => true,
18066 _ => false,
18067 }
18068}
18069
18070#[cfg(all(not(target_arch = "wasm32"), windows))]
18079fn make_borderless_fullscreen(hwnd: isize, screen_w: i32, screen_h: i32) {
18080 if hwnd == 0 {
18081 return;
18082 }
18083 unsafe {
18084 extern "system" {
18085 fn SetWindowLongPtrW(hwnd: isize, index: i32, new: isize) -> isize;
18086 fn SetWindowPos(
18087 hwnd: isize,
18088 insert_after: isize,
18089 x: i32,
18090 y: i32,
18091 cx: i32,
18092 cy: i32,
18093 flags: u32,
18094 ) -> i32;
18095 fn ShowWindow(hwnd: isize, cmd: i32) -> i32;
18096 }
18097 const GWL_STYLE: i32 = -16;
18098 const GWL_EXSTYLE: i32 = -20;
18099 SetWindowLongPtrW(hwnd, GWL_STYLE, 0x9000_0000isize);
18102 SetWindowLongPtrW(hwnd, GWL_EXSTYLE, 0);
18104 SetWindowPos(hwnd, -1isize, 0, 0, screen_w, screen_h, 0x0020 | 0x0040);
18106 ShowWindow(hwnd, 3); }
18108}
18109
18110#[cfg(all(not(target_arch = "wasm32"), windows))]
18124fn force_window_focus(hwnd: isize) {
18125 if hwnd == 0 {
18126 return;
18127 }
18128 unsafe {
18129 extern "system" {
18130 fn GetForegroundWindow() -> isize;
18131 fn GetWindowThreadProcessId(hwnd: isize, pid: *mut u32) -> u32;
18132 fn GetCurrentThreadId() -> u32;
18133 fn AttachThreadInput(id_attach: u32, id_attach_to: u32, attach: i32) -> i32;
18134 fn SetForegroundWindow(hwnd: isize) -> i32;
18135 fn BringWindowToTop(hwnd: isize) -> i32;
18136 fn SetFocus(hwnd: isize) -> isize;
18137 fn SetActiveWindow(hwnd: isize) -> isize;
18138 }
18139 let fg = GetForegroundWindow();
18140 if fg != 0 && fg != hwnd {
18141 let mut fg_pid: u32 = 0;
18142 let fg_tid = GetWindowThreadProcessId(fg, &mut fg_pid);
18143 let my_tid = GetCurrentThreadId();
18144 if fg_tid != 0 && fg_tid != my_tid {
18145 AttachThreadInput(my_tid, fg_tid, 1);
18146 SetForegroundWindow(hwnd);
18147 BringWindowToTop(hwnd);
18148 SetFocus(hwnd);
18149 SetActiveWindow(hwnd);
18150 AttachThreadInput(my_tid, fg_tid, 0);
18151 return;
18152 }
18153 }
18154 SetForegroundWindow(hwnd);
18155 BringWindowToTop(hwnd);
18156 SetFocus(hwnd);
18157 SetActiveWindow(hwnd);
18158 }
18159}
18160
18161#[cfg(all(not(target_arch = "wasm32"), windows))]
18166fn set_window_topmost(hwnd: isize, topmost: bool) {
18167 if hwnd == 0 {
18168 return;
18169 }
18170 unsafe {
18171 extern "system" {
18172 fn SetWindowPos(
18173 hwnd: isize,
18174 insert_after: isize,
18175 x: i32,
18176 y: i32,
18177 cx: i32,
18178 cy: i32,
18179 flags: u32,
18180 ) -> i32;
18181 }
18182 let insert_after: isize = if topmost { -1 } else { -2 }; SetWindowPos(hwnd, insert_after, 0, 0, 0, 0, 0x0002 | 0x0001 | 0x0010);
18186 }
18187}
18188
18189#[cfg(not(target_arch = "wasm32"))]
18195fn apply_frame_pacing(win: &mut minifb::Window, vsync: bool) {
18196 match std::env::var("LING_FPS_CAP")
18197 .ok()
18198 .and_then(|v| v.parse::<usize>().ok())
18199 {
18200 Some(0) => win.set_target_fps(100_000),
18201 Some(cap) => win.set_target_fps(cap),
18202 None if vsync => win.set_target_fps(monitor_info().2.max(30) as usize),
18203 None => win.set_target_fps(100_000),
18204 }
18205}
18206
18207#[cfg(all(not(target_arch = "wasm32"), windows))]
18210fn monitor_info() -> (i32, i32, i32) {
18211 unsafe {
18212 extern "system" {
18213 fn GetSystemMetrics(index: i32) -> i32;
18214 fn GetDC(hwnd: isize) -> isize;
18215 fn ReleaseDC(hwnd: isize, hdc: isize) -> i32;
18216 fn GetDeviceCaps(hdc: isize, index: i32) -> i32;
18217 }
18218 let w = GetSystemMetrics(0).max(1); let h = GetSystemMetrics(1).max(1); let hdc = GetDC(0);
18221 let mut hz = if hdc != 0 { GetDeviceCaps(hdc, 116) } else { 0 }; if hdc != 0 {
18223 ReleaseDC(0, hdc);
18224 }
18225 if hz <= 1 {
18226 hz = 60; }
18228 (w, h, hz)
18229 }
18230}
18231
18232#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
18236fn monitor_info() -> (i32, i32, i32) {
18237 let (w, h) = native_screen_size();
18238 (w as i32, h as i32, linux_refresh_hz().unwrap_or(60))
18239}
18240
18241#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
18246fn linux_refresh_hz() -> Option<i32> {
18247 let out = std::process::Command::new("xrandr")
18248 .arg("--current")
18249 .output()
18250 .ok()?;
18251 if !out.status.success() {
18252 return None;
18253 }
18254 parse_xrandr_max_hz(&String::from_utf8_lossy(&out.stdout))
18255}
18256
18257#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
18260fn parse_xrandr_max_hz(text: &str) -> Option<i32> {
18261 text.split_whitespace()
18262 .filter(|tok| tok.contains('*'))
18263 .filter_map(|tok| {
18264 tok.trim_matches(|c: char| !c.is_ascii_digit() && c != '.')
18265 .parse::<f64>()
18266 .ok()
18267 })
18268 .map(|hz| hz.round() as i32)
18269 .filter(|&hz| (24..=1000).contains(&hz))
18270 .max()
18271}
18272
18273#[cfg(target_arch = "wasm32")]
18275fn monitor_info() -> (i32, i32, i32) {
18276 let (w, h) = crate::gfx::webgl::canvas_size();
18277 (w as i32, h as i32, 60)
18278}
18279
18280#[cfg(all(test, not(target_arch = "wasm32"), not(windows)))]
18281mod xrandr_tests {
18282 use super::parse_xrandr_max_hz;
18283
18284 #[test]
18285 fn picks_highest_active_output() {
18286 let text = "\
18287eDP-1 connected primary 1920x1080+0+0
18288 1920x1080 60.00*+ 59.94
18289DP-1 connected 2560x1440+1920+0
18290 2560x1440 144.00*+ 120.00 60.00
18291";
18292 assert_eq!(parse_xrandr_max_hz(text), Some(144));
18293 }
18294
18295 #[test]
18296 fn single_output() {
18297 let text = " 1920x1080 75.00*+ 60.00\n";
18298 assert_eq!(parse_xrandr_max_hz(text), Some(75));
18299 }
18300
18301 #[test]
18302 fn no_active_mode_returns_none() {
18303 let text = "eDP-1 disconnected\n";
18304 assert_eq!(parse_xrandr_max_hz(text), None);
18305 }
18306
18307 #[test]
18308 fn out_of_range_hz_filtered() {
18309 let text = " 1x1 5000.00*+\n";
18310 assert_eq!(parse_xrandr_max_hz(text), None);
18311 }
18312}
18313
18314#[cfg(all(not(target_arch = "wasm32"), not(windows)))]
18317fn native_screen_size() -> (f64, f64) {
18318 (1920.0, 1080.0)
18321}
18322
18323struct LingProfileState {
18335 enabled: bool,
18336 every: u64,
18337 frames: u64,
18338 calls: std::collections::HashMap<String, (u64, u128)>, }
18340
18341thread_local! {
18342 static LING_PROFILE: std::cell::RefCell<LingProfileState> = std::cell::RefCell::new({
18343 let enabled = std::env::var("LING_PROFILE").map(|v| v != "0" && !v.is_empty()).unwrap_or(false);
18344 let every = std::env::var("LING_PROFILE_EVERY").ok()
18345 .and_then(|v| v.parse::<u64>().ok()).filter(|&n| n > 0).unwrap_or(240);
18346 if enabled {
18347 eprintln!("[ling-profile] ON — report every {every} frames (set LING_PROFILE_EVERY to change)");
18348 }
18349 LingProfileState { enabled, every, frames: 0, calls: std::collections::HashMap::new() }
18350 });
18351}
18352
18353#[inline]
18354fn ling_profile_enabled() -> bool {
18355 LING_PROFILE.with(|p| p.borrow().enabled)
18356}
18357
18358thread_local! {
18359 static LING_FPS: std::cell::RefCell<(bool, f64, u32, f64)> = std::cell::RefCell::new(
18360 (std::env::var("LING_FPS").map(|v| v != "0" && !v.is_empty()).unwrap_or(false), 0.0, 0, 0.0)
18361 );
18362}
18363
18364#[cfg(not(target_arch = "wasm32"))]
18365fn ling_fps_tick() {
18366 LING_FPS.with(|s| {
18367 let mut s = s.borrow_mut();
18368 if !s.0 {
18369 return;
18370 }
18371 let now = crate::runtime::now_secs();
18372 if s.1 > 0.0 {
18373 s.3 += now - s.1;
18374 s.2 += 1;
18375 if s.2 >= 120 {
18376 let avg = s.3 / s.2 as f64;
18377 eprintln!(
18378 "[fps] {:.1} fps ({:.2} ms/frame, wall, {} frames)",
18379 1.0 / avg,
18380 avg * 1000.0,
18381 s.2
18382 );
18383 s.2 = 0;
18384 s.3 = 0.0;
18385 }
18386 }
18387 s.1 = now;
18388 });
18389}
18390
18391thread_local! {
18396 static LING_PHASE: std::cell::RefCell<(bool, u64, [u128; 5])> = std::cell::RefCell::new(
18397 (std::env::var_os("LING_PHASE").is_some(), 0, [0; 5])
18398 );
18399}
18400
18401pub mod phase {
18403 pub const FLUSH: usize = 0;
18404 pub const TOON: usize = 1;
18405 pub const BLIT: usize = 2;
18406 pub const DISTORT: usize = 3;
18407 pub const SORT: usize = 4;
18408}
18409
18410#[cfg(not(target_arch = "wasm32"))]
18411#[inline]
18412pub fn ling_phase_add(idx: usize, nanos: u128) {
18413 LING_PHASE.with(|p| {
18414 let mut p = p.borrow_mut();
18415 if p.0 {
18416 p.2[idx] += nanos;
18417 }
18418 });
18419}
18420
18421#[cfg(not(target_arch = "wasm32"))]
18422fn ling_phase_frame() {
18423 LING_PHASE.with(|p| {
18424 let mut p = p.borrow_mut();
18425 if !p.0 {
18426 return;
18427 }
18428 p.1 += 1;
18429 if p.1 >= 120 {
18430 let f = p.1 as f64;
18431 let ms = |i: usize| p.2[i] as f64 / 1e6 / f;
18432 eprintln!(
18433 "[phase] sort={:.2} flush={:.2} toon={:.2} blit={:.2} distort={:.2} ms/frame",
18434 ms(phase::SORT),
18435 ms(phase::FLUSH),
18436 ms(phase::TOON),
18437 ms(phase::BLIT),
18438 ms(phase::DISTORT)
18439 );
18440 p.1 = 0;
18441 p.2 = [0; 5];
18442 }
18443 });
18444}
18445
18446fn ling_profile_record(name: &str, nanos: u128) {
18447 let is_frame = matches!(
18449 name,
18450 "present" | "แสดงผล" | "gfx_present" | "show" | "显" | "呈现" | "表示" | "표시"
18451 );
18452 LING_PROFILE.with(|p| {
18453 let mut p = p.borrow_mut();
18454 let e = p.calls.entry(name.to_string()).or_insert((0, 0));
18455 e.0 += 1;
18456 e.1 += nanos;
18457 if is_frame {
18458 p.frames += 1;
18459 if p.frames % p.every == 0 {
18460 ling_profile_print(&p);
18461 }
18462 }
18463 });
18464}
18465
18466fn ling_profile_print(p: &LingProfileState) {
18467 let mut rows: Vec<(&String, u64, u128)> =
18468 p.calls.iter().map(|(n, (c, ns))| (n, *c, *ns)).collect();
18469 use std::cmp::Reverse;
18470 rows.sort_by_key(|x| Reverse(x.2)); let total_ns: u128 = p.calls.values().map(|(_, ns)| *ns).sum();
18472 let total_calls: u64 = p.calls.values().map(|(c, _)| *c).sum();
18473 let fr = p.frames.max(1) as f64;
18474 eprintln!(
18475 "\n┌─ LING PROFILE ── frames={} ─ builtin calls by total inclusive time ─────────────",
18476 p.frames
18477 );
18478 eprintln!(
18479 "│ {:<24} {:>9} {:>9} {:>10} {:>9} {:>6}",
18480 "builtin", "calls", "calls/fr", "total_ms", "ms/frame", "%time"
18481 );
18482 eprintln!("├──────────────────────────────────────────────────────────────────────────────");
18483 for (name, count, ns) in rows.iter().take(30) {
18484 let ms = *ns as f64 / 1e6;
18485 let pct = if total_ns > 0 {
18486 *ns as f64 / total_ns as f64 * 100.0
18487 } else {
18488 0.0
18489 };
18490 eprintln!(
18491 "│ {:<24} {:>9} {:>9.1} {:>10.1} {:>9.3} {:>5.1}%",
18492 truncate_name(name),
18493 count,
18494 *count as f64 / fr,
18495 ms,
18496 ms / fr,
18497 pct
18498 );
18499 }
18500 eprintln!("├──────────────────────────────────────────────────────────────────────────────");
18501 eprintln!(
18502 "│ TOTAL {} builtin calls, {:.1} ms over {} frames → {:.0} calls/frame, {:.2} ms/frame in builtins",
18503 total_calls,
18504 total_ns as f64 / 1e6,
18505 p.frames,
18506 total_calls as f64 / fr,
18507 total_ns as f64 / 1e6 / fr
18508 );
18509 eprintln!("└──────────────────────────────────────────────────────────────────────────────");
18510}
18511
18512fn truncate_name(s: &str) -> String {
18515 let max = 24;
18516 if s.chars().count() <= max {
18517 s.to_string()
18518 } else {
18519 let mut t: String = s.chars().take(max - 1).collect();
18520 t.push('…');
18521 t
18522 }
18523}