Çakışan dairelerin birleşik alanı


107

Geçenlerde dört dairenin (orta noktalar ve yarıçap) olduğu ve bu dairelerin birleşim alanını hesaplamak zorunda olduğum bir problemle karşılaştım.

Örnek resim:

İki daire için oldukça kolay

Üçgenlerin içinde olmayan her dairenin alanının kesirini hesaplayabilir ve sonra üçgenlerin alanını hesaplayabilirim.

Ama ikiden fazla daire olduğunda kullanabileceğim akıllıca bir algoritma var mı?


15
Bu gerçekten ilginç bir problem, bunu lise geometri dersinde gördüğümü hatırlıyorum ama bir çözüm bulamadım. Burada bir cevap bulamazsanız, bunu mathoverflow.net adresinde yayınlamayı deneyin ve matematikçilerin bir çatlağı olmasına izin verin: P
Charles Ma

25
bazen gerçek programcılar gerçek matematiğe ihtiyaç duyar
fa.

1
Bu sorunun cevabını bulmaya ne dersiniz - "Bu 4 lokasyonda yaşayan ve her biri bu 4 yarıçapla bir bölgeye hizmet veren satış temsilcilerimiz var. Ülkenin ne kadarını kapsıyoruz?" Değişen bir satış temsilcileri veritabanınız varsa, bu bir programlama sorusu haline gelir!
Chris Roberts

5
Aslında bu, gerçek programcıların düşünmeyi sevdiği türden bir sorundur.
MAK

2
@zvolkov: Devre kartları, kareleri çizen ve aşağıya doğru daireler çizen ve isteğe bağlı olarak onları sürükleyen bir dille tanımlanır. "Bakır alanı hesaplayın". (Bu, aşındırma sürelerini hesaplamak, süpürücü sanat eserleri, çeşitli şeyler eklemek isteyip istemediğinizi bilmek için gerekli olabilir.)
DigitalRoss

Yanıtlar:


97

Dış çevredeki tüm daire kavşaklarını bulun (örneğin aşağıdaki diyagramda B, D, F, H). Bir çokgen oluşturmak için bunları karşılık gelen dairelerin merkezleriyle birbirine bağlayın. Dairelerin birleşim alanı, çokgenin alanı + ardışık kesişim noktaları ile tanımlanan daire dilimlerinin alanı ve bunların arasındaki daire merkezidir. Ayrıca tüm delikleri de hesaba katmanız gerekir.

daire örtüşmesi


17
Merkezde bir delik olduğunda ne olur?
John Gietzen

3
Delik için merkeze bağlı çokgeni toplamdan çıkarmanız ve bu çokgen için daire dilimlerini toplama eklemeniz gerekir.
Karıncalar Aasma

3
güzel ama sanırım bu, tüm özel durumları ele almak için birçok uygulama ayrıntısına ihtiyaç duyacak (diğerinin içindeki daire, kesişme yok, delikler, tek nokta teması ...)
fa.

1
Özel durumlar oldukça kolaydır. Diğerlerinin içindeki daireler, herhangi bir çevre kesişimi olmaması nedeniyle atılır. Bir nokta temas aslında sıfır mesafeli iki kesişme noktasıdır. Bağlantısız şekiller, merkezlerin mesafesi yarıçapların toplamından daha az ise, iki dairenin bağlandığı grafik üzerinde bağlı bileşenler algoritması aracılığıyla bulunabilir. Delikler, en büyük alana sahip olan dışında tüm çokgenlerdir. Çevre kavşakları, kesinlikle herhangi bir dairenin içinde olmayan kavşaklardır.
Karıncalar Aasma

4
evet, ancak deliklerin sınırları da (küçük) yaylardır. Hala bunun iyi çalışması için çok fazla koda ihtiyacı olduğunu düşünüyorum.
fa.

32

Eminim zekice bir algoritma vardır, ama burada onu aramak zorunda kalmamak için aptal bir algoritma var;

  • dairelerin etrafına bir sınırlayıcı kutu koyun;
  • sınırlayıcı kutu içinde rastgele noktalar oluşturmak;
  • rastgele noktanın çemberlerden birinin içinde olup olmadığını belirleyin;
  • alanı bazı basit toplama ve bölme ile hesaplayın (orantılı_puanlar_inside * alan_of_bounding_box).

