3#include <tess/core/assert.h>
4#include <tess/core/shape.h>
17 std::uint32_t x_count = 0;
19 friend constexpr bool operator==(
TileSpan lhs,
25 std::uint64_t spans = 0;
26 std::uint64_t tiles = 0;
29template <
typename Shape>
31[[nodiscard]]
constexpr auto shape_bounds() noexcept ->
Box3 {
32 return Box3{{0, 0, 0}, ShapeTraits<Shape>::size};
38 std::int64_t origin = 0;
39 std::uint64_t count = 0;
42[[nodiscard]]
constexpr auto clip_axis(std::int64_t origin,
44 std::uint64_t limit)
noexcept
46 if (extent == 0 || limit == 0) {
49 const auto first = std::max<std::int64_t>(origin, 0);
50 const auto skipped = tess::detail::axis_delta(origin, first);
51 const auto first_unsigned =
static_cast<std::uint64_t
>(first);
52 if (skipped >= extent || first_unsigned >= limit) {
57 std::min(extent - skipped, limit - first_unsigned),
61[[nodiscard]]
constexpr auto lower_radius_bound(std::int64_t center,
62 std::uint32_t radius)
noexcept
64 constexpr auto min = std::numeric_limits<std::int64_t>::min();
65 const auto distance =
static_cast<std::uint64_t
>(radius);
66 if (center < 0 && tess::detail::magnitude(center) >
67 tess::detail::magnitude(min) - distance) {
70 return center -
static_cast<std::int64_t
>(radius);
73[[nodiscard]]
inline auto integer_sqrt(std::uint64_t value)
noexcept
76 static_cast<std::uint64_t
>(std::sqrt(
static_cast<long double>(value)));
77 while (root != 0 && root > value / root) {
80 while (root < std::numeric_limits<std::uint32_t>::max() &&
81 root + 1 <= value / (root + 1)) {
84 return static_cast<std::uint32_t
>(root);
87constexpr void add_tiles(SpanQueryStats& stats, std::uint64_t count)
noexcept {
88 stats.tiles = tess::detail::saturating_add(stats.tiles, count);
92void emit_x_runs(std::int64_t x, std::uint64_t count, std::int64_t y,
93 std::int64_t z, SpanQueryStats& stats, Fn& fn) {
94 constexpr auto max = std::numeric_limits<std::uint32_t>::max();
95 auto emitted = std::uint64_t{0};
96 while (emitted < count) {
97 const auto run =
static_cast<std::uint32_t
>(
98 std::min<std::uint64_t>(count - emitted, max));
100 TileSpan{{x +
static_cast<std::int64_t
>(emitted), y, z}, run});
101 stats.spans = tess::detail::saturating_add(stats.spans, 1);
102 add_tiles(stats, run);
109template <
typename Shape,
typename Fn>
112 const auto size = ShapeTraits<Shape>::size;
113 const auto x = detail::clip_axis(box.origin.x, box.extent.x, size.x);
114 const auto y = detail::clip_axis(box.origin.y, box.extent.y, size.y);
115 const auto z = detail::clip_axis(box.origin.z, box.extent.z, size.z);
117 if (x.count == 0 || y.count == 0 || z.count == 0) {
120 auto&& callback = fn;
121 for (std::uint64_t z_offset = 0; z_offset < z.count; ++z_offset) {
122 for (std::uint64_t y_offset = 0; y_offset < y.count; ++y_offset) {
124 x.origin, x.count, y.origin +
static_cast<std::int64_t
>(y_offset),
125 z.origin +
static_cast<std::int64_t
>(z_offset), stats, callback);
131template <
typename Shape,
typename Fn>
133auto for_each_radius_span(
Coord3 center, std::uint32_t radius, Fn&& fn)
135 const auto diameter =
static_cast<std::uint64_t
>(radius) * 2 + 1;
136 const auto broad =
Box3{
138 detail::lower_radius_bound(center.x, radius),
139 detail::lower_radius_bound(center.y, radius),
140 detail::lower_radius_bound(center.z, radius),
142 {diameter, diameter, diameter},
144 const auto size = ShapeTraits<Shape>::size;
145 const auto y = detail::clip_axis(broad.origin.y, broad.extent.y, size.y);
146 const auto z = detail::clip_axis(broad.origin.z, broad.extent.z, size.z);
147 const auto radius_squared =
static_cast<std::uint64_t
>(radius) * radius;
149 auto&& callback = fn;
151 for (std::uint64_t z_offset = 0; z_offset < z.count; ++z_offset) {
152 const auto world_z = z.origin +
static_cast<std::int64_t
>(z_offset);
153 const auto dz = tess::detail::abs_delta(center.z, world_z);
157 const auto dz_squared = dz * dz;
158 for (std::uint64_t y_offset = 0; y_offset < y.count; ++y_offset) {
159 const auto world_y = y.origin +
static_cast<std::int64_t
>(y_offset);
160 const auto dy = tess::detail::abs_delta(center.y, world_y);
164 const auto dy_squared = dy * dy;
165 if (dy_squared > radius_squared - dz_squared) {
168 const auto half_width =
169 detail::integer_sqrt(radius_squared - dz_squared - dy_squared);
170 const auto x = detail::clip_axis(
171 detail::lower_radius_bound(center.x, half_width),
172 static_cast<std::uint64_t
>(half_width) * 2 + 1, size.x);
173 detail::emit_x_runs(x.origin, x.count, world_y, world_z, stats, callback);
179template <
typename Shape,
typename Fn>
181auto for_each_chunk_span(
ChunkKey key,
Box3 local_box, Fn&& fn)
184 TESS_ASSERT(key.value < Traits::chunk_count);
185 if (key.value >= Traits::chunk_count) {
188 const auto chunk = chunk_coord<Shape>(key);
190 static_cast<std::int64_t
>(chunk.x * Traits::chunk.x),
191 static_cast<std::int64_t
>(chunk.y * Traits::chunk.y),
192 static_cast<std::int64_t
>(chunk.z * Traits::chunk.z),
194 const auto x = detail::clip_axis(local_box.origin.x, local_box.extent.x,
196 const auto y = detail::clip_axis(local_box.origin.y, local_box.extent.y,
198 const auto z = detail::clip_axis(local_box.origin.z, local_box.extent.z,
201 if (x.count == 0 || y.count == 0 || z.count == 0) {
204 auto&& callback = fn;
205 for (std::uint64_t z_offset = 0; z_offset < z.count; ++z_offset) {
206 for (std::uint64_t y_offset = 0; y_offset < y.count; ++y_offset) {
208 base.x + x.origin, x.count,
209 base.y + y.origin +
static_cast<std::int64_t
>(y_offset),
210 base.z + z.origin +
static_cast<std::int64_t
>(z_offset), stats,
Counts exact canonical tiles and emitted x-axis runs for one query.
Definition span.h:24
One contiguous canonical x-axis run at a fixed world y and z coordinate.
Definition span.h:15