Skip to main content

computational geometry - Collision detection algorithm (discrete) with sweep and prune algorithm


For a more complex collision detection approach, I need an efficient (aka fast) algorithm to prune the elements by using boundary boxes. A classical algorithm is the so called sweep and prune (SAP) algorithm (as outlined in this Web-Article). However so far I did not succeed in vectorize the SAP algorithm, so I went for an sequential implementation. Furthermore I did not suceed in taking advantage of the quoted temporal coherence by exploiting an insertionsort approach, since Mathematica sort is not open to external tweaking of the sort algorithm as far as I know. The sequential code of the SAP algorithm for one axis looks as follows.


SweepAndPruneAxis[objaxes_] := 
Module[{axisList, activeList, collcand, j, i},
axisList = Ordering[objaxes, All, First[#1] < First[#2] &];
activeList = {First[axisList]};
collcand = {};
For[j = 2, j <= Length[axisList], j++,
i = 1;
While[i > 0 && i <= Length[activeList] && i <= Length[axisList],

If[objaxes[[axisList[[j]], 1]] <= objaxes[[activeList[[i]], 2]],
collcand = Append[collcand, {activeList[[i]], axisList[[j]]}];
i++;
,
activeList = Delete[activeList, i];
];
];
activeList = Append[activeList, axisList[[j]]];
];
Return[collcand];

];

Here the objaxes contains {xmin,xmax} pairs of coordinates which get analyzed with respect of overlap and a list of indices of the elements in objaxes which overlap are returned.


The following code snippet demonstrates the capabilities of the SAP algorithm.


 SeedRandom[35];
objects = Sort /@ RandomReal[{0, 15}, {10, 2}];
Graphics[MapIndexed[{Rectangle[{#1[[1]], -First[#2]}, {#1[[2]], -First[#2] - 0.5}],
Text[First[#2], {0.1, -First[#2] - 0.25}]} &, objects]]
Sort[Sort /@ SweepAndPruneAxis[objects]]


which yields the output


SAP Algorithm output


and the sorted list of collision pairs


{{1, 3}, {1, 5}, {1, 6}, {1, 7}, {1, 8}, {1, 9}, {2, 6}, {3, 5}, {3, 7}, {3, 8}, {3, 9}, {4, 6}, {5, 7}, {5, 9}, {7, 8}, {7, 9}, {7, 10}}


This approach can be extended to e.g. 3 dimensions by simply taking the intersection of the 3 resulting SAP results for the 3 coordinate axes using


 Intersection[Sequence @@ (SweepAndPruneAxis /@ 
(Transpose[Transpose[objboxes, {1, 3, 2}]][[1 ;; dim]])),
SameTest -> (Sort[#1] == Sort[#2] &)
]


where here now objboxes is a list of 3 min/max pairs for the respective axis.


My main issue is now that an "almost" brute force implementation which is using on-board commands of Mathematica is computationally more efficient in getting the same result:


 Extract[Subsets[Range[Length[objboxes]], {2}], 
Transpose[Position[Apply[IntervalIntersection,
Map[Interval, Subsets[Transpose[Transpose[objboxes, {1, 3, 2}]][[#]], {2}] & /@
Range[dim], {3}], {2}]], {Repeated[Interval[_List], {dim}]}]]

with objboxes as described above and dim as the dimension of the objboxes sets.


This can be demonstrated by defining a "bracket" function as


FindCollisionCandidates[objboxes_, dim_: 3, sap_: True] := If[sap,

Intersection[
Sequence @@ (SweepAndPruneAxis /@ (Transpose[
Transpose[objboxes, {1, 3, 2}]][[1 ;; dim]])),
SameTest -> (Sort[#1] == Sort[#2] &)]
,
Extract[Subsets[Range[Length[objboxes]], {2}],
Position[
Transpose[Apply[IntervalIntersection,
Map[Interval,
Subsets[Transpose[

Transpose[objboxes, {1, 3, 2}]][[#]], {2}] & /@
Range[dim], {3}], {2}]], {Repeated[
Interval[_List], {dim}]}]]]

and apply this to an randomly generated dataset with some intersections.


 rval = 0.1;
Nbox = 250;
dim = 3;
SeedRandom[35];
p = Prepend[RandomReal[{-5 + rval, 5 - rval}, {Nbox, dim}], {0, 0, 0}];

r = ConstantArray[rval, Length[p]];
Sort[Sort /@ FindCollisionCandidates[{p - r, p + r}\[Transpose], 2, False]] // Timing
Sort[Sort /@ FindCollisionCandidates[{p - r, p + r}\[Transpose], 2, True]] // Timing

yielding


{0.374402, {{4, 133}, {6, 199}, {7, 42}, {7, 52}, {7, 132}, {9, 98}, {10, 229}, {14, 117}, {15, 44}, {15, 100}, {15, 226}, {16, 49}, {21, 231}, {22, 237}, {26, 157}, {27, 207}, {34, 173}, {34, 185}, {35, 89}, {35, 119}, {41, 242}, {42, 52}, {42, 132}, {43, 154}, {44, 100}, {44, 226}, {46, 248}, {48, 245}, {50, 223}, {52, 132}, {53, 195}, {54, 194}, {55, 242}, {61, 94}, {67, 170}, {71, 155}, {71, 179}, {71, 216}, {74, 200}, {75, 141}, {81, 114}, {87, 159}, {89, 119}, {92, 131}, {104, 227}, {105, 225}, {112, 235}, {129, 176}, {142, 188}, {142, 222}, {144, 204}, {144, 210}, {146, 236}, {148, 207}, {164, 168}, {173, 185}, {179, 216}, {180, 210}, {183, 199}, {197, 221}, {214, 224}, {228, 244}}}


for the brute-force approach, and


{3.681624, {{4, 133}, {6, 199}, {7, 42}, {7, 52}, {7, 132}, {9, 98}, {10, 229}, {14, 117}, {15, 44}, {15, 100}, {15, 226}, {16, 49}, {21, 231}, {22, 237}, {26, 157}, {27, 207}, {34, 173}, {34, 185}, {35, 89}, {35, 119}, {41, 242}, {42, 52}, {42, 132}, {43, 154}, {44, 100}, {44, 226}, {46, 248}, {48, 245}, {50, 223}, {52, 132}, {53, 195}, {54, 194}, {55, 242}, {61, 94}, {67, 170}, {71, 155}, {71, 179}, {71, 216}, {74, 200}, {75, 141}, {81, 114}, {87, 159}, {89, 119}, {92, 131}, {104, 227}, {105, 225}, {112, 235}, {129, 176}, {142, 188}, {142, 222}, {144, 204}, {144, 210}, {146, 236}, {148, 207}, {164, 168}, {173, 185}, {179, 216}, {180, 210}, {183, 199}, {197, 221}, {214, 224}, {228, 244}}}


for the SAP approach.


This demonstrates a rather poor performance for the SAP implementation of almost 10x slower on my machine.



Now my question(s):



  1. Has anyone implemented a SAP (or similiar) collision detection algorithm using on-board Mathematica commands and in vectorized form?

  2. Is anyone aware of a potential "undocumented" feature for collision detection (e.g. for 2D or 3D graphics in Mathematica?

  3. Of course any suggestion to speed up the SAP code by vectorizing it, or extending it with temporal coherence exploitation are welcome too.



Answer



Edit: 4x faster after refactoring and using {# - #2, +##} &[+##, Abs[# - #2]] & instead of Sort/@.


Let we have the following 1D intervals


$$ [a_1,b_1], [a_2,b_2], [a_3, b_3]. $$



Then we can take all these edges of intervals and sort them. For example,


                                                             enter image description here


Now we see that the first interval intersects with all intervals, which have edges between $a_1$ and $b_1$.


We can easily implement it with Ordering


sap1[obj_] := Module[{ord = Quotient[Ordering@Flatten@obj + 1, 2]}, 
Quotient[#, 2] &@ DeleteDuplicates[
Transpose@{# - #2, +##} &[+##, Abs[# - #2]] &[
Flatten[Unitize@# Range@Length@#], Flatten@#] &[
ord[[# + 1 ;; #2 - 1]] & @@@ Partition[Ordering@ord, 2]]]]


sap1@objects // Sort
(* {{1, 3}, {1, 5}, {1, 6}, {1, 7}, {1, 8}, {1, 9}, {2, 6}, {3,
5}, {3, 7}, {3, 8}, {3, 9}, {4, 6}, {5, 7}, {5, 9}, {7, 8}, {7,
9}, {7, 10}} *)

For multidimensional collision detecting you can use


Intersection @@Table[sap1@objboxes[[;; , ;; , d]], {d, dim}]

Timings:


FindCollisionCandidates[objboxes_, dim_: 3, sap_: True] := 

If[sap, Intersection @@Table[sap1@objboxes[[;; , ;; , d]], {d, dim}],
Extract[Subsets[Range[Length[objboxes]], {2}],
Position[Transpose[Apply[IntervalIntersection,
Map[Interval, Subsets[Transpose[
Transpose[objboxes, {1, 3, 2}]][[#]], {2}] & /@
Range[dim], {3}], {2}]], {Repeated[Interval[_List], {dim}]}]]]

rval = 0.1;
Nbox = 250;
dim = 3;

SeedRandom[35];
p = Prepend[RandomReal[{-5 + rval, 5 - rval}, {Nbox, dim}], {0, 0, 0}];
r = ConstantArray[rval, Length[p]];
res1 = Sort[
Sort /@ FindCollisionCandidates[{p - r, p + r}\[Transpose], 2,
False]]; // AbsoluteTiming
res2 = FindCollisionCandidates[{p - r, p + r}\[Transpose], 2,
True]; // AbsoluteTiming
res1 == res2
(* {0.577945, Null} *)

(* {0.008889, Null} *)
(* True *)

It is definitely faster!


Comments

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1.