Elbette aptalca, ama:

  • istediğiniz kadar doğru bir cevap alabilirsiniz, sadece daha fazla puan üretin;
  • iç / dış ayrımını hesaplayabileceğiniz herhangi bir şekil için çalışacaktır;
  • güzel bir şekilde paralel olacaktır, böylece tüm çekirdeklerinizi kullanabilirsiniz.

2
Bu işe yarayacak, ancak bunun gibi basitçe tek tip örneklemeye dayanan Monte-Carlo yöntemleri genellikle en iyi yakınsama oranlarına sahip değildir.
ShreevatsaR

2
Üzgünüm, çabanızı takdir etsem ve çözümünüzün "pratik olarak kullanılabilir" olduğunu düşünmeme rağmen, yaklaşımınızın çok yanlış olduğunu düşünüyorum. Bu, kaba kuvvetle değil matematikle çözülebilecek ve çözülmesi gereken bir sorundur. Bu tür problemlerde enerji ve çekirdek israfı savurgan ve cömerttir.
mafu

5
Haklısın, kendimden utanıyorum, ama 12.000 çekirdekli bir kümem var, cömert olmayı göze alabilirim. Ve zarif matematiksel çözümün bu kadar çok işlemciye nasıl ölçekleneceğini çözemiyorum.
Yüksek Performans Mark

8
Gerekli doğruluk derecesini sağlaması ve bunu makul bir süre içinde yapması koşuluyla, Monte-Carlo (veya herhangi bir rastgele) yaklaşımında doğası gereği yanlış bir şey yoktur.
MAK

@mafutrct, kesinlikle haklısın. Ancak, matematikte küçük hatalar yapmak kolaydır. Bu çözüm, doğruluğu test etmenin basit bir yolunu sağlar.
Richard

18

Karıncalar Aasma'nın cevabı temel fikri verdi ama ben onu biraz daha somut hale getirmek istedim. Aşağıdaki beş daireye ve bunların nasıl ayrıştırıldığına bir göz atın.

Misal

  • Mavi noktalar daire merkezleridir.
  • Kırmızı noktalar, daire sınırı kesişimleridir.
  • Beyaz iç kısmı olan kırmızı noktalar , diğer dairelerde bulunmayan daire sınırı kesişimleridir .

Bu 3 tür noktayı belirlemek kolaydır. Şimdi, düğümlerin mavi noktalar ve beyaz iç kısımlara sahip kırmızı noktalar olduğu bir grafik veri yapısı oluşturun. Her daire için, çemberin ortası (mavi nokta) ile sınırındaki kesişme noktalarının her biri (içi beyaz olan kırmızı noktalar) arasına bir kenar koyun.

Bu, daire birleşimini bir dizi çokgen (gölgeli mavi) ve çift olarak ayrık olan ve orijinal birleşimi (yani bir bölümü) örten dairesel pasta parçalarına (gölgeli yeşil) ayırır. Buradaki her parça, alanını hesaplaması kolay bir şey olduğundan, parçaların alanlarını toplayarak birleşmenin alanını hesaplayabilirsiniz.


Sanırım bir dizi kırmızı / beyaz noktayı oldukça kolay bir şekilde hesaplayabilirim, ancak grafik teorim çok iyi değil: algoritmik olarak, bir düğümler + kenarlar listesinden hesaplanan bir alana nasıl ulaşırsınız?
user999305

1
Algoritma, çokgenler yerine üst üste binmeyen bir dizi üçgen kullanılarak basitleştirilebilir. Yaylar (yeşil alanlar) yalnızca bir daire içinde bulunan alanlardır. Daha fazla daire ekledikçe çokgenin boyutunu genişletin. (sonunda çokgenlerden bahsettiğinizi bile unutabilirsiniz). Boole özelliklerini yapar ve alanların da hesaplanması daha kolaydır. İçi boş bir kırmızı nokta düz kırmızı bir noktaya dönüştüğünde, setinize daha fazla üçgen eklersiniz ve gittikçe daha fazla kesişen daireler tarafından "yeneceği" yayı ayarlarsınız.
Steve

16

Bir öncekinden farklı bir çözüm için, dörtlü bir ağaç kullanarak keyfi bir hassasiyetle bir tahmin üretebilirsiniz.

