3B: çizimde dönen parçalar
İsteğe bağlı 3B mod ("dönen gruplar"), bir şeklin bazı parçalarının rijit cisimler olarak hareket etmesini sağlar: dönen bir kadran ya da döner tabla, bükülen parmaklar, kopup yuvarlanan bir parça. Bu parçalar her karede kitin kendi kurallarıyla yeniden izdüşürülür ve gölgelendirilir ve ayırıcı düzlemlerden çözülen bir sırayla boyanır. Geri kalan her şey sıradan statik kit sanatı olarak kalır. Bunu seçmeyen bir şekle dokunulmaz.
Durum: beta. Motor examples/turning-dial üzerinde ve tek bir test şeklinde (yalnızca bu belgeden kurulmuş bir masa vantilatörü) kanıtlanmıştır, henüz bir üretim şeklinde değil. Bir seferde tek bir konuyu döndürün, dağınıklığı statik tutun ve parça eklemeden önce şeklinizi bölüm 15'e göre ölçün.
İçindekiler
- Bir şekil 3B'yi ne zaman hak eder
- Model
- Minimal bir sayfa
- Yazım API'si
- Parça türleri ve onları doğru tutan kurallar
- Malzemeler
- Serbest gruplar: kopan parçalar
- Menteşe zincirleri: eller ve parmaklar
- Ressamın sırası ve neden doğru olduğu
- Gölgelendirme ve sönmeler
- Statik katmanlar ve bir döner tablanın etrafında dağınık bir atölye
- Dönüşü ve grupları izleyen shader'lar
- Yörünge denetimi
- Tarayıcı kontrolleri
- Performans
- Bilinen sınırlar
1. Bir şekil 3B'yi ne zaman hak eder
Yalnızca hareketin kendisi açıklama olduğunda: rakamları dönerken okunan bir kadran, bir nesneyi her yönden gösteren bir döner tabla, bir çark paketi, kavrayan ya da kıran bir el. Asla süs olarak değil. Yalnızca bir parçanın kaymasına ya da bir ibrenin salınmasına ihtiyaç duyan bir şekil 2B kalır: translateAlong ve yeniden çizilen bir ibre daha ucuz ve kesindir.
2. Model
- Gruplar. Bir grup pozu olan rijit bir çerçevedir. Türler:
turn(pivot'tan geçen dikey bir eksen etrafında, herhangi bir açı),slide(bir yön boyunca,0…travel),hinge(bir noktadan geçen herhangi bir eksen etrafında,rangeiçinde, derece cinsinden) vefree(herhangi bir dönme artı bir öteleme; şekil tarafından her karede ayarlanır). Gruplar iç içe geçer: bir poz ebeveyne görelidir. - Dinlenme. Her kurucu dinlenmedeki dünya koordinatlarını alır (her grup değeri 0, her serbest grup
null). Motor her grubun orijinini çıkarır; böylece kaydedici (A.recorder), kit ve motor tek bir dünyayı paylaşır. - Canlı katmanlar. Canlı bir katman sahnedeki tek bir
<svg class="iso-svg iso-live" data-live="name">'dir. Canlı ve statik katmanlarT.stack([...])sırasına göre yığılır: örneğinback svg → [dial live] → back canvas → [hand live] → front canvas → front svg. - Özdeşlik. Bir grubu dikey bir eksen etrafında θ kadar döndürmek, tam olarak kitin
a − θazimutundaki çizimidir; çünkü kitin ışığı kameraya kilitlidir. Bu yüzden bir dönme grubunun her karesi kitin P′(θ)'daki kendi resmidir, ötelenmiş olarak; P′(θ)'daki kit kurucuları sadakat referansıdır ve kitin her ekran uzayı denetimi P′(θ)'da değişmeden çalışır. Menteşeler ve serbest gruplar bunun yerine matrisleri birleştirir (doğru, biraz daha yavaş).
3. Minimal bir sayfa
examples/turning-dial/ referans sahnedir: tırtıllı, çentikli ve rakamlı bir kadran, ikinci bir dönme grubu (bir pencerenin altındaki bir gösterge çarkı), bir menteşe (tırtılın üzerinde gezen bir mandal), malzemeli yaklaşık 80 parçalı bir el, kalkan üç menteşeli bir parmak, serbest bir grup olarak kopan altıgen bir kapsül ve bir WebGL LED'i, ışık havuzu ve kıvılcımlar. examples/turning-dial/stress.mjs performans sahnesidir (dağınık bir atölyede bir döner tabla üzerinde yoğun bir el); denetimden temiz geçmez, bu yüzden kalıpları build.mjs'ten alın.
İskeletine indirgenmiş build.mjs:
import * as k from "../../kit/iso-kit.mjs";
import * as A from "../../kit/audit.mjs";
import { turning } from "../../kit/turn-build.mjs";
import * as TA from "../../kit/turn-audit.mjs";
import { turnScript, glScript } from "../../scripts/inline-kit.mjs";
export const P = k.fitProjection(points, W, H, { pad: 22, azimuth: 40, elevation: 31 });
export const R = A.recorder(P);
export const T = turning(P, { recorder: R });
const dial = T.group("dial", { turn: { pivot: [0, 0] } });
const lift = T.group("f2.mcp", { parent: dial, hinge: { point: C1, axis: w, range: [-22, 0] } });
const hand = T.layer("hand");
T.stack(["back", "hand", "front"]);
export const follow = (values) => ({ ...values, "f2.mcp": -22 * (values.lift ?? 0) });
export const poses = () => Array.from({ length: 720 }, (_, i) => follow({ dial: i * 0.5, lift: (i % 90) / 90 }));
T.round({ name: "dial.body", group: dial, layer: hand, F: VERT(0, 0), s0: 10, s1: 14.4, r: 50, material: "steel", details: { ribs: { s0: 10.9, s1: 13.7, count: 150, seams: true, tone: "lo" } } });
T.prism({ name: "hand.palm", group: dial, layer: hand, plan: PALM, z: 19.4, h: 8, bevel: 0.9, material: "gold" });
T.tube({ name: "cable.red", group: dial, layer: hand, route, r: 0.7, hue: "red", material: "rubber", gaps });
// … statik kit sanatı: her zamanki gibi R.put({ name, layer: "back", svg, shapes }) …
const out = T.build({ statics: R.items, poses, verify: argv.includes("--verify") });
A.settle(R, P);
if (argv.includes("--audit")) {
A.auditOrExit(R, P);
TA.orbitOrExit(T, R, P, { sweep: { at: (x) => follow({ dial: x }), from: 0, to: 360, frame: 0.3 }, clips: [{ name: "tap", at: (t) => follow({ dial: 40 + 12 * t, lift: tapAt(t) }), from: 0, to: 4, frame: 1 / 60 }] });
}
const stage = `<div class="iso-stage iso-turn" tabindex="0" role="slider" …>${backSvg}${out.layerSvg("hand", { width: W, height: H })}${frontSvg}</div>`;
const page = k.pageHtml({ title, theme, body: k.plateHtml({ …, body: `<style>${out.css}</style>${stage}` }), script: turnScript() + LIVE });
live.js:
const controller = TURN.mount(stage, DATA.turn, { follow, onHold: () => (touring = false), onFrame: () => paint() });
function tick(now) {
…
controller.set({ dial: angle, lift });
frame = visible && moving ? requestAnimationFrame(tick) : 0;
}
Döngünün sahibi şekildir (motion.md); kontrolcü asla kendi requestAnimationFrame'ini çalıştırmaz. out.css, TURN_CSS'tir (yüz bandı kuralları, malzemeler, renkli borular ve .iso-turn yığınlaması): bir kez ekleyin.
React ya da Next.js'te (references/react.md): turn.mjs, turn-build.mjs ve turn-audit.mjs'i içe aktarmaları yeniden yazılmış olarak (./iso-kit.mjs → ./kit vb.) kitin yanına kopyalayın ve TURN_CSS'i bir kez içe aktarılan bir turn.css olarak koyun. Geometri modülü (sunucu) turning(…) ve T.build(…)'i çalıştırır ve sahne işaretlemesini (her canlı katman için out.layerSvg(name, { width, height }), yığın sırasıyla statik SVG'ler ve tuvaller arasında) artı şeklin verisinde out.data'yı döndürür. İstemci bağlaması TURN.mount(stageElement, data.turn, { follow, onHold, onFrame })'u çağırır ve controller.set(...)'i kendi döngüsünden sürer; effect'in temizliğinden controller.destroy'u döndürün. Bir doğrulama derlemesi out.verify'ı veriye koyabilir ve turn-check --order için bağlamanın window.__isoVerify'ı atamasını sağlayabilir.
4. Yazım API'si
Sahne
| Çağrı | Ne yapar |
|---|---|
turning(P, { recorder, scale = 1 }) |
Kurucu T. Her canlı şekil kit denetimi için de kaydedilsin diye kit kaydedicisini verin. |
T.group(name, { parent?, turn: { pivot: [x, y] } }) |
pivot'tan geçen dikey etrafında dönme, değer derece cinsinden. |
T.group(name, { parent?, slide: { direction, travel } }) |
Öteleme, değer 0…travel dünya birimi. |
T.group(name, { parent?, hinge: { point, axis, range: [lo, hi] } }) |
point'ten geçen axis etrafında dönme, değer range içinde derece cinsinden. |
T.group(name, { parent?, free: { origin } }) |
Serbest bir rijit cisim (bölüm 7). Değer { R: [9] | q: [x, y, z, w] | axis, angle, t: [3], world? } ya da dinlenme için null. |
T.layer(name), T.stack([…]) |
Canlı katmanlar ve canlı ve statik katman adlarının arkadan öne tam yığını. |
T.build({ statics, poses, verify = false, strict = true }) |
Her çifti çözer, her statik öğenin katmanını kanıtlar, sayfa verisini paketler. poses şeklin ulaşılabilir durumlarıdır (bir liste ya da bir liste döndüren bir fonksiyon): örneklenmiş her hareket klibi. Ayırıcı düzlemi olmayan herhangi bir çiftte, kanıtlanamayan herhangi bir statikte, dışbükey olmayan herhangi bir parçada okunabilir bir hata fırlatır. strict: false kilitlenmiş çiftleri korur (böylece yerleştirilmiş bir hata denetime ulaşır). |
T.build, out = { svg, data, css, report, layerSvg(name, { width, height }), verify } döndürür. out.data sayfaya gider (DATA.turn). out.report'ta parts, layers (katman başına: planar, framed, dynamic, skip sayıları), tight, margins, proofs, seconds vardır.
Kurucular
Her kurucudaki ortak seçenekler: { name, group, layer, tone = "hi", crease = "faint", lit = false, material, owner, details = {} }.
| Kurucu | Parça |
|---|---|
T.prism({ plan, z, h, steps, bevel }) |
Dik bir kit levhası (kitin extrude kuralı). |
T.prism({ F, polygon, s0, s1, round = 0, steps = 4, bevel = 0 }) |
F = { o, a, u, v } çerçevesindeki dışbükey bir çokgen, F.a boyunca s0'dan s1'e uzatılmış, herhangi bir eksen (prismOf kuralı). |
T.round({ F, s0, s1, r | [r0, r1], ends = ["flat", "flat"], fixed }) |
Silindir ya da kesik koni; her uç "flat", "dome" (yarıçap yanınkine eşit olmalı: bir kapsül) ya da "open". Bir kubbe ve bir düz uç bir yarım kapsüldür ve her uzunluk çalışır, yarıçaptan kısa olanı bile (bir motor kutusundaki burun konisi). Dik bir gruptaki dikey bir yuvarlak otomatik olarak fixed bir resme dönüşür (fixed: false bunu durdurur). |
T.lathe({ F, profile, slope, radius }) |
Genel bir dışbükey pürüzsüz gövde (r(s) içbükey). Maliyetli; yuvarlakları tercih edin. |
T.ball({ c, r, flats: [{ n, d }] }) |
Bir küre; her düzlük n·(p − c) ≤ d'yi korur ve kenarını bir kapak olarak çizer; bu yüzden n düzlüğün dış normalidir. Düzlükler olmadan fixed bir resimdir ve details almaz. Çizilmesi en maliyetli türdür (bölüm 15). |
T.tube({ route, r, chunk = 6, turn = 20, gaps, breaks, caps, rings, hue, material, bundle, owner, touch }) |
Kitin tubePieces anlamı, parçalara bölünmüş; hue, "red" | "green" | "blue"'dur; R.route kaydeder. Yol bağlantı yüzeyinden bağlantı yüzeyine uzanır. Her uçtaki port, salmastra ya da kelepçe, owner'ı borunun adına ayarlanmış ayrı bir parçadır ve yol boyunca açıklığı gaps'e gider. Bir portun dış yüzünde başlayan bir yol kit denetiminde terminals'tan kalır (aşağıda). |
T.fixed({ svg, anchor, shapes, hull, F }) |
Resmi grubun hareketi altında hiç değişmeyen, translate ile taşınan herhangi bir kit işaretlemesi. |
T.plane({ part, o, u, v, svg, fade = [0.04, 0.3], normal }) |
Parçanın bir düzlemindeki çizgi ya da metin işaretlemesi (çentikler, rakamlar, etiket plakaları); tek bir matrisle yerleştirilir, yönüne göre söner. |
T.billboard({ part, at, normal, svg, fade }) |
İzdüşürülmüş bir noktada ekrana hizalı işaretleme (bir shader parlaması için bir SVG yedeği), yönüne göre söner. svg dünya birimlerinde değil, izdüşürülmüş noktada ortalanmış viewBox px cinsindendir: dünya boyutlarını G.scaleOf(P) ile çarpın (<circle r="${(1.1 * G.scaleOf(P)).toFixed(2)}"/> 1.1 yarıçaplı bir merceği doldurur). |
T.split(part, { n, d }) |
Düzlemle kesilmiş iki parça (prizma: eksen boyunca ya da ona dik içinden geçen herhangi bir düzlem; yuvarlak: eksene dik). Kesik kenarlar konturlanmaz. Gerçek bir döngüyü kırmak için kullanın. |
Ayrıntılar (parçanın çerçevesinde: eksen boyunca s, u ve v'de x ve y)
Her kurucunun kendi çerçevesi vardır ve kendi ayrıntı türlerini alır. Diğer her şey uyarısız yok sayılır.
| Kurucu | Çerçeve [s, x, y] |
Aldıkları |
|---|---|---|
T.round, T.lathe |
F: F.a boyunca s, F.u boyunca x, F.v boyunca y |
seams, ribs, bolts, dots, rings, rules |
T.prism({ F, polygon }) |
F |
dots, seams, bevel |
T.prism({ plan, z, h }) |
Dünya: s z'dir, x ve y dünya x ve y'sidir |
dots, seams (z değerleri), bevel |
Düzlüklü T.ball |
Orijin c, a = ilk düzlüğün n'i, u ve v G.frameAlong(c, n)'den. Düzlük s = d'de ve uzak kutup s = −r'de |
bolts, dots, rings, rules |
Düzlüksüz T.ball |
Yok: sabit bir resim | Hiçbir şey |
T.fixed({ F }) |
F (F yoksa: ayrıntı yok) |
bolts, dots, rings, rules |
Kubbeli bir döndürücüdeki T.ball({ c, r: 4.5, flats: [{ n: [-1, 0, 0], d: 0 }] }) (düzlük −x'e, kubbe +x'e bakar) bir merkez noktası at: [-4.5, 0, 0], normal: [1, 0, 0]'da durur: s = −r kutuptur. [4.5, 0, 0] düzlüğün 4.5 arkasına, döndürücünün bağlı olduğu şeyin içine düşerdi. Bir prizma yüzündeki bir halka ya da çizgi için T.plane kullanın.
details: {
seams: [s…], // yuvarlaklarda halkalar, prizmalarda halka dikişleri
ribs: { s0, s1, count, phase, twist, fade: [0.1, 0.55], seams: true, tone }, // yuvarlaklarda tırtıl sıraları
bolts: [{ s, r, count, phase, size: 0.5, tone: "mid", fade: [0.04, 0.42] }], // cıvata çemberleri
dots: [{ at: [s, x, y], normal, size, tone }],
rings: [{ at: [s, x, y], normal, r, tone }], // düzlemlere dönüşür
rules: [{ points: [[s, x, y]…], tone, free }], // düzlemlere dönüşür
bevel: 0.4…1.6, // prizma kapak pahı (sönmüş yuvalar)
}
Her ayrıntı yönüne göre söner: noktalar ve cıvatalar smoothstep(0.04, 0.42, n·V) ile, nervürler nervür başına, pah çizgileri kenar başına, düzlemler grubun opaklığıyla. Hiçbir doku tam güçte durmaz.
Çalışma zamanı (kit/turn.mjs, turnScript() ile gömülür)
| Çağrı | Ne yapar |
|---|---|
TURN.mount(stage, data, { onHold, onFrame, follow }) |
stage'deki her canlı katmanı bağlar, dinlenme pozunu çizer, window.__isoTurn'ü kurar. follow(values) her set'te şekil değerlerini grup değerlerine eşler (tek bir girdi bağlı grupları sürer). onFrame(controller) her çizimden sonra çalışır (shader'ları burada çizin). onHold() bir araç kancayı çağırdığında çalışır. |
controller.set(values | θ, { detail }) |
Grup değerlerini ayarlar (bir sayı ilk dönme grubunu ayarlar), eşzamanlı olarak çizer, istatistikler döndürür. detail (0…1) sönmüş çizgileri, nervürleri ve noktaları ölçekler (bölüm 15). |
controller.angleAt(clientX, clientY, z, group) |
İşaretçi, z yüksekliğindeki düzlemde grubun pivotu etrafında açılmış bir açı olarak (bir kadranı sürükleme). |
controller.pose(name) |
Grubun dünya pozu { R, t }. |
controller.detach(name) |
Serbest bir grubun mevcut pozunu dünya çerçevesinde sürdüren { world: true, R, t } değeri (sıçrama yok). |
controller.anchor(group, restPoint, restDir?) |
Bir grubun dinlenme-dünyası noktasının (ve yönünün) şimdi nerede olduğu: { world, screen, direction }. Kıvılcım kaynakları, alev kökleri, LED konumları. |
controller.cover(names, options), order(layer), orderIndex(layer), events(), stats(), frames(), cams(), scene(), values, destroy() |
Örtü maskeleri (bölüm 12), boyama sırası, sıra değişikliği olayları, { ms, writes, moved, forced, refined, unseparated, emitted }. |
TURN.cover(controller, names | [names, names…], { polygons = 6, edges = 10, pad = 0.3, layers }) |
coverOf için { uCoverEdge, uCoverCount, dropped, parts }. |
TURN.poseUniforms(controller, group, prefix = "uGroup") |
toGroup(p)'nin grubun dinlenme-dünyası koordinatlarını vermesini sağlayan { prefixX, prefixY, prefixZ, prefixT }. |
TURN.materials, TURN.materialCss() |
Malzeme adları; yalnızca malzeme CSS'i (zaten TURN_CSS içindedir). |
TURN.exact(on) |
Yol koordinatlarını 0.01 px'e değil, yuvarlamadan (6 ondalık) yazar. Yörünge denetimi bunu süreklilik satırı için açar; bir sayfanın buna hiç ihtiyacı olmaz. |
TURN.rotation(axis, degrees), TURN.quat(q), TURN.compose(A, B) |
Serbest pozlar için diziler olarak 3×3 dönmeler. |
TURN.floorOf(scene, group, R) |
R dönmesi altında bir grubun parçalarının en düşük z'si (orijinine göre): bir parçayı aynı hizada yere koymak için. |
window.__isoTurn |
{ controller, set, get, order, events, stats, layers }; her çağrı şekli tutar (onHold). set bir sayı ya da bir değerler nesnesi alır. |
5. Parça türleri ve onları doğru tutan kurallar
Bunlar bağlayıcıdır; derleme ve denetim onlar olmadan başarısız olur.
Yalnızca dışbükey parçalar. Basamaklı bir kadran katmanlardır; çentikli bir çark bir göbek diski artı kenar dilimleridir; bir U braket üç prizmadır; bir elin kabuğu plakalardır.
T.prismçokgenini,T.latheprofilini kontrol eder.Temaslar düz, teğet ya da düzlük üzerinde kenardır. Bir küre üzerindeki kenar ayrılabilir değildir: bilyeye bir düzlük verin (
T.ball({ flats })) ve kenarı onun üzerine indirin. Ev sahibine gömülü bir ped canlı bir katmanda izin verilmez (separation,interlock A × B −0.6ile başarısız olur).Eklemler. İki bağlantı en az 0.2 uzakta kalır ve eklem alt grup üzerindeki bir mafsal diskiyle köprülenir: menteşe ekseniyle eş eksenli ve ebeveynin bağlantısına aynı hizada ya da teğet değen. Menteşesiyle eş eksenli bir disk (ya da kendi düzleminde dönen herhangi bir düz yüz) menteşe altında değişmezdir; bu yüzden asla çarpışmaz ve kit denetiminin ihtiyaç duyduğu temas odur. Alt grupta ebeveyne değen hiçbir şey yoksa
rigid, alt grubun her parçasınısupport looseolarak bildirir. Bir eklemde buluşan bağlantılar tek bir yarıçap paylaşır; kubbe merkezleri eklemden aynı uzaklıkta durur ki boşluk her açıda korunsun (eklemden c uzaklıkta r yarıçaplı iki kubbe cos(β/2) ≥ r/c olduğu sürece ayrı kalır). Bir çatal üzerinde bir muylu:T.round({ name: "yaw.arm+", group: yaw, layer, F: VERT(0, 11.7), s0: 40, s1: 61, r: 1 }); T.round({ name: "head.can", group: tilt, layer, F: frameOf([0, 0, 56], [1, 0, 0], [0, 0, 1]), s0: -12, s1: 8, r: 9 }); T.round({ name: "head.trunnion+", group: tilt, layer, F: frameOf([0, 9, 56], [0, 1, 0], [0, 0, 1]), s0: 0, s1: 1.7, r: 4.5 });tilt,[0, 0, 56]'dan geçen y etrafında menteşelenir. Kutu y = 9'a ulaşır ve kol y = 10.7'de başlar; böylece bağlantılar 1.7 uzaktadır. Muylu 9'dan 10.7'ye uzanır: kutunun üzerinde durur ve uç yüzü kola teğettir. Onu kola 0.2 boşlukla 10.5'te bitirmek bütün kafa içinrigid'i başarısız kılar.Hiçbir canlı çizgi iki hareketli grubu kapsamaz. Bükülen bir eklemi geçen bir kablo ya da hortum, eklemin her iki yanında bir klips ya da portta biten iki yoldur ya da menteşe ekseninde döner bir bağlantıdan (banjo) geçer. Statik zeminden dönen bir parçaya ulaşması gereken bir kablo eksenden yukarı göbekteki bir kayar halkadan geçer (bölüm 11).
Borular neredeyse düz ya da bağlantı parçalarının içinde bükülür. Yataydan izleyiciye doğru θ eğimli bir düzlemde R yarıçaplı bir büküm, ekranda en sıkı yarıçapı R·sin²(e − θ) olan bir elipstir (dünya birimlerinde; px için ×
G.scaleOf(P)).folds, ekranda 2r'den az bir yarıçap etrafında 90°'den fazla dönen bir bükümü,selfise r'nin altındakini başarısız sayar. Bu yüzden bir büküm R ≥ 2r / sin²(e − θmax) gerektirir; burada θmax düzlemin kendi eğimi artı bindiği her menteşenin eğim aralığıdır (bir dönme grubu eğimi her azimuttan geçirir, bu yüzden en kötü durum her zaman gelir). e = 30°'de:θmax 0° 5° 8° 10° 12° 15° 20° R ≥ 8r 11r 14r 17r 21r 30r 66r 8r yatay bir düzlemde bir pay değil, sınırın ta kendisidir: 10r alın. Yaklaşık 15°'yi geçince pratik hiçbir yarıçap tutmaz; bu yüzden bir bağlantı parçasının (klips bloğu, banjo, dirsek bilyesi, bir bağlantı kutusu) içinde dönün ya da hattı düz uzatın. Bükümleri görüşe bağlı fileto ile değil,
G.fillet(points, R)ile çizin. Denetiminfoldsveself'i her açıda ve pozda çalışır.Borular asla görüş yönüne bakmaz. Bir borunun teğet yüksekliği e = 30°'de her pozda 15°–45° ve −45°–−15° dışında kalır (
endon): bir menteşe zincirinde bağlantının eğim aralığını da kontrol edin (30° bükülen bir bağlantıdaki bir kablo başarısız olur).Torna parçaları küçük artık basamakları olan tek türdür (bir bandın topolojisinin değiştiği yerde ≤ 2.5 ton·px²). Canlı parçalar için yuvarlakları, kubbeleri ve düzlüklü bilyeleri tercih edin.
6. Malzemeler
Bir parçanın malzemesi kurucusundaki tek bir seçenektir (material: "gold"). Tonlar kitinkiler gibi tam olarak yönden gelir; bir malzeme rampayı yeniden eşler (dört gölge, üst, kâğıt, dış hat ve çizgi tonları, noktalar ve boru gövdesi, parlaklık ve gölge); her birinin bir açık ve bir koyu takımı vardır.
| Malzeme | Görünüş |
|---|---|
gold |
Şampanya altını eloksallı alüminyum: sıcak soluk üst, bronz gölgeler, koyu bronz dış hat. |
chrome |
Parlak cilalı çelik: açık bir uzak bant, parlak bir bant ve beyaz bir parlaklığın yanında koyu bir yansıma bandı (n·L 0.2–0.42). Bilerek monoton değil: bantlar yönle hareket eder, asla sıçramaz. |
steel |
Saten çelik, monoton ve serin. |
gunmetal |
Açık noktalı koyu mavi-gri metal. |
rubber |
Yumuşak parlaklıklı siyah kauçuk ve kablo kılıfı. hue ile bir boru renkli bir kılıf olur (renk tonu kauçuk rampasına karıştırılır). |
brass, copper |
Bağlantı parçaları, portlar, valfler için sıcak metaller. |
Statik parçalar aynı aileleri düz sınıflar olarak kullanır: kit işaretlemesini <g data-mat="brass">…</g> ile sarın (örnekteki gösterge kaması). Kurallar TURN_CSS içinde yaşar; yalnızca statik sanatı olan bir sayfa TURN.materialCss()'i ekleyebilir. Açık bir sayfadaki koyu bir kart koyu takımı seçer (.iso[data-theme="dark"] [data-mat] en sona sıralanmıştır).
Açık bir sayfada malzemeli bir şekil kit grilerinden çok daha güçlüdür: zemini (tezgâh, taban, plakalar) kit tonlarında tutun ve malzemeleri konuya koyun; böylece aydınlatılmış, bitmiş nesne olarak okunur.
7. Serbest gruplar: kopan parçalar
T.group("pod", { parent: dial, free: { origin } }), pozunu şeklin her karede ayarladığı rijit bir cisim yapar. Dinlenmede (null) tam olarak kurulduğu yerde, bağlı olarak durur: ebeveyn onu taşır. Onu koparmak için ebeveyne göre bir poz ayarlamaya başlayın: ilk değer özdeşlik olduğundan tek karelik bir değişiklik olmaz.
- Döner tablaya düşme: ebeveyni döner tabla grubu olarak tutun; böylece parça iner ve sonra onunla döner. Uçuşu zamanın saf bir fonksiyonu olarak hesaplayın (örnekteki
breakAt(t): ağırlık merkezi etrafında bir takla, balistik bir yay,TURN.floorOfile bulunan aynı hizada bir iniş, sönümlenen bir kayma ve sapma). Saf bir fonksiyon derleme ve denetim tarafından örneklenebilir ve geriye oynatılabilir. - Tezgâha düşme: mevcut pozu sürdüren
{ world: true, R, t }döndürencontroller.detach(name)ile dünya uzayına geçin, sonra oradan entegre edin. - Sıra: serbest bir grubun parçaları çalışma zamanında katmanlarının her parçasıyla eşlenir (ekran kutusuyla, sonra göreli poz başına önbelleğe alınan bir GJK düzlemiyle); grup içi çiftler düzlemseldir. Serbest parçalar arasında döngüler ilke olarak mümkündür; denetim klipleri örnekler ve bunları bildirir.
- Kanıt: derlemeye klipleri
poses()içinde verin (kopmanın her karesi, birkaç dönme açısında) ve denetime aynı klipleri (clips). Denetim her şekli her klip karesinde pozlar ve canlı ve statik parçalara karşı açıklıkları (posed), sırayı, döngüleri ve sürekliliği kontrol eder.
8. Menteşe zincirleri: eller ve parmaklar
Her bağlantı, ebeveyni önceki bağlantı olan bir menteşe grubudur (T.group("f0.j1", { parent: f0j0, hinge: { point: J1, axis: w, range } })). Bir bilek avucu taşıyan bir menteşedir; bir başparmak tabanı başparmak zincirini taşıyan bir menteşedir (dikey eksen). Zinciri follow üzerinden tek bir şekil değerinden sürün (lift → üç açı).
- Farklı bağlantılardaki çiftler mümkün olduğunda derlemede çözülür: derlemenin
poses()'u üzerindeki süpürülmüş hacimleri ortak atalarının çerçevesinde ≥ 0.1 paylı bir düzleme sahip bir çift çerçeveli bir düzlem olur (çalışma zamanı GJK'sı yok). Yalnızca ekleme yakın çiftler dinamik kalır (GJK, sıcak başlatılmış, iki grup göreli pozlarını korurken önbelleğe alınmış). Bu yüzdenposes()hareketi kapsamalıdır: bir hareket örneklenen aralığın dışına çıkarsa denetiminunseparatedsatırı çifti adlandırır ("framed plane: … add it to poses()"). - Aralıkları fiziksel ve küçük tutun: eklem başına ±5°'lik üç bağlantılı bir parmak zaten hareket eden bir el gibi okunur; eklem başına 20° her yolun ve temasın yeniden kanıtlanmasını gerektirir.
- Menteşeli bir bağlantıdaki yol, bağlantının eğim aralığını taşır. Bölüm 5'in
endonkuralı ve büküm sınırı ikisi de dinlenmedeki eğimi değil, hattın ulaşabileceği eğimi kullanır: ±12°'lik bir menteşede R = 8.6r'de (düz bir düzlem için 8r kuralının içinde) yatay 90°'lik bir bükümfoldsveself'ten kalır, çünkü 12° eğimde 21r gerektirir. Hattı bir bağlantı kutusuna düz uzatın ya da bükümü ebeveyne taşıyın.
9. Ressamın sırası ve neden doğru olduğu
Ekranda örtüşebilecek her parça çifti tek bir ilişki alır: ortak gruplarında sabit bir düzlem (düzlemsel), ortak atalarının çerçevesinde sabit bir düzlem (çerçeveli), her karede bir GJK düzlemi (dinamik, birbirine göre hareket eden farklı gruplar) ya da bir atlama (bir yolun komşu parçaları). O çerçevede n·V > 0 olduğunda B, A'nın önündedir. Sıra, önceki karenin sırasıyla anahtarlanmış kararlı bir Kahn sıralamasıdır; bir tıkanmada tam ekran zarfları ayrı olan (ya da 0.1 px içinde değen) parçalar arasındaki kenarlar düşürülür; kalan bir tıkanma, sayılan ve denetim tarafından ölümcül kılınan zorunlu bir seçimdir.
Doğrudur, çünkü izdüşümleri örtüşen iki ayrık dışbükey katı ikisi birden bir görüş ışını tarafından vurulur ve ayırıcı bir düzlem o ışını bir kez keser. Titreşimsizdir, çünkü bir düzlemin ilişkisi yalnızca düzlem kenardan göründüğünde döner ve o zaman iki iz bir çizginin karşı taraflarında durur: onları değiştirmek hiçbir pikseli değiştirmez. Kenardan görünen ilişkiler (|n·V| < 1e-7) kenar eklemez.
10. Gölgelendirme ve sönmeler
- Düz yüzler (prizma yanları, kapaklar, bilye düzlükleri) kitin eşiklerini (dik prizmalar için
EB, aksi haldeLB) pürüzsüzleştirilmiş bir merdivenle kullanır: bir yüz, bir eşiğin ±0.035'i içinde olmadıkça kit tonunu tutar; orada bant başına 64 adımda çapraz geçiş yapar (data-q, 0…256). Bu ince adım kromun yüksek kontrastlı bantlarını pürüzsüz tutar. - Süreklilik satırı yuvarlanmamış geometriyi ölçer. Sayfa yol koordinatlarını 0.01 px'e yuvarlanmış yazar; bu bir kenarı en fazla 0.005 px taşır, bir aygıt pikselinin çok altında. Bu yuvarlanmış yollar üzerinde ölçüldüğünde uzun bir kenarın yuvarlama adımları asla küçülmez; bu yüzden yarılama testi bir zamanlar büyük düz yüzleri (örneğin avucu, bir masa vantilatörünün 21 yüksekliğindeki prizma kolları) var olmayan sıçramalar olarak okudu.
orbitbu yüzden süreklilik satırı için çalışma zamanını tam koordinatlara (TURN.exact(true)) geçirir ve sonrasında geri alır; büyük düz yüzler dönen gruplarda sorun değildir. - Eğri yüzeyler her zaman analitik eş ışık bantlarını (
labelledArcs) kullanır, asla fasetleri değil. Kapsül kubbeleri iç içe zarflardır; düzlüklü bilyeler kırpılmış kapaklardır. Bantlar sıfır genişlikte doğar. - Sönmüş çizgi yuvaları nervürleri ve pahları tutar: 1/32'ye nicelenmiş alfalar, derlemenin yörünge taramasının bir kümenin ihtiyaç duyduğunu gösterdiği kadar yuva.
lit: trueaydınlatılmış belirteçleri kullanır; bir malzemeyle, malzemenin aydınlatılmış takımını.
11. Statik katmanlar ve bir döner tablanın etrafında dağınık bir atölye
Her statik öğe layer taşır (R.put({ …, layer: "back" })) ve derleme, tüm ulaşılabilir durumlar üzerinde her canlı katmanla sabit bir ilişkiyi, geçerli olan ilk kuralla kanıtlar: bir yatay düzlem (öğe tamamen katmanın z aralığının altında ya da üstündedir), silindir kuralı (öğe görüşün zemin yönü boyunca dönme grubunun süpürülmüş silindirinin tamamen arkasında ya da önündedir), ekranda asla örtüşmeme ya da bir süpürülmüş zarf düzlemi. Aksi halde: static S interleaves live layer Λ: split S at z = …, or move it.
Yoğun bir atölye için yazım kuralları (examples/turning-dial/stress.mjs 450 ilişkiyi kanıtlar: tezgâh, döner tabla tabanı, güç ünitesi, alet rafı, kablo makarası, tepsi, anahtar, üç tezgâh kablosu):
- Dağınıklığı süpürülmüş silindirin dışında ya da döner tabla düzleminin altında tutun. Uzun şeyler (güç üniteleri, raflar, lambalar) görüşün zemin yönü (cos a, sin a) boyunca R yarıçaplı silindirin (canlı katmanın eksenden erimi) arkasına ya da önüne gider. Yanındaki, ne arkasında ne önünde olan şeyler alçak olmalıdır.
- Döner tablanın en düşük canlı z'sinin altındaki her şey, tezgâhta önde dursa bile canlı katmanın arkasındadır.
front'a değil, canlı olanın altındaki bir katmana (back) aittir: derleme bunu söyler. - Dönen parçaya ulaşması gereken bir kablo tezgâhta sabit tabana uzanır, içinde tırmanır ve dönen parçaya göbekteki bir kayar halka ya da döner eklemden ulaşır. Statik uzunluğu tabandaki bir porta çizin ve canlı kabloları dönen tabla üzerindeki bir klemens bloğundan başlatın. Hiçbir yol statik zemini ve dönen bir grubu birlikte kapsamaz.
- Statik kablolar tezgâhta düz yatar (tezgâh düzleminde bükümler, R ≥ 8r); böylece asla katlanmaz ve bir portta, bir makarada ya da
freeişaretli serbest bir uçta biter. Tezgâhta yatan bir kablo (merkezi tezgâh üstü + r'de) ona değer; yol bunu belirtmedikçe kitinclearancekontrolü bunu başarısız sayar (close stand.feed × stand.bench clearance 0.00 < 0.40):R.route(name, route, r, { owner: name, limp: true, touch: ["stand.bench"] }). stress.mjsbir performans ve katman kanıtı sahnesidir, denetimden temiz geçen bir sahne değil: tezgâh kablolarındatouchyoktur ve elinde gevşek bağlantılar vardır; bu yüzden kit denetimi onu başarısız sayar. Yazım kalıplarınıbuild.mjs'ten kopyalayın.
12. Dönüşü ve grupları izleyen shader'lar
kit/gl.mjs şunları kazanır (ikisi de isteğe bağlı; eski parçalar yeni bir şey görmez):
GLSL_TURN:uGroupX/Y/Z/T,toGroup(p),toWorld(g),dirToGroup(d),dirToWorld(d)ve örtü maskeleriuCoverEdge[60],uCoverCount[6],coverOf(vb, soft).glslPose(Name, prefix = "uName"): herhangi bir grup için aynı dört fonksiyon (toDial,fromDial,dirToDial,dirFromDial); böylece tek bir shader birkaç grubu izleyebilir.glLayer, 4'ten uzun dizileri bildirilen türlerine görevec2/vec3/vec4uniform dizilerine gönderir (float dizileri değişmez).
Bir shader hareketi nasıl izler:
- Hareket eden bir parçadaki içerik (bir parmak ucundaki LED, kopmuş bir hattın ucundan çıkan bir alev, bir boru ucundan fışkıran yağ): çapayı her karede
controller.anchor(group, restPoint, restDir)ile alın veworld,screenvedirection'ı uniform olarak verin. Onu ön tuvalde (canlı katmanın üzerinde) çizin ve ondan sonra boyanan parçalarla örtün:const c = TURN.cover(controller, [partName]); layer.draw({ ...c, … })veink *= 1. - coverOf(vb, .7 * px). Her canlı parça dışbükeydir; bu yüzden zarfı tam silüetidir; örtü 0.3 vb dışa itilir, böylece mürekkep örtenin konturunda durur, asla içinde değil. Birkaç hedef altı çokgeni paylaşır:TURN.cover(controller, [["hand.f2.p3"], ["hand.pod.socket"]]). - Dönen bir yüzeye sabitlenmiş içerik (döner tablada biriken yağ, bir kadranda ışıkla ortaya çıkan bir doku):
TURN.poseUniforms(controller, "dial", "uDial")'i verin ve dinlenme-dünyası koordinatlarında değerlendirin:vec3 q = toDial(onFloor(vb, FACE_Z));Havuz kadranla döner. - Dünya uzayındaki içerik (yükselen duman, kaynaktan ayrıldıktan sonraki kıvılcımlar, döner tablanın dışında tezgâhta biriken yağ): asla bir grup dönüşümüne dokunmayın. Parçacıkları doğuşta bir çapanın dünya konumundan yayın ve dünya uzayında entegre edin (örnekteki kıvılcımlar): dönüş onları sürüklemez.
- Arka tuval (onu örten parçaların altındaki bir yüzeye düşen ışık): tuvali iki canlı katmanın arasına koyun. Örnek kadranını
hand'in altında kendi canlı katmanıdial'a ayırır; böylece LED'in havuzu kadran yüzüne düşer ve el onu hiç maske olmadan örter.
webgl.md'nin her kuralı hâlâ geçerlidir: zarflar, herhangi bir sınırdan önce 0'a pencereler, açık tema mürekkepleri (doygun gövdeler, soluk çekirdekler, renklendirilmiş zemin, mürekkep yalnızca ışıkla büyür), titreşim, 0'da tuval kenarları, bir SVG yedeği (sahip parçanın içinde T.billboard, [data-gl] altında gizli, (1 − u)/(1 − u·α) ile devredilen). Örtü parçası 70 uniform vektör kullanır: MAX_FRAGMENT_UNIFORM_VECTORS'ü okuyun ve SVG yedeğini 128'in altında tutun. GLSL_TURN toWorld'ü tanımlar: onu kendininkini bildiren bir parçanın başına eklemeyin.
13. Yörünge denetimi
node build.mjs --audit kit denetimini, ardından her kontrol için bir satır yazdıran ve turn audit passed ya da turn audit failed: n problems (çıkış 1) ile biten TA.orbitOrExit(T, R, P, options)'ı çalıştırır. Yalnızca tam bir çalıştırma turn audit passed ile biter: only ile bir çalıştırma turn audit partial: no problems in what ran (n lines skipped by only) ile, hızlı bir çalıştırma turn audit quick: no problems in what ran (quick run, undersampled: the full run is the proof) ile biter.
Seçenekler: sweep: { at(x), from, to, frame } (dönüş), clips: [{ name, at(t), from, to, frame }] (hareketler, kopma: 60 fps'de her kare), poses (ek durumlar), crowd: [[route, part]…] (baktıktan sonra crowding'den muaf tutulan çiftler), cover, fittings: [[route, from, to]] (bir bağlantı parçasının içindeki uzunluklar, endon'dan muaf), quick (yineleme için ≈ 10× daha az durum), only: ["order", "rigid", "swept", "posed", "routes", "coverage", "continuity", "depth"] (bilinmeyen bir ad hata fırlatır), log. Örnek --quick, --only=a,b ve --verbose alır. only şu satırları çalıştırır: order → unseparated, cycles, forced, swaps; routes → folds, self, crowding, endon; depth → depth, layers; diğerleri kendi satırlarını. separation her zaman çalışır. Çalışmayan bir satır skipped yazar.
Süre ve nasıl yinelenir. Yörünge denetimi yavaştır ve süresi parçalar × durumlarla büyür. 28 parçalı bir masa vantilatöründe (bir dönüş, bir menteşe, bir klip) ölçüldü: --quick 85 s, tam 17 dakika. Örnek sahne birkaç dakika sürer; tam bir çalıştırma için parça başına yaklaşık 30–40 s bütçe ayırın. Bu 10 dakikalık bir kabuk zaman aşımını geçer, bu yüzden:
- Düzelttiğiniz satırlar üzerinde
--quick --only=…ile yineleyin. - Son çalıştırmadan önce yalnızca
--quick. Az örnekler: tam çalıştırmadaki 800'e karşı 100 yarılama adayını kontrol etti ve tam çalıştırmanın bulduğu 20continuityhatasını kaçırdı. - Son tam çalıştırmayı arka planda (
node build.mjs --audit > audit.txt 2>&1 &) yapın, bitince okuyun. Yalnızca o çalıştırmanınturn audit passed'ı sayılır.
| Satır | Neyde başarısız olur | Çözüm |
|---|---|---|
separation |
Düzlemi olmayan (pay < −0.08) aynı gruptan bir çift | Bölme, bir düzlük ya da aynı hizada bir temas |
unseparated |
Zarfları örtüşürken iç içe geçen gruplar arası bir çift; bir durumun bozduğu çerçeveli bir düzlem | Teması ya da hareketi düzeltin; hareketi poses()'a ekleyin |
cycles |
Bir durumda örtüşen parçalar arasında bir döngü; parçaları, durum aralığını ve bölme düzlemini (en büyük paylı döngü kenarı) yazdırır | O parçayı o düzlemde T.split edin |
forced |
Çalışma zamanı bir seçimi zorlamak zorunda kaldı | Döngüler gibi |
swaps |
Örtüşen iki parça 0.25 px²'den fazla örtüşürken sıra değiştiriyor (aynı hizada kurulmuş, payı 0.05'in altında bir çift için 0.5 px²: temas düzlemlerinin kenardan göründüğü anda gerçek örtüşme bir çizgidir ve çizilen eğriler yaklaşık 0.1 px genişliğinde bir şerit bırakır) | Önce bakın: drive.mjs page.html '[["orbit", "swap.png", θ−0.3, θ+0.2, 0.1, null, 6, [x, y, w, h]]]' (θ satırdan; çiftin etrafındaki bölge kartın CSS px'inde, 6 ölçekte). Tek bir adımda beliren ya da kaybolan bir şey sıra hatası değil, çizim hatasıdır. 2026-10-06'dan önceki motorlar, bir "dome" ucu yarıçapından kısa olan bir yuvarlağı düz ucunun arkasında bir kamayla çiziyordu ve bu, o uç kenardan görününce kayboluyordu: güncel turn.mjs'i kopyalayın. İki tarafta da makul sırayla temiz bir değişim (__isoTurn.order) yanlış bir düzlemdir: parça çifti ve θ ile bildirin |
layers |
Canlı bir parçanın yanlış tarafındaki statik bir öğe (ışın atma) | Onu taşıyın ya da bölün (bölüm 11) |
rigid |
Dinlenmede kit açıklıkları, uç noktaları, destekler, katı açıklıkları | Kit denetimindeki gibi. Menteşeli bir grubun her parçasında support loose: içindeki hiçbir şey ebeveyne değmiyor (mafsal diski, bölüm 5). terminal … from X: yol bağlantıda değil, bir portun yüzünde başlıyor (T.tube satırı, bölüm 4). clearance … × stand.bench: touch'ı olmayan bir tezgâh kablosu (bölüm 11) |
swept |
Bir dönme grubunun süpürülmüş zarfının içindeki statik bir şekil | Onu silindirin dışına taşıyın |
posed |
Herhangi bir örneklenmiş durumda birbirinin içinden geçen farklı grupların şekilleri (ve hareketli canlı × statik) | Hareket ya da geometri |
folds, self, endon |
Her açıda ve pozlanmış durumda canlı yollar | Bölüm 5 (eğim aralığıyla büküm yarıçapı, bölüm 8) |
crowding |
Canlı bir yolun ucu ya da bükümü bir açıda ince bir katının (bir çubuk, direk, gövde mili) dış hattına düşüyor | Dönen bir çubuğa paralel bir hat, ötelemesi ne olursa olsun bir açıda çubuğun dış hattını keser; bu yüzden onu tasarımla yok edemezsiniz. Adlandırdığı açılara bakın (["turn", θ] ve 4× bir görüntü) ve iki ayrı parça olarak okunuyorsa çifti crowd: [["cable.a", "yaw.stem"]] içinde listeleyin ya da hattı çubuğun içinden yönlendirin. Paralel olmayan bir hat: çubuğun karşısından yaklaşın |
coverage |
Bir parçanın kaydedilmiş şekli çiziminin < %60'ını kaplıyor | Gerçek şekli kaydedin |
depth |
Işın atma kâhini motor sırasıyla uyuşmuyor | Bildirin |
continuity |
Her 0.05°'de: > 1/16 bantlık ton adımı, > 2 px²'lik bir doğum, > 0.01'lik bir nokta α adımı, > 1/32 + 0.005'lik bir sönmüş α adımı, 1.5·ω·Δθ + 0.05 px'i aşan dış hat hareketi; ve yarılama testi | Duran bir ayrıntı: söndürün. Dış hattı ya da yüz sınırı tek adımda sıçrayan bir parça: açıyı 0.1°'lik bir yörünge sayfasıyla bulun; düz bir kit ilkelinde bu bir motor hatasıdır, parça ve θ ile bildirin. --quick az örnekler: yalnızca tam çalıştırma sayılır |
Yarılama testi her aday adımı kendisinin ½, ¼ ve ⅛'i olarak tam koordinatlarla yeniden yayar, kareleri 4×'te rasterleştirir (8 alt tarama satırında titreşimli örnekler) ve tonla ağırlıklandırılmış olarak sahip değiştiren pikselleri ölçer. Pürüzsüz bir değişim adımla yarılanır; bir sıçrama yarılanmaz. Bir adım, her yarılamada %60'ın üstünde kalıyor ve ⅛'de hâlâ ≥ 2 ton·px² ise, 4×'te ve yeniden 16×'te ölçülerek bir süreksizliktir: 4×'te başarısız olan bir adım ancak 16×'te de başarısız olursa başarısız sayılır. Bunun yerine sayfanın yuvarlanmış yolları üzerinde ölçüldüğünde, her uzun kenar asla küçülmeyen yaklaşık uzunluğu × 0.01 px'lik bir tabanı korur; tam koordinatlara geçişten önce büyük düz yüzleri işaretleyen o tabandı. Her satırın onu başarısız kılan yerleştirilmiş bir hatası vardır (derleme oturumundaki 3d/engine/plants.mjs: gömülü bir ped, üç eğik çubuktan bir fırıldak, dikey düzlemde bir lif bükümü, 30°'de bir boru, durmuş bir nervür sırası).
14. Tarayıcı kontrolleri
node examples/turning-dial/build.mjs --light --verify # turning-dial-verify-light.html yazar
node scripts/turn-check.mjs examples/turning-dial/build.mjs --fidelity
node scripts/turn-check.mjs page-verify.html --order --step 1
node scripts/turn-check.mjs page-verify.html --order --states clip.json
node scripts/turn-check.mjs page.html --pops --no-webgl --step 1 --out shots/pops
node scripts/turn-check.mjs page.html --lines # ve koyu sayfa
node scripts/turn-check.mjs page.html --perf --width 1440
node scripts/turn-check.mjs page.html --perf --width 390 --throttle 4
node scripts/turn-bench.mjs examples/turning-dial/build.mjs
node scripts/drive.mjs page.html '[["turn", 37], ["shot", "a.png"], ["orbit", "orbit.png", 0, 345, 15]]'
--fidelity(Node): P′(θ)'dak.extrude/G.prismOf'a karşı prizmalar (dış hat, üst ve yüzler her biri ≤ 0.06 px),G.lathe(…, smooth)'a karşı yuvarlaklar ve torna parçaları (10×'te çizilen kite karşı dış hat ≤ 0.05 px, bantlar ≤ 0.5 px; satır ayrıca kitin 0.1 px'e yuvarlanan kendi yollarına karşı dış hattı yazdırır, bu yüzden orada 0.08–0.13 px beklenir ve yargılanmaz),tubePieces'e karşı borular (0.05 px). Kontrol örneği: 2° yanlış çizilen bir prizma başarısız olmalıdır.--order: DOM ressamının dolgularına karşı bir WebGL z-buffer'ındaki kaydedilmiş şekiller, 2×; 4+ iç pikselli canlı bir çift başarısız olur. Kontrol örneği: her canlı katman tersine çevrildiğinde ≥ 1000 px vermelidir. Statik × canlı uyuşmazlıkları listelenir (statik kaydedilmiş şekiller çoğu zaman vekildir; örneğin pencereli bir plaka için bir levha).--part namebir parçanın izini iki görüntüde de yazdırır.--stepyalnızca ilk dönme grubunu tarar, diğer her grup dinlenmede: bir menteşe, bir kayma ya da bir klip--statesgerektirir.--pops: birinci fark (θ → θ + 0.02°, iki erozyon) ve ikinci fark (0.1°, |2b − a − c| > 56 ya da düz piksellerde > 6, bir erozyon; 6 px'lik bir çekirdek başarısız olur). Kontrol örneği: en büyük parça bir kare gizlenir.--out dirilk çekirdeğin karelerini kaydeder.--statesile ardışık girdiler komşu kareler olarak ele alınır ve yalnızca birinci fark çalışır; bu yüzden liste yaklaşık 0.02°'lik adımlarla tek bir sürekli yol olmalıdır: 529 ilgisiz sapma × eğim pozu 404 yanlış başarısızlık verdi; 0.02°'de 1201 durumlu bir eğim taraması 0 verdi.--states file.json: açılar yerine değer nesnelerinin bir listesi. Bunlarıfollow'u ve klip fonksiyonlarını dışa aktaran derleme modülünden yazın:import { writeFileSync } from "node:fs"; import { follow, nodAt } from "./build.mjs"; const at = (t) => follow({ yaw: 20 + 30 * t, tilt: nodAt(t) }); writeFileSync("clip.json", JSON.stringify(Array.from({ length: 241 }, (_, i) => at(i / 60)))); writeFileSync("tilt-path.json", JSON.stringify(Array.from({ length: 1201 }, (_, i) => follow({ yaw: 30, tilt: -12 + i * 0.02 }))));clip.json(klibin her 60 fps karesi)--orderiçindir;tilt-path.json(0.02°'de taranan tek bir menteşe)--popsiçindir.--pops'u menteşe başına bir yolda, menteşenin parçalarının en çok örtüştüğü bir dönme açısında çalıştırın.
15. Performans
turn-bench (Node CPU) ve turn-check --perf (tarayıcı, 1.1°'de 240 kare) ile ölçülür.
Bir parçanın maliyeti. turn-bench türe göre parça başına Node süresini yazdırır. Prizma birimlerinde (tek bir canlı prizma, derleme makinesinde kare başına yaklaşık 0.03–0.04 ms):
| Tür | Maliyet | Tür | Maliyet |
|---|---|---|---|
fixed (dikey yuvarlaklar, düz bilyeler) |
0.5 | round |
2.5–4 |
prism |
1 | düzlüklü ball |
8–10 |
| boru parçası | 2–3 | lathe |
yaklaşık 25 |
Bir şeklin ağırlığı toplamdır. Küçük bir şekil yaklaşık 50'ye kadar (birkaç düzine parça, çoğunlukla prizmalar ve sabit resimler), yoğun bir şekil yaklaşık 230'a kadar tartar. Masa vantilatörü (28 parça: 6 prizma, 7 sabit, 5 boru parçası, 8 yuvarlak, 2 düzlüklü bilye) yaklaşık 65 tartar ve kare başına 1.6–1.9 ms Node CPU ve 1440'ta 6.0 ms'lik bir tarayıcı görevi ölçüldü; bu yüzden küçük şekil çizgisinin üstündedir, çoğunlukla iki bilyesi yüzünden. Dönen kadran (88 parça, 42 yuvarlak, bir torna parçası) yaklaşık 225 tartar ve 6.4–7.2 ms ölçer. Düzlüklü bir bilye, üzerine bir kenarın indiği kubbeli bir uç için doğru parçadır (bölüm 5), ama dokuz prizma kadar maliyetlidir; düz bir bilye ya da dikey bir yuvarlak sabit bir resimdir ve neredeyse bedavadır. Süreler makineye ve yüke göre değişir: türleri tek bir çalıştırma içinde karşılaştırın ve kendi şeklinizi ölçün.
| Ölçü | Küçük bir şekil için bütçe (ağırlık ≤ 50) | Yoğun bir şekil için bütçe (ağırlık ≤ 230) |
|---|---|---|
| Kare başına Node CPU | ≤ 2 ms | ≤ 7 ms |
| 1440'ta, DPR 2'de tarayıcı ana iş parçacığı görevi | ≤ 6 ms | ≤ 13 ms (60 fps) |
| 390'da, DPR 3, 4× CPU ile ana iş parçacığı | ≤ 16 ms | ≤ 33 ms (30 fps) |
data.turn JSON |
≤ 250 KB |
Kollar, sırayla:
- Her dikey eksenli tornalanmış parça ve her düz küre için
fixed(otomatik). - Bir yüzdeki her çizgi ya da metin kümesi için düzlemler; dik prizma üstleri tek bir matristir.
- Küçük parçalar aygıt piksellerine göre örneklenir: 30 px'ten küçük bir parçanın halkaları, kapakları ve kubbeleri 12–16 örnek alır, asla büyük parça minimumlarını değil.
- Çerçeveli düzlemler ve göreli poz önbelleği (otomatik): gruplar arası çiftler, grupları göreli pozlarını korurken (saf bir döner tabla dönüşü) hiçbir şeye mal olmaz ve hareketini derlemenin örneklediği çiftler çerçeveli düzlemlerdir.
- Uyarlanabilir yarım hız (şeklin döngüsü):
controller.set(...)süresinin üstel hareketli ortalamasını tutun; 14 ms'nin üstünde canlı katmanları bir kare atlayarak çizin (zamana dayalı hareket değişmez), 9 ms'nin altında yeniden her karede (stress.mjs'in döngüsü). - Hıza göre ayrıntı (isteğe bağlı):
detail = settle(1 − smoothstep(120, 240, |ω|°/s), 0.25 s)ilecontroller.set(values, { detail }): sönmüş çizgiler, nervürler ve noktalar ölçeklenir ve 0.01'in altında atlanır. Dış hatlar ve tonlar asla azaltılmaz.
Bir şekli bütçe içinde tutan yazım kuralları: hareketli grup başına yaklaşık 150–250 canlı parça; küçük ayrıntılar parça olarak değil details (noktalar, halkalar, cıvatalar) olarak; statik dağınıklık statik sanat olarak; bağımsız hareket eden her bölge için bir canlı katman; bükümlerin izin verdiği kadar az parçayla 6'da parçalanmış borular. Örnek ve stres sahnesi için ölçülen sayılar becerinin derleme notlarındadır; bu makinede 380 parçalı bükülen bir el, yarım hız koluyla 1440'ta 60 fps değil, 30 fps'lik bir şekildir.
16. Bilinen sınırlar
- Dikey dönme eksenleri azimut kısayolunu alır; diğer dönmeler matrisleri birleştirir.
- Yalnızca dışbükey parçalar. İçbükey gövdeler dışbükey parçalardır; bilye düzlükleri küre üzerinde kenar eklemlerinin yerini alır.
- Canlı borular katlanma ve uçtan görünme kurallarına uyar: süpürülmüş boru siluetleri yoktur; bu yüzden dönüşler düz ya da bağlantı parçalarında olur ve hiçbir yol iki hareketli grubu kapsamaz.
- Otomatik bölme yok: bir döngü, önerilen bir düzlemle bir denetim başarısızlığıdır.
- Düz yüzler ±0.035 puan (yaklaşık 8° dönüş) boyunca çapraz geçiş yapar.
- Torna parçaları bir bandın topolojisinin değiştiği yerde küçük artık basamaklar korur.
- Serbest gruplar çalışma zamanında bütün katmanlarıyla eşlenir; düzinelerce enkaz parçası hareket ederken GJK'ya mal olur.
- Telefonlar: bütçe 60 değil 30 fps'yi hedefler ve yalnızca onun içindeki bir şekil oraya ulaşır. 380 parçalı stres sahnesi (
examples/turning-dial/stress.mjs) yoğun yük altındaki bir makinede, yarım hız koluyla 1440'ta yaklaşık 20 fps ve 4× CPU kısıtlamasıyla 390'da 13–15 fps ölçüldü. Bir tuval ressamı yoktur.