Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,291 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "BKE_curves.hh"
#include "BKE_gtest_base.hh"
#include "GEO_interpolate_curves.hh"
#include "testing/testing.h"
namespace blender::bke::tests {
class GreasePencilInterpolate : public BlenderGTestBase {
public:
enum class TestCurveShape {
Zero,
Circle,
Eight,
Helix,
};
static void create_test_shape(const TestCurveShape shape, MutableSpan<float3> positions)
{
switch (shape) {
case TestCurveShape::Zero:
positions.fill(float3(0.0f));
break;
case TestCurveShape::Circle:
for (const int point_i : positions.index_range()) {
const float angle = 2.0f * M_PI * float(point_i) / float(positions.size());
positions[point_i] = float3(math::cos(angle), math::sin(angle), 0.0f);
}
break;
case TestCurveShape::Eight:
for (const int point_i : positions.index_range()) {
const float angle = 2.0f * M_PI * float(point_i) / float(positions.size());
positions[point_i] = float3(math::cos(angle), math::sin(angle * 2.0f), 0.0f);
}
break;
case TestCurveShape::Helix:
const int turns = 3;
const float pitch = 0.3f;
for (const int point_i : positions.index_range()) {
const float factor = float(turns) * float(point_i) / float(positions.size() - 1);
const float angle = 2.0f * M_PI * factor;
const float height = pitch * factor;
positions[point_i] = float3(math::cos(angle), math::sin(angle), height);
}
break;
}
}
static bke::CurvesGeometry create_test_curves(Span<int> offsets,
Span<bool> cyclic,
TestCurveShape shape)
{
BLI_assert(!offsets.is_empty());
const int curves_num = offsets.size() - 1;
BLI_assert(cyclic.size() == curves_num);
const int points_num = offsets.last();
bke::CurvesGeometry curves(points_num, curves_num);
curves.offsets_for_write().copy_from(offsets);
curves.cyclic_for_write().copy_from(cyclic);
MutableSpan<float3> positions = curves.positions_for_write();
for (const int curve_i : curves.curves_range()) {
const IndexRange points = curves.points_by_curve()[curve_i];
create_test_shape(shape, positions.slice(points));
}
/* Attribute storing original indices to test point remapping. */
SpanAttributeWriter<int> test_indices_writer =
curves.attributes_for_write().lookup_or_add_for_write_span<int>(
"test_index", bke::AttrDomain::Point, bke::AttributeInitConstruct());
array_utils::fill_index_range(test_indices_writer.span);
test_indices_writer.finish();
return curves;
}
void test_sample_curve(const bke::CurvesGeometry &curves,
const int curve_index,
const bool reverse,
const Span<int> expected_indices,
const Span<float> expected_factors,
const float threshold = 1e-4f)
{
const int num_dst_points = expected_indices.size();
BLI_assert(expected_factors.size() == num_dst_points);
const bool cyclic = curves.cyclic()[curve_index];
Array<int> indices(num_dst_points, -9999);
Array<float> factors(num_dst_points, -12345.6f);
geometry::sample_curve_padded(curves, curve_index, cyclic, reverse, indices, factors);
EXPECT_EQ_SPAN(expected_indices, indices.as_span());
EXPECT_EQ(expected_factors.size(), factors.size());
if (expected_factors.size() == factors.size()) {
for (const int i : expected_factors.index_range()) {
EXPECT_NEAR(expected_factors[i], factors[i], threshold)
<< "Element mismatch at index " << i;
}
}
}
};
TEST_F(GreasePencilInterpolate, sample_curve_empty_output)
{
bke::CurvesGeometry curves = create_test_curves(
{0, 1, 3}, {false, false}, TestCurveShape::Eight);
test_sample_curve(curves, 0, false, {}, {});
test_sample_curve(curves, 1, false, {}, {});
}
TEST_F(GreasePencilInterpolate, sample_curve_same_length)
{
bke::CurvesGeometry curves = create_test_curves(
{0, 1, 3, 13, 14, 16, 26}, {false, false, false, true, true, true}, TestCurveShape::Eight);
test_sample_curve(curves, 0, false, {0}, {0.0f});
test_sample_curve(curves, 0, true, {0}, {0.0f});
test_sample_curve(curves, 1, false, {0, 1}, {0.0f, 0.0f});
test_sample_curve(curves, 1, true, {1, 0}, {0.0f, 0.0f});
test_sample_curve(curves,
2,
false,
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
test_sample_curve(curves,
2,
true,
{9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
test_sample_curve(curves, 3, false, {0}, {0.0f});
test_sample_curve(curves, 3, true, {0}, {0.0f});
test_sample_curve(curves, 4, false, {0, 1}, {0.0f, 0.0f});
test_sample_curve(curves, 4, true, {1, 0}, {0.0f, 0.0f});
test_sample_curve(curves,
5,
false,
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
test_sample_curve(curves,
5,
true,
{9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
}
TEST_F(GreasePencilInterpolate, sample_curve_shorter)
{
bke::CurvesGeometry curves = create_test_curves(
{0, 1, 3, 13, 14, 16, 26}, {false, false, false, true, true, true}, TestCurveShape::Eight);
test_sample_curve(curves, 1, false, {0}, {0.0f});
test_sample_curve(curves, 1, true, {1}, {0.0f});
test_sample_curve(curves, 2, false, {0, 2, 5, 9}, {0.0f, 0.82178f, 0.88113f, 0.0f});
test_sample_curve(curves, 2, true, {9, 5, 2, 0}, {0.0f, 0.88113f, 0.82178f, 0.0f});
test_sample_curve(curves, 4, false, {0}, {0.0f});
test_sample_curve(curves, 4, true, {1}, {0.0f});
test_sample_curve(curves, 5, false, {0, 2, 5, 7}, {0.0f, 0.5f, 0.0f, 0.5f});
test_sample_curve(curves, 5, true, {9, 6, 4, 1}, {0.0f, 0.50492f, 0.0f, 0.50492f});
}
TEST_F(GreasePencilInterpolate, sample_curve_longer)
{
bke::CurvesGeometry curves = create_test_curves(
{0, 1, 3, 13, 14, 16, 26}, {false, false, false, true, true, true}, TestCurveShape::Eight);
test_sample_curve(curves,
1,
false,
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{0.0f,
0.09091f,
0.18182f,
0.27273f,
0.36364f,
0.45455f,
0.54545f,
0.63636f,
0.72727f,
0.81818f,
0.90909f,
0.0f});
test_sample_curve(curves,
1,
true,
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0.0f,
0.90909f,
0.81818f,
0.72727f,
0.63636f,
0.54545f,
0.45455f,
0.36364f,
0.27273f,
0.18182f,
0.09091f,
0.0f});
test_sample_curve(curves,
2,
false,
{0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9},
{0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f});
test_sample_curve(curves,
2,
true,
{9, 8, 7, 6, 6, 5, 4, 3, 2, 2, 1, 0},
{0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f});
test_sample_curve(curves,
4,
false,
{0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
{0.0f,
0.16667f,
0.33333f,
0.5f,
0.66667f,
0.83333f,
0.0f,
0.16667f,
0.33333f,
0.5f,
0.66667f,
0.83333f});
test_sample_curve(curves,
4,
true,
{1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
{0.83333f,
0.66667f,
0.5f,
0.33333f,
0.16667f,
0.0f,
0.83333f,
0.66667f,
0.5f,
0.33333f,
0.16667f,
0.0f});
test_sample_curve(curves,
5,
false,
{0, 1, 2, 2, 3, 4, 5, 6, 7, 7, 8, 9},
{0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f});
test_sample_curve(curves,
5,
true,
{9, 8, 7, 6, 6, 5, 4, 3, 2, 1, 1, 0},
{0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f});
}
TEST_F(GreasePencilInterpolate, sample_zero_length_curve)
{
bke::CurvesGeometry curves = create_test_curves(
{0, 10, 20}, {false, true}, TestCurveShape::Zero);
test_sample_curve(curves,
0,
false,
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
test_sample_curve(curves,
1,
false,
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
}
} // namespace blender::bke::tests

View File

@@ -0,0 +1,204 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: Apache-2.0 */
#include "BKE_attribute.hh"
#include "BKE_curves.hh"
#include "BKE_gtest_base.hh"
#include "BLI_array_utils.hh"
#include "GEO_merge_curves.hh"
#include "testing/testing.h"
namespace blender {
using namespace blender::bke;
namespace geometry::tests {
class MergeCurvesTest : public bke::BlenderGTestBase {};
static bke::CurvesGeometry create_test_curves(Span<int> offsets, Span<bool> cyclic)
{
BLI_assert(!offsets.is_empty());
const int curves_num = offsets.size() - 1;
BLI_assert(cyclic.size() == curves_num);
const int points_num = offsets.last();
bke::CurvesGeometry curves(points_num, curves_num);
curves.offsets_for_write().copy_from(offsets);
curves.cyclic_for_write().copy_from(cyclic);
/* Attribute storing original indices to test point remapping. */
SpanAttributeWriter<int> test_indices_writer =
curves.attributes_for_write().lookup_or_add_for_write_span<int>(
"test_index", bke::AttrDomain::Point, bke::AttributeInitConstruct());
array_utils::fill_index_range(test_indices_writer.span);
test_indices_writer.finish();
return curves;
}
TEST_F(MergeCurvesTest, NoConnections)
{
bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
{false, true, true, false});
Array<int> connect_to_curve(4, -1);
Array<bool> flip_direction(4, false);
bke::CurvesGeometry dst_curves = geometry::curves_merge_endpoints(
src_curves, connect_to_curve, flip_direction, {});
const VArraySpan<bool> cyclic = dst_curves.cyclic();
EXPECT_EQ(dst_curves.points_num(), 12);
EXPECT_EQ(dst_curves.curves_num(), 4);
EXPECT_EQ_SPAN(Span({0, 3, 6, 9, 12}), dst_curves.offsets());
EXPECT_EQ_SPAN(Span({false, true, true, false}), cyclic);
}
TEST_F(MergeCurvesTest, ConnectSingleCurve)
{
bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
{false, true, true, false});
Array<int> connect_to_curve = {-1, -1, -1, 1};
Array<bool> flip_direction(4, false);
bke::CurvesGeometry dst_curves = geometry::curves_merge_endpoints(
src_curves, connect_to_curve, flip_direction, {});
const VArraySpan<bool> cyclic = dst_curves.cyclic();
const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
EXPECT_EQ(dst_curves.points_num(), 12);
EXPECT_EQ(dst_curves.curves_num(), 3);
EXPECT_EQ_SPAN(Span({0, 3, 6, 12}), dst_curves.offsets());
EXPECT_EQ_SPAN(Span({false, true, false}), cyclic);
EXPECT_EQ_SPAN(Span({0, 1, 2, 6, 7, 8, 9, 10, 11, 3, 4, 5}), dst_indices);
}
TEST_F(MergeCurvesTest, ReverseCurves)
{
bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
{false, true, true, false});
Array<int> connect_to_curve = {-1, -1, -1, -1};
Array<bool> flip_direction = {false, true, false, true};
bke::CurvesGeometry dst_curves = geometry::curves_merge_endpoints(
src_curves, connect_to_curve, flip_direction, {});
const VArraySpan<bool> cyclic = dst_curves.cyclic();
const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
EXPECT_EQ(dst_curves.points_num(), 12);
EXPECT_EQ(dst_curves.curves_num(), 4);
EXPECT_EQ_SPAN(Span({0, 3, 6, 9, 12}), dst_curves.offsets());
EXPECT_EQ_SPAN(Span({false, true, true, false}), cyclic);
EXPECT_EQ_SPAN(Span({0, 1, 2, 5, 4, 3, 6, 7, 8, 11, 10, 9}), dst_indices);
}
TEST_F(MergeCurvesTest, ConnectAndReverseCurves)
{
bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
{false, true, true, false});
Array<int> connect_to_curve = {3, 0, -1, -1};
Array<bool> flip_direction = {true, false, true, false};
bke::CurvesGeometry dst_curves = geometry::curves_merge_endpoints(
src_curves, connect_to_curve, flip_direction, {});
const VArraySpan<bool> cyclic = dst_curves.cyclic();
const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
EXPECT_EQ(dst_curves.points_num(), 12);
EXPECT_EQ(dst_curves.curves_num(), 2);
EXPECT_EQ_SPAN(Span({0, 9, 12}), dst_curves.offsets());
EXPECT_EQ_SPAN(Span({false, true}), cyclic);
EXPECT_EQ_SPAN(Span({3, 4, 5, 2, 1, 0, 9, 10, 11, 8, 7, 6}), dst_indices);
}
TEST_F(MergeCurvesTest, CyclicConnection)
{
bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
{false, true, true, false});
Array<int> connect_to_curve = {-1, 3, -1, 1};
Array<bool> flip_direction(4, false);
bke::CurvesGeometry dst_curves = geometry::curves_merge_endpoints(
src_curves, connect_to_curve, flip_direction, {});
const VArraySpan<bool> cyclic = dst_curves.cyclic();
const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
EXPECT_EQ(dst_curves.points_num(), 12);
EXPECT_EQ(dst_curves.curves_num(), 3);
EXPECT_EQ_SPAN(Span({0, 3, 9, 12}), dst_curves.offsets());
EXPECT_EQ_SPAN(Span({false, true, true}), cyclic);
EXPECT_EQ_SPAN(Span({0, 1, 2, 3, 4, 5, 9, 10, 11, 6, 7, 8}), dst_indices);
}
TEST_F(MergeCurvesTest, SelfConnectCurve)
{
bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
{false, false, false, false});
Array<int> connect_to_curve = {-1, 1, 2, -1};
Array<bool> flip_direction(4, false);
bke::CurvesGeometry dst_curves = geometry::curves_merge_endpoints(
src_curves, connect_to_curve, flip_direction, {});
const VArraySpan<bool> cyclic = dst_curves.cyclic();
const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
EXPECT_EQ(dst_curves.points_num(), 12);
EXPECT_EQ(dst_curves.curves_num(), 4);
EXPECT_EQ_SPAN(Span({0, 3, 6, 9, 12}), dst_curves.offsets());
EXPECT_EQ_SPAN(Span({false, true, true, false}), cyclic);
EXPECT_EQ_SPAN(Span({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), dst_indices);
}
TEST_F(MergeCurvesTest, MergeAll)
{
bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
{false, true, true, false});
Array<int> connect_to_curve = {2, 0, 3, 1};
Array<bool> flip_direction(4, false);
bke::CurvesGeometry dst_curves = geometry::curves_merge_endpoints(
src_curves, connect_to_curve, flip_direction, {});
const VArraySpan<bool> cyclic = dst_curves.cyclic();
const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
EXPECT_EQ(dst_curves.points_num(), 12);
EXPECT_EQ(dst_curves.curves_num(), 1);
EXPECT_EQ_SPAN(Span({0, 12}), dst_curves.offsets());
EXPECT_EQ_SPAN(Span({true}), cyclic);
EXPECT_EQ_SPAN(Span({0, 1, 2, 6, 7, 8, 9, 10, 11, 3, 4, 5}), dst_indices);
}
TEST_F(MergeCurvesTest, Branching)
{
bke::CurvesGeometry src_curves = create_test_curves({0, 3, 6, 9, 12},
{false, true, true, false});
/* Multiple curves connect to curve 2, one connection is ignored. */
Array<int> connect_to_curve = {2, 2, -1, -1};
Array<bool> flip_direction(4, false);
bke::CurvesGeometry dst_curves = geometry::curves_merge_endpoints(
src_curves, connect_to_curve, flip_direction, {});
const VArraySpan<bool> cyclic = dst_curves.cyclic();
const VArraySpan<int> dst_indices = *dst_curves.attributes().lookup<int>("test_index");
EXPECT_EQ(dst_curves.points_num(), 12);
EXPECT_EQ(dst_curves.curves_num(), 3);
EXPECT_EQ_SPAN(Span({0, 6, 9, 12}), dst_curves.offsets());
EXPECT_EQ_SPAN(Span({false, false, false}), cyclic);
EXPECT_EQ_SPAN(Span({0, 1, 2, 6, 7, 8, 3, 4, 5, 9, 10, 11}), dst_indices);
}
} // namespace geometry::tests
} // namespace blender

View File

@@ -0,0 +1,73 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: Apache-2.0 */
#include "BLI_array_utils.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_gtest_base.hh"
#include "BKE_instances.hh"
#include "BKE_lib_id.hh"
#include "DNA_curves_types.h"
#include "GEO_realize_instances.hh"
#include "testing/testing.h"
namespace blender {
using namespace blender::bke;
namespace geometry::tests {
class RealizeInstancesTest : public bke::BlenderGTestBase {};
static void create_test_curves(bke::CurvesGeometry &curves, Span<int> offsets)
{
BLI_assert(!offsets.is_empty());
const int curves_num = offsets.size() - 1;
const int points_num = offsets.last();
curves.resize(points_num, curves_num);
curves.offsets_for_write().copy_from(offsets);
curves.update_curve_types();
/* Attribute storing original indices to test point remapping. */
SpanAttributeWriter<int> test_indices_writer =
curves.attributes_for_write().lookup_or_add_for_write_span<int>(
"test_index", bke::AttrDomain::Point, bke::AttributeInitConstruct());
array_utils::fill_index_range(test_indices_writer.span);
test_indices_writer.finish();
}
/* Regression test for builtin curve attributes:
* The attribute can be added with arbitrary type/domain on instances,
* but is built-in and restricted on curves, which will not allow writing it
* to the realized curves geometry. #142163 */
TEST_F(RealizeInstancesTest, InstanceAttributeToBuiltinCurvesAttribute)
{
Curves *curves_id = BKE_id_new_nomain<Curves>("TestCurves");
create_test_curves(curves_id->geometry.wrap(), {0, 3});
bke::GeometrySet curves_geometry = GeometrySet::from_curves(curves_id);
auto instances = std::make_unique<Instances>(2);
const int handle = instances->add_reference(bke::InstanceReference{curves_geometry});
/* The issue only occurs with 2 or more instances. In case of a single instance the code takes a
* special path that does not run cause this problem. */
instances->reference_handles_for_write().fill(handle);
instances->transforms_for_write().fill(float4x4::identity());
/* This attribute will be converted to the point domain, where it is invalid on curves. */
instances->attributes_for_write().add<float>(
"curve_type", AttrDomain::Instance, AttributeInitDefaultValue());
bke::GeometrySet instances_geometry = GeometrySet::from_instances(std::move(instances));
geometry::RealizeInstancesOptions options;
options.realize_instance_attributes = true;
GeometrySet realized_geometry_set =
geometry::realize_instances(instances_geometry, options).geometry;
}
} // namespace geometry::tests
} // namespace blender