Bu, bir karenin içinde mi yoksa dışında mı olduğunu veya şeklin kesiştiğini anlayabiliyorsanız, herhangi bir şekil birleşimi için de işe yarar.

Her hücre şu durumlardan birine sahiptir: boş, dolu, kısmi

Algoritma, düşük bir çözünürlükle başlayarak dörtlü ağaçtaki dairelerin "çizilmesinden" oluşur (örneğin 4 hücre boş olarak işaretlenmiştir). Her hücre şunlardan biridir:

  • en az bir daire içinde, ardından hücreyi dolu olarak işaretleyin,
  • tüm dairelerin dışında, hücreyi boş olarak işaretleyin,
  • aksi takdirde hücreyi kısmi olarak işaretler.

Tamamlandığında, alan için bir tahmin hesaplayabilirsiniz: dolu hücreler alt sınırı verir, boş hücreler yüksek sınırı verir, kısmi hücreler maksimum alan hatası verir.

Hata sizin için çok büyükse, doğru kesinliği elde edene kadar kısmi hücreleri iyileştirirsiniz.

Bunun, birçok özel durumu ele alması gerekebilecek geometrik yöntemden daha kolay uygulanacağını düşünüyorum.


3
Benim tahminim bu çok Monte Carlo iç / dış nokta algoritması daha hızlı yakınsama olacaktır.
Frank Krueger

Bunu uygulamak çok daha kolay görünüyor. Kesinlikle önerilen en iyi kaba kuvvet yöntemi. Teşekkürler!
Anton Hansson

Burada kaba kuvvet sıkma teoremi
fa

Aralık aritmetiğinde kullandığınız türden algoritma budur. en.wikipedia.org/wiki/Interval_arithmetic
rjmunro

13

Kesişen 2 daireye yaklaşımı seviyorum - işte daha karmaşık örnek için aynı yaklaşımın hafif bir varyasyonunu nasıl kullanacağım.

Algoritmanın daha fazla sayıda yarı örtüşen daire için genelleştirilmesi konusunda daha iyi bir fikir verebilir.

Buradaki fark, merkezleri birbirine bağlayarak başlamamdır (yani dairelerin kesiştiği yerler arasında değil, dairelerin merkezi arasında bir tepe noktası vardır) bence bu, daha iyi bir genelleme sağlar.

(pratikte belki de monte carlo yöntemi faydalıdır)

alternatif metin
(kaynak: secretGeek.net )


1
Görüntünüzün önerdiği türden bir çokgen bölme yapmanın muhtemelen çok iyi bir yaklaşım olacağını düşünüyorum. Kodlamak için üzerinde çalışılması gereken çok fazla ayrıntı var. Her biri zincirin yalnızca sonuncusu ve sonuncusuyla çakışan yirmi daireden oluşan bir zinciri nasıl idare edebilirdi? El ile bulmak kolay, ancak algoritmanız nedir?
PeterAllenWebb

4

Kesikli (sürekli bir yanıtın aksine) bir yanıt istiyorsanız, piksel boyama algoritmasına benzer bir şey yapabilirsiniz.

