Ci stavo lavorando un po ', perché anch'io avevo bisogno di qualcosa di simile, ma avevo ritardato lo sviluppo dell'algoritmo. Mi hai aiutato a ottenere un impulso: D
Avevo anche bisogno del codice sorgente, quindi eccolo qui. L'ho elaborato in Mathematica, ma poiché non ho utilizzato pesantemente le caratteristiche funzionali, immagino che sarà facile tradurre in qualsiasi linguaggio procedurale.
Una prospettiva storica
Per prima cosa ho deciso di sviluppare l'algoritmo per i cerchi, perché l'intersezione è più facile da calcolare. Dipende solo dai centri e dai raggi.
Sono stato in grado di utilizzare il risolutore di equazioni di Mathematica e ha funzionato bene.
Guarda:
È stato facile. Ho appena caricato il risolutore con il seguente problema:
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
]
Tanto semplice quanto quello, e Mathematica ha fatto tutto il lavoro.
Ho detto "Ah! È facile, ora andiamo per i rettangoli!". Ma mi sbagliavo ...
Blues rettangolari
Il problema principale con i rettangoli è che interrogare l'intersezione è una funzione sgradevole. Qualcosa di simile a:
Quindi, quando ho provato a nutrire Mathematica con molte di queste condizioni per l'equazione, si è comportato così male che ho deciso di fare qualcosa di procedurale.
Il mio algoritmo è finito come segue:
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
Si può notare che la condizione di "minimo movimento" non è completamente soddisfatta (solo in una direzione). Ma ho scoperto che spostare i rettangoli in qualsiasi direzione per soddisfarlo, a volte finisce con un cambiamento di mappa confuso per l'utente.
Mentre progetto un'interfaccia utente, scelgo di spostare il rettangolo un po 'più in là, ma in un modo più prevedibile. Puoi cambiare l'algoritmo per ispezionare tutti gli angoli e tutti i raggi che circondano la sua posizione corrente fino a trovare un posto vuoto, anche se sarà molto più impegnativo.
Comunque, questi sono esempi dei risultati (prima / dopo):
Modifica> Altri esempi qui
Come puoi vedere, il "movimento minimo" non è soddisfatto, ma i risultati sono abbastanza buoni.
Inserirò il codice qui perché ho dei problemi con il mio repository SVN. Lo rimuoverò quando i problemi saranno risolti.
Modificare:
Puoi anche usare R-Tree per trovare le intersezioni dei rettangoli, ma sembra eccessivo per gestire un piccolo numero di rettangoli. E non ho ancora implementato gli algoritmi. Forse qualcun altro può indicarti un'implementazione esistente sulla tua piattaforma preferita.
Avvertimento! Il codice è un primo approccio .. non è ancora di grande qualità e sicuramente ha alcuni bug.
È 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 *)];)
];
Principale
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!
Modifica: ricerca multi-angolo
Ho implementato una modifica all'algoritmo permettendo di cercare in tutte le direzioni, ma privilegiando l'asse imposto dalla simmetria geometrica.
A scapito di più cicli, questo ha portato a configurazioni finali più compatte, come puoi vedere qui sotto:
Altri esempi qui .
Lo pseudocodice per il ciclo principale è cambiato in:
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
Non includo il codice sorgente per brevità, ma chiedilo se pensi di poterlo usare. Penso che, se dovessi andare in questo modo, è meglio passare agli R-tree (qui sono necessari molti test a intervalli)