20 [[maybe_unused]] MissingChunkPolicy policy,
const Provider& provider)
22 static_assert(std::derived_from<Class, movement::movement_class_tag>,
23 "build_weighted_distance_field_in_box<World, Class> requires "
24 "a MovementClass; pass a movement class such as "
25 "PositiveCostFieldMovement.");
26 using Shape =
typename World::shape_type;
27 using Space = detail::NodeIndexSpace<World>;
29 constexpr auto infinite_distance = std::numeric_limits<std::uint32_t>::max();
31 TESS_DIAG_EVENT_VALUE(path_clear, scratch.touched_.size());
32 scratch.clear_build();
33 if (!contains<Shape>(goal) || !contains(domain, goal)) {
36 if constexpr (!Space::is_dense) {
39 const Space residency{world};
40 if (!residency.is_resident_index(detail::tile_index<Shape>(goal))) {
42 policy == MissingChunkPolicy::ReportIndeterminate
43 ? PathStatus::Indeterminate
44 : PathStatus::InvalidGoal,
48 TESS_DIAG_EVENT(path_goal_passability_check);
49 if (!detail::is_passable<World, Class>(world, goal)) {
53 const auto goal_index = detail::tile_index<Shape>(goal);
54 if (detail::tile_entry_cost_index<World, Class>(world, goal_index) == 0) {
58 const Space space{world};
59 const auto node_count = space.capacity_hint();
60 if (scratch.distance_.size() != node_count) {
61 TESS_DIAG_EVENT(path_initialize);
62 scratch.generation_.assign(node_count, 0);
63 scratch.distance_.assign(node_count, infinite_distance);
66 const auto goal_offset = space.offset(goal_index);
67 const auto model = Model{provider};
69 scratch.has_goal_ =
true;
70 scratch.template stamp_model<Model>(
71 model, detail::transition_provider_instance_identity(provider));
72 scratch.stamp_residency(world);
73 scratch.distance_[goal_offset] = 0;
74 scratch.touch_node(goal_offset, goal_index);
75 TESS_DIAG_EVENT(path_touch_node);
76 scratch.weighted_frontier_.push_back(
77 detail::PackedOpenNode::make(goal_index, 0, 0));
78 std::push_heap(scratch.weighted_frontier_.begin(),
79 scratch.weighted_frontier_.end(),
80 detail::packed_open_node_less);
81 TESS_DIAG_EVENT(path_heap_push);
83 std::size_t expanded_nodes = 0;
84 [[maybe_unused]]
bool crossed_missing =
false;
85 auto cost_overflow =
false;
86 while (!scratch.weighted_frontier_.empty()) {
87 TESS_DIAG_EVENT(path_heap_pop);
88 std::pop_heap(scratch.weighted_frontier_.begin(),
89 scratch.weighted_frontier_.end(),
90 detail::packed_open_node_less);
91 const auto current = scratch.weighted_frontier_.back();
92 scratch.weighted_frontier_.pop_back();
94 const auto current_offset = space.offset(current.index);
95 const auto current_distance =
96 scratch.distance_at(current_offset, infinite_distance);
97 if (current.g() != current_distance) {
98 TESS_DIAG_EVENT_VALUE(path_skip_pop,
false);
103 const auto current_coord = detail::tile_coord<Shape>(current.index);
104 model.for_each_reverse(
105 world, current_coord, current.index, [&](
auto probe) {
106 if (!contains(domain, probe.to)) {
109 TESS_DIAG_EVENT(path_neighbor_candidate);
110 if (probe.availability == TransitionAvailability::MissingTopology) {
111 crossed_missing = true;
114 if (probe.cost_overflow) {
115 cost_overflow = true;
118 const auto neighbor_index = probe.to_index;
119 if constexpr (!Space::is_dense) {
120 if (!space.is_resident_index(neighbor_index)) {
121 crossed_missing =
true;
125 const auto neighbor_offset = space.offset(neighbor_index);
126 TESS_DIAG_EVENT(path_relax_attempt);
127 if (!scratch.is_current(neighbor_offset)) {
128 scratch.distance_[neighbor_offset] = infinite_distance;
129 scratch.touch_node(neighbor_offset, neighbor_index);
130 TESS_DIAG_EVENT(path_touch_node);
133 const auto next_distance =
134 detail::saturating_add(current_distance, probe.cost);
135 if (next_distance == infinite_distance) {
136 cost_overflow =
true;
140 scratch.distance_at(neighbor_offset, infinite_distance)) {
141 TESS_DIAG_EVENT(path_relax_success);
142 scratch.distance_[neighbor_offset] = next_distance;
143 scratch.weighted_frontier_.push_back(detail::PackedOpenNode::make(
144 neighbor_index, next_distance, next_distance));
145 std::push_heap(scratch.weighted_frontier_.begin(),
146 scratch.weighted_frontier_.end(),
147 detail::packed_open_node_less);
148 TESS_DIAG_EVENT(path_heap_push);
153 if constexpr (!Space::is_dense) {
154 if (crossed_missing && policy == MissingChunkPolicy::ReportIndeterminate) {
155 scratch.publish_build_status(PathStatus::Indeterminate);
157 scratch.touched_.size()};
161 scratch.discard_build_result();
163 scratch.touched_.size()};
165 scratch.publish_build_status(PathStatus::Found);
167 scratch.touched_.size()};