8 puan yazan GN⁺ 2023-08-10 | 1 yorum | WhatsApp'ta paylaş
  • g9.js, sayısal anahtar-değer verilerini bir render fonksiyonuna bağlayan; kullanıcı grafik öğelerini sürüklediğinde yeni veri değerlerini bulup tüm sahneyi yeniden çizen etkileşimli bir grafik kütüphanesidir
  • Temel API yalnızca g9(initialData, render[, onChange]) şeklindedir; initialData ve çizim bağlamını render(data, ctx) içine aktararak nokta, çizgi, daire, görüntü vb. çizer
  • Sürükleme gibi bir etkileşim gerçekleştiğinde g9, amaçlanan değişime en yakın değerleri bulmak için olası veri değerleri uzayını optimize eder ve gerekirse yeni veriyi onChange ile iletir
  • Galeri, temel nokta aynalamadan Bézier eğrilerine, üç noktayla tanımlanan daireye, küpe, dragon eğrisine, görüntülere, saate, LOGO Turtle’a, Grid Warp’a ve Crank and Piston’a kadar örnekleri aşamalı olarak gösterir
  • affects, ctx.pure, getData, setData, isManipulating, Math.max/Math.min gibi kalıplarla manipüle edilebilir değişken aralıkları, yeniden render etme ve animasyon sırasında kullanıcı girdisini işleme kurgulanabilir

g9.js’in çalışma akışı

  • g9.js tek bir fonksiyon sunar: g9(initialData, render[, onChange])
  • Önce sayısal değerlere sahip anahtar-değer verisi oluşturulur
  • Veri ve çizim bağlamı render(data, ctx) içine aktarılır; render fonksiyonu veri değerlerine göre ne çizileceğine karar verir
    • Örnekte ctx.point(data.foo, 17) ile bir nokta çizilir
    • İlgili belge dokümanlardaki render bölümündedir
  • Kullanıcı bir öğeyi yeni bir konuma sürüklediğinde g9, olası veri değerleri uzayında en yakın değişimi üreten değeri bulur
    • Daire sola sürüklenirse örnekte {foo: 8} gibi yeni bir veri ortaya çıkabilir
  • Yeni veri belirlendiğinde tutarlılığı korumak için tüm sahne yeniden render edilir
    • onChange callback’i sağlanmışsa g9, sayfanın diğer bölümlerinin güncellenebilmesi için yeni veriyi aktarır
  • API’nin tamamı Docs içinde görülebilir

Temel şekiller ve veri kısıtları

  • Basic örneği yalnızca iki nokta kullanan en küçük örnektir
    • Render fonksiyonu bir noktayı (x, y) konumuna, diğerini (y, x) konumuna çizer
    • Bir nokta sürüklendiğinde diğer nokta, birbirlerini aynalıyormuş gibi hareket eder
  • Rings, radius ve angle verilerini trigonometrik fonksiyonlara vererek noktaları iki daire biçiminde yerleştirir
    • İç noktalar kırmızı çizilir
    • Yorum satırına alınmış ctx.line(...) açılırsa iki dairenin noktaları arasına çizgi eklenebilir
  • The affects option, g9’un temel şekillerinin aldığı affects seçeneğini gösterir
    • affects, şeklin verinin hangi bölümünü değiştirebileceğini belirtir
    • Üç çizgi farklı affects değerleri kullanır
    • Bir çizgi yalnızca l2_start_x, l2_start_y değerlerini değiştirirken, başka bir çizgi l3_start_x, l3_end_y değerlerini değiştirir
  • Using Max and Min to limit a point's range of motion, Math.max(Math.min(...)) ile bir noktanın hareket aralığını sınırlar
    • Ortadaki noktanın konumu x1 + 20 ile x3 - 20 arasında sınırlandırılır
    • Ayrı bir nokta data.x2 değerini olduğu gibi gösterir

Eğriler, geometri ve 3D örnekleri

  • Bézier Curve, Wikipedia’daki quadratic Bézier curve animasyonunu etkileşimli bir sürüme dönüştüren örnektir
    • Veri olarak start, middle, end koordinatları ve t değeri kullanılır
    • tween= etiketi gösterilir ve çeşitli aşamalardaki çizgi parçalarıyla yumuşak bir eğri oluşturulur
    • Ara yardımcı çizgilere ve eğri parçalarına affects: ['t'] atanır
  • Three points define a circle, üç noktayla bir daire tanımlar
    • Üç nokta arasındaki çizgi parçalarını, orta noktaları ve dik doğrultulu çizgileri hesaplar
    • Hesaplanan merkeze bir nokta koyar ve birinci noktaya olan mesafeyi yarıçap alan yeşil bir daire çizer
  • cube, küpün köşelerini ve kenarlarını oluşturup 3D döndürme, taşıma ve projeksiyon işlemlerinden geçirerek 2D çizgiler olarak çizer
    • rx, ry, rz fonksiyonlarıyla eksen döndürmeleri hesaplanır
    • project fonksiyonu 300 * p[0] / p[2], 300 * p[1] / p[2] yöntemiyle projeksiyon yapar
    • Kenarlar, sürükleyerek döndürme değerlerini değiştirebilmek için affects: ['ax', 'ay'] kullanır

Fraktallar, prosedürel çizim ve görüntüler

  • dragon, dragon(...) özyinelemeli fonksiyonuyla dragon eğrisini çizer
    • Veri squareness, fromX, fromY, toX, toY değerlerini içerir
    • Çizgi seçenekleri, eğri biçimini manipüle etmek için affects: ['squareness'] kullanır
    • Başlangıç ve bitiş noktaları yarıçapı 5 olan noktalarla gösterilir
  • tree, ağacı özyinelemeli olarak çizer
    • Veri deltaAngle, attenuation, startLength değerleridir
    • Dal uçları ve çizgiler affects: ['deltaAngle', 'attenuation'] kullanır
    • n > 3 ise çizgi rengi kahverengi, aksi durumda yeşil çizilir
    • onChange callback’i değişen veriyi konsola yazdırır
  • LOGO Turtle simulator, make_turtle(ctx) ile basit bir Turtle arayüzü oluşturur ve çizgileri tekrarlı olarak çizer
    • Veri r ve theta değerleridir
    • forward mevcut yönde ilerleyerek çizgi çizer; dturn derece cinsinden dönüşü radyana çevirir
  • Grid Warp, ızgara noktalarını büken bir dönüşüm uygular
    • Veri spin ve scale değerleridir
    • Her noktanın uzaklığı r ve açısı theta hesaplandıktan sonra dönüş miktarı theta + data.spin * r ile değiştirilir
    • -10 ile 10 arasındaki i, j kombinasyonlarıyla noktalar çizilir
  • image, ctx.image ile görüntü çizer
    • Görüntü URL’si, x, y, genişlik 200 ve yükseklik 200 belirtilir

Mekanik düzenekler ve zaman tabanlı etkileşim

  • Kevin Clock, time verisini kullanarak saat biçimli bir çizim yapar
    • Dakika ibresi data.time / 60, saniye ibresi data.time kullanılarak Math.cos, Math.sin ile uç noktaları hesaplar
    • setInterval, her 50 ms’de bir time değerini 0.01 artırır
    • Yalnızca graphics.isManipulating doğru değilken getData() ve setData(data) ile zamanı günceller
  • Crank and Piston, krank ve piston yapısını çizer
    • Veri yalnızca time değeridir
    • Gövde rengi, Math.cos(data.time) ile oluşturulan HSL değerine göre değişir
    • Krank, mil ve piston çizgiler, dikdörtgenler ve dairelerle çizilir
    • Animasyon bloğu yorum satırına alınmıştır; açılırsa her 10 ms’de bir time değerini 0.05 artıracak şekilde düzenlenmiştir
  • tongs, döndürme ve tekrarlanan çizgi parçalarıyla maşa biçimli bir yapı oluşturur
    • Veri a, b değerleridir
    • rotate(x, y, a) ile koordinatları döndürür ve dört tekrar ile iki sıra çizgi parçası çizer
    • onChange callback’i değişen veriyi konsola yazdırır

1 yorum

 
GN⁺ 2023-08-10
Hacker News yorumları
  • Özyinelemeli ağacın 5-6 seviye derinliğinde render edilmiş bir düğümü tutup hareket ettirdiğinizde, ağacın geri kalanının tepki vermesi sihir gibi görünüyor.
    Herhangi bir yaprak düğüm konumundan ağacın başlangıç parametrelerini açıkça çıkaran bir fonksiyon olmadan da çalışıyor.
    Bunun nasıl uygulandığını ve noktaların konumları makul ölçüde düzgün bir fonksiyonla bağlı olmadığında ne kadar iyi çalıştığını merak ediyorum.

    • Tahminimce, hareket ettirilen nesne için her parametrenin etkisini iki fonksiyon örneklemesiyle kısmi türev alarak hesaplıyor, sıradan gradyan inişi uyguluyor ve yakınsayana kadar bunu tekrarlıyordur.
      Ya da hazır bir optimizasyon çözücüyü koymak da mümkün olabilir.
    • Ters kinematike benzer bir yöntem gibi geliyor.
      Ters kinematik problemini modellemenin ve çözmenin birçok yolu var.
      Bunların en esnek olanları genellikle yinelemeli optimizasyonla yaklaşık çözümler bulur; çünkü ileri kinematik denklemlerini tersine çözmek zordur ve çözüm uzayı boş da olabilir.
      Birçok yöntemin özünde, ileri kinematik denklemlerini Taylor serisi açılımı ile modellemek vardır; bu, tersine çözmesi özgün sisteme göre daha kolay olabilir.
  • İlk başta bunun havalı ve temiz, basit bir API olduğunu düşündüm; aynı işi yapmak için d3'ten daha çok hoşuma giderdi.
    Ama “Birisi grafikle etkileşime geçtiğinde, örneğin bir öğeyi yeni bir konuma sürüklemeye çalıştığında, g9 olası veri değerleri uzayında optimizasyon yaparak bu değişime en yakın değeri bulur” kısmını okuyunca, bu bana epey sihirli bir DOM manipülasyonu gibi geldi.
    Bir grafiği manipüle edip veriyi değiştirmesi gereken uygulamaların çoğu oldukça özel görünüyor; ama eğlence amaçlı kullanım evrensel.
    Böyle bir kullanım senaryosu gelirse çok keyifli olur.

    • Bu grafikler için değil, interaktif demolar için.
      Sayfadaki diğer örnekleri de doğrudan kurcalayıp kurcalamadığını merak ediyorum.
    • Uygulama alanı açısından bakınca, etkileşime izin verildiğinde etkili biçimde aktarılabilecek çok sayıda STEM kavramı var.
    • Buna ileri düzey bilim demezdim.
      Sürükleme olayının ilk birkaç karesinde hızı ve vektörü ortalayıp, daha fazla kareyle ortalamayı güncellemeye devam ettikten sonra, tahmin edilen bitiş noktasına doğru her şeyi yumuşak bir ease-in-out ile hareket ettirmeye başlarsanız, bu “olası değerler uzayında optimizasyon”a epey yaklaşır.
  • Bu daha önce HN'de tartışıldığında ilginç yorumlar vardı: https://news.ycombinator.com/item?id=12572412

  • Nasıl çalıştığına dair bir açıklama görmek isterim. Bana kara büyü gibi geliyor.
    Örneğin iki nokta var; biri (0, 10), diğeri (10, 0) konumunda. Arkadaki noktayı (20, 0)'a sürüklersem, sistem ilk noktanın da aynı mesafe kadar yansıtılması gerektiğini nasıl anlıyor?
    Tekrar edeyim, bu sihir ve yapan kişi gerçekten iyi iş çıkarmış.

    • Çizilmiş öğenin konumu ile sürükleme hareketinin gerektirdiği yeni konum arasındaki hatayı ölçen bir fonksiyon tanımlıyor.
      Bu fonksiyonun veri değişkenlerine göre kısmi türevini alıyor, fonksiyonu minimize ederek yeni veri değerini buluyor ve ardından her şeyi yeni optimum veri değeriyle tekrar render ediyor.
      Biraz kafa karıştırıcı olmasının nedeni, burada genelde “veri” dediğimiz şeyin matematiksel optimizasyonda parametre, istatistik/makine öğrenmesinde ise ağırlık diye adlandırılabilecek şey olması.
      Makine öğrenmesinde sabit olana “veri” denirdi; dolayısıyla burada sürüklenen öğenin konumu veriye karşılık geliyor.
    • Yeterince gelişmiş matematik sihirden ayırt edilemez; burada “yeterince gelişmiş” eşiği de sanılandan daha düşük.
  • Oldukça havalı görünüyor.
    Bir eksik yanı, işaretçi ya da seçim göstergesi olmaması; açıklamayı önce okumazsanız nesnelerin sürüklenebildiği pek anlaşılmıyor.
    Nesne konumunu yeniden ayarlama biçimi de biraz kafa karıştırıcı.
    Normalde bir pointer handler tanımlar ve girdiye göre dönüşüm uygularsınız; doğru anladıysam motor bunu otomatikleştirmek için en iyi tahminini yapmaya çalışıyor.

  • Birkaç yıl önce bunu görüp heyecanlanmıştım; tarayıcıda PyTorch benzeri bir kütüphaneyle birlikte çalışması için Pyodide'a port etmiştim: https://srush.github.io/g9py/
    JavaScript'ten biraz daha takılıyor, ama en başta çalışıyor olması bile şaşırtıcı.

  • Bu cadılık gibi hissettiriyor. Son derece etkileyici.

  • 2D CAD'deki kısıt tabanlı çizim araçlarıyla ortak noktaları var gibi görünüyor.

  • Nerede kullanılması gerektiğinden pek emin değilim, ama gerçekten zekice yapılmış.

    • Potansiyel olarak herhangi bir sürükle-bırak arayüzünde kullanılabilir gibi görünüyor.
      Probleme bakış biçimi o kadar farklı ki, kavrayabilmek için bir şeyde bizzat kullanmam gerekecek.
  • Eğitim ortamlarında trigonometri veya diğer matematik kavramlarını aktarmak için çok faydalı olabilir.
    Formülleri görsel uzamla eşleştirmekte zorlanan çok sayıda görsel öğrenen var; biraz etkileşim eklemek, anlayışı pekiştirmede büyük yardımcı olur.