GPv3: Add curve plane normal cache

This adds a cache to read a normal vector for a plane that (roughly) fits a curve.
If the curve lies on a plane, the vector always point along this plane normal, otherwise it's an approximation.
The cache is lazily calculated and invalidated when the positions are tagged for a change.
This commit is contained in:
Falk David
2024-01-17 14:09:34 +01:00
parent f020e436d5
commit 049e48b431
2 changed files with 56 additions and 0 deletions

View File

@@ -49,6 +49,11 @@ class DrawingRuntime {
*/
mutable SharedCache<Vector<uint3>> triangles_cache;
/**
* Normal vector cache for every stroke. Computed using Newell's method.
*/
mutable SharedCache<Vector<float3>> curve_plane_normals_cache;
/**
* Number of users for this drawing. The users are the frames in the Grease Pencil layers.
* Different frames can refer to the same drawing, so we need to make sure we count these users
@@ -69,6 +74,10 @@ class Drawing : public ::GreasePencilDrawing {
* The triangles for all the fills in the geometry.
*/
Span<uint3> triangles() const;
/**
* Normal vectors for a plane that fits the stroke.
*/
Span<float3> curve_plane_normals() const;
void tag_positions_changed();
void tag_topology_changed();

View File

@@ -293,6 +293,7 @@ Drawing::Drawing(const Drawing &other)
this->runtime = MEM_new<bke::greasepencil::DrawingRuntime>(__func__);
this->runtime->triangles_cache = other.runtime->triangles_cache;
this->runtime->curve_plane_normals_cache = other.runtime->curve_plane_normals_cache;
}
Drawing::~Drawing()
@@ -357,6 +358,51 @@ Span<uint3> Drawing::triangles() const
return this->runtime->triangles_cache.data().as_span();
}
Span<float3> Drawing::curve_plane_normals() const
{
this->runtime->curve_plane_normals_cache.ensure([&](Vector<float3> &r_data) {
const CurvesGeometry &curves = this->strokes();
const Span<float3> positions = curves.positions();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
r_data.reinitialize(curves.curves_num());
threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
if (points.size() < 2) {
r_data[curve_i] = float3(1.0f, 0.0f, 0.0f);
continue;
}
/* Calculate normal using Newell's method. */
float3 normal(0.0f);
float3 prev_point = positions[points.last()];
for (const int point_i : points) {
const float3 curr_point = positions[point_i];
add_newell_cross_v3_v3v3(normal, prev_point, curr_point);
prev_point = curr_point;
}
float length;
normal = math::normalize_and_get_length(normal, length);
/* Check for degenerate case where the points are on a line. */
if (math::is_zero(length)) {
for (const int point_i : points.drop_back(1)) {
float3 segment_vec = math::normalize(positions[point_i] - positions[point_i + 1]);
if (math::length_squared(segment_vec) != 0.0f) {
normal = float3(segment_vec.y, -segment_vec.x, 0.0f);
break;
}
}
}
r_data[curve_i] = normal;
}
});
});
return this->runtime->curve_plane_normals_cache.data().as_span();
}
const bke::CurvesGeometry &Drawing::strokes() const
{
return this->geometry.wrap();
@@ -409,6 +455,7 @@ void Drawing::tag_positions_changed()
{
this->strokes_for_write().tag_positions_changed();
this->runtime->triangles_cache.tag_dirty();
this->runtime->curve_plane_normals_cache.tag_dirty();
}
void Drawing::tag_topology_changed()