113 const std::vector<Polygonf>& occluders,
float direction,
float cone)
115 thread_local std::vector<Quadf> occluder_bounds;
116 occluder_bounds.clear();
117 for (
const auto& polygon : occluders) {
118 occluder_bounds.push_back(
bounds(polygon));
120 visibility(result, from, radius, occluders, occluder_bounds, direction, cone);
124 const std::vector<Polygonf>& occluders,
const std::vector<Quadf>& occluder_bounds,
125 float direction,
float cone)
128 if (!(radius > 0.0F)) {
132 thread_local std::vector<Segment> segments;
133 thread_local std::vector<Vec2f> corners;
134 thread_local std::vector<Ray> rays;
140 const std::array<Vec2f, 4> square { {
141 { from.
x - radius, from.
y - radius },
142 { from.
x + radius, from.
y - radius },
143 { from.
x + radius, from.
y + radius },
144 { from.
x - radius, from.
y + radius },
146 for (std::size_t i = 0; i < square.size(); ++i) {
147 const auto& a = square[i];
148 const auto& b = square[(i + 1) % square.size()];
150 corners.push_back(a);
155 const asw::Quadf reach(square[0],
Vec2f(radius * 2.0F, radius * 2.0F));
156 for (std::size_t p = 0; p < occluders.size(); ++p) {
157 const auto& polygon = occluders[p];
158 if (polygon.size() < 2 || p >= occluder_bounds.size()
159 || !occluder_bounds[p].collides(reach)) {
162 for (std::size_t i = 0; i < polygon.size(); ++i) {
163 Vec2f a = polygon[i];
164 Vec2f b = polygon[(i + 1) % polygon.size()];
165 const Vec2f original_b = b;
175 corners.push_back(a);
176 if (b != original_b) {
177 corners.push_back(b);
185 const bool spot = cone > 0.0F && cone <
TAU;
186 const float half_cone = cone / 2.0F;
187 auto add_ray = [&](
float angle,
const Vec2f& dir) {
188 if (!spot || std::abs(angle) <= half_cone) {
189 rays.push_back({ angle, dir });
196 for (
const auto& corner : corners) {
197 const Vec2f to = corner - from;
199 if (length <= 0.0F) {
203 const Vec2f dir = to / length;
204 const float angle = std::remainder(std::atan2(dir.
y, dir.
x) - direction,
TAU);
211 for (
const float edge : { -half_cone, half_cone }) {
212 add_ray(edge,
Vec2f(std::cos(direction + edge), std::sin(direction + edge)));
218 result.reserve(rays.size());
219 for (
const auto& ray : rays) {
221 float nearest = std::numeric_limits<float>::max();
222 for (
const auto& segment : segments) {
223 if (segment.distance >= nearest) {
226 if (
const auto t =
ray_hit(from, ray.dir, segment.a, segment.b)) {
227 nearest = std::min(nearest, *t);
231 if (nearest < std::numeric_limits<float>::max()) {
232 result.push_back(from + (ray.dir * nearest));
Polygonf visibility(const Vec2f &from, float radius, const std::vector< Polygonf > &occluders, float direction=0.0F, float cone=0.0F)
Find the area that can be seen from a point.
std::optional< float > ray_hit(const Vec2f &origin, const Vec2f &direction, const Vec2f &a, const Vec2f &b)
Find where a ray hits a line segment.