Ich habe ein bisschen daran gearbeitet, da ich auch etwas Ähnliches brauchte, aber ich hatte die Algorithmusentwicklung verzögert. Du hast mir geholfen, einen Impuls zu bekommen: D.
Ich brauchte auch den Quellcode, also hier ist er. Ich habe es in Mathematica ausgearbeitet, aber da ich die Funktionsmerkmale nicht stark genutzt habe, wird es wahrscheinlich einfach sein, in eine prozedurale Sprache zu übersetzen.
Eine historische Perspektive
Zuerst habe ich beschlossen, den Algorithmus für Kreise zu entwickeln, da der Schnittpunkt einfacher zu berechnen ist. Es kommt nur auf die Zentren und Radien an.
Ich konnte den Mathematica-Gleichungslöser verwenden, und er lief gut.
Schau einfach:
Es war einfach. Ich habe gerade den Solver mit folgendem Problem geladen:
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
]
So einfach wie das, und Mathematica hat die ganze Arbeit gemacht.
Ich sagte "Ha! Es ist einfach, jetzt lass uns zu den Rechtecken gehen!". Aber ich habe mich getäuscht ...
Rechteckiger Blues
Das Hauptproblem bei den Rechtecken besteht darin, dass das Abfragen der Kreuzung eine unangenehme Funktion ist. Etwas wie:
Als ich versuchte, Mathematica mit vielen dieser Bedingungen für die Gleichung zu versorgen, lief es so schlecht, dass ich mich entschied, etwas prozedurales zu tun.
Mein Algorithmus endete wie folgt:
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
Möglicherweise stellen Sie fest, dass die Bedingung "kleinste Bewegung" nicht vollständig erfüllt ist (nur in eine Richtung). Aber ich fand heraus, dass das Bewegen der Rechtecke in eine beliebige Richtung, um sie zu befriedigen, manchmal zu einer verwirrenden Kartenänderung für den Benutzer führt.
Während ich eine Benutzeroberfläche entwerfe, verschiebe ich das Rechteck etwas weiter, aber vorhersehbarer. Sie können den Algorithmus ändern, um alle Winkel und alle Radien zu untersuchen, die seine aktuelle Position umgeben, bis eine leere Stelle gefunden wird, obwohl dies viel anspruchsvoller ist.
Auf jeden Fall sind dies Beispiele für die Ergebnisse (vorher / nachher):
Bearbeiten> Weitere Beispiele hier
Wie Sie vielleicht sehen, ist die "minimale Bewegung" nicht zufriedenstellend, aber die Ergebnisse sind gut genug.
Ich werde den Code hier veröffentlichen, da ich Probleme mit meinem SVN-Repository habe. Ich werde es entfernen, wenn die Probleme gelöst sind.
Bearbeiten:
Sie können auch R-Bäume verwenden, um Rechteckkreuzungen zu finden, aber es scheint ein Overkill für den Umgang mit einer kleinen Anzahl von Rechtecken zu sein. Und ich habe die Algorithmen noch nicht implementiert. Vielleicht kann Sie jemand anderes auf eine vorhandene Implementierung auf der Plattform Ihrer Wahl verweisen.
Warnung! Code ist ein erster Ansatz. Noch keine gute Qualität und hat sicherlich einige Fehler.
Es ist 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 *)];)
];
Main
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!
Bearbeiten: Mehrwinkelsuche
Ich habe eine Änderung im Algorithmus implementiert, die es ermöglicht, in alle Richtungen zu suchen, wobei jedoch die durch die geometrische Symmetrie auferlegte Achse bevorzugt wird.
Auf Kosten von mehr Zyklen führte dies zu kompakteren endgültigen Konfigurationen, wie Sie hier unten sehen können:
Weitere Beispiele hier .
Der Pseudocode für die Hauptschleife wurde geändert 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
Der Kürze halber füge ich den Quellcode nicht hinzu, aber frage einfach danach, wenn du denkst, dass du ihn verwenden kannst. Ich denke, wenn Sie diesen Weg gehen, ist es besser, zu R-Bäumen zu wechseln (hier sind viele Intervalltests erforderlich).