lingmodeler
↓ 0 downloadsLingModeler: box modeling with quads and tris, rigging + weight painting, 2D vector illustration and animation, procedural textures and game assets; .lmesh / .ling native formats, OBJ, glTF and SVG import / export
Keywords 3d, modeling, vector, svg, gltf, obj, rigging, animation, textures, ling
lingfu install lingmodelerCode / Files (67)
ling/plat_os.ling
# ━━━ plat_os.ling — LingOS platform layer (in-kernel ling runtime: lingrt / lingapp) ━━━
# The kernel has px_* drawing, fonts, files and dialogs but no polygon fill, no images and
# no byte files. Polygons are scan-converted here with px_hline; image features report "unavailable".
# no list_join in the kernel runtime: join pairwise (halves the list each pass)
fn p_join(l, sep) {
if len(l) < 0.5 { return "" }
bind cur = list_new()
bind i = 0.0
while i < len(l) { bind cur = list_push(cur, "" + list_get(l, i)) bind i = i + 1.0 }
while len(cur) > 1.5 {
bind nxt = list_new()
bind i = 0.0
while i < len(cur) {
if i + 1.0 < len(cur) { bind nxt = list_push(nxt, list_get(cur, i) + sep + list_get(cur, i + 1.0)) }
else { bind nxt = list_push(nxt, list_get(cur, i)) }
bind i = i + 2.0
}
bind cur = nxt
}
return list_get(cur, 0)
}
fn p_frame_hook(frame) { if frame % 50.0 == 0.0 { println("lingmodeler frame ", frame, " mem ", os_info("memory")) } }
# The kernel runtime frees nothing yet, so an unchanged screen is not rebuilt. Returns true when nothing happened
# since the last drawn frame (no pointer move, button, wheel, key or animation); the caller then only presents.
fn p_idle_frame() {
bind mx = mouse_x()
bind my = mouse_y()
bind b = 0.0
if mouse_down() { bind b = 1.0 }
if mouse_down_right() { bind b = b + 2.0 }
if mouse_down_middle() { bind b = b + 4.0 }
bind changed = b > 0.5 || mouse_scroll() != 0.0
if mx != st_get("ix_mx", 0.0 - 1.0) || my != st_get("ix_my", 0.0 - 1.0) || b != st_get("ix_b", 0.0) { bind changed = true }
st_set("ix_mx", mx)
st_set("ix_my", my)
st_set("ix_b", b)
bind t = text_poll()
if t != "" { st_set("os_text", st_get("os_text", "") + t) bind changed = true }
if !changed {
if key_pressed("escape") || key_pressed("enter") || key_pressed("tab") || key_pressed("backspace") || key_pressed("delete") { bind changed = true }
if key_pressed("up") || key_pressed("down") || key_pressed("left") || key_pressed("right") || key_pressed("home") { bind changed = true }
if key_pressed("f1") || key_pressed("f2") || key_pressed("f3") || key_pressed("f4") || key_pressed("f5") || key_pressed("f6") || key_pressed("f7") { bind changed = true }
}
if tl_playing() || v3_modal_active() || st_get("vc_drag", "") != "" { bind changed = true }
# after any change keep drawing two more frames so release edges and hover states settle
if changed { st_set("ix_tail", 2.0) return false }
bind tail = st_get("ix_tail", 0.0)
if tail > 0.5 { st_set("ix_tail", tail - 1.0) return false }
return true
}
# kernel text input hands out only what was typed since the last call: keep our own edit buffer
fn p_text_set(v) {
st_set("os_tbuf", v)
st_set("os_text", "")
}
fn p_text_poll() {
bind pend = st_get("os_text", "") + text_poll()
st_set("os_text", "")
bind buf = st_get("os_tbuf", "")
if key_pressed("backspace") && str_len(buf) > 0.0 { bind buf = str_slice(buf, 0.0, str_len(buf) - 1.0) }
bind ok_chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
bind i = 0.0
while i < str_len(pend) {
bind ch = str_slice(pend, i, 1.0)
if str_find(ok_chars, ch) >= 0.0 { bind buf = buf + ch }
bind i = i + 1.0
}
st_set("os_tbuf", buf)
return buf
}
fn p_init() { st_set("plat", 1.0) }
fn p_is_os() { return true }
fn p_has_images() { return false }
# even-odd scanline polygon fill (one contour)
fn p_poly(pts, c, a) {
bind n = floor(len(pts) / 2.0)
if n < 3.0 { return 0.0 }
if p_convex(pts, n) { p_poly_convex(pts, n, c, a) return 0.0 }
bind ymin = 1000000.0
bind ymax = 0.0 - 1000000.0
bind i = 0.0
while i < n {
bind y = list_get(pts, i * 2.0 + 1.0)
if y < ymin { bind ymin = y }
if y > ymax { bind ymax = y }
bind i = i + 1.0
}
bind H = get_height()
bind y = max(0.0, floor(ymin))
bind yend = min(H - 1.0, ceil(ymax))
while y <= yend {
bind sy = y + 0.5
# collect crossings (insertion sorted)
bind xs = list_new()
bind i = 0.0
while i < n {
bind j = i + 1.0
if j >= n { bind j = 0.0 }
bind x0 = list_get(pts, i * 2.0)
bind y0 = list_get(pts, i * 2.0 + 1.0)
bind x1 = list_get(pts, j * 2.0)
bind y1 = list_get(pts, j * 2.0 + 1.0)
if (y0 <= sy && y1 > sy) || (y1 <= sy && y0 > sy) {
bind xc = x0 + (sy - y0) * (x1 - x0) / (y1 - y0)
bind xs = list_push(xs, xc)
# bubble the new crossing into place
bind k = len(xs) - 1.0
while k > 0.0 && list_get(xs, k - 1.0) > list_get(xs, k) {
bind tmp = list_get(xs, k - 1.0)
bind xs = list_set(xs, k - 1.0, list_get(xs, k))
bind xs = list_set(xs, k, tmp)
bind k = k - 1.0
}
}
bind i = i + 1.0
}
bind k = 0.0
while k + 1.0 < len(xs) {
bind xa = floor(list_get(xs, k) + 0.5)
bind xb = floor(list_get(xs, k + 1.0) + 0.5)
if xb > xa { px_hline(xa, y, xb - xa, c, a) }
bind k = k + 2.0
}
bind y = y + 1.0
}
}
fn p_convex(pts, n) {
bind pos = false
bind neg = false
bind i = 0.0
while i < n {
bind j = (i + 1.0) % n
bind k = (i + 2.0) % n
bind ax = list_get(pts, j * 2.0) - list_get(pts, i * 2.0)
bind ay = list_get(pts, j * 2.0 + 1.0) - list_get(pts, i * 2.0 + 1.0)
bind bx = list_get(pts, k * 2.0) - list_get(pts, j * 2.0)
bind by = list_get(pts, k * 2.0 + 1.0) - list_get(pts, j * 2.0 + 1.0)
bind cr = ax * by - ay * bx
if cr > 0.0001 { bind pos = true }
if cr < 0.0 - 0.0001 { bind neg = true }
bind i = i + 1.0
}
return !(pos && neg)
}
# one span per scanline: the leftmost and rightmost edge crossings
fn p_poly_convex(pts, n, c, a) {
bind ymin = 1000000.0
bind ymax = 0.0 - 1000000.0
bind i = 0.0
while i < n {
bind y = list_get(pts, i * 2.0 + 1.0)
if y < ymin { bind ymin = y }
if y > ymax { bind ymax = y }
bind i = i + 1.0
}
bind y = max(0.0, floor(ymin))
bind yend = min(get_height() - 1.0, ceil(ymax))
while y <= yend {
bind sy = y + 0.5
bind xl = 1000000.0
bind xr = 0.0 - 1000000.0
bind i = 0.0
while i < n {
bind j = i + 1.0
if j >= n { bind j = 0.0 }
bind y0 = list_get(pts, i * 2.0 + 1.0)
bind y1 = list_get(pts, j * 2.0 + 1.0)
if (y0 <= sy && y1 > sy) || (y1 <= sy && y0 > sy) {
bind x0 = list_get(pts, i * 2.0)
bind xc = x0 + (sy - y0) * (list_get(pts, j * 2.0) - x0) / (y1 - y0)
if xc < xl { bind xl = xc }
if xc > xr { bind xr = xc }
}
bind i = i + 1.0
}
if xr > xl { px_hline(floor(xl + 0.5), y, floor(xr + 0.5) - floor(xl + 0.5), c, a) }
bind y = y + 1.0
}
}
fn p_tri(x0, y0, x1, y1, x2, y2, c, a) { p_poly([x0, y0, x1, y1, x2, y2], c, a) }
fn p_img_new(w, h) { return 0.0 - 1.0 }
fn p_img_set(id, x, y, r, g, b, a) { return 0.0 }
fn p_img_save(id, path) { return 0.0 - 1.0 }
fn p_img_load(path) { return 0.0 - 1.0 }
fn p_img_w(id) { return 0.0 }
fn p_img_h(id) { return 0.0 }
fn p_img_r(id, x, y) { return 0.0 }
fn p_img_g(id, x, y) { return 0.0 }
fn p_img_b(id, x, y) { return 0.0 }
fn p_img_draw(id, x, y, w, h) { return 0.0 }
fn p_dlg_open(title, exts) { return dlg_open(title, exts, st_get("last_dir", "")) }
fn p_dlg_save(title, exts, name) { return dlg_save(title, exts, name, st_get("last_dir", "")) }
fn p_dlg_folder(title) { return dlg_folder(title, st_get("last_dir", "")) }
fn p_write_bytes(path, bytes) { return false }
fn p_shot(name) { screenshot(name) }
fn p_quit() { close_window() }