Blog Kapaklarını Ölçerek Tasarlanan Üretken Desenlerle Çizmek
Her yazıya kendine özgü bir kapak çizen küçük bir motor yazdık ve desenleri gözle değil ölçerek seçtik.
Bu blogun kartlarında başından beri görsel yoktu. Tarih, kategori, başlık ve yazar vardı. Kartlar okunaklıydı ama birbirine benziyordu ve liste uzadıkça aradığın yazıyı gözle bulmak zorlaşıyordu.
Bize yazının konusunu anlatan bir görsel gerekmiyordu. Kartları birbirinden ayıran ve hepsi aynı elden çıkmış gibi duran bir görsel gerekiyordu. Bilinen iki yol bunu vermedi. Stok fotoğraf konuyu anlatmaya çalışıyor ama önbellek sürümlemesi ya da bel sağlığı üzerine bir yazıda ya alakasız ya da klişe kalıyor. Her yazıya ayrı bir illüstrasyon çizdirmek ise hem yavaş hem de tutarsız. On yazı sonra blog on farklı elden çıkmış gibi görünüyor.
Başka bir yolu Render'ın blogundaki kapaklar gösterdi. İki renk, ince bir çerçeve ve tek bir kural. Çizgiler bir yerde kalınlaşıp bir yerde inceliyor, dama karoları merkeze doğru sıkışıyor, dalgalar sütundan sütuna yön değiştiriyor. Uzaktan bakınca sakin bir yüzey, yaklaşınca titreşen bir desen. Hiçbiri yazının konusunu anlatmaya çalışmıyor ama hepsi aynı aileden olduğunu söylüyor. Aradığımız şey tam olarak buydu.
Aklımıza şu geldi. Bu bir üslupsa kurallara dökülebilir. Kurallara dökülebiliyorsa her yazı için ayrı ve otomatik üretilebilir.
Önce bakmayı öğrendik
Render'ın 5 kapağının ekran görüntüsünü kodla ölçtük. Görüntüler 1296×864 pikseldi ve çerçeve 4 kenarda da tam 48 pikseldi, yani kısa kenarın 1/18'i. Kenar yumuşatması dışında her görselde yalnızca iki renk vardı. Çoğunda ikisi aynı tonun koyu ve açık hâliydi. Mavide ise açık renk camgöbeğine doğru kaymıştı, ton açısında yaklaşık 26°.
Renkleri algıya göre kurulmuş OKLCH renk uzayında ölçtük. Açıklık koyu zeminde 0,24 ile 0,31, açık renkte 0,80 ile 0,91 arasındaydı. İki rengin kontrast oranı 8,3:1 ile 11,5:1 arasında değişiyordu. Açık rengin çerçeve içindeki payı ise hep yarıya yakındı, %47 ile %57 arasında. Bu sayılar ileride motorun sınırları olacaktı.
Bu 5 kapak aynı iskeleti paylaşıyordu. Renk ailesi dışında asıl değişen, deseni hangi kuralın yönettiğiydi. Birinde dikey çubukların kalınlığı merkeze doğru basamak basamak artıyordu. Bir başkasında kalınlık satırdan satıra doğrusal artıyor, komşu sütunda tersine dönüyordu. Üçüncüsünde dama karolarının genişliği merkeze yaklaştıkça küçülüyordu. Dördüncüsünde kalınlık bir sinüs dalgasına bağlıydı ve komşu sütun ters fazda çiziliyordu. Sonuncusunda her çizgi bir noktada kalından inceye geçiyor, geçiş noktası köşegen boyunca kayıyordu.
İlk sürümümüz beşinin de benzerini çizebildi. Ama üçü için o kapağa özel bir kural yazmamız gerekmişti ve bir görüntüyü yalnızca onun için yazılmış bir kuralla yeniden çizmek pek bir şey kanıtlamaz. Asıl işaret öbür ikisiydi. Doğrusal artan ve sinüsle değişen çizgiler, motorun genel parçalarıyla, yani bir alan fonksiyonu ve bir çizgi ızgarasıyla çıktı. Üslubun birkaç kurala indirgenebildiğini bu ikisi gösterdi.
Sonra referanslara özgü ne varsa motordan çıkardık. Amacımız Render'ın kapaklarını kopyalamak değildi. Dilbilgisini öğrenip kendi cümlelerimizi kurmak istiyorduk. Özel yazdığımız 3 kural koddan tamamen silindi, öbür 2 deseni veren birleşim de bilerek kapatıldı. Motor bugün o 5 deseni birebir üretmiyor. Parçalar tesadüfen yan yana gelince uzak akrabaları çıkabiliyor ama hiçbir kural onları hedeflemiyor. Elindeki 11 motifi de bilinen tekniklerden kendimiz kurduk.
Tek bir fikir, çok sayıda desen
Motiflerin yaklaşık yarısının merkezinde basit bir fikir var, alan fonksiyonu. Bu fonksiyon tuvalin her noktasına sıfır ile bir arasında bir değer veriyor. Çizgiler ve noktalar bu değeri okuyup kalınlaşıyor ya da inceliyor, pikseller yanıyor ya da sönük kalıyor. Aşağıda solda bir kapak, sağda o kapağın altındaki alan duruyor. Haritada açık yerler kalın çizgi demek.

