Reports, for every unit of y, whether it comes within radius of
x, and hands back y with that answer written onto it. The test
is asymmetric: x is indexed once and y is streamed against it,
so the results line up with y, and the larger or repeatedly reused
region belongs in x.
Arguments
- x, y
the two regions, or anything
as_ieegio_roiaccepts; each is put throughresolve_roi_asbefore being tested- mode_x, mode_y
representation to resolve each region into first:
"auto"(default, keep whatever the region already is),"volume","pointcloud","surface", or"streamlines"- radius
distance tolerance; an overlap is reported when the distance is at most
radius. Default is0, meaning literal contact. A volume raises this to a floor of its own; see 'Details'- early_stop
whether to stop measuring a unit at its first overlapping element instead of keeping the closest one; default is
FALSE. Every unit is answered either way; see 'Details'- include_interior
whether geometry lying strictly inside a closed
xcounts as overlapping even when it never comes withinradiusof the surface itself; default isFALSE. Only a surface has an interior, so whenxresolves to anything else this is reported and dropped rather than raised as an error, and the test falls back to contact alone. The setting that was actually used is recorded incollision_detection$summary$include_interior- ...
ignored, present for compatibility with
print
Value
An "ieegio_roi_overlap_result" list with
overlappedwhether the two regions overlap at all, taking each as a whole.
hit_ratiothe ratio, between 0 and 1, of the units of
ythat overlapx. Units that could not be tested are left out of both sides rather than counted as misses, and a region with nothing testable in it gives0rather thanNaN. A surface reports two,c(vertex = , face = ), a face-level answer and its vertex-level reduction being different numbers.early_stopthe setting actually used.
annotatedthe resolved
y, annotated as above.collision_detectionthe raw
vcg_detect_collisionoutput, untouched.mode_x,mode_ythe representations the two regions resolved to, which is what was tested, rather than the arguments, which may both have said
"auto".
Details
Answers are reported per unit of y, which is coarser than a
vertex or a point: a point for a point cloud, a voxel center for a volume, a
face for a surface, and a whole tract for streamlines. A
surface of 642 vertices and 1280 faces therefore yields 1280 answers, and a
bundle of 20 tracts yields 20, however many points each holds.
Both regions must already share one coordinate space. Nothing here
transforms them into a common one: a volume contributes its vox2ras,
streamlines their header$vox2ras, and a surface whichever transform
geometry$transforms lists first, which for a GIFTI carrying
several is not necessarily the scanner one. Comparing a region in
"MNI152" against one in "ScannerAnat" returns confident
nonsense rather than an error.
radius has a lower bound that the regions themselves set.
ravetools has no volume mode, so a volume is tested as its voxel
centers, and each center then stands in for a voxel it can no longer
describe. The radius actually used is
max(radius, inflation_x, inflation_y), where a volume's inflation is
half its voxel diagonal and every other representation inflates by zero -
a point cloud is genuinely points, and surfaces and streamlines are carried
exactly. Two volumes therefore floor at the larger of their two, a volume
against anything else floors at its own, and a pair without a volume leaves
radius untouched.
early_stop chooses only which element inside a unit gets reported, not
how many units are looked at: FALSE measures every element and keeps
the closest, TRUE stops at the first that overlaps. Both a hit and a
distance come back either way. Since a point and a face are each their own
unit, the setting changes nothing for them, and matters only for streamlines,
where a tract's reported distance becomes that of its first overlapping
segment rather than its nearest one.
Tracts that are not lines are dropped before the test: a tract needs at least two points to have any segment, and points with missing coordinates are removed first. A region can therefore report fewer tracts than it was built with.
Annotating y
annotated is the resolved y, in whatever mode_y asked
for, carrying the result in that representation's own idiom:
- point cloud, surface
two vertex measurements,
Overlap(1or0) anddistance(the distance, orNAwhere clear). A surface is answered per face, so both are read at the vertices: a vertex overlaps if any face meeting there does, and takes the nearest of them.- streamlines
the same two as per-tract properties, one value each, since a unit is a whole tract.
- volume
voxel values on the region's own grid and
vox2ras. A volume cannot holdNA- it becomes0, which a distance field would read as exact contact - so two negative sentinels keep the three states apart, neither of which a measured distance can take:-2outside the region and never tested,-1inside it but not overlapping, and>= 0overlapping, the value being the distance. Thresholding at>= -1therefore recovers the region's own mask, and at>= 0the part of it that overlaps.
See also
as_ieegio_roi records how a region is derived, and
resolve_roi_as applies that description; this function consumes
the result of both.
Examples
if (interactive()) {
# ---- Two spheres, offset so that they partly overlap -----------------
sphere <- ravetools::vcg_sphere()
x <- as_ieegio_roi(sphere)
sphere$vb[1:2, ] <- sphere$vb[1:2, ] + 1
y <- as_ieegio_roi(sphere)
result <- detect_roi_overlap(x, y)
result
# `y` is answered per face, and carries the answer at its vertices
length(result$collision_detection$hit_unit)
head(result$annotated$measurements$data_table)
# ---- The same question asked of a volume -----------------------------
mask <- array(0, c(20, 20, 20))
mask[8:13, 8:13, 8:13] <- 1
vox2ras <- rbind(cbind(diag(1, 3), c(-10, -10, -10)), c(0, 0, 0, 1))
region <- as_ieegio_roi(mask, vox2ras = vox2ras, threshold_lb = 0.5)
result <- detect_roi_overlap(x, region, mode_y = "volume")
result$hit_ratio
# `>= -1` is the region itself, `>= 0` the part of it that overlaps
values <- result$annotated[]
c(region = sum(values >= -1), overlapping = sum(values >= 0))
}