Yeni Bir Seviye İçin Giriş

Şu ana kadar yaptığımız şey bir çokgen yaratmaktı. Bunu da aşağıdaki gibi yaptık. Bize bir yarıçap r, bir nokta c, ve kenar sayısı s verildiği zaman eşit kenarlı bir çokgen yarattık.

5 kenarlı bir çokgen yaratma yolumuz

Daha sonra bunun kenarından yeni bir çokgen üretmek istediğimiz zaman ise bize hangi kenardan, kaç kenarlı bir çokgen istenildiği soruldu. Yeni çokgeni yaratmak için önce yeni çokgenin merkezinin koordinatlarını ve sonra da yeni çokgenin yarıçapını bulduk. Bunları yapmak epey karmaşık bir işlemdi hatırlarsanız. Aşağıya bununla ilgili olan kod parçasını koyuyorum.

  // Bu Multiangle'in "i" numarali kenarina, "snew" adet kenari
  // olan yeni bir Multiangle cizer
  public Multiangle createNewAt(int i, int snew) {

    int s = elements.length;
    if (i >= s) return null;
    float sf = (float)snew;
    float correct = 0;
    if ( sf/2 <= (int)(snew/2) ) {
      correct = - PI/snew;
      println ("odd");
    }
    float angle = TWO_PI/s;
    println (elements[i].getDistance(elements[(i+1)%s]));
    float rnew = elements[i].getDistance(elements[(i+1)%s])/2/sin(PI/snew);
    float dis = rnew * cos(PI/snew) + radius * cos(PI/s);
    Point cennew = new Point(center.x + dis * sin((i+0.5) * angle + skew ),
                             center.y + dis * cos((i+0.5) * angle + skew ));
    Multiangle ret =  new Multiangle(snew, rnew, cennew,
              (PI/s + (i*2*PI/s))%(2*PI/snew) + skew + correct);
    return ret;

  }

  public Point[] getElements() {
    return elements;
  }

  public float getRadius() {
    return radius;
  }
}

Burada cennew ve rnew‘un ne kadar zor hesaplandığını görüyorsunuz.

Bunlardan çeşitli örüntüler üretmek olduğu zaman Multiangle ile sınırlı kalmamam lazım. Yıldızlar da üretebilmeliyim örneğin. Multiangle‘dan sonra herhalde ikinci adım Star isminde bir class yaratmaktır. Elbette bir Multiangle‘ın kenarından bir Star ve tersini de yapabilmek isteyeceğim. Yukarıdaki koda bakarak bunun ne kadar zor olacağını tahmin etmek zor değil.

Diğer bir sorun da Multiangle ile Star‘ı tek bir class altında ifade edemiyor olmak. Böyle olmadığı zaman bütün bunları bir torbanın içine koyamıyorum örneğin.

Aslında daha da büyük bir sorun: sadece eşit kenarlı çokgenler ve yıldızlar mı üretmek zorundayım? Hayır! İstediğim herhangi bir şekli üretebilmeliyim. İster düzgün çokgenler olsun ister kenarları eşit olmayanlar olsun. Bunda tamamen özgür olabilmem lazım.

Bütün bunları nasıl yaparım?


Yeni Seviye

Farklı bir soyutlamaya ihtiyacımız olduğu ortaya çıkıyor. Bir önceki yazıma bakın lütfen…

Öncelikle sadece düzgün çokgenler ve yıldızlar değil bütün şekilleri kullanabilmek istiyorum. Ama istediğim zaman da düzgün çokgen ve düzgün yıldızlar yapabilmek istiyorum. O zaman yapılacak şey çok basit: Sekil isminde bir class‘ım olsun ve içinde Nokta‘ları barındırsın ama bazı özel şekil fabrikaları kullandığım zaman bunlar bana çokgenleri ve yıldızları versin.

Tekrar Türkçe class isimlerine döndüm kusura bakmayın. Galiba böyle daha anlaşılır oluyor.

Sekil içinde bir sürü Nokta barındırıyor

Şekil noktalardan oluşmuş bir agregasyon. Bu agregasyonu Array class’ı ile yapacağız.

Bunun kodu kabaca şöyle olacak:

class Sekil {
  Array noktalar;

  public void Sekil {
    noktalar = new ArrayList();  // Torbayi hazirla
  }
  .....

  // bu metod seklime yeni bir nokta ekler
  public void noktaEkle (Nokta bunuEkle) {
    noktalar.add(bunuEkle);
  }
}

Array ve ArrayList programlama dilimizin içinde bulunan bir tür torba. İçlerinde her türlü nesneyi barındırabiliyorlar. Ben noktalar isimi verdiğim bu torbanın içine bu şekli oluşturacak bütün Nokta’ları atabiliyorum. Bunu da noktaEkle metodu ile yapıyorum.

