ฉันกำลังทำงานเล็กน้อยในเรื่องนี้เนื่องจากฉันต้องการสิ่งที่คล้ายกัน แต่ฉันล่าช้าในการพัฒนาอัลกอริทึม คุณช่วยให้ฉันได้รับแรงกระตุ้น: D
ฉันต้องการซอร์สโค้ดด้วยดังนั้นนี่คือ ฉันใช้มันใน Mathematica แต่เนื่องจากฉันไม่ได้ใช้คุณสมบัติการทำงานมากนักฉันเดาว่ามันจะง่ายต่อการแปลเป็นภาษาขั้นตอนใด ๆ
มุมมองทางประวัติศาสตร์
ก่อนอื่นฉันตัดสินใจที่จะพัฒนาอัลกอริทึมสำหรับวงกลมเนื่องจากจุดตัดนั้นคำนวณได้ง่ายกว่า มันขึ้นอยู่กับศูนย์และรัศมี
ฉันสามารถใช้ตัวแก้สมการ Mathematica ได้และทำได้ดีมาก
แค่ดู:
มันง่าย. ฉันเพิ่งโหลดตัวแก้ปัญหาต่อไปนี้:
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
]
ตรงไปตรงมาและ Mathematica ก็ทำทุกอย่าง
ฉันบอกว่า "ฮ่ามันง่ายมากตอนนี้ไปหาสี่เหลี่ยมกันเถอะ!" แต่ฉันคิดผิด ...
สี่เหลี่ยมบลูส์
ปัญหาหลักของรูปสี่เหลี่ยมคือการค้นหาจุดตัดเป็นฟังก์ชันที่น่ารังเกียจ สิ่งที่ต้องการ:
ดังนั้นเมื่อฉันพยายามป้อน Mathematica ด้วยเงื่อนไขเหล่านี้จำนวนมากสำหรับสมการมันทำงานได้แย่มากจนฉันตัดสินใจทำบางอย่างตามขั้นตอน
อัลกอริทึมของฉันลงเอยดังนี้:
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
คุณอาจสังเกตว่าเงื่อนไข "การเคลื่อนไหวน้อยที่สุด" ไม่เป็นที่พอใจอย่างสมบูรณ์ (ในทิศทางเดียวเท่านั้น) แต่ฉันพบว่าการย้ายรูปสี่เหลี่ยมไปในทิศทางใดก็ได้เพื่อให้เป็นไปตามนั้นบางครั้งอาจมีการเปลี่ยนแปลงแผนที่ที่สับสนสำหรับผู้ใช้
ขณะที่ฉันกำลังออกแบบอินเทอร์เฟซผู้ใช้ฉันเลือกที่จะเลื่อนสี่เหลี่ยมผืนผ้าให้ไกลขึ้นเล็กน้อย แต่ด้วยวิธีที่คาดเดาได้ง่ายกว่า คุณสามารถเปลี่ยนอัลกอริทึมเพื่อตรวจสอบมุมทั้งหมดและรัศมีทั้งหมดรอบตำแหน่งปัจจุบันจนกว่าจะพบสถานที่ว่างแม้ว่าจะมีความต้องการมากกว่านี้ก็ตาม
อย่างไรก็ตามนี่คือตัวอย่างของผลลัพธ์ (ก่อน / หลัง):
แก้ไข> ดูตัวอย่างเพิ่มเติมที่นี่
อย่างที่คุณเห็น "การเคลื่อนไหวขั้นต่ำ" ไม่เป็นที่พอใจ แต่ผลลัพธ์ก็เพียงพอแล้ว
ฉันจะโพสต์รหัสที่นี่เพราะฉันมีปัญหากับที่เก็บ SVN ของฉัน ฉันจะลบออกเมื่อปัญหาได้รับการแก้ไข
แก้ไข:
คุณยังสามารถใช้R-Treesในการค้นหาจุดตัดของรูปสี่เหลี่ยมผืนผ้า แต่ดูเหมือนว่าจะเกินความจำเป็นสำหรับการจัดการกับรูปสี่เหลี่ยมจำนวนน้อย และฉันยังไม่ได้ใช้อัลกอริทึม บางทีอาจมีคนอื่นชี้ให้คุณเห็นถึงการนำไปใช้งานบนแพลตฟอร์มที่คุณเลือก
คำเตือน! Code เป็นแนวทางแรก .. ยังมีคุณภาพไม่ดีนักและมีข้อบกพร่องอยู่บ้าง
มันคือ 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 *)];)
];
หลัก
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*);
เฮ้!
แก้ไข: การค้นหาหลายมุม
ฉันใช้การเปลี่ยนแปลงในอัลกอริทึมเพื่อให้สามารถค้นหาได้ทุกทิศทาง แต่ให้ความสำคัญกับแกนที่กำหนดโดยสมมาตรทางเรขาคณิต
ด้วยค่าใช้จ่ายของรอบที่มากขึ้นสิ่งนี้ส่งผลให้การกำหนดค่าขั้นสุดท้ายมีขนาดกะทัดรัดมากขึ้นดังที่คุณเห็นด้านล่างนี้:
ตัวอย่างเพิ่มเติมที่นี่
pseudocode สำหรับลูปหลักเปลี่ยนเป็น:
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
ฉันไม่ได้รวมซอร์สโค้ดเพื่อความกะทัดรัด แต่ขอแค่ถ้าคุณคิดว่าคุณสามารถใช้มันได้ ฉันคิดว่าคุณควรไปทางนี้จะดีกว่าถ้าเปลี่ยนไปใช้ R-trees (ต้องมีการทดสอบช่วงเวลามากมายที่นี่)