Blender V5.0
sculpt_geodesic.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdlib>
10
11#include "BLI_bit_vector.hh"
12#include "BLI_linklist_stack.h"
13#include "BLI_math_geom.h"
14#include "BLI_math_vector.h"
15
16#include "DNA_mesh_types.h"
17
18#include "sculpt_geodesic.hh"
19
20#define SCULPT_GEODESIC_VERTEX_NONE -1
21
23
24/* Propagate distance from v1 and v2 to v0. */
26 const int v0,
27 const int v1,
28 const int v2,
30 const Set<int> &initial_verts)
31{
32 if (initial_verts.contains(v0)) {
33 return false;
34 }
35
36 BLI_assert(dists[v1] != FLT_MAX);
37 if (dists[v0] <= dists[v1]) {
38 return false;
39 }
40
41 float dist0;
43 BLI_assert(dists[v2] != FLT_MAX);
44 if (dists[v0] <= dists[v2]) {
45 return false;
46 }
48 vert_positions[v0], vert_positions[v1], vert_positions[v2], dists[v1], dists[v2]);
49 }
50 else {
51 float vec[3];
52 sub_v3_v3v3(vec, vert_positions[v1], vert_positions[v0]);
53 dist0 = dists[v1] + len_v3(vec);
54 }
55
56 if (dist0 < dists[v0]) {
57 dists[v0] = dist0;
58 return true;
59 }
60
61 return false;
62}
63
65 const Span<int2> edges,
67 const Span<int> corner_verts,
68 const GroupedSpan<int> vert_to_edge_map,
69 const GroupedSpan<int> edge_to_face_map,
70 const Span<bool> hide_poly,
71 const Set<int> &initial_verts,
72 const float limit_radius)
73{
74 const float limit_radius_sq = limit_radius * limit_radius;
75
76 Array<float> dists(vert_positions.size());
77 BitVector<> edge_tag(edges.size());
78
79 /* Both contain edge indices encoded as *void. */
80 BLI_LINKSTACK_DECLARE(queue, void *);
81 BLI_LINKSTACK_DECLARE(queue_next, void *);
82
83 BLI_LINKSTACK_INIT(queue);
84 BLI_LINKSTACK_INIT(queue_next);
85
86 for (const int i : vert_positions.index_range()) {
87 if (initial_verts.contains(i)) {
88 dists[i] = 0.0f;
89 }
90 else {
91 dists[i] = FLT_MAX;
92 }
93 }
94
95 /* Masks vertices that are further than limit radius from an initial vertex. As there is no need
96 * to define a distance to them the algorithm can stop earlier by skipping them. */
97 BitVector<> affected_vert(vert_positions.size());
98
99 if (limit_radius == FLT_MAX) {
100 /* In this case, no need to loop through all initial vertices to check distances as they are
101 * all going to be affected. */
102 affected_vert.fill(true);
103 }
104 else {
105 /* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When
106 * this optimization is needed, it is expected for the tool to request the distance to a low
107 * number of vertices (usually just 1 or 2). */
108 for (const int v : initial_verts) {
109 const float *v_co = vert_positions[v];
110 for (const int i : vert_positions.index_range()) {
111 if (len_squared_v3v3(v_co, vert_positions[i]) <= limit_radius_sq) {
112 affected_vert[i].set();
113 }
114 }
115 }
116 }
117
118 /* Add edges adjacent to an initial vertex to the queue. */
119 for (const int i : edges.index_range()) {
120 const int v1 = edges[i][0];
121 const int v2 = edges[i][1];
122 if (!affected_vert[v1] && !affected_vert[v2]) {
123 continue;
124 }
125 if (dists[v1] != FLT_MAX || dists[v2] != FLT_MAX) {
127 }
128 }
129
130 do {
131 while (BLI_LINKSTACK_SIZE(queue)) {
132 const int e = POINTER_AS_INT(BLI_LINKSTACK_POP(queue));
133 int v1 = edges[e][0];
134 int v2 = edges[e][1];
135
136 if (dists[v1] == FLT_MAX || dists[v2] == FLT_MAX) {
137 if (dists[v1] > dists[v2]) {
138 std::swap(v1, v2);
139 }
141 vert_positions, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_verts);
142 }
143
144 for (const int face : edge_to_face_map[e]) {
145 if (!hide_poly.is_empty() && hide_poly[face]) {
146 continue;
147 }
148 for (const int v_other : corner_verts.slice(faces[face])) {
149 if (ELEM(v_other, v1, v2)) {
150 continue;
151 }
153 vert_positions, v_other, v1, v2, dists, initial_verts))
154 {
155 for (const int e_other : vert_to_edge_map[v_other]) {
156 int ev_other;
157 if (edges[e_other][0] == v_other) {
158 ev_other = edges[e_other][1];
159 }
160 else {
161 ev_other = edges[e_other][0];
162 }
163
164 if (e_other != e && !edge_tag[e_other] &&
165 (edge_to_face_map[e_other].is_empty() || dists[ev_other] != FLT_MAX))
166 {
167 if (affected_vert[v_other] || affected_vert[ev_other]) {
168 edge_tag[e_other].set();
169 BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(e_other));
170 }
171 }
172 }
173 }
174 }
175 }
176 }
177
178 for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
179 const int e = POINTER_AS_INT(lnk->link);
180 edge_tag[e].reset();
181 }
182
183 BLI_LINKSTACK_SWAP(queue, queue_next);
184
185 } while (BLI_LINKSTACK_SIZE(queue));
186
187 BLI_LINKSTACK_FREE(queue);
188 BLI_LINKSTACK_FREE(queue_next);
189
190 return dists;
191}
192
193} // namespace blender::ed::sculpt_paint::geodesic
#define BLI_assert(a)
Definition BLI_assert.h:46
float geodesic_distance_propagate_across_triangle(const float v0[3], const float v1[3], const float v2[3], float dist1, float dist2)
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
bool contains(const Key &key) const
Definition BLI_set.hh:310
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
void fill(const bool value)
static char faces[256]
Array< float > distances_create(const Span< float3 > vert_positions, const Span< int2 > edges, const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > vert_to_edge_map, const GroupedSpan< int > edge_to_face_map, const Span< bool > hide_poly, const Set< int > &initial_verts, const float limit_radius)
static bool sculpt_geodesic_mesh_test_dist_add(Span< float3 > vert_positions, const int v0, const int v1, const int v2, MutableSpan< float > dists, const Set< int > &initial_verts)
#define SCULPT_GEODESIC_VERTEX_NONE
#define FLT_MAX
Definition stdcycles.h:14
struct LinkNode * next
i
Definition text_draw.cc:230