Fix: GPv3: Outline perimeter cap radius too small when points are not coplanar

Stroke outlines are used by modifiers, fill tool, exporters, etc. When
the input strokes are not co-planar (different depths from camera view)
the end caps have smaller radii than they should have.

When generating the outline caps the normal vector is created from
the point tangents in 2D (after projection). When points have different
depths (Z positions) the tangents have a non-zero Z component as well
and discarding it leaves them unnormalized, thereby scaling the radius
by an unwanted factor.

Normalizing the vector fixes the issue. Object scale is now also taken
into account.

Pull Request: https://projects.blender.org/blender/blender/pulls/126687
This commit is contained in:
Lukas Tönne
2024-08-23 14:24:39 +02:00
parent 6607d66274
commit e42b9a73be

View File

@@ -494,7 +494,7 @@ static void generate_cap(const float3 &point,
Vector<float3> &r_perimeter,
Vector<int> &r_src_indices)
{
const float3 normal = {tangent.y, -tangent.x, 0.0f};
const float3 normal = math::normalize(float3{tangent.y, -tangent.x, 0.0f});
switch (cap_type) {
case GP_STROKE_CAP_ROUND:
generate_arc_from_point_to_point(point - normal * radius,
@@ -564,7 +564,7 @@ static void generate_corner(const float3 &pt_a,
}
static void generate_stroke_perimeter(const Span<float3> all_positions,
const VArray<float> all_radii,
const Span<float> all_radii,
const IndexRange points,
const int corner_subdivisions,
const bool is_cyclic,
@@ -701,13 +701,20 @@ bke::CurvesGeometry create_curves_outline(const bke::greasepencil::Drawing &draw
const VArray<int> src_material_index = *src_attributes.lookup_or_default(
"material_index", bke::AttrDomain::Curve, 0);
/* Transform positions. */
/* Transform positions and radii. */
const float scale = math::average(math::to_scale(transform));
Array<float3> transformed_positions(src_positions.size());
Array<float> transformed_radii(src_radii.size());
threading::parallel_for(transformed_positions.index_range(), 4096, [&](const IndexRange range) {
for (const int i : range) {
transformed_positions[i] = math::transform_point(transform, src_positions[i]);
}
});
threading::parallel_for(transformed_radii.index_range(), 4096, [&](const IndexRange range) {
for (const int i : range) {
transformed_radii[i] = src_radii[i] * scale;
}
});
const float4x4 transform_inv = math::invert(transform);
threading::EnumerableThreadSpecific<PerimeterData> thread_data;
@@ -725,7 +732,7 @@ bke::CurvesGeometry create_curves_outline(const bke::greasepencil::Drawing &draw
const IndexRange points = src_curves.points_by_curve()[curve_i];
generate_stroke_perimeter(transformed_positions,
src_radii,
transformed_radii,
points,
corner_subdivisions,
is_cyclic_curve,