Gözün desen sandığı şey, bu haritanın çizgilerle örneklenmiş hâli. Alanı değiştirince desen değişiyor, çizgi ızgarası aynı kalıyor. Motorda radyal, baklava, yastık, dalgacık, spiral, eyer, yumurta kolisi, dalga ve rampa olmak üzere 9 alan var. Çizgi alanı, nokta ızgarası, piksel tarama, halkalar ve dokuma bu alanları okuyor. Rampa ve dalga çizgi motifine kapalı, çünkü referanslardaki 2 deseni tam da bu birleşim veriyordu.
Alanın üstünde 2 ayar daha var. Birincisi basamak. Alan bazen 3 ile 6 arasında bir düzeye indiriliyor ve kalınlık yumuşak değil, basamak basamak değişiyor. İkincisi hücreler. Tuval bazen birkaç hücreye bölünüyor ve her ikinci hücrede alan ters çevriliyor, aynalanıyor ya da fazı kaydırılıyor. Tek bir alan bu sayede kendi içinde bir ritim kazanıyor.
Alandan çıkmayan desenler
Bazı desenler bir alandan değil, bir yerleşim kuralından doğuyor.
Truchet karolarının en bilineni bir kare ve onun iki karşı köşesinden çizilen çeyrek yaylar. Karo döndürülünce yaylar öbür iki köşeye geçiyor. Hangi karonun döneceğini bir kural seçiyor. Kural rastgele olunca yaylar birbirine bağlanıp kıvrılan yollar çiziyor. Kural tuvalin çeyreklerine göre olunca aynı karo ortada açan bir çiçeğe dönüşüyor. Bunların yanında dama, sıra ve dalga kuralları da var. Karo da yalnızca yaydan ibaret değil, üçgen ve köşegen taramalı karolar da seçilebiliyor.
Kutupsal desenlerde ızgara açı ve yarıçaptan kuruluyor. Dilimler ve halkalar aynı SVG yoluna (path) yazılıyor ve evenodd dolgu kuralı ikisinin çakıştığı yerleri boşaltıyor. Ortaya bir dart tahtası çıkıyor. Bu kesişim için ayrıca hesap yapmıyoruz, tarayıcı dolguyu çizerken kendisi yapıyor. Moiré da aynı yoldan çalışıyor. İki düzenli deseni aynı yola yazınca farkları kendi desenini çiziyor. Dilimleri logaritmik bir eğriyle bükünce dart tahtası girdaba dönüyor.
Eşikten geçen yüzeyler
Üçüncü grup pürüzsüz bir yüzeyi iki tona indiriyor.
Topoğrafya motifi tuvale birkaç tepe ve çukur yerleştiriyor, altına da hafif dalgalı bir zemin seriyor. Yüzeyin eş yükselti çizgilerini marching squares yöntemiyle, yani ızgaranın her karesinde çizginin hangi kenarlardan geçtiğine bakarak çıkarıyor. Ham çizgiler kırık çıkıyor. Chaikin köşe kesmesi onları yumuşatıyor, Ramer-Douglas-Peucker sadeleştirmesi de düz giden noktaları atıp kıvrımı koruyor. Çizgi kalınlığını elle seçmiyoruz. Toplam eğri boyunu ölçüp kaplama hedefini tutturan kalınlığı hesaplıyoruz. Boyu kendi kalınlığının birkaç katına varmayan eğriler kart boyutunda nokta kümesi gibi durduğu için çizilmiyor.
Dalgalı zemin başta isteğe bağlıydı. Motif seçimini kategoriden bağımsız yaptığımızda topoğrafya daha sık çıkmaya başladı ve dalgasız örneklerde tepelerden uzak bölgelerin düz kaldığını, oraya hiç çizgi düşmediğini gördük. Dalgalı zemini zorunlu yaptık. 300 topoğrafya kapağında en zayıf kapağın kaplaması %10'dan %28'e, en zayıf %5'lik dilimin sınırı %28'den %42'ye çıktı. Kalan açığı aşağıda anlatacağımız kod incelemesi kapattı.
Piksel tarama alan fonksiyonlarını Bayer eşik matrisinden geçiriyor. Eski bilgisayar grafiklerinden tanıdık bir yöntem. Her hücre, alanın değeri matristeki eşiği aşıyorsa yanıyor. Yumuşak bir geçiş düzenli bir piksel dokusuyla anlatılmış oluyor. Piksel her zaman kare değil, yatay bir tire ya da artı da olabiliyor.
11 aile
Motorun bugünkü 11 motif ailesi aşağıda. Her ailenin kendi içinde modları ve seçenekleri var.
| Motif | Ne yapıyor |
|---|---|
| Çizgi alanı | Paralel çizgilerin kalınlığı bir alanı izliyor |
| Nokta ızgarası | Noktaların boyu bir alanı izliyor, kare, artı ve çubuklar bazen tuval boyunca dönüyor |
| Piksel tarama | Bir alan Bayer matrisiyle iki tona iniyor |
| Halkalar | İç içe daire, kare ya da baklava, kalınlık bir alanı izliyor |
| Dalga çizgileri | Çizgiler bir profil boyunca kayıyor, eğimde inceliyor |
| Topoğrafya | Tepe ve çukurlardan kurulan bir yüzeyin eş yükselti çizgileri |
| Truchet karoları | Çeyrek yay, üçgen ya da köşegen karo, yönünü bir kural seçiyor |
| Kutupsal | Dilim ve halkalardan dart tahtası, girdap, spiral kol ve güneş ışını |
| Moiré | İki düzenli desen üst üste, farkları kendi desenini çiziyor |
| Yama | Tuval bölünüyor, parçalar kendi açısıyla taranıyor ya da düz bırakılıyor |
| Dokuma | Kare karolar çubuklarla ya da balıksırtı taramayla örülüyor |
Buraya kadar görmediğimiz ailelerden birkaç örnek.
Renk
Her kategorinin 5 ana tonu var. Teknik yazılar mor, lacivert, çivit, petrol ve mürdümden birini alıyor. Sağlık yazıları yaprak, nane, çam, zeytin ve okyanustan, kesişim yazıları limon, kehribar, mercan, gül ve tarçından seçiyor. Ton seçildikten sonra iki renk o tondan türetiliyor. Zemin koyu ve doygun, desen açık ve yumuşak.
Renkleri RGB'de değil OKLCH'de kuruyoruz. OKLCH'de açıklık algıya göre ölçüldüğü için sarı ile mor aynı açıklık değerinde gözümüze de yaklaşık aynı açıklıkta görünüyor. HSL gibi RGB tabanlı modellerde böyle bir eşitlik yok, aynı açıklık değeriyle üretilen sarı parlarken mor kararıyor.
Referanslardaki mavi kaymayı da genelleştirdik. Mor dışındaki bütün tonlarda açık renk ton açısında biraz kayıyor. En belirgini lacivertte, yaklaşık 28°.
Kontrastın alt sınırı 7:1, WCAG'nin metin için koyduğu en sıkı eşik. Açık renk bu oran tutana kadar adım adım açılıyor, yetmezse koyu renk koyulaşıyor. Kapakların yaklaşık %22'si açık zeminle, yine yaklaşık %22'si doygunluğu kırılmış tonlarla çıkıyor. Açık zeminde roller yer değiştiriyor ve desen koyu mürekkeple çiziliyor.
Rastgele ama hep aynı
Kapakların rastgele görünmesini istiyoruz ama gerçekten rastgele olmalarını istemiyoruz. Aynı yazı her derlemede aynı kapağı almalı. Bu yüzden motor zar atmıyor, tohumu yazının kendisinden türetiyor. Kategori, slug ve varyant numarası tek bir dizgede birleşiyor, 32 bitlik bir sayıya özetleniyor ve bu sayı küçük bir sözde rastgele sayı üretecini başlatıyor.
const a = render(design("bir-yazi", "teknik"))
const b = render(design("bir-yazi", "teknik"))
assert.equal(a, b)Bu, motorun testlerindeki ilk kontrol. Aynı dosyadaki öbür testler her kategori ve her motif için, kapak ve paylaşım görseli boyutları dahil 3 boyutta SVG'yi baştan sona tarıyor. İzin verilmeyen bir öğe, kapanmamış bir etiket, koordinatlarda geçersiz bir sayı ya da boş bir desen testi düşürüyor. Kontrast ise her kategoride ve her zemin tipinde 2000 rastgele palet üzerinde ölçülüyor.
Kategori desene doğrudan karışmıyor, yalnızca renk ailesini seçiyor. Her kategoride 11 aileden her biri eşit olasılıkla çıkıyor. Bir sağlık yazısı da piksel tarama alabiliyor, bir teknik yazı da topoğrafya. Yine de kategori tohumun parçası olduğu için kategorisi değişen bir yazının kapağı baştan değişiyor.
Bir ayrıntı önemli. Palet, motif seçimi ve parametreler aynı üreteçten değil,
ondan çatallanan ayrı akışlardan sayı çekiyor. Böylece palete yeni bir ayar
eklediğimizde motif seçimi ve desenler yerinde kalıyor. Bir yazıya coverMotif
ile belli bir motif zorlandığında da o motifin parametreleri, motif kendiliğinden
seçilseydi alacağı değerlerle aynı kalıyor. Tek akışta paletteki küçük bir ekleme
sonraki bütün sayıları kaydırır ve her kapağın deseni değişirdi.
Peki kapak beğenilmezse? Yazının frontmatter'ına coverVariant alanını ekleyip
bir sayı yazmak yetiyor. Slug ve kategori aynı kalıyor, tohum değişiyor. Bu yazı
için motorun ilk 6 önerisine baktık ve 1 numaralı varyantı seçtik.
Görsel ağırlık
Rastgele parametrelerin en büyük riski, bazı kapakların neredeyse boş, bazılarının neredeyse dolu çıkması. Listede yan yana durduklarında göz hemen ağır olana gidiyor. Bu yüzden motiflerin çoğunda desen renginin çerçeve içindeki payı için bir hedef seçiyoruz.
Çizgi, halka, yama ve kare pikselli taramada hedef %46 ile %57 arasından seçiliyor, referanslarda ölçtüğümüz aralıkla neredeyse aynı. Dokumada aralık %46 ile %54, topoğrafyada %44 ile %54. Nokta ızgaralarında hedefi bilerek biraz düşük tuttuk, %38 ile %48 arasında. Noktaların çizgilerden biraz daha hafif durması gözümüze daha iyi geldi. Yaylı Truchet karolarında yay genişliği, kaplamayı %42 ile %50 arasına getirecek şekilde doğrudan geometriden hesaplanıyor. Tire ve artı pikseller hücrenin ancak bir kısmını doldurduğu için orada hücrelerin %62 ile %82'si yakılıyor. Dalga, moiré ve kutupsal motiflerde hedef yok. Orada çizgi kalınlığı ya da dilim doluluğu yarıya yakın, dar bir aralıktan seçiliyor.
Alan kullanan motiflerde hedefi tutturmak için alanın ton eğrisini değiştiriyoruz. Alanı bir üsse yükseltince ortalaması değişiyor ve doğru üssü ikiye bölme yöntemiyle arıyoruz.
for (let i = 0; i < 18; i++) {
const mid = (lo + hi) / 2
sh.gamma = Math.exp(mid)
if (meanOf(sh) > target) lo = mid
else hi = mid
}Buradaki ortalama soyut bir integral değil. meanOf alanı tam olarak çizimin
okuduğu noktalarda ölçüyor. Çizgi motifinde bu bir hücre ızgarası, dokumada karo
merkezleri, nokta ızgarasında her noktanın kendi yeri.
Bazı alanlarda üs yetmiyor. Alanın büyük kısmı tam sıfır ya da tam bir olduğunda üs o bölgeleri hiç değiştirmiyor. O zaman kalan farkı alanı yukarı ya da aşağı iten ikinci bir düzeltmeyle kapatıyoruz ve onu da ikiye bölmeyle arıyoruz. Basamaklı alanlar ayrıca dikkat istiyor. Orada ortalama yumuşak değil, sıçrayarak değişiyor ve ikiye bölme sıçramanın iki yanında durup kalıyor. Arama bitince iki yanı da ölçüp hedefe yakın olanı seçiyoruz.
Ölçerek karar vermek
Kapakların dengeli çıktığına birkaç örneğe bakarak karar vermek, ürün eşleştirme yazısında anlattığımız hatanın aynısı olurdu. Elle bakılan örnek son değiştirilen şeyi doğrular. Bu yüzden kategori başına 120 tohum, toplam 360 kapak ürettik ve her birini tarayıcıda piksele çevirip saydık.
| Ölçüt | Sonuç |
|---|---|
| Kaplama, en düşük | %35 |
| Kaplama, 5. yüzdelik | %41 |
| Kaplama, medyan | %50 |
| Kaplama, 95. yüzdelik | %56 |
| Kaplama, en yüksek | %60 |
| %38 ile %62 bandının dışında kalan | 4/360 |
| En düşük kontrast | 7,6:1 |
| Görülen farklı alt aile | 137 |
| SVG boyutu, medyan | 17,7 KB |
| SVG boyutu, en ağır | 151 KB |
| Sıkıştırılmış boyut (gzip), medyan | 5,0 KB |
| Üretim süresi, medyan (tarayıcıda) | 2,3 ms |
Alt aile sayarken motif, mod, karo kuralı ve alan gibi gözle görülür seçimlerin farklı birleşimlerini ayrı saydık. 137, bu 360 kapakta karşımıza çıkan birleşim sayısı, motorun üretebileceği bütün birleşimler değil.
En düşük kaplamaların çoğu nokta ızgarasından geliyor, yani hedefi bilerek düşük tuttuğumuz yerden. Onları artı pikselli taramalar ve dalga çizgileri izliyor. En düşük tek kapak ise %35 ile bir güneş ışını, hedefi olmayan bir motif.
Çeşitliliği de ölçmek istedik. Her kapağı 48×32 bloktan oluşan kaba bir ize indirdik, renkleri yok saydık ve her kapağı kendisine en çok benzeyen komşusuyla karşılaştırdık. Tipik bir kapağın en yakın komşusuyla farkı 0,19 çıktı. 360 kapağın 11'inde bu fark 0,05'in altına düştü. Bunların 9'unda iki taraftan biri dalga kapağıydı, kalan 2'si de birbirine benzeyen iki dokumaydı. En yakın çift, iki ayrı kategoriden gelen iki dalga kapağıydı ve farkları tipik farkın 1/12'si kadardı.
Bu çifti kart boyutunda yan yana koyunca ise ikisi rahatça ayırt ediliyor. Biri koyu zeminde zikzak, öbürü açık zeminde sinüs profilli. Yakınlığı abartan şey ölçümün kendisi, çünkü kaba iz ince ve düzenli dokuların hepsini aynı griye indiriyor. Vardığımız yargı da bu yüzden iki parçalı. Dalga ailesi dışında birbirinin kopyası gibi duran kapak çıkmıyor. Dalgalarda ise bu ölçüm soruyu cevaplayamıyor.
Dışarıdan bir göz
Yazının ilk taslağı bittiğinde taslağı, bu işi daha önce hiç görmemiş bağımsız bir inceleme ajanına verdik ve ondan mantık hatası aramasını istedik. Bulguların bir kısmı yazıdaydı. Daha önemlisi, bir kısmı doğrudan kodu işaret ediyordu. Bunun üzerine kodu da ayrı bir incelemeden geçirdik. Bu inceleme 1650 kapak üretip ölçtü ve bizim ölçümümüzün kaçırdığı yerleri buldu.
En çarpıcı iki bulgu dengelemedeydi. Halka motifinde iç daire, halkaya kalınlık hissi versin diye bazen bir yana kaydırılıyordu. Dengeleme bu kaymayı hiç görmüyordu. Kayma halkalar arasındaki boşluktan büyük olunca iç daire bir önceki halkayı kesiyor, evenodd kuralı da kesişimi siliyordu. Hedefi %51 olan bir kapak %18 kaplamayla çıkıyordu. Dokumada ise dengeleme alanın ortalamasını bütün tuval üzerinden alıyordu. Oysa balıksırtı dokuma alanı yalnızca 6 ile 12 sütun karonun merkezinde okuyor. Alan 3 basamaklı olunca bu fark, kaplamanın %83'e çıkması demekti.
Kayma artık yalnızca tuvalin ortasındaki tek daireli halkalarda açılıyor ve halkalar arasındaki boşlukla sınırlı. Dengeleme her motifte alanı çizimin gerçekten okuduğu noktalarda ölçüyor ve basamaklı alanlarda sıçramanın iki yanına bakıyor. Yukarıda anlattığımız hâl bu düzeltmelerden sonraki hâl.
İnceleme başka açıklar da buldu. Topoğrafyada çizgi kalınlığının bir tavanı var. Eğriler kısa kalınca kalınlık tavana dayanıyor ve hedef tutmuyordu. Artık böyle durumlarda motor seviye sayısını artırıyor ve 180 topoğrafya kapağının en düşüğü %39. Baklava noktalar ile tire ve artı pikseller ise yanlış alan formülleri yüzünden hedefe hiç ulaşamıyordu. Baklavalar yarım satır kaydırmalı bir ızgaraya geçti, öbür şekillerin alanı gerçek geometriden hesaplanıyor. Sonuç toplu ölçümde de görünüyor. 360 kapaklık ölçümde %38 ile %62 bandının dışında kalan kapak sayısı 29'dan 4'e indi. En düşük kaplama %24'ten %35'e, 5. yüzdelik %35'ten %41'e çıktı.
Bir bulgu da sayfa ağırlığındaydı. İlk sürümde SVG doğrudan sayfanın içine yazılıyordu ve React sayfayı canlandırmak için aynı içeriği bir kez daha taşıyordu, yani her kapak sayfaya 2 kez giriyordu. Üretim derlemesinde liste sayfası 360,6 KB tutuyordu ve bunun yarısından fazlası kapaktı. Kapaklar artık derleme sırasında ayrı bir dosya olarak üretiliyor. Adreste içeriğin özeti olduğu için tarayıcı dosyayı bir yıl boyunca önbellekte tutabiliyor, kapak değişirse adres de değişiyor. Liste sayfası 360,6 KB'tan 160 KB'a, sıkıştırılmış hâliyle 84,3 KB'tan 26 KB'a indi. Bir yazı sayfası 466 KB'tan 178 KB'a düştü. Paylaşım görseli de her istekte 100 ile 160 milisaniye harcayarak yeniden çiziliyordu. Artık o da derleme sırasında bir kez üretiliyor.
Testler de sitenin gerçekten kullandığı durumları kanıtlamıyordu. Motorun varsayılan 3:2 boyutunda çalışıyorlardı ve desen rengi her SVG'de yazıldığı için boş bir desen bile testten geçerdi. Bugünkü testleri yukarıda anlattık. Üstüne yeni bir koruma ekledik. Yayındaki her yazının kapağının özeti bir snapshot dosyasında duruyor. Motordaki bir değişiklik yayındaki bir kapağı değiştirirse test kırılıyor ve değişikliği ancak bilerek kabul edebiliyoruz.
Paylaşım görseli
Kapak 4 yerde görünüyor. Liste sayfasındaki sade kartlarda, her yazının altındaki ilgili yazı kartlarında, her yazının metninin başında ve yazı paylaşıldığında çıkan 1200×630 piksellik görselde. Öne çıkan büyük kartlar bilerek kapaksız. Paylaşım görselinde kapak aynı tohumdan bu boyutta yeniden çiziliyor. Motif ve renkler aynı kalıyor, yalnızca yerleşim yeni boyuta uyuyor.
Paylaşım görselinde kuralımız katıydı ve mevcut yerleşim değişmeyecekti. Logo, kategori, başlık, yazar ve site adresi eski yerlerinde duruyor. Eski sıcak renkli zeminin yerini kapağın alması dışında değişen iki şey var. Öğelerin rengi marka kahvesi yerine kapağın kendi iki rengi oldu, yani her yazıda farklı. Desen öğelere değmesin diye de her öğenin arkasına zemin renginde bir açıklık koyduk. Bu açıklığın biçimine 3 denemede vardık.



