leibniz 0.1.0

The package provides a differentiable vector graphics rasterization loss.
Documentation
//! Quadratic winding contributions.

use ::burn::tensor::{Bool, Tensor, backend::Backend};

use crate::base::geometry::Indices;

use super::super::{contour, roots};
use super::{Values, segment_coordinate};

type Coefficients<B> = (Values<B>, Values<B>, Values<B>);

pub fn coefficients<B: Backend>(
    arguments: contour::Arguments<B>,
    indices: Indices,
) -> (Coefficients<B>, Coefficients<B>) {
    let start = indices.start();
    let control = indices.control();
    let end = indices.end();
    let x = point_coefficients(
        segment_coordinate(arguments.clone(), start, 0),
        segment_coordinate(arguments.clone(), control, 0),
        segment_coordinate(arguments.clone(), end, 0),
    );
    let y = point_coefficients(
        segment_coordinate(arguments.clone(), start, 1),
        segment_coordinate(arguments.clone(), control, 1),
        segment_coordinate(arguments, end, 1),
    );

    (x, y)
}

pub fn evaluate<B: Backend>(
    x_coefficients: Coefficients<B>,
    y_coefficients: Coefficients<B>,
    x: Values<B>,
    y: Values<B>,
) -> Values<B> {
    let (x2, x1, x0) = x_coefficients;
    let (y2, y1, y0) = y_coefficients;
    let roots = roots::quadratic::solve(y2.clone(), y1.clone(), y0.clone() - y);
    let x_offset = x - x0.clone();
    let linear_sign = y1.greater_elem(0.0).float() * 2.0 - 1.0;
    let quadratic_sign = linear_sign.ones_like() * -1.0;
    let first_sign = linear_sign.mask_where(roots.valid_quadratic0.clone(), quadratic_sign);
    let first_valid = roots.valid_linear.bool_or(roots.valid_quadratic0);

    let first = root_contribution(
        roots.t0,
        first_valid,
        x_offset.clone(),
        (x2.clone(), x1.clone()),
        first_sign,
    );
    // `valid_quadratic1` only covers the second quadratic root, whose y
    // derivative is positive, so its winding sign is always +1.
    let second = positive_root_contribution(roots.t1, roots.valid_quadratic1, x_offset, (x2, x1));

    first + second
}

fn evaluate_polynomial_without_constant<B: Backend>(
    (a, b): (Values<B>, Values<B>),
    t: Values<B>,
) -> Values<B> {
    (a * t.clone() + b) * t
}

fn point_coefficients<B: Backend>(p0: Values<B>, p1: Values<B>, p2: Values<B>) -> Coefficients<B> {
    (
        p0.clone() - p1.clone() * 2.0 + p2,
        -p0.clone() * 2.0 + p1 * 2.0,
        p0,
    )
}

fn positive_root_contribution<B: Backend>(
    t: Values<B>,
    valid: Tensor<B, 1, Bool>,
    x_offset: Values<B>,
    x_coefficients: (Values<B>, Values<B>),
) -> Values<B> {
    let curve_x = evaluate_polynomial_without_constant(x_coefficients, t.clone());
    let valid = valid
        .bool_and(t.clone().greater_equal_elem(0.0))
        .bool_and(t.lower_equal_elem(1.0))
        .bool_and(curve_x.greater(x_offset));

    valid.float()
}

fn root_contribution<B: Backend>(
    t: Values<B>,
    valid: Tensor<B, 1, Bool>,
    x_offset: Values<B>,
    x_coefficients: (Values<B>, Values<B>),
    sign: Values<B>,
) -> Values<B> {
    let curve_x = evaluate_polynomial_without_constant(x_coefficients, t.clone());
    let valid = valid
        .bool_and(t.clone().greater_equal_elem(0.0))
        .bool_and(t.lower_equal_elem(1.0))
        .bool_and(curve_x.greater(x_offset));

    valid.float() * sign
}