I am trying to find a faster way to do this than what I have put together or by looping through a bunch of points.
I want to select all pairs of integer points ((x1,y1),(x2,y2)) such that:
- Point (x1,y1) is at a specified distance r1 from center point (xc,yc).
- Point (x2,y2) is farther away from the center point than the point (x1,y1) is.
- Points (x1,y1) and (x2,y2) are separated by a specified distance r2.
- These points will be referring to 512X512 images, so each x and y value is limited to be between 1 and 512. Also because of this, I am having distances r1 and r2 only be integers, and I round any distances calculated between two points to the nearest integer.
Conceptually, for each point (x1, y1) that is a distance r1 from the center point, I am selecting all points that are a distance r2 from (x1, y1) and farther away from the center point. In the end, I want a list of pairs of points ((x1,y1),(x2,y2)). Here is my function:
points[r1_, r2_] := Select[{x1, y1, x2, y2} /. FindInstance[ 1 <= x1 <= 512 && 1 <= y1 <= 512 && 1 <= x2 <= 512 && 1 <= y2 <= 512 && Round[Sqrt[(x1 - xc)^2 + (y1 - yc)^2]] == r1 && r1 <= Round[Sqrt[(x2 - xc)^2 + (y2 - yc)^2]] <= (r1 + r2), {x1, y1, x2, y2}, Integers, 512^2], Round[Sqrt[(#[[1]] - #[[3]])^2 + (#[[2]] - #[[4]])^2]] == r2 &]
I put condition 3 in the Select function rather than in the FindInstance function because the FindInstance function couldn't handle it. 512^2 maximum number of instances is a lot larger than I need, but it seems like having that number be too large doesn't hurt anything.
My code works, it is just very slow. The smallest r1 value I am using is 197, and r2 starts at 0 (r1 and r2 are limited to integers as well). My center point for now is at (229,256). I am sure there is an easier way to do this that is also faster. Any suggestions?
UPDATE:
Thanks to Michael E2 and Henrik Schumacher for helping with a faster solution. Although the below answers were not what I was looking for specifically, I was able to use them to obtain what I was looking for. Here is what I have put together:
pointFind[r_, c_] := ArrayPad[(SparseArray@DiskMatrix[r]* SparseArray@(1 - DiskMatrix[r - 1, 2*r + 1])) // Transpose, Transpose@{c - r - 1, 512 - c - r}]["NonzeroPositions"];points[r1_, r2_, c_] := ({#, pointFind[r2, #]} /. {x_?NumericQ, y_?NumericQ} /; Round[Norm[{x, y} - c]] < r1 -> Nothing) & /@ pointFind[r1, c];
Here is an example of the result:
test = points[20, 10, {100, 100}];Show[ ListPlot[test[[;; , 1]], PlotRange -> {{65, 125}, {65, 125}}, PlotStyle -> Black], ListPlot[test[[1, 2]], PlotStyle -> Red], Graphics[{Red, Point[test[[1, 1]]]}], AspectRatio -> 1]
I have plotted all points a rounded distance r1 (20) from the center, and then I picked one such point and show all of the points a distance r2 (10) from the first point that are farther from the center.
This works much much faster than what I had before. Thanks for everything!