136 {
137 const size_t target = size_t(ptrdiff_t(layer) + direction);
139 const auto &next =
m_layers[target];
141 const double noise = source.width *
m_options.noise_width_factor;
142 const double reach = std::max({radius,
bounds, noise});
143 const auto candidates =
query(target, p, reach);
144
145 for (
size_t i :
query(layer, p, reach))
147 for (size_t i : candidates)
149 std::vector<size_t> fast, nearby;
150 for (size_t i : candidates) {
151 const Contour &c = next[i];
152 if (c.points.size() < 3) continue;
153
155 if (c.hole == source.hole && (c.min - source.min).cwiseAbs().maxCoeff() <=
bounds &&
156 (c.max - source.max).cwiseAbs().maxCoeff() <=
bounds) fast.push_back(i);
157 }
158 const auto source_normals =
normals(source, p);
159 const auto compatible_foot = [&](const Contour &c) {
160
161 return closest(c, p, -1.0, c.hole == source.hole ? &source_normals :
nullptr,
163 };
164 const auto normal_agreement = [&](
const Contour &c,
const Vec2d &foot) {
165 double agreement = -1.0;
166 const auto target_normals =
normals(c, foot);
167 for (
const Vec2d &a : source_normals)
168 for (
const Vec2d &b : target_normals) agreement = std::max(agreement, a.dot(b));
169 return agreement;
170 };
171 if (fast.size() == 1) {
172 const size_t index = fast.front();
173 const Foot foot = compatible_foot(next[index]);
174
175 if (foot.distance <= noise && normal_agreement(next[index], foot.point) >=
m_options.normal_cosine) {
176 bool competing = false;
177 for (size_t other : nearby) {
178 if (other == index ||
box_distance(next[other], p) > noise)
continue;
179 const Foot alternative = compatible_foot(next[other]);
180 if (alternative.distance <= noise &&
181 normal_agreement(next[other], alternative.point) >=
m_options.normal_cosine) {
182 competing = true;
183 break;
184 }
185 }
186
187 if (!competing) {
189 const Result back =
select(target, index, foot.point, -direction, radius,
false);
190
191 if (back.uncertain) return back;
192 if (std::find(back.contours.begin(), back.contours.end(),
contour) != back.contours.end())
194 }
195 }
196 }
197 std::vector<Witness> witnessed;
198 for (size_t i : nearby) {
199 const Foot foot = compatible_foot(next[i]);
200 if (foot.distance > radius) continue;
201 const double agreement = normal_agreement(next[i], foot.point);
202 if (agreement <
m_options.normal_cosine)
continue;
203 bool band = true;
204 if (foot.distance > noise) {
205
206 for (double t : std::array<double, 3>{0.25, 0.5, 0.75}) {
207 const Vec2d probe = p + t * (foot.point - p);
210 band = false;
211 break;
212 }
213 }
214 }
215 if (!band) continue;
216 if (check_reverse) {
217 const Result back =
select(target, i, foot.point, -direction, radius,
false);
218 if (back.uncertain) return back;
219
220 if (std::find(back.contours.begin(), back.contours.end(),
contour) == back.contours.end())
continue;
221 }
222 witnessed.push_back({i, foot.distance, std::acos(std::clamp(agreement, -1.0, 1.0)) *
223 180.0 / std::acos(-1.0)});
224 }
225 Result result;
226 const double distance_margin = std::max(1e-6, source.width *
m_options.dominance_width_factor);
227 const double angle_margin = std::max(1e-6,
m_options.dominance_angle_degrees);
228 for (const Witness &candidate : witnessed) {
229
230 const bool dominated = std::any_of(witnessed.begin(), witnessed.end(), [&](const Witness &other) {
231 return other.distance + distance_margin <= candidate.distance &&
232 other.angle_degrees + angle_margin <= candidate.angle_degrees;
233 });
234 if (!dominated) result.contours.push_back(candidate.contour);
235 }
238 return result;
239}
bool near_boundary(size_t layer, const Vec2d &point, double noise) const
Definition SeamContour.cpp:109
bool in_material(size_t layer, const Vec2d &point) const
Definition SeamContour.cpp:93
std::vector< Vec2d > normals(const Contour &c, const Vec2d &p)
Definition SeamContour.cpp:63
@ Bounds
Definition SeamContour.hpp:35
@ Multiple
Definition SeamContour.hpp:34
@ Unique
Definition SeamContour.hpp:34
void bounds(const NSVGimage &image, Vec2f &min, Vec2f &max)
Definition NSVGUtils.cpp:91