Ben de benzer bir şeye ihtiyacım olduğu için bunun üzerinde biraz çalışıyordum ama algoritma geliştirmeyi geciktirmiştim. Biraz dürtü almama yardım ettin: D
Kaynak koduna da ihtiyacım vardı, işte burada. Bunu Mathematica'da çözdüm, ancak işlevsel özellikleri yoğun bir şekilde kullanmadığım için, herhangi bir prosedürel dile tercüme etmek kolay olacağını tahmin ediyorum.
Tarihi bir bakış açısı
İlk önce daireler için algoritma geliştirmeye karar verdim, çünkü kesişimin hesaplanması daha kolay. Sadece merkezlere ve yarıçaplara bağlıdır.
Mathematica denklem çözücüsünü kullanabildim ve iyi bir performans gösterdi.
Sadece bakmak:
Kolaydı. Çözücüyü az önce aşağıdaki problemle yükledim:
For each circle
Solve[
Find new coördinates for the circle
Minimizing the distance to the geometric center of the image
Taking in account that
Distance between centers > R1+R2 *for all other circles
Move the circle in a line between its center and the
geometric center of the drawing
]
Bu kadar basit ve Mathematica tüm işi yaptı.
"Ha! Çok kolay, şimdi dikdörtgenler için gidelim!" Dedim. Ama yanılmışım ...
Dikdörtgen Blues
Dikdörtgenlerle ilgili temel sorun, kesişimin sorgulanmasının kötü bir işlev olmasıdır. Gibi bir şey:
Bu yüzden, Mathematica'yı denklem için bu koşulların çoğuyla beslemeye çalıştığımda, o kadar kötü bir performans gösterdi ki, prosedürle ilgili bir şey yapmaya karar verdim.
Algoritmam şu şekilde sonuçlandı:
Expand each rectangle size by a few points to get gaps in final configuration
While There are intersections
sort list of rectangles by number of intersections
push most intersected rectangle on stack, and remove it from list
// Now all remaining rectangles doesn't intersect each other
While stack not empty
pop rectangle from stack and re-insert it into list
find the geometric center G of the chart (each time!)
find the movement vector M (from G to rectangle center)
move the rectangle incrementally in the direction of M (both sides)
until no intersections
Shrink the rectangles to its original size
"En küçük hareket" koşulunun tamamen tatmin edilmediğini fark edebilirsiniz (sadece bir yönde). Ancak, dikdörtgenleri tatmin etmek için herhangi bir yönde hareket ettirmenin, bazen kullanıcı için kafa karıştırıcı bir haritanın değişmesine neden olduğunu buldum.
Bir kullanıcı arayüzü tasarlarken, dikdörtgeni biraz daha ileriye taşımayı seçiyorum, ancak daha öngörülebilir bir şekilde. Algoritmayı, boş bir yer bulunana kadar mevcut konumunu çevreleyen tüm açıları ve tüm yarıçapları incelemek için değiştirebilirsiniz, ancak bu çok daha zorlu olacaktır.
Her neyse, bunlar sonuçların örnekleridir (önce / sonra):
Düzenle> Burada daha fazla örnek
Görebileceğiniz gibi, "minimum hareket" tatmin edici değil, ancak sonuçlar yeterince iyi.
Kodu buraya göndereceğim çünkü SVN depomla ilgili bazı sorunlar yaşıyorum. Sorunlar çözüldüğünde onu kaldıracağım.
Düzenle:
Dikdörtgen kesişimlerini bulmak için R-Ağaçları da kullanabilirsiniz , ancak az sayıda dikdörtgenle uğraşmak aşırı bir şey gibi görünüyor. Ve algoritmaları henüz uygulamadım. Belki başka biri sizi seçtiğiniz platformda mevcut bir uygulamaya yönlendirebilir.
Uyarı! Kod ilk yaklaşımdır .. henüz çok kaliteli değildir ve kesinlikle bazı hatalar içerir.
Mathematica.
(*Define some functions first*)
Clear["Global`*"];
rn[x_] := RandomReal[{0, x}];
rnR[x_] := RandomReal[{1, x}];
rndCol[] := RGBColor[rn[1], rn[1], rn[1]];
minX[l_, i_] := l[[i]][[1]][[1]]; (*just for easy reading*)
maxX[l_, i_] := l[[i]][[1]][[2]];
minY[l_, i_] := l[[i]][[2]][[1]];
maxY[l_, i_] := l[[i]][[2]][[2]];
color[l_, i_]:= l[[i]][[3]];
intersectsQ[l_, i_, j_] := (* l list, (i,j) indexes,
list={{x1,x2},{y1,y2}} *)
(*A rect does intesect with itself*)
If[Max[minX[l, i], minX[l, j]] < Min[maxX[l, i], maxX[l, j]] &&
Max[minY[l, i], minY[l, j]] < Min[maxY[l, i], maxY[l, j]],
True,False];
(* Number of Intersects for a Rectangle *)
(* With i as index*)
countIntersects[l_, i_] :=
Count[Table[intersectsQ[l, i, j], {j, 1, Length[l]}], True]-1;
(*And With r as rectangle *)
countIntersectsR[l_, r_] := (
Return[Count[Table[intersectsQ[Append[l, r], Length[l] + 1, j],
{j, 1, Length[l] + 1}], True] - 2];)
(* Get the maximum intersections for all rectangles*)
findMaxIntesections[l_] := Max[Table[countIntersects[l, i],
{i, 1, Length[l]}]];
(* Get the rectangle center *)
rectCenter[l_, i_] := {1/2 (maxX[l, i] + minX[l, i] ),
1/2 (maxY[l, i] + minY[l, i] )};
(* Get the Geom center of the whole figure (list), to move aesthetically*)
geometryCenter[l_] := (* returs {x,y} *)
Mean[Table[rectCenter[l, i], {i, Length[l]}]];
(* Increment or decr. size of all rects by a bit (put/remove borders)*)
changeSize[l_, incr_] :=
Table[{{minX[l, i] - incr, maxX[l, i] + incr},
{minY[l, i] - incr, maxY[l, i] + incr},
color[l, i]},
{i, Length[l]}];
sortListByIntersections[l_] := (* Order list by most intersecting Rects*)
Module[{a, b},
a = MapIndexed[{countIntersectsR[l, #1], #2} &, l];
b = SortBy[a, -#[[1]] &];
Return[Table[l[[b[[i]][[2]][[1]]]], {i, Length[b]}]];
];
(* Utility Functions*)
deb[x_] := (Print["--------"]; Print[x]; Print["---------"];)(* for debug *)
tableForPlot[l_] := (*for plotting*)
Table[{color[l, i], Rectangle[{minX[l, i], minY[l, i]},
{maxX[l, i], maxY[l, i]}]}, {i, Length[l]}];
genList[nonOverlap_, Overlap_] := (* Generate initial lists of rects*)
Module[{alist, blist, a, b},
(alist = (* Generate non overlapping - Tabuloid *)
Table[{{Mod[i, 3], Mod[i, 3] + .8},
{Mod[i, 4], Mod[i, 4] + .8},
rndCol[]}, {i, nonOverlap}];
blist = (* Random overlapping *)
Table[{{a = rnR[3], a + rnR[2]}, {b = rnR[3], b + rnR[2]},
rndCol[]}, {Overlap}];
Return[Join[alist, blist] (* Join both *)];)
];
Ana
clist = genList[6, 4]; (* Generate a mix fixed & random set *)
incr = 0.05; (* may be some heuristics needed to determine best increment*)
clist = changeSize[clist,incr]; (* expand rects so that borders does not
touch each other*)
(* Now remove all intercepting rectangles until no more intersections *)
workList = {}; (* the stack*)
While[findMaxIntesections[clist] > 0,
(*Iterate until no intersections *)
clist = sortListByIntersections[clist];
(*Put the most intersected first*)
PrependTo[workList, First[clist]];
(* Push workList with intersected *)
clist = Delete[clist, 1]; (* and Drop it from clist *)
];
(* There are no intersections now, lets pop the stack*)
While [workList != {},
PrependTo[clist, First[workList]];
(*Push first element in front of clist*)
workList = Delete[workList, 1];
(* and Drop it from worklist *)
toMoveIndex = 1;
(*Will move the most intersected Rect*)
g = geometryCenter[clist];
(*so the geom. perception is preserved*)
vectorToMove = rectCenter[clist, toMoveIndex] - g;
If [Norm[vectorToMove] < 0.01, vectorToMove = {1,1}]; (*just in case*)
vectorToMove = vectorToMove/Norm[vectorToMove];
(*to manage step size wisely*)
(*Now iterate finding minimum move first one way, then the other*)
i = 1; (*movement quantity*)
While[countIntersects[clist, toMoveIndex] != 0,
(*If the Rect still intersects*)
(*move it alternating ways (-1)^n *)
clist[[toMoveIndex]][[1]] += (-1)^i i incr vectorToMove[[1]];(*X coords*)
clist[[toMoveIndex]][[2]] += (-1)^i i incr vectorToMove[[2]];(*Y coords*)
i++;
];
];
clist = changeSize[clist, -incr](* restore original sizes*);
HTH!
Düzenleme: Çok açılı arama
Algoritmada her yönden arama yapmaya izin veren bir değişiklik uyguladım, ancak geometrik simetrinin dayattığı ekseni tercih ettim.
Daha fazla döngü pahasına, bu, aşağıda görebileceğiniz gibi, daha kompakt son yapılandırmalarla sonuçlandı:
Daha fazla örnek burada .
Ana döngü için sözde kod şu şekilde değiştirildi:
Expand each rectangle size by a few points to get gaps in final configuration
While There are intersections
sort list of rectangles by number of intersections
push most intersected rectangle on stack, and remove it from list
// Now all remaining rectangles doesn't intersect each other
While stack not empty
find the geometric center G of the chart (each time!)
find the PREFERRED movement vector M (from G to rectangle center)
pop rectangle from stack
With the rectangle
While there are intersections (list+rectangle)
For increasing movement modulus
For increasing angle (0, Pi/4)
rotate vector M expanding the angle alongside M
(* angle, -angle, Pi + angle, Pi-angle*)
re-position the rectangle accorging to M
Re-insert modified vector into list
Shrink the rectangles to its original size
Kısaca kaynak kodunu dahil etmiyorum, ancak kullanabileceğinizi düşünüyorsanız isteyin. Bence bu şekilde giderseniz, R-ağaçlarına geçmeniz daha iyi olur (burada çok sayıda aralık testi gereklidir)