Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ink/strokes/internal/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@ cc_test(
srcs = ["stroke_subtraction_test.cc"],
deps = [
":stroke_subtraction",
":stroke_vertex",
"//ink/geometry:affine_transform",
"//ink/geometry:fuzz_domains",
"//ink/geometry:mesh_format",
Expand Down
1 change: 1 addition & 0 deletions ink/strokes/internal/brush_tip_extruder/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ cc_test(
":mutable_mesh_view",
"//ink/geometry:mutable_mesh",
"//ink/geometry:point",
"//ink/geometry:triangle",
"//ink/geometry:type_matchers",
"//ink/geometry:vec",
"//ink/strokes/internal:stroke_vertex",
Expand Down
60 changes: 30 additions & 30 deletions ink/strokes/internal/brush_tip_extruder/derivative_calculator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -358,18 +358,6 @@ void DerivativeCalculator::SaveSideMarginUpperBound(uint32_t index,

namespace {

// Returns the segment starting at a triangle's `vertex_position`, and ending
// at `vertex_position +/- derivative` such that the segment is oriented away
// from the triangle's interior.
//
// `outset_sign` is expected to be the return value of
// `StrokeVertex::Label::DerivativeOutsetSign()`.
Segment MakeOutsetSegment(Point vertex_position, float outset_sign,
Vec derivative) {
return {.start = vertex_position,
.end = vertex_position + outset_sign * derivative};
}

// Returns one of the segments that will be used to constrain vertex outsets.
//
// For a triangle, the returned segment will start at the given
Expand Down Expand Up @@ -422,24 +410,37 @@ Point TrianglePosition(const Triangle& triangle, int vertex_index) {
void DerivativeCalculator::AddMarginUpperBoundsForTriangle(
const MutableMeshView& mesh,
const std::array<uint32_t, 3>& triangle_indices) {
Triangle triangle = GetTriangleFromIndices(mesh, triangle_indices);
std::array<Vec, 3> outset_vectors;
for (int i = 0; i < 3; ++i) {
float outset_sign =
mesh.GetSideLabel(triangle_indices[i]).DerivativeOutsetSign();
outset_vectors[i] =
outset_sign * mesh.GetSideDerivative(triangle_indices[i]);
}
std::array<float, 3> bounds = ComputeTriangleMarginUpperBounds(
GetTriangleFromIndices(mesh, triangle_indices), outset_vectors);
for (int i = 0; i < 3; ++i) {
SaveSideMarginUpperBound(triangle_indices[i], bounds[i]);
}
}

std::array<float, 3> DerivativeCalculator::ComputeTriangleMarginUpperBounds(
const Triangle& triangle, const std::array<Vec, 3>& outset_vectors) {
// Degenerate triangles must be handled separately:
if (triangle.SignedArea() == 0) {
// Check if the triangle is degenerate, but no two vertices of the triangle
// are coincident. In that case, the three vertices must always remain
// collinear, so we need to set all of the side margins to 0.
if (triangle.p0 != triangle.p1 && triangle.p0 != triangle.p2 &&
triangle.p1 != triangle.p2) {
for (int i = 0; i < 3; ++i) {
SaveSideMarginUpperBound(triangle_indices[i], 0);
}
return {0.0f, 0.0f, 0.0f};
}
// Otherwise, since two of the vertices share the same position, we can skip
// the entire triangle. Coincident vertices will be given the same
// derivative values and be repositioned the same way in the shader, so the
// degenerate triangle does not impact the margins of any of its vertices.
return;
return {StrokeVertex::kMaximumMargin, StrokeVertex::kMaximumMargin,
StrokeVertex::kMaximumMargin};
}

// Each triangle splits its exterior into three regions according to the
Expand Down Expand Up @@ -476,25 +477,24 @@ void DerivativeCalculator::AddMarginUpperBoundsForTriangle(
MakeBoundingSegment(triangle.p1, triangle.GetEdge(2)),
MakeBoundingSegment(triangle.p2, triangle.GetEdge(0))};

std::array<float, 3> bounds = {StrokeVertex::kMaximumMargin,
StrokeVertex::kMaximumMargin,
StrokeVertex::kMaximumMargin};
for (int i = 0; i < 3; ++i) {
Point vertex_position = TrianglePosition(triangle, i);
float side_outset_sign =
mesh.GetSideLabel(triangle_indices[i]).DerivativeOutsetSign();
if (side_outset_sign == 0) {
// If the outset sign is 0, this vertex should not be repositioned at
if (outset_vectors[i] == Vec{0, 0}) {
// If the outset vector is 0, this vertex should not be repositioned at
// all, and the upper bound is 0. This happens for an interior vertex
// label.
SaveSideMarginUpperBound(triangle_indices[i], 0);
bounds[i] = 0.0f;
continue;
}
Segment side_outset_segment =
MakeOutsetSegment(vertex_position, side_outset_sign,
mesh.GetSideDerivative(triangle_indices[i]));
float margin_upper_bound = std::min(
MarginUpperBound(side_outset_segment, bounding_segments[(i + 1) % 3]),
MarginUpperBound(side_outset_segment, bounding_segments[(i + 2) % 3]));
SaveSideMarginUpperBound(triangle_indices[i], margin_upper_bound);
Point pos = TrianglePosition(triangle, i);
Segment outset_segment = {.start = pos, .end = pos + outset_vectors[i]};
bounds[i] = std::min(
MarginUpperBound(outset_segment, bounding_segments[(i + 1) % 3]),
MarginUpperBound(outset_segment, bounding_segments[(i + 2) % 3]));
}
return bounds;
}

namespace {
Expand Down
13 changes: 13 additions & 0 deletions ink/strokes/internal/brush_tip_extruder/derivative_calculator.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <vector>

#include "absl/types/span.h"
#include "ink/geometry/triangle.h"
#include "ink/geometry/vec.h"
#include "ink/strokes/internal/brush_tip_extruder/mutable_mesh_view.h"
#include "ink/strokes/internal/stroke_vertex.h"
Expand Down Expand Up @@ -82,6 +83,18 @@ class DerivativeCalculator {
absl::Span<const uint32_t> right_indices_to_update,
MutableMeshView& mesh);

// Computes the margin upper bounds imposed by `triangle` on its vertices
// given the vertex `outset_vectors`.
//
// The margins bound the extents to which vertices can safely be outset along
// their respective outset vectors without inverting the triangle. See
// `StrokeVertex::Label` for further background on margins.
//
// The margins computed by this function are approximate and clamped to the
// range [0, StrokeVertex::kMaximumMargin].
static std::array<float, 3> ComputeTriangleMarginUpperBounds(
const Triangle& triangle, const std::array<Vec, 3>& outset_vectors);

private:
// Prepares the tracked average derivatives and minimum margins for
// calculating new values. The derivatives are zeroed out, and the margins are
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "ink/strokes/internal/brush_tip_extruder/derivative_calculator.h"

#include <array>
#include <cstdint>
#include <vector>

Expand All @@ -22,6 +23,7 @@
#include "absl/types/span.h"
#include "ink/geometry/mutable_mesh.h"
#include "ink/geometry/point.h"
#include "ink/geometry/triangle.h"
#include "ink/geometry/type_matchers.h"
#include "ink/geometry/vec.h"
#include "ink/strokes/internal/brush_tip_extruder/mutable_mesh_view.h"
Expand Down Expand Up @@ -585,5 +587,58 @@ TEST(DerivativeCalculatorDeathTest, NonEmptyIndicesWithNoDataMeshView) {
"");
}

TEST_F(DerivativeCalculatorTest,
ComputeTriangleMarginUpperBoundsUnconstrained) {
// 2
// |\
// | \
// | \
// | \
// | \
// 0-----1 - - o outset
// /
// o
// outset

Triangle triangle = {.p0 = {0, 0}, .p1 = {10, 0}, .p2 = {0, 10}};
std::array<Vec, 3> outset_vectors = {Vec{-1, -1}, Vec{1, 0}, Vec{0, 0}};

std::array<float, 3> bounds =
DerivativeCalculator::ComputeTriangleMarginUpperBounds(triangle,
outset_vectors);

EXPECT_FLOAT_EQ(bounds[0], StrokeVertex::kMaximumMargin);
EXPECT_FLOAT_EQ(bounds[1], StrokeVertex::kMaximumMargin);
EXPECT_FLOAT_EQ(bounds[2], 0.0f);
}

TEST_F(DerivativeCalculatorTest, ComputeTriangleMarginUpperBoundsConstrained) {
// Construct a triangle where vertex 0's outset ray crosses the bounding ray
// of vertex 1.
// . bounding ray of 1
// .
// 0 - - - - - - - - - - - - - o outset
// / \ .
// / \ .
// / \ .
// / \ .
// / . \
// / . \
// / . \
// /. \
// 1-----------------2

Triangle triangle = {.p0 = {0, 10}, .p1 = {-10, 0}, .p2 = {10, 0}};
std::array<Vec, 3> outset_vectors = {Vec{10, 0}, Vec{-10, 0}, Vec{10, 0}};

std::array<float, 3> bounds =
DerivativeCalculator::ComputeTriangleMarginUpperBounds(triangle,
outset_vectors);

// Vertex 0's margin is clamped by the intersection with the bounding ray
// radiating from p1 through edge p0-p2 (at ratio 2/9).
EXPECT_FLOAT_EQ(bounds[0], 2.0f / 9.0f);
}

} // namespace
} // namespace ink::brush_tip_extruder_internal
94 changes: 90 additions & 4 deletions ink/strokes/internal/stroke_subtraction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
namespace ink::strokes_internal {
namespace {

using AverageDerivative =
::ink::brush_tip_extruder_internal::DerivativeCalculator::AverageDerivative;
using ::ink::brush_tip_extruder_internal::DerivativeCalculator;
using AverageDerivative = DerivativeCalculator::AverageDerivative;
using ::ink::geometry_internal::ComputeSubtraction;
using ::ink::geometry_internal::ComputeTriangulation;
using ::ink::geometry_internal::Intersects;
Expand Down Expand Up @@ -230,10 +230,22 @@ bool IsRight(BoundaryLabel label) { return label % 3 == 2; }
bool IsFront(BoundaryLabel label) { return label / 3 == 1; }
bool IsBack(BoundaryLabel label) { return label / 3 == 2; }

float SideOutsetSign(BoundaryLabel label) {
if (IsLeft(label)) return -1.0f;
if (IsRight(label)) return 1.0f;
return 0.0f;
}

float ForwardOutsetSign(BoundaryLabel label) {
if (IsFront(label)) return -1.0f;
if (IsBack(label)) return 1.0f;
return 0.0f;
}

// Converts encoded float values to a BoundaryLabel enum value.
BoundaryLabel DecodeBoundaryLabel(float side, float fwd) {
int side_idx = (side > 0.5f) ? 2 : (side < -0.5f ? 1 : 0);
int fwd_idx = (fwd > 0.5f) ? 2 : (fwd < -0.5f ? 1 : 0);
int side_idx = (side >= 0.5f) ? 2 : (side <= -0.5f ? 1 : 0);
int fwd_idx = (fwd >= 0.5f) ? 2 : (fwd <= -0.5f ? 1 : 0);
return static_cast<BoundaryLabel>(3 * fwd_idx + side_idx);
}

Expand Down Expand Up @@ -465,6 +477,12 @@ class MeshBuilder {
return {sd[0], sd[1]};
}

Vec GetForwardDerivative(uint32_t vertex_index) const {
auto fd = mutable_mesh_.FloatVertexAttribute(
vertex_index, attr_indices_.forward_derivative);
return {fd[0], fd[1]};
}

void SetDerivatives(uint32_t vertex_index, Vec side, Vec forward) {
// Override zero vectors with a small non-zero value to avoid undefined
// zero-divided-by-zero in the rendering pipeline.
Expand All @@ -477,6 +495,20 @@ class MeshBuilder {
vertex_index, attr_indices_.forward_derivative, {forward.x, forward.y});
}

void SetMargins(uint32_t vertex_index, float side_margin,
float forward_margin) {
BoundaryLabel label = GetLabel(vertex_index);
auto [side, forward] = EncodeBoundaryLabel(label);
mutable_mesh_.SetFloatVertexAttribute(
vertex_index, attr_indices_.side_label,
{StrokeVertex::Label{side}.WithMargin(side_margin).encoded_value});
mutable_mesh_.SetFloatVertexAttribute(vertex_index,
attr_indices_.forward_label,
{StrokeVertex::Label{forward}
.WithMargin(forward_margin)
.encoded_value});
}

const MutableMesh& GetMesh() const { return mutable_mesh_; }

const auto& GetEdgeTriangleAdjacencyMap() const { return edge_tri_adj_map_; }
Expand Down Expand Up @@ -1006,6 +1038,59 @@ void ComputeAndSetDerivatives(MeshBuilder& mesh_builder) {
forward_derivative[i].Value());
}
}

// Computes anti-aliasing margins for all vertices in the mesh.
void ComputeAndSetMargins(MeshBuilder& mesh_builder) {
// Recall that margins are used during rendering to constrain anti-aliasing
// vertex outsets and prevent self-overlap and triangle inversion. See the
// comments in `StrokeVertex::Label` and the implementation of
// `DerivativeCalculator::ComputeTriangleMarginUpperBounds` for details.
//
// In this function, we recompute the margins for all vertices by iterating
// through all triangles in the mesh. Each triangle imposes an upper bound
// margin on its vertices. We compute these per-triangle constraints, and
// assign each vertex the tightest (minimum) bound across its incident
// triangles, and then write the margins to the mesh.
// TODO(b/521448869): Recompute margins only for vertices in a neighborhood
// of the subtracted area.
const MutableMesh& mesh = mesh_builder.GetMesh();
const uint32_t num_vertices = mesh.VertexCount();
std::vector<float> side_margins(num_vertices, StrokeVertex::kMaximumMargin);
std::vector<float> forward_margins(num_vertices,
StrokeVertex::kMaximumMargin);

for (uint32_t tri_idx = 0; tri_idx < mesh.TriangleCount(); ++tri_idx) {
std::array<uint32_t, 3> indices = mesh.TriangleIndices(tri_idx);
Triangle triangle = mesh.GetTriangle(tri_idx);

std::array<Vec, 3> side_outsets, forward_outsets;
for (int i = 0; i < 3; ++i) {
BoundaryLabel label = mesh_builder.GetLabel(indices[i]);
side_outsets[i] =
SideOutsetSign(label) * mesh_builder.GetSideDerivative(indices[i]);
forward_outsets[i] = ForwardOutsetSign(label) *
mesh_builder.GetForwardDerivative(indices[i]);
}

std::array<float, 3> side_bounds =
DerivativeCalculator::ComputeTriangleMarginUpperBounds(triangle,
side_outsets);
std::array<float, 3> forward_bounds =
DerivativeCalculator::ComputeTriangleMarginUpperBounds(triangle,
forward_outsets);

for (int i = 0; i < 3; ++i) {
side_margins[indices[i]] =
std::min(side_margins[indices[i]], side_bounds[i]);
forward_margins[indices[i]] =
std::min(forward_margins[indices[i]], forward_bounds[i]);
}
}

for (uint32_t i = 0; i < num_vertices; ++i) {
mesh_builder.SetMargins(i, side_margins[i], forward_margins[i]);
}
}
// LINT.ThenChange(
// //depot/google3/third_party/ink/strokes/internal/brush_tip_extruder/derivative_calculator.cc,
// //depot/google3/third_party/ink/rendering/skia/common_internal/sksl_vertex_shader_helper_functions.h:calculate_antialiasing_and_position_outset,
Expand Down Expand Up @@ -1100,6 +1185,7 @@ SubtractedMesh SubtractMeshes(absl::Span<const Mesh> meshes,
if (HasAntiAliasingAttributes(format) && anti_aliasing_enabled) {
ComputeAndSetLabels(outlines, sub_mesh);
ComputeAndSetDerivatives(sub_mesh);
ComputeAndSetMargins(sub_mesh);
}

return SubtractedMesh{
Expand Down
Loading
Loading