Benim şekillerim içlerinde kesinlikle düzgün bir çokgeni oluşturmak veya düzgün bir yıldızı oluşturmak bilgisini taşımıyor. İstediğim şekli oluşturmak konusunda özgürüm. İstersem rastgele üç noktayı şeklimin içine atarım ve onlar bir şekil oluşturular.

Çokgen oluşturmak için başka bir class kullanacağım. Bu yeni class‘ım özel bir şekil fabrikası olacak. Öyle ki, bu fabrikanın oluşturduğu şekiller hep düzgün (eşit kenarlı) çokgenler olacak.

Yani CokgenFabrikasi class‘ı bana istediğim sayıdaki Nokta‘yı yaratıp bunları bir Sekil class‘ının içine yerleştirip verecek.

CokgenFabrikasi'nin çalışması

Aynı yolla bir de YildizFabrikasi tanımlayabilirim ve bu fabrika bana içinde bir yıldız şeklini verecek şekilde koordinatları belirlenmiş noktalardan oluşur.

Örnek olarak CokgenFabrikasi‘nin kodunu kabaca görelim:

class CokgenFabrikasi {
  CokgenFabrikasi () {   }   // Bos bir constructor

  public Nesne yarat(float ycap, int kenarSayisi, float kaciklik) {
    Nesne yeniNesne = new Nesne();
    // kenar sayisi, yaricap ve kaciklik'a gore noktalari yarat
    // yaratilan noktalari yeniNesne'nin icine yerlestir

    return yeniNesne;  // ve bu nesneyi gönder
  }
}

Bütün detayları burada vermiyorum. Bunları daha sonra dropbox’a yerleştireceğim.

Ama neden şimdi değil çünkü daha yapacak şeyler var. Henüz kavramsal tasarımım bitmedi. Çünkü başka dertlerim daha var. Bütün dertlerimi çözemedim.

Yeni Seviyeye Yeni Sorun

Yeni bir sorun var demiştim. Yeni sorun mevcut şeklin kenarından yaratacağım yeni şekil ile devreye giriyor.

Bir şekil yarattım diyelim. Bu şeklin iki noktasını (ki bir doğru parçası oluşturur) gösterip demek istiyorum ki, bunları kullanan yeni bir şekil yarat. İşte aşağıda bir örnek.

...
  // once bir fabrika yaratayim
  CokgenFabrikasi cf = new CokgenFabrikasi();
  // simdi de bunu kullanip yeni bir cokgen yaratalim
  Sekil cokgen = cf.yarat(20.0f, 5, 0.0f);
  // simdi yeni bir cokgen istiyorum
  // bunun icin nevcut cokgenin hangi kenarina istedigimi belirtmem
  // gerekcek. O halde once o kenara ait noktalari alayim
  Nokta birinciNokta = cokgen.getNokta(1);
  Nokta ikinciNokta = cokgen.getNokta(2);
  // bu bana birinci ve ikinci noktadan gecen 5 kenarli yeni bir cokgen
  // yaratacak.
  Sekil ikinciCokgen = cf.kenardanYarat(birinciNokta, ikinciNokta, 5);
 ...

Dikkat ederseniz birinci çokgenin kenarından yarattığım ikinci çokgen için de CokgenFabrikasi‘ni kullandım. Çünkü çokgen yaratma bilgisi sadece CokgenFabrikasi‘nda var.

Fakat sorunum bunda değil. Sorunum elimde cokgen ve ikinciCokgen isminde iki ayrı Sekil‘in kalması. Bunlar ayrı ayrı duruyorlar elimde. Tek bir isim altından onları çağıramıyorum. Oysa ben örneğin 5 adet çokgeni bir isimle çağırabilmek istiyorum.

Kare, üçgenler ve beşgeni ayrı ayrı değil tek isimle çağırmak istiyorum.

Üstelik bunu yaptığım zaman bunun altındaki şekilleri de kaybetmek istemiyorum. Bu öbeği obek olarak isimlendirdiysem onun altındaki beşgen’e ve ayrı ayrı üçgenlere de ulaşabilmeliyim.

Bir başka değişle daha karmaşık şekillerle uğraşabilmek istiyorum. Sanki bir çizim programında birden fazla şekli fare ile seçip bunları “Group” komutu ile tek bir vücüt haline getirebilmem gibi. Grupladıktan sonra artık onları hep birlikte sürükleyip bırakabiliyorum ve diğer taraftan istediğim zaman da bunları ayırabiliyorum.

Ama neden bunu yapmak isteyeyim. Şu anda belki çok açık değil amacım ama şimdilik şunu söyleyebilirim: ilk etapta kendimi düzgün çokgen ve yıldızlardan kurtardım. Şimdi de tek parça halindeki şekillerden kurtarıyorum. Bundan sonra benim birimlerim daha karmaşık birden fazla şekil grubundan oluşabilecek.

Derleme Toparlama

Devam etmeden önce programımdan ne beklediğimi daha açık yazmam lazım.

Sadece bunun gibi döşemeler yapmak istemiyorum:

Bundan daha esnek bir kabiliyet isitiyorum. Simetrik olmasıa şart olmayan ve bir iki parçadan oluşan birimleri yanyana dizebilmeliyim. Bu birimlerin kenarları tam olarak öpüşmeli.

O zaman bir birimin yanına bir tane daha koyduğum zaman üstüste gelmediklerine (overlap) emin olmalıyım.

Her yeni birimi ayrıca çağırabilmeliyim ama diğer taraftan derli toplu tek bir nesne ile ifade etmeliyim. Örneğin bütün döşediğim örüntünün ismi A ise bunun içindeki bütün birimleri tek tek çağıdabilmeliyim ki, istediğim yerden büyümeye devam etmelerini sağlayabileyim.

Elbette bu esnekliğin yanında hala simetrik çokgenleri de yaratabilmem lazım.

İşte bu yönde gitmeye çalışıyorum.

Bir Küçük Detay Ama Önemli

Bir önceki post’ta küçük bir detay vardı. Çokgenlerin üst üste binmelerini istemiyoruz. Daha doğrusu üst üste binmelerini istemeyeğimiz durumlar olabilir.

Üst üste binmeyi istemiyoruz

Bunu nasıl kontrol ederiz diye bakarsak en kolay yolun bir birini kesen doğru parçalarını tesbit etmek olduğunu görürüz.

Bunun yapabilmek için ise DogruParcasi isminde bir class tanımına ihtiyaç olduğu ortaya çıkar.

class DogruParcasi {
  private Nokta n1;
  private Nokta n2;

  public boolean kesiyorMu(DogruParcasi d) {
    ...
  }
}

kesiyorMu() fonksiyonu bize eğer bu doğru parçası ile (this) d doğru parçası birbirini kesiyorsa true kesmiyorsa false değerini verecek.

Cokgen ve Noktları Aynı Anda Kullanmak

Önemli: poligonun Türkçe’sine baktım. Doğru çevirisinin çokgen olduğunu gördüm. Yukarıdan bu ana kadar hep Sekil olarak sözünü ettiğim class’ın ismini bu sebepten dolayı değiştiriyorum ve artık Cokgen ismine dönüyorum. Yıldızlar ve düzgün eşitkenarlı çokgenler aslında hep çokgen ailesine giriyor. Şekil ise çok daha genel kalıyor. Çokgenin kenarları doğru parçaları. Şekillerin kenarları ise eğriler olabilir.

İsteklerimizden biri de yeni yarattığımız çokgenleri gruplar halinde kullanabilmekti. Elimde bir C çokgeni varsa bunun içine başka bir B çokgeninini gömebilmeliyim. Ve bunları çizdiğim zaman hepsini birden çizebilecek.

Bunun için aşağıdaki gibi bir yapıyı kullanabiliriz.

Nesne2B bir interface'dir. Cokgen ondan türer ama ondan içerir.

Nesne2B (yani 2 boyutlu nesneler) ismini verdiğim genel bir interface yaratıyorum. Bunun amacı Nokta ve Cokgen‘leri aynı isimle çağırabilmek. Bundan türemiş olan Cokgen aynı zamanda Nesne2B‘lerden oluşan bir array’e sahip.

Kodun tamamı Dropbox’ta kutsi klasörünün altında CokgenDeneme ismiyle kayıtlı.

CokgenDeneme Kodunun Detaylı Açıklaması

CokgenDeneme kodunun açıklaması

Kodun ikinci satırında c Cokgen‘i tanımlandığında c‘nin içindekiler sadece Nokta olduğu için ve c kendi içinde başka bir çokgen barındırmadığı için derin = false olarak kalıyor. Dikkat ederseniz nesneler‘in içinde Nokta‘lar var. Resimde sağ yukarıdaki hale geliyor c‘nin içi.

Ama c.cokgenEkle (c1) dediğimiz zaman bu method önce c‘nin tamamını kopyalıyor ve nesneler‘in içine atıyor. Sonra da c1‘i alıp yine c‘nin içine atıyor. Aynı zamanda c‘nin derin değişkenini true olarak değiştiriyor. Bu değişken bize c çokgeninin içindekilerin noktalar değil yine başka çokgenler olduğunu söylüyor. Bu son halini de resimde altta görüyorsunuz. Üst tarafta c‘nin ilk hali, ilk noktaları, hemen altına da c1 kopyalanmış.

Yukarıdaki kod dropbox’taki kodtur.