获取圆内整数点
分析
圆心位置无所谓,我们先计算相对坐标最后进行统一平移就好,所以圆心就当做(0,0),然后拿半径为7的圆举例:
首先,圆是对称图形,所以我们可以只计算八分之一圆的点,然后在对称到整个圆即可。也就是说我们可以只计算y = x线下部分的半圆。
首先是获取坐标轴上的点,也就是(0,0)-(0,7);
然后获取y = x上的点,此时需要根据勾股定理算出直线和圆的交点并向下取整,也就是floor(radius / sqrt(2)),从(1,1)取到他。
剩下的需要继续分析:
剩下的就是被红线包围的区域,这些线都很好获取。我们不妨将这个区域使用蓝线分为上下两部分,其中下面一部分的点都在圆内,较为好计算。
首先获得蓝线,就是竖着的红线与圆的交点纵坐标值向下取整:floor(sqrt(7 * 7 - 6* 6));
下半部分的直角梯形的计算很简单,循环即可。
然后是上半部分,比较困难,每一行都需要计算与圆相交点的坐标,并逐行添加。
然后是对称,很简单。不过记得如果圆心坐标不是(0,0)要在最后对所有点进行一个偏移。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| struct Point2i { Point2i() { x = 0; y = 0; }
Point2i(const int& _x, const int& _y) { x = _x; y = _y; }
int x, y; };
std::vector<Point2i> GetAllPointInCircle(const Point2i& center, const int& radius) { assert(radius > 0);
std::vector<Point2i> points; for (int i = 0; i <= radius; i++) { points.push_back(Point2i(i, 0)); } int maxI = floor(radius / SQRT2); for (int i = 1; i <= maxI; i++) { points.push_back(Point2i(i, i)); } int xBottomMax = radius - 1; int yBottomMax = floor(sqrt(radius * radius - xBottomMax * xBottomMax)); for (int i = 1; i <= yBottomMax; i++) { for (int j = i + 1; j <= xBottomMax; j++) { points.push_back(Point2i(j, i)); } }
for (int i = yBottomMax + 1; i <= maxI; i++) { int xMaxUpper = floor(sqrt(radius * radius - i * i)); for (int j = i + 1; j <= xMaxUpper; j++) { points.push_back(Point2i(j, i)); } }
int pointNum; pointNum = points.size(); for (int i = 0; i < pointNum; i++) { if (points[i].x != points[i].y) { points.push_back(Point2i(points[i].y, points[i].x)); } }
pointNum = points.size(); for (int i = 0; i < pointNum; i++) { if (points[i].x != 0) { points.push_back(Point2i(-points[i].x, points[i].y)); } }
pointNum = points.size(); for (int i = 0; i < pointNum; i++) { if (points[i].y != 0) { points.push_back(Point2i(points[i].x, -points[i].y)); } }
for (auto& point : points) { point.x += center.x; point.y += center.y; } return points; }
|
分析
其中最耗的肯定属这些开方运算,我进行了一个分析:
半径 |
开方 |
开方总数 |
如果使用平方距离 |
7 |
1 + 1 |
2 |
196 |
8 |
1 + 2 |
3 |
256 |
9 |
1 + 2 |
3 |
324 |
10 |
1 + 3 |
4 |
400 |
11 |
1 + 3 |
4 |
484 |
64 |
1 + 34 |
35 |
16384 |
还算可以接受
获取圆环内整数点
分析
有了上面获取圆内点的前置知识,我们只需要在加入数组的时候判断是不是在小圆内就可以了。
同样也是红线中的上半部分计算比较痛苦。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| std::vector<Point2i> GetAllPointInRing(const Point2i& center, const int& radiusSmall, const int& radiusBig) { assert(radiusBig > radiusSmall); assert(radiusSmall >= 0);
std::vector<Point2i> points; for (int i = radiusSmall + 1; i <= radiusBig; i++) { points.push_back(Point2i(i, 0)); }
int maxXSmall = floor(radiusSmall / SQRT2); int maxXBig = floor(radiusBig / SQRT2); for (int i = maxXSmall + 1; i <= maxXBig; i++) { points.push_back(Point2i(i, i)); }
int maxXBottomSmall = radiusSmall - 1; int maxYBottomSmall = floor(sqrt(radiusSmall * radiusSmall - maxXBottomSmall * maxXBottomSmall)); int maxXBottomBig = radiusBig - 1; int maxYBottomBig = floor(sqrt(radiusBig * radiusBig - maxXBottomBig * maxXBottomBig));
for (int i = 1; i <= maxYBottomBig; i++) { int xMaxUpperSmall = 0; if (i > maxYBottomSmall && i <= maxXSmall) { xMaxUpperSmall = floor(sqrt(radiusSmall * radiusSmall - i * i)); } for (int j = i + 1; j <= maxXBottomBig; j++) { if (j <= maxXBottomSmall && i <= maxYBottomSmall) continue; if (i > maxYBottomSmall && i <= maxXSmall) { if (j <= xMaxUpperSmall) continue; } points.push_back(Point2i(j, i)); } }
for (int i = maxYBottomBig + 1; i <= maxXBig; i++) { int xMaxUpper = floor(sqrt(radiusBig * radiusBig - i * i)); int xMaxUpperSmall = 0; if (i <= maxXSmall) { xMaxUpperSmall = floor(sqrt(radiusSmall * radiusSmall - i * i)); } for (int j = i + 1; j <= xMaxUpper; j++) { if (i <= maxXSmall) { if (j <= xMaxUpperSmall) continue; } points.push_back(Point2i(j, i)); } }
···
··· }
|
其他
在计算其他点上半部分的时候需要一行一开方操作,考虑可以将特定那部分换成一个长方形中所有点的计算平方距离操作,如果平方距离大于平方半径,那就在圆外。不过这种操作需要更多的乘法,远多于开方,具体性能还需要做测试。
资源
https://github.com/1keven1/Get-lattice-point-in-circle-ring