Daireleri bir ızgara üzerine çizin ve ardından çoğunlukla bir daire içinde yer alıyorsa (yani, alanının en az% 50'si dairelerden birinin içindeyse) ızgaranın her hücresini renklendirin. Bunu ızgaranın tamamı için yapın (ızgaranın dairelerin kapladığı tüm alanı kapladığı yer), ardından ızgaradaki renkli hücrelerin sayısını sayın.


3

Hmm, çok ilginç bir problem. Yaklaşımım muhtemelen aşağıdakiler doğrultusunda bir şey olacaktır:

  • Rasgele sayıda daire arasındaki kesişme alanlarının ne olduğunu bulmanın bir yolunu bulun, yani 3 dairem varsa, bu daireler arasındaki kesişimin ne olduğunu bulabilmem gerekir. "Monte-Carlo" yöntemi buna yaklaşmanın iyi bir yolu olabilir ( http://local.wasp.uwa.edu.au/~pbourke/geometry/circlearea/ ).
  • Tamamen başka bir büyük daire içinde bulunan tüm daireleri ortadan kaldırın (yarıçapa ve iki dairenin merkezi arasındaki mesafenin modülüne bakın) Zorunlu olduğunu düşünmüyorum.
  • 2 daire seçin (bunlara A ve B deyin) ve bu formülü kullanarak toplam alanı hesaplayın:

(bu, daire veya başka herhangi bir şekil için geçerlidir)

area(A∪B) = area(A) + area(B) - area(A∩B)

Burada A ∪ BA birliği B ve A ∩ BA ile kesişen B anlamına gelir (bunu ilk adımdan itibaren çözebilirsiniz.

  • Şimdi daire eklemeye devam edin ve dairelerin alanlarının ve daireler arasındaki kesişme alanlarının toplamı / çıkarılması olarak eklenen alanı çalışmaya devam edin. Örneğin 3 daire için (fazladan C çemberi diyelim) alanı şu formülü kullanarak hesaplıyoruz:

(Bu, Aile değiştirildiği yukarıdakiyle aynıdır A∪B)

area((A∪B)∪C) = area(A∪B) + area(C) - area((A∪B)∩C)

Nerede area(A∪B)biz sadece dışarı çalıştı ve area((A∪B)∩C)bulunabilir:

area((A∪B)nC) = area((A∩C)∪(B∩C)) = area(A∩C) + area(A∩B) - area((A∩C)∩(B∩C)) = area(A∩C) + area(A∩B) - area(A∩B∩C)

Yine yukarıdan alanı (A∩B∩C) bulabilirsiniz.

Zor olan son adımdır - ne kadar çok daire eklenirse, o kadar karmaşık hale gelir. Sonlu bir birleşimle bir kesişme alanını bulmak için bir genişleme olduğuna inanıyorum, ya da alternatif olarak bunu yinelemeli olarak çözebilirsin.

Ayrıca, Monte-Carlo'yu kestirim alanına yaklaştırmak için kullanma ile ilgili olarak, rasgele sayıda dairenin kesişimini, tam olarak hesaplanabilen bu dairelerin 4'ünün kesişim noktasına indirgemenin mümkün olduğuna inanıyorum (bunun nasıl yapılacağı hakkında hiçbir fikrim yok) ancak).

Muhtemelen bunu yapmanın daha iyi bir yolu vardır - eklenen her fazladan daire için karmaşıklık önemli ölçüde artar (muhtemelen üssel olarak, ancak emin değilim).


Biçimlendirmeden ne haber? Ayrıca n ve u'nun kesişim ve birleşim için kullanılması için de üzgünüm, muhtemelen daha iyi bir yolu var ...
Justin

1
bazı unicode birleşimi (∪) ve kesişme (∩) işaretleri eklendi. umarım çalışırlar.
Spoike

3

Büyük parlak yıldızların daha sönük olanları maskeleyebildiği yoğun alanlardaki gerçek disk alanlarından gerçek yıldız sayılarını tahmin etmeye çalışarak, üst üste binen yıldız alanlarını simüle etme problemi üzerinde çalışıyorum. Ben de bunu titiz bir resmi analizle yapmayı ummuştum, ancak görev için bir algoritma bulamadım. Mavi zemin üzerinde yıldız alanlarını, çapı olasılık algoritması ile belirlenen yeşil diskler olarak üreterek çözdüm. Basit bir rutin, bir örtüşme olup olmadığını görmek için onları eşleştirebilir (yıldız çiftini sarıya çevirmek); daha sonra teorik alanla karşılaştırmak için renklerin piksel sayısı gözlemlenen alanı oluşturur. Bu daha sonra gerçek sayımlar için bir olasılık eğrisi oluşturur. Kaba kuvvet belki ama işe yarıyor gibi görünüyor. (kaynak: 2from.com )


2

İşte pratikte uygulaması kolay olması gereken ve keyfi olarak küçük hata üretecek şekilde ayarlanabilen bir algoritma:

  1. Her daireyi aynı noktada ortalanmış normal bir çokgenle yaklaşık olarak belirleyin
  2. Yaklaşık dairelerin birleşimi olan çokgeni hesaplayın
  3. Birleştirilmiş çokgenin alanını hesaplayın

Adım 2 ve 3, hesaplamalı geometriden standart, bulunması kolay algoritmalar kullanılarak gerçekleştirilebilir.

Açıktır ki, her bir yaklaşık çokgen için ne kadar çok taraf kullanırsanız, cevabınız tam olarak o kadar yakın olacaktır. Kesin cevaba ilişkin sınırları bulmak için yazılı ve sınırlı çokgenleri kullanarak yaklaşık olarak tahmin edebilirsiniz.


2

Güç diyagramları olarak bilinenleri kullanarak bu soruna etkili çözümler vardır. Yine de bu gerçekten ağır bir matematik ve hazırlıksız ele almak isteyeceğim bir şey değil. "Kolay" bir çözüm için, satır tarama algoritmalarına bakın. Buradaki temel ilke, şekli şeritlere bölmenizdir; burada her şeritteki alanı hesaplamanın nispeten kolay olduğu yerlerde.

Bu nedenle, hiçbir şey silinmemiş tüm daireleri içeren şekil üzerinde, her konumda bir dairenin üstü, bir dairenin altı veya 2 dairenin kesişimi olan yatay bir çizgi çizin. Bu şeritlerin içinde hesaplamanız gereken tüm alanların aynı göründüğüne dikkat edin: iki tarafı dairesel segmentlerle değiştirilmiş bir "yamuk". Öyleyse, böyle bir şekli nasıl hesaplayacağınızı bulabilirseniz, bunu tüm şekiller için yaparsınız ve onları bir araya getirirsiniz. Bu naif yaklaşımın karmaşıklığı O (N ^ 3) 'dir, burada N, şekildeki daire sayısıdır. Bazı akıllı veri yapısı kullanımıyla, bu satır tarama yöntemini O (N ^ 2 * log (N)) olarak geliştirebilirsiniz, ancak gerçekten ihtiyacınız olmadıkça, muhtemelen zahmete değmeyecektir.



1

Hangi sorunu çözmeye çalıştığınıza bağlı olarak, bir üst ve alt sınır elde etmek yeterli olabilir. Üst sınır kolaydır, sadece tüm dairelerin toplamı. Daha düşük bir sınır için, dairelerin hiçbiri çakışmayacak şekilde tek bir yarıçap seçebilirsiniz. Bunu daha iyi yapmak için, her daire için en büyük yarıçapı (gerçek yarıçapa kadar) bulup üst üste binmesin. Ayrıca, tamamen örtüşen daireleri kaldırmak da oldukça önemsiz olmalıdır (Tüm bu daireler | P_a - P_b | <= r_a'yı sağlar) burada P_a, A çemberinin merkezidir, P_b, B çemberinin merkezidir ve r_a, A'nın yarıçapıdır. ) ve bu hem üst hem de alt sınırı daha iyi hale getirir. Çift formülünüzü sadece tüm dairelerin toplamı yerine rastgele çiftler üzerinde kullanırsanız daha iyi bir Üst sınır elde edebilirsiniz. "En iyi" yi seçmenin iyi bir yolu olabilir

Bir üst ve alt sınır verildiğinde, bir Monte-carlo yaklaşımını daha iyi ayarlayabilirsin, ancak akla özel bir şey gelmiyor. Diğer bir seçenek (yine uygulamanıza bağlı olarak) daireleri rasterleştirmek ve pikselleri saymaktır. Temelde sabit bir dağılıma sahip Monte-carlo yaklaşımıdır.


0

Bu, n ^ 2log (n) karmaşıklığıyla Green Teoremi kullanılarak çözülebilir . Green Teoremine aşina değilseniz ve daha fazlasını öğrenmek istiyorsanız, burada Khan Academy'den video ve notlar . Ama sorunumuz için açıklamamın yeterli olacağını düşünüyorum.

Resimleri gönderemediğim için fotoğrafların bağlantıları için özür dilerim. (Yeterli itibar puanı yok)

Green Teoreminin Genel Denklemi

L ve M'yi öyle koyarsam

Durum

o zaman RHS basitçe R Bölgesinin alanıdır ve kapalı integral veya LHS çözülerek elde edilebilir ve biz de tam olarak yapacağımız şey budur.

Tüm sendikalar, birbiriyle kesişen böylesine ayrık çemberlere bölünebilir.

Yol boyunca saat yönünün tersine integral almak bize bölgenin Alanını verir ve saat yönünde integral almak Alanın negatifini verir . Yani

AreaOfUnion = ( Saat yönünün tersine kırmızı yaylar boyunca entegrasyon + saat yönünde mavi yaylar boyunca entegrasyon)

Ama işin güzel yanı, her daire için başka bir çemberin içinde olmayan yayları entegre edersek, gerekli alanımızı elde edersek, yani tüm kırmızı yaylar boyunca saat yönünün tersine ve tüm mavi yaylar boyunca saat yönünde entegrasyon elde ederiz. İŞ BİTMİŞ!!!

Bir dairenin diğeriyle kesişmediği durumlar bile halledilir.

İşte C ++ Koduma giden GitHub bağlantısı


-1

Piksel boyama yaklaşımı (@Loadmaster tarafından önerildiği üzere) matematiksel çözümden çeşitli şekillerde üstündür:

  1. Uygulama çok daha basittir. Yukarıdaki sorun, bu JSFiddle çözümünün gösterdiği gibi (çoğunlukla kavramsal olarak daha basit olduğu ve başa çıkılması gereken uç durumlar veya istisnalar olmadığı için) 100 satırdan daha az kodla çözülebilir .
  2. Daha genel sorunlara kolayca uyum sağlar. 2B çizim kitaplıkları (yani "hepsi") - daireler, elipsler, spline'lar, poligonlar, siz adlandırın, morfolojiden bağımsız olarak herhangi bir şekilde çalışır. Heck, hatta bitmap görüntüleri.
  3. Matematiksel çözüm için ~ O [n * n] ile karşılaştırıldığında piksel boyama çözümünün karmaşıklığı ~ O [n] 'dir. Bu, şekil sayısı arttıkça daha iyi performans göstereceği anlamına gelir.
  4. Ve performanstan bahsetmişken, çoğu modern 2D kitaplığı (HTML5'in tuvali gibi) oluşturma işini grafik hızlandırıcılara yükleyeceğine inanıyorum.

Piksel boyamanın bir dezavantajı, çözümün sınırlı doğruluğu. Ancak bu, durumun gerektirdiği şekilde daha büyük veya daha küçük tuvallere dönüştürülerek ayarlanabilir. Not, o da anti-aliasing kodu render 2D (genellikle varsayılan olarak açık) daha iyi-daha-piksel seviyesinde verecektir doğruluğu. Bu nedenle, örneğin, 100x100'lük bir figürü aynı boyutlarda bir tuvalde işlemek, sanırım 1 / (100 x 100 x 255) = .000039% düzeyinde doğruluk sağlamalı ... ki bu muhtemelen "yeterince iyi" en zorlu sorunlar hariç hepsi için.

<p>Area computation of arbitrary figures as done thru pixel-painting, in which a complex shape is drawn into an HTML5 canvas and the area determined by comparing the number of white pixels found in the resulting bitmap.  See javascript source for details.</p>

<canvas id="canvas" width="80" height="100"></canvas>

<p>Area = <span id="result"></span></p>
// Get HTML canvas element (and context) to draw into
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

// Lil' circle drawing utility
function circle(x,y,r) {
  ctx.beginPath();
  ctx.arc(x, y, r, 0, Math.PI*2);
  ctx.fill();
}

// Clear canvas (to black)
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);

// Fill shape (in white)
ctx.fillStyle = 'white';
circle(40, 50, 40);
circle(40, 10, 10);
circle(25, 15, 12);
circle(35, 90, 10);

// Get bitmap data
var id = ctx.getImageData(0, 0, canvas.width, canvas.height);
var pixels = id.data; // Flat array of RGBA bytes

// Determine area by counting the white pixels
for (var i = 0, area = 0; i < pixels.length; i += 4) {
  area += pixels[i]; // Red channel (same as green and blue channels)
}

// Normalize by the max white value of 255
area /= 255;

// Output result
document.getElementById('result').innerHTML = area.toFixed(2);

Bu çözüm, dairelerin alanlarıyla matematiksel hesaplamalar yapmayı hesaba katmaz. OP sorusunun amacını kaçırıyor. Genellikle geometrik şekillerle uğraşırken, işleme geometrisi savaşın sadece yarısıdır
Steve
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.