Tôi đã làm việc một chút trong việc này, vì tôi cũng cần thứ gì đó tương tự, nhưng tôi đã trì hoãn việc phát triển thuật toán. Bạn đã giúp tôi có được một số xung lực: D
Tôi cũng cần mã nguồn, vì vậy nó đây. Tôi đã giải quyết nó trong Mathematica, nhưng vì tôi chưa sử dụng nhiều các tính năng chức năng, tôi đoán sẽ dễ dàng dịch sang bất kỳ ngôn ngữ thủ tục nào.
Một viễn cảnh lịch sử
Đầu tiên tôi quyết định phát triển thuật toán cho các vòng tròn, vì giao điểm dễ tính hơn. Nó chỉ phụ thuộc vào các tâm và bán kính.
Tôi đã có thể sử dụng công cụ giải phương trình Mathematica và nó hoạt động rất tốt.
Chỉ cần nhìn:
Thật dễ dàng. Tôi vừa tải trình giải quyết vấn đề sau:
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
]
Đơn giản như vậy, và Mathematica đã làm tất cả công việc.
Tôi nói "Ha! Thật dễ dàng, bây giờ chúng ta hãy đi cho các hình chữ nhật!". Nhưng tôi đã nhầm ...
Blues hình chữ nhật
Vấn đề chính với các hình chữ nhật là truy vấn giao điểm là một hàm khó chịu. Cái gì đó như:
Vì vậy, khi tôi cố gắng bổ sung Mathematica với rất nhiều điều kiện này cho phương trình, nó hoạt động tệ đến mức tôi quyết định làm một cái gì đó theo thủ tục.
Thuật toán của tôi kết thúc như sau:
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
Bạn có thể lưu ý rằng điều kiện "chuyển động nhỏ nhất" không được thỏa mãn hoàn toàn (chỉ theo một hướng). Nhưng tôi nhận thấy rằng việc di chuyển các hình chữ nhật theo bất kỳ hướng nào để đáp ứng nó, đôi khi kết thúc bằng việc thay đổi bản đồ gây nhầm lẫn cho người dùng.
Khi tôi đang thiết kế giao diện người dùng, tôi chọn di chuyển hình chữ nhật ra xa hơn một chút, nhưng theo cách dễ đoán hơn. Bạn có thể thay đổi thuật toán để kiểm tra tất cả các góc và tất cả các bán kính xung quanh vị trí hiện tại của nó cho đến khi tìm thấy một vị trí trống, mặc dù nó sẽ đòi hỏi nhiều hơn.
Dù sao, đây là những ví dụ về kết quả (trước / sau):
Chỉnh sửa> Các ví dụ khác tại đây
Như bạn có thể thấy, "chuyển động tối thiểu" không được thỏa mãn, nhưng kết quả là đủ tốt.
Tôi sẽ đăng mã ở đây vì tôi đang gặp một số rắc rối với kho lưu trữ SVN của mình. Tôi sẽ xóa nó khi vấn đề được giải quyết.
Biên tập:
Bạn cũng có thể sử dụng R-Trees để tìm các giao điểm hình chữ nhật, nhưng có vẻ quá mức cần thiết để xử lý một số lượng nhỏ hình chữ nhật. Và tôi chưa triển khai các thuật toán. Có lẽ ai đó khác có thể chỉ cho bạn một triển khai hiện có trên nền tảng bạn chọn.
Cảnh báo! Mã là cách tiếp cận đầu tiên .. chất lượng chưa tuyệt vời và chắc chắn có một số lỗi.
Đó là 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 *)];)
];
Chủ yếu
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!
Chỉnh sửa: Tìm kiếm nhiều góc độ
Tôi đã thực hiện một thay đổi trong thuật toán cho phép tìm kiếm theo mọi hướng, nhưng ưu tiên cho trục do đối xứng hình học áp đặt.
Với chi phí của nhiều chu kỳ hơn, điều này dẫn đến cấu hình cuối cùng nhỏ gọn hơn, như bạn có thể thấy ở đây bên dưới:
Nhiều mẫu hơn ở đây .
Mã giả cho vòng lặp chính đã thay đổi thành:
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
Tôi không bao gồm mã nguồn cho ngắn gọn, nhưng chỉ cần yêu cầu nó nếu bạn nghĩ rằng bạn có thể sử dụng nó. Tôi nghĩ rằng, nếu bạn đi theo hướng này, tốt hơn nên chuyển sang cây R (rất nhiều bài kiểm tra ngắt quãng cần thiết ở đây)