Aynı yerleşim her yazıda kendi renklerini alıyor.







TypeScript ve SVG
Motor, bağımlılığı olmayan bir TypeScript modülü. Renk, alan, geometri ve çizim ayrı dosyalarda, her motif de kendi dosyasında duruyor. Sitenin geri kalanıyla aynı dilde yazıldığı için ikinci bir çalışma ortamı gerekmiyor. Kapak derleme sırasında sunucuda çiziliyor, paylaşım görseli de aynı fonksiyondan besleniyor.
Motor 2 adımda çalışıyor. design bütün rastgele kararları verip düz bir JSON
tarifi çıkarıyor, render bu tarifi SVG'ye çeviriyor. Tarif saklanabilir, elle
düzenlenebilir ve tekrar çizilebilir. Çıktının SVG olması da kapağı her boyutta
keskin tutuyor.
Bilinen sınırlar
Motor değişince kapaklar da değişiyor. Kapak yazının slug'ından ve motorun o günkü kodundan çıkıyor. Kapakları 3:2'den 16:9'a geçirdiğimizde bütün kapakların deseni yeni genişliğe göre yeniden yerleşti. Kod incelemesinden sonraki düzeltmeler de 8 yazının 3'ünde kapağın ayrıntısını değiştirdi. Motif ve renk aynı kaldı ama desen yeniden dengelendi. Elle seçilen varyant da bundan korunmuyor, bu yazının 1 numaralı kapağı dahil. Snapshot testi böyle bir değişikliği gözden kaçırmamızı engelliyor ama eski kapağı geri getirmiyor. Bir kapağı motordan bağımsız sabitlemek için ya motorun eski sürümlerini saklamamız ya da her kapağın tarifini yazının yanında tutmamız gerekecek.
Bazı kapaklar ağır. Medyan kapak sıkıştırılmış hâliyle 5 KB. En ağır uç değerler dalga ve girdap motiflerinden geliyor, 129 ile 151 KB arasında, sıkıştırınca 44 ile 52 KB. Medyanda en ağır aile ise nokta ızgarası, yaklaşık 45 KB. Kapaklar artık ayrı dosya olduğu için sayfayı büyütmüyor ve tarayıcı önbelleğinde kalıyor ama ilk ziyarette bu boyut yine iniyor. Koordinatları tam piksele yuvarlamak dosyaları ham hâliyle yaklaşık %27 küçültürdü. Bunu henüz yapmadık.
Benzerlik ölçümümüz ince dokulara kör. Kaba iz, dalga gibi sık desenleri aynı griye indiriyor ve çeşitliliği olduğundan az gösteriyor. Kart boyutunda ölçen bir yöntemimiz henüz yok, en yakın çiftlere hâlâ gözle bakıyoruz.
Güzelliği ölçmüyoruz. Kaplama, kontrast ve çeşitlilik ölçülüyor ama bir kapağın güzel olup olmadığını hiçbir sayı söylemiyor. O karar hâlâ gözde ve varyant numarası bu yüzden var.
Başta tek istediğimiz, kartların birbirinden ayrılmasıydı. Sonunda elimizde her yazıya kendi kapağını çizen, motor değişmedikçe aynı yazıya hep aynı kapağı veren ve beğenilmeyen bir kapağı tek bir sayıyla değiştirmeye izin veren bir motor var. Render'ın kapaklarından öğrendiğimiz şey belli bir desen değil, bir yöntemdi. İki renk, bir çerçeve ve tek bir kural.