Translate the whole repository to English
The kit is going public, so everything a reader sees is now English:
27 model sources, the core vocabulary, meta.ts (titles, descriptions and
every slider label), the viewer UI, all script output, REFERENCE.md and
the READMEs.
Rules applied: only comments, doc comments and human-facing strings were
touched. Identifiers, numbers, logic, registry item ids, part names and
material slot names are unchanged — those are a published contract and
were already English.
Verified rather than assumed: for all 53 changed .ts files the code
skeleton (source with comments stripped and string contents replaced by a
placeholder) is byte-identical before and after. Full gate is green —
typecheck, registry conformance, ~510 model checks, GLB round trip and a
production build.
Also fixed here:
- index.html and viewer.html were missed by the translation pass because
the file list only covered .ts/.md/.css. Both had lang="tr" and Turkish
titles.
- The viewer formatted triangle counts with toLocaleString('tr-TR'), which
prints 15.425 rather than 15,425.
- REFERENCE.md's model table header did not match what
scripts/catalog-table.ts emits, so the next regeneration would have
silently changed it. Aligned to the generator.
- Removed scripts/probe.ts and scripts/zdiag.ts; they were throwaway
diagnostics that got committed by accident.
This commit is contained in:
parent
72f219d087
commit
50c7fc60b9
97 changed files with 4572 additions and 4270 deletions
|
|
@ -1,30 +1,34 @@
|
|||
/**
|
||||
* @medieval-kit/wooden-fence
|
||||
*
|
||||
* Zıvanalı riven çit: yarılmış kalın direkler, direğin İÇİNDEN geçen kirişler.
|
||||
* Mortised riven fence: riven heavy posts, rails passing THROUGH the post.
|
||||
*
|
||||
* İKİNCİ deneme ve sebebi tek kelimeyle söylenebilir: BİRLEŞİM. İlk hâlde
|
||||
* dört kare çubuk ve önlerine konmuş iki ince lata vardı; hiçbir noktada iki
|
||||
* parçanın birbirine nasıl tutunduğu görünmüyordu, dolayısıyla nesnenin bütün
|
||||
* konusu eksikti. Render'da çit değil "çitin teknik resmi" gibi okunuyordu —
|
||||
* 4.89 × 1.10 × 0.09 m, yani derinlik/boy oranı 54:1, karton.
|
||||
* SECOND attempt, and the reason can be said in one word: JOINERY. The first
|
||||
* version was four square sticks with two thin battens laid across their
|
||||
* front; at no point was it visible how two pieces held on to each other, so
|
||||
* the whole subject of the object was missing. In the render it read not as a
|
||||
* fence but as "the technical drawing of a fence" — 4.89 × 1.10 × 0.09 m,
|
||||
* i.e. a depth-to-length ratio of 54:1, cardboard.
|
||||
*
|
||||
* Gerçek riven post-and-rail çitinde direğe DİKDÖRTGEN BİR DELİK açılır ve
|
||||
* kiriş o delikten geçer. Modelin tamamı bu tek gerçeğin etrafında yeniden
|
||||
* kuruldu:
|
||||
* In a real riven post-and-rail fence a RECTANGULAR HOLE is cut through the
|
||||
* post and the rail passes through that hole. The entire model was rebuilt
|
||||
* around this single fact:
|
||||
*
|
||||
* - Direk artık tek kutu değil: iki YANAK ve aralarındaki KÖPRÜ blokları.
|
||||
* Delik böylece geometrik olarak var oluyor, boyanmış bir çentik değil.
|
||||
* `bakeOcclusion` da ağzını kendiliğinden karartıyor.
|
||||
* - Kiriş delikten geçip çitin iki ucunda karşı yüzden TAŞIYOR. Zıvana dili
|
||||
* siluete giren tek yatay çıkıntı ve "bu nasıl duruyor" sorusunu tek
|
||||
* başına cevaplıyor.
|
||||
* - Kiriş delikten dar: her yanda birkaç milimlik boşluk kalıyor, yani delik
|
||||
* kapanmıyor. Deliği delik gösteren şey o boşluk.
|
||||
* - The post is no longer a single box: two CHEEKS with BRIDGE blocks
|
||||
* between them. The hole therefore exists geometrically, it is not a
|
||||
* painted notch. `bakeOcclusion` darkens its mouth on its own too.
|
||||
* - The rail passes through the hole and PROTRUDES from the far face at the
|
||||
* two ends of the fence. The tenon tongue is the only horizontal
|
||||
* protrusion that enters the silhouette, and it answers the question
|
||||
* "how is this standing up" all by itself.
|
||||
* - The rail is narrower than the hole: a few millimetres of gap remain on
|
||||
* each side, so the hole does not close up. That gap is what shows the
|
||||
* hole as a hole.
|
||||
*
|
||||
* Bir de dizilim: eski kiriş dağılımı `0.28 + 0.5·r/(count−1)` idi, yani kaç
|
||||
* kiriş olursa olsun HEP 0.28–0.78 aralığını dolduruyordu. Üstü ve altı
|
||||
* doldurmak yapısal olarak imkânsızdı; siluetin üst kenarı bu yüzden boştu.
|
||||
* And the spacing: the old rail distribution was `0.28 + 0.5·r/(count−1)`,
|
||||
* i.e. no matter how many rails there were it ALWAYS filled the 0.28–0.78
|
||||
* band. Filling the top and the bottom was structurally impossible; that is
|
||||
* why the upper edge of the silhouette was empty.
|
||||
*/
|
||||
import { Color } from 'three'
|
||||
import type { BufferGeometry } from 'three'
|
||||
|
|
@ -39,16 +43,16 @@ import {
|
|||
} from '../core/index.ts'
|
||||
|
||||
export interface WoodenFenceConfig {
|
||||
/** Bölüm sayısı. Her bölüm iki direk arası. */
|
||||
/** Number of sections. Each section is the span between two posts. */
|
||||
readonly sections: number
|
||||
/** Bir bölümün uzunluğu (metre). Kiriş bir kütükten yarıldığı için 2–3 m. */
|
||||
/** Length of one section (metres). 2–3 m, because a rail is riven from one log. */
|
||||
readonly sectionLength: number
|
||||
readonly height: number
|
||||
/** Yatay kiriş sayısı. */
|
||||
/** Number of horizontal rails. */
|
||||
readonly railCount: number
|
||||
/** Direklerin eğrilik ve boy sapması. 0 = fabrikasyon düzgünlük. */
|
||||
/** Curvature and height deviation of the posts. 0 = factory straightness. */
|
||||
readonly rough: number
|
||||
/** Bir uca payanda konsun mu (0/1). */
|
||||
/** Whether a brace is placed at one end (0/1). */
|
||||
readonly brace: number
|
||||
readonly seed: number
|
||||
}
|
||||
|
|
@ -68,10 +72,10 @@ export type WoodenFenceParts = 'posts' | 'rails'
|
|||
export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
||||
return createKitModel<WoodenFenceConfig, 'oak', WoodenFenceParts>({
|
||||
id: 'wooden-fence',
|
||||
// Alaca hücresi elle veriliyor: çit 4.8 m uzun, otomatik türetme onu
|
||||
// modelin ölçeğinden çıkarınca tek direk tek hücreye düşüyor ve doku
|
||||
// sistemi hiçbir şey yapmıyor. Ahşabın damar lekesi nesnenin boyundan
|
||||
// bağımsız olarak birkaç santimdir.
|
||||
// The mottle cell is given by hand: the fence is 4.8 m long, and when the
|
||||
// automatic derivation takes it from the model's scale a single post falls
|
||||
// into a single cell and the texture system does nothing. The grain mottle
|
||||
// of wood is a few centimetres regardless of the object's size.
|
||||
mottle: { cell: 0.05 },
|
||||
defaults: woodenFenceDefaults,
|
||||
slots: ['oak'],
|
||||
|
|
@ -82,7 +86,7 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
tint.offsetHSL(jitter(random, 0.012), jitter(random, 0.05), lift + jitter(random, 0.06))
|
||||
return tint
|
||||
}
|
||||
/** Damar ucu: yarılmış yüzey ve kesik uçlar. Çit bunu hiç kullanmıyordu. */
|
||||
/** End grain: riven surface and cut ends. The fence never used this. */
|
||||
const endGrain = (lift = 0): Color => {
|
||||
tint.copy(MEDIEVAL_PALETTE.oakEnd)
|
||||
tint.offsetHSL(jitter(random, 0.01), jitter(random, 0.04), lift + jitter(random, 0.05))
|
||||
|
|
@ -100,21 +104,21 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
const half = config.height / 2
|
||||
const rough = Math.max(0, config.rough)
|
||||
|
||||
// --- Ölçüler, hepsi yükseklikten türetiliyor --------------------------
|
||||
const postW = config.height * 0.12 // direğin çit boyunca genişliği
|
||||
const mortise = config.height * 0.062 // deliğin Z açıklığı
|
||||
const cheek = config.height * 0.03 // deliğin iki yanındaki et
|
||||
const postD = mortise + cheek * 2 // direğin toplam derinliği
|
||||
const railH = config.height * 0.098 // kirişin dikey yüksekliği
|
||||
const railD = config.height * 0.053 // kirişin derinliği — delikten DAR
|
||||
const tenon = config.height * 0.088 // uçlardaki taşma
|
||||
// --- Dimensions, all derived from the height --------------------------
|
||||
const postW = config.height * 0.12 // post width along the fence
|
||||
const mortise = config.height * 0.062 // Z opening of the hole
|
||||
const cheek = config.height * 0.03 // material on each side of the hole
|
||||
const postD = mortise + cheek * 2 // total depth of the post
|
||||
const railH = config.height * 0.098 // vertical height of the rail
|
||||
const railD = config.height * 0.053 // rail depth — NARROWER than the hole
|
||||
const tenon = config.height * 0.088 // overhang at the ends
|
||||
|
||||
// Kiriş yükseklikleri. Üs 1.12: aralıklar aşağı doğru sıkışıyor, çünkü
|
||||
// altından geçmeye çalışan hayvan küçük olandır.
|
||||
// Rail heights. Exponent 1.12: the gaps tighten towards the bottom,
|
||||
// because the animal trying to get under it is the small one.
|
||||
const railT = Array.from({ length: count }, (_, r) =>
|
||||
count === 1 ? 0.55 : 0.19 + 0.71 * Math.pow(r / (count - 1), 1.12))
|
||||
|
||||
// --- Direkler -----------------------------------------------------------
|
||||
// --- Posts --------------------------------------------------------------
|
||||
const postPieces: BufferGeometry[] = []
|
||||
const slotHalf = railH / 2 + config.height * 0.008
|
||||
|
||||
|
|
@ -123,7 +127,7 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
const postH = config.height + jitter(random, 0.075 * rough)
|
||||
const pieces: BufferGeometry[] = []
|
||||
|
||||
// İki yanak: deliğin duvarları. Tam boy, tabandan tepeye.
|
||||
// Two cheeks: the walls of the hole. Full height, base to top.
|
||||
for (const side of [-1, 1]) {
|
||||
pieces.push(chamferedBoxGeometry(
|
||||
[postW, cheek],
|
||||
|
|
@ -136,15 +140,15 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
))
|
||||
}
|
||||
|
||||
// Köprüler: yuvaların ARASINI dolduran bloklar. Delik tam olarak
|
||||
// bunların bıraktığı boşluk. Kesitleri yanakların İÇİNDE kalıyor
|
||||
// (±Z yüzleri yanak katısına gömülü), yani hiçbir yüz çifti eş
|
||||
// düzlemde değil.
|
||||
// Bridges: blocks that fill the space BETWEEN the slots. The hole is
|
||||
// exactly the gap they leave. Their cross-sections stay INSIDE the
|
||||
// cheeks (their ±Z faces are buried in the cheek solid), so no pair
|
||||
// of faces is coplanar.
|
||||
//
|
||||
// Köprüler direğin iki UCUNA kadar gitmiyor: uçları yanakların
|
||||
// uçlarıyla aynı düzleme oturup titriyordu. İçeri çekilen paylar
|
||||
// görünmüyor — alttaki toprak yığınının, üstteki başlığın içinde
|
||||
// kalıyor.
|
||||
// The bridges do not run all the way to the two ENDS of the post:
|
||||
// their ends sat on the same plane as the cheek ends and z-fought.
|
||||
// The insets are not visible — they stay inside the soil mound below
|
||||
// and inside the cap above.
|
||||
const inset = cheek * 0.3
|
||||
const bounds = [0, ...railT.flatMap((t) => [t * postH - slotHalf, t * postH + slotHalf]), postH]
|
||||
for (let k = 0; k + 1 < bounds.length; k += 2) {
|
||||
|
|
@ -161,9 +165,9 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
))
|
||||
}
|
||||
|
||||
// Başlık: baltayla yontulmuş, suyu akıtan sırt. Tabanı gövdenin İÇİNDE
|
||||
// ve kesiti gövdenin o yükseklikteki kesitinden BÜYÜK — `toolSocket`
|
||||
// deseninin aynısı, eş düzlem yüz çifti bu yüzden oluşmuyor.
|
||||
// Cap: an axe-hewn ridge that sheds water. Its base sits INSIDE the body
|
||||
// and its section is LARGER than the body's section at that height — the
|
||||
// same pattern as `toolSocket`; that is why no coplanar face pair forms.
|
||||
pieces.push(taperedBoxGeometry(
|
||||
[postW * 0.88, postD * 0.98],
|
||||
[postW * 0.74, postD * 0.13],
|
||||
|
|
@ -173,10 +177,10 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
endGrain(0.07),
|
||||
))
|
||||
|
||||
// Kur → DÖNDÜR → taşı. Eski kod merkezi doğrudan geometri çağrısına
|
||||
// geçirdiği için döndürmek imkânsızdı; ızgara gibi dizilmesinin sebebi
|
||||
// buydu. Dönüşler küçük tutuluyor: 0.045 rad, delik yolunda 7 mm yanal
|
||||
// kayma demek ve delik payı 8 mm.
|
||||
// Build → ROTATE → translate. The old code passed the centre straight
|
||||
// into the geometry call, so rotating was impossible; that is why they
|
||||
// lined up like a grid. Rotations are kept small: 0.045 rad means 7 mm
|
||||
// of lateral drift along the hole, and the hole slack is 8 mm.
|
||||
const post = mergeColoured(pieces)
|
||||
post.rotateY(jitter(random, 0.045 * rough))
|
||||
post.rotateZ(jitter(random, 0.03 * rough))
|
||||
|
|
@ -185,7 +189,7 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
post.translate(x, -half - sink, 0)
|
||||
postPieces.push(post)
|
||||
|
||||
// Toprak yığını. DÖNMEZ: direğin eğikliği yığını yerden kaldırırdı.
|
||||
// Soil mound. DOES NOT ROTATE: the post's lean would lift it off the ground.
|
||||
postPieces.push(taperedBoxGeometry(
|
||||
[postW * 2.2, postD * 2],
|
||||
[postW * 1.25, postD * 1.15],
|
||||
|
|
@ -195,14 +199,14 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
))
|
||||
}
|
||||
|
||||
// --- Kirişler -------------------------------------------------------------
|
||||
// --- Rails ----------------------------------------------------------------
|
||||
const railPieces: BufferGeometry[] = []
|
||||
for (let r = 0; r < count; r += 1) {
|
||||
const y = -half + railT[r]! * config.height + jitter(random, config.height * 0.005)
|
||||
for (let i = 0; i < sections; i += 1) {
|
||||
const xc = -total / 2 + (i + 0.5) * config.sectionLength
|
||||
// Gövde iki komşu direğin deliğine giriyor ve orada komşu bölmenin
|
||||
// gövdesiyle uç uca buluşuyor.
|
||||
// The body enters the holes of the two neighbouring posts and meets
|
||||
// the body of the adjacent bay end to end in there.
|
||||
const body = chamferedBoxGeometry(
|
||||
[config.sectionLength + postW * 0.55, railD],
|
||||
[config.sectionLength + postW * 0.55, railD * 0.88],
|
||||
|
|
@ -215,10 +219,10 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
railPieces.push(body)
|
||||
}
|
||||
|
||||
// Zıvana dili: YALNIZ iki uçta. Karşı yüzden taşan bu parça siluete
|
||||
// giren tek yatay çıkıntı; kirişin direğin içinden geçtiğini tek
|
||||
// başına anlatıyor. Ara direklerde taşma yok, çünkü orada iki gövde
|
||||
// deliğin içinde buluşuyor.
|
||||
// Tenon tongue: ONLY at the two ends. Protruding from the far face, this
|
||||
// piece is the only horizontal projection entering the silhouette; it
|
||||
// tells on its own that the rail runs through the post. No overhang at
|
||||
// the intermediate posts, because there two bodies meet inside the hole.
|
||||
for (const side of [-1, 1]) {
|
||||
const px = side * total / 2
|
||||
railPieces.push(taperedBoxGeometry(
|
||||
|
|
@ -232,8 +236,8 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Payanda ---------------------------------------------------------------
|
||||
// Modelin tek eksen dışı hattı. Uç direği tarlaya doğru destekliyor.
|
||||
// --- Brace -----------------------------------------------------------------
|
||||
// The model's only off-axis line. It props the end post towards the field.
|
||||
if (config.brace >= 0.5) {
|
||||
const rise = config.height * 0.72
|
||||
const run = config.sectionLength * 0.3
|
||||
|
|
@ -248,17 +252,17 @@ export function createModel(overrides: Partial<WoodenFenceConfig> = {}) {
|
|||
shade(-0.05),
|
||||
endGrain(0.02),
|
||||
)
|
||||
// İşaret TERS görünüyor ama doğrusu bu: payandanın TEPESİ direğe
|
||||
// yaslanır, AYAĞI tarlaya basar. Ters çevrildiğinde ayağı direğin
|
||||
// dibinde, tepesi havada kalan bir çubuk çıkıyordu — hiçbir şeyi
|
||||
// desteklemeyen bir payanda.
|
||||
// The sign looks INVERTED but this is the correct one: the TOP of the
|
||||
// brace leans against the post, its FOOT stands on the field. Flipped,
|
||||
// what came out was a stick with its foot at the base of the post and
|
||||
// its top in the air — a brace that supports nothing.
|
||||
const angle = Math.atan2(run, rise)
|
||||
brace.rotateZ(atStart ? angle : -angle)
|
||||
brace.translate(
|
||||
(atStart ? -1 : 1) * (total / 2 - run / 2),
|
||||
-half + rise / 2 + config.height * 0.02,
|
||||
// Direğin arka yüzü EĞİK (konik), payandanınki dik — hiçbir
|
||||
// yükseklikte eş düzleme gelmiyorlar.
|
||||
// The post's back face is SLOPED (tapered), the brace's is upright —
|
||||
// they never become coplanar at any height.
|
||||
-postD * 0.62,
|
||||
)
|
||||
postPieces.push(brace)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue