Herkese selamlar. Umarım iyisinizdir. Öncelikle 30 Ağustos Zafer Bayramımız kutlu olsun. Bu ayın sonunda Uygulamalarda yaptığım değişiklikleri, düzeltmeleri ve özellikleri sizlere anlatmak isterim.
XDraw
Daha önce yaptığım optimizasyonlara ek olarak daha farklı optimizasyonlar yaptım.
Garbage Collector baskısı
Yüksek seviyeli dillerde hep olan ve varlığını hissetmediğimiz bir durumdur. Çöp toplayıcısı olarak çevrilebilir. Bu atıl durumdaki değerleri, referansları RAM’den silmeye yarar.
C gibi dillerde RAM’den bir alan almak için malloc, calloc ve yeniden boyutlandırmak için realloc kullanmamız gerekiyor. Ve daha da önemlisi bu referanslarla işimiz bittiği sırada free kullanarak değerleri temizlememiz gerekiyor. Yoksa uygulama sürekli RAM’i şişirecekti ve en sonunda en iyi ihtimalle OutOfMemory ile crashlenecekti, ya da işletim sistemini bir deadlock’a sokarak tepkisiz hale getirecektir.
Daha doğrusu gerekiyordu… Artık bunu bizim yerimize yüksek seviyeli dillerin çalıştırıcısı (Örn: Java VM, Javascript içinse V8 ve benzeri motorlar) yapıyor. Örneğin: void metot çağırılıp bitiyorsa, orada örnek bir obje olarak
const userInfo = {
name: "Hüseyin Can",
surname: "Gündüz",
isNewUser: true,
foo: "bar",
};
buradaki userInfo objesi void metot bittikten sonra garbage collectorler tarafından temizlenir. Eğer saniyede maks 10-15 kere oluşturuluyorsa, bunun varlığını hissetmezsiniz. Ancak bu çok fazla oluyorsa zaten tek thread’de çalışan kod çok daha fazla kasmaya neden olacaktır. Örneğin, kanvas’ta crop edilirken noktalar için, arrayleri filter ile filtreliyordum. Ekranda görünmeyen noktaları filtreliyordum.
...
determinedPoints = drawElement.points.slice(startIndex, endIndex + 1);
...
Bu Array.slice metodu indeksler arasındaki kısmı alıp yeni array olarak veriyor. Tabi ki bu array, canvas’a rasterize edilirken her bir fırça vuruşuna dönüşüyor, ve ardından atıl durumda beklemeye başlıyor. Tabi deminden dediğim gibi bu saniyede olan bir şey değil, milisaniyede muhtemelen bin kez olan bir şey. Bu yüzden de zaten hali hazırdaki tüm noktaları yeni array olarak oluşturmak yerine direkt element içindekini aynen veriyorum. Rasterizer bunu zaten değiştirmiyor ya da düzenlemediği için ise ekstra atıl durumda bir obje de oluşmuyor.
onFound({
...initialFoundElement,
element: drawElement,
elementId: drawElement.id,
elementType: drawElement.type,
points: drawElement.points,
pointStartIndex: startIndex,
pointEndIndex: endIndex,
color: drawElement.color,
partial: partial,
} as any as XDrawElementCropData);
Ve ayrıca tek bir döngüde ilk ve son noktaları kontrol edebiliyoruz. Ekranda görünen uç noktaları startIndex ve endIndex olarak ayarlayabiliyoruz.
const scanCount = Math.ceil(pointCount / 2);
for (let i = 0; i < scanCount; i++) {
const lastIndex = pointCount - 1 - i;
if (startIndex === -1 && cropCondition(drawElement.points[i])) {
startIndex = i;
}
if (endIndex === -1 && cropCondition(drawElement.points[lastIndex])) {
endIndex = lastIndex;
}
if (startIndex !== -1 && endIndex !== -1) {
break;
}
}
Bu sayede en az bir indeks ekranda görünüyorsa (startIndex > -1 || endIndex > 1 ) bunu ekrana çizdiriyoruz. Yoksa görünmeyecek…
Ve ayrıca, bir obje ile işimiz bittiği sırada ve final, readonly ya da const ile değişkeni bir sabit olarak tanımlamamız gerekmiyorsa, normal değişken olarak tanımlayıp sonradan da degisken = null atanarak GC’ye daha az iş düşmesini sağlayarak verimlilik elde edebilirsiniz. Tüm dillerde farklı olabilir ama aklıma javascript geldi.
OffscreenCanvas, part 2
Aslında planım Offscreencanvas kullanmak yoktu ancak çok daha fazla tepkisel hale getirmek istiyordum. Bu yüzden ilk önce tüm elementlerin cachelerinde kendilerine özel bir OffscreenCanvas ekledim. Sonuç hüsrandı çünkü çok ama çok fazla elemente tek tek canvas eklemek yine astarı yüzünü geçiyordu. Tabi yine Claude ve diğer Github Copilot modelleri ile bol token ikramlı gırgır şamatadan sonra tüm alanı rasterize ederek cachelemeye karar verdik. Burada tam ekranın biraz marginli (ekrandan %50 civarı daha büyük çözünürlüğünü croplamak) rasterize ediyoruz. Ve bu durumlarda tekrardan rasterize ediliyor.
- Silgi ile silinirse
- Çok fazla yakınlaştırıp uzaklaştırılırsa
- Kaydırmaya başladığından itibaren kaydırılma x ve y’si margin’den daha fazla kaydırılırsa
- Kalem ile çizim tamamlanıp ekrandan kalem çekilirse, ya da
mouse upolursa
O zaman cache geçersiz sayılıyor ve tekrardan çizdiriliyor.
Ve ayrıca akıcılık bozulmaması için aktif çizilen element cache’den bağımsız şekilde çizilmeye devam ediyor.

Izgaralar artık CSS’de
Tablette denemeler yaparken şunu farkettim. Eğer gridler açık ise tepkiselliği azalıyor, kapalı ise artıyor. Çünkü yine her rasterize işleminde OffscreenCanvas cacheden bağımsız gridler her bir fırça darbesi ile tekrar tekrar çiziliyordu. Elbette cache’ye dahil edilebilirdi, ancak yeniden oluşturması da biraz daha ağırlaşacaktı. Tek thread sürekli dediğim şey ancak klişe bile olsa web app geliştirdiğim için bu biraz önemli. Ve biraz daha düşününce aslında tarayıcıların bu kadar ağır durumu daha iyi kompanse ediyorlar. Bu yüzden canvas’ın arka planını tamamen saydam bırakıp tamamen css sınıfına yöneldim
gridSize = computed([this.zoomFactor], ([zoom]) => {
const gridSizeRaw = 20 * zoom;
return gridSizeRaw + "px " + gridSizeRaw + "px";
});
/// Kaydırılırken sanki tam kayıyor hissi vermesi için (x * zoom bölü size) işleminden kalan sonuç, (yani mod) kadar bi kaydırıyoruz...
gridOffset = computed(
[this.worldX, this.worldY, this.zoomFactor],
([x, y, zoom]) => {
const gridSizeRaw = 20 * zoom;
return `${-(x * zoom) % gridSizeRaw}px ${-(y * zoom) % gridSizeRaw}px`;
},
);
gridLineWidth = computed([this.zoomFactor], ([zoom]) => {
return zoom + "px";
});
canvas = (
<canvas
id="myCanvas"
width={this.canvasWidth}
height={this.canvasHeight}
style={{
cursor: "crosshair",
touchAction: "none",
"--gridSize": this.gridSize,
"--gridOffset": this.gridOffset,
"--lineWidth": this.gridLineWidth,
}}
className="xdraw-grid xdraw-grid-lines"
></canvas>
);
Normalde rootta verdiğimiz değişkenleri style içine --değişken olarak verebiliyoruz. Tabi bu değişkenler piksel biriminde ifade etmek için sonu px ile bitirerek string olarak veriyoruz. Ve en önemlisi ise css sınıflarımız…
.xdraw-grid {
background-repeat: repeat;
background-clip: content-box;
background-origin: 0px 0px;
}
.xdraw-grid-lines {
background-image:
repeating-linear-gradient(
0deg,
rgba(139, 139, 139, 0.3) 0px,
rgba(139, 139, 139, 0.3) var(--lineWidth),
transparent var(--lineWidth),
transparent var(--gridSize)
),
repeating-linear-gradient(
90deg,
rgba(139, 139, 139, 0.3) 0px,
rgba(139, 139, 139, 0.3) var(--lineWidth),
transparent var(--lineWidth),
transparent var(--gridSize)
);
background-position: var(--gridOffset);
background-size: var(--gridSize);
}
Ve bu sayede çizimlerde çok daha fazla akıcı gezinme sağlanabiliyor.
Tabi ki burada gösterebilmek adına basitleştirdim. Çok daha fazlası kaynak kodlarından inceleyebilirsiniz…
Postral, Lotus UI ve UBS Mona Platform
Country-State-City-District servisi
Farklı bir dünyadan başka farklı bir dünyaya geçebiliriz. Öncelikle adres olayları karışık frontend’de saklamak için ise fazla büyükler. Bu yüzden ubs-platform/ubs-mona-mr içinde cscd adında yeni bir uygulama açtım. Bu uygulama countries(ülkeleri), subdivision (2. seviye sınırlar) ve locality(3. seviye sınırlar) saklanabiliyor. Eğer Subentity olarak geçiyorsa ülke ve subdivision’larda hasSubdivisions: true oluyor. Gerçi isimlendirmelerde fena batırmışım ama neyse 😅🤫. Bu sayede Postral’de adres alanları UBL’e şekilde doğru verilebiliyor.


Eğer ülkede hasSubdivisions ise, countrySubentity alanları dolduruluyor, aksi halde cityName ve İlçe alanları dolduruluyor. Tabi otomatize edilebilir form alanları, dümdüz input tanımlamak yerine proje içinde Minky Reform adını verdiğim bir framework çalışıyor. Tabi burada ince ayar yapmaya vaktim olmadığı için alan adlarını daha otomatize edemedim ama o da planlarım arasında
Bu arada çok fazla item olduğu zaman da artık yazarak filtrelenebiliyor. Yeni özellik gibi tanıtmak çok saçma ama maalesef bunu yeni ekleyebildim

Çoklu dil desteği
Proje içinde bir dil seçim ekranı var, ancak neredeyse tüm string alanları “aman sonra hallederim” diye hardcoded şekilde yazdım. Ancak belli bir süre sonra çok baş ağrıtan bir şeye dönüştü. Çok fazla string hardcoded ve oluşturduğum kütüphane de yetersiz gelmeye başladı. Tabi @ubs-platform/translator-core ve @ubs-platform/translator-ngx üzerinde çalışmaya başlayacağım. Muhtemelen proje içinde çalışan bir sistem hale gelebilir npm’den kaldırıp. Kimse indirip kullandığını da düşünmüyorum, ve ayrıca gerçekten o kadar zahmete değmiyor… UBSMona’daki XR aracı gibi de scriptlemekle de uğraşmak istemiyorum ama o bir yana kalsın, yapay zeka desteği ile artık daha fazla sayfayı çevirmeyi başardım. Şu an sadece Türkçe ve İngilizce olmasına karşın daha fazla dil desteği eklemek istiyorum. RTL diller yakın zamanda gelemeyebilir, ya da arayüz değişimini yakın zamanda ekleyemeyebilirim.


Postral tarafında diğer geliştirmeler ve bugfixler
- Açık faturalar artık zaman aşımından etkilenmiyor. Komisyon ödemesi için açılan faturalar bir süre sonra kabul edilemez hale geliyor. O yüzden timeouttan etkilenmiyor.
- Faturalar kısmında bir çok alan refactor edildiği için müşteri tarafında finalized olan faturalar gelmiyordu, bu düzeltildi.
- Banka hesapları birden fazla eklenebiliyor. Ayrıca banka hesaplarının kabul ettiği para birimini de alıyor.
- Artık payment snapshotları fatura oluştuğunda değil, ödeme anında oluşturuluyor. Önemli bilgiler anında kaydedilmiş oluyor…
Son sözler
Evet, bu ay baya araştırma ile ve bugfixlerle geçti. İleriki dönemlerde bir işe girebilirsem yavaşlayabilirim (mesai sonrası ve ya haftasonu gibi devam ederim) ancak son dönemin işten çıkarma olaylarından sonra vazgeçme gibi bir lüksümün olmadığını hatırladım. Bu ay içinde XDraw’ın web versiyonunu tetakent[nokta]com adresine yükleyeceğim ve daha da oturttuktan sonra Google Play’e yükleyeceğim bir aksilik olmazsa. Apple cihazlar için şu an ekstra bir şey söyleyemem. Çünkü müzik yapım gibi hobiler dışında çok Apple cihaz kullanan birisi değilim. Ancak UBS Mona ve Postral buildleri ARM64 için devam edecek. ARM32 için hem Raspberry Pi cihazım olmadığı için hem de CI/CD makinem yavaş olduğu için ara verdim.
Buraya kadar okuduğunuz için çok teşekkürler. Geribildirimleriniz ve yorumlarınız ve hatta en azından like atarsanız mutlu olurum. Kendinize dikkat edin, seviliyorsunuz 🌹