mirror of
https://github.com/servo/servo.git
synced 2025-08-07 22:45:34 +01:00
style: Add negate node to use in place of mul_by in sum nodes
Sum nodes would use mul_by to negate nodes to do subtraction, but some nodes are not distributive. This patch adds a negate node, so that the operations inside these negate nodes can be resolved first and then the "subtraction" can be applied. Differential Revision: https://phabricator.services.mozilla.com/D172941
This commit is contained in:
parent
9fd6f09a41
commit
a6b97563c3
2 changed files with 163 additions and 28 deletions
|
@ -9,7 +9,7 @@
|
||||||
use num_traits::{Float, Zero};
|
use num_traits::{Float, Zero};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use std::ops::{Add, Div, Mul, Rem, Sub};
|
use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
|
||||||
use std::{cmp, mem};
|
use std::{cmp, mem};
|
||||||
use style_traits::{CssWriter, ToCss};
|
use style_traits::{CssWriter, ToCss};
|
||||||
|
|
||||||
|
@ -161,6 +161,8 @@ pub enum SortKey {
|
||||||
pub enum GenericCalcNode<L> {
|
pub enum GenericCalcNode<L> {
|
||||||
/// A leaf node.
|
/// A leaf node.
|
||||||
Leaf(L),
|
Leaf(L),
|
||||||
|
/// A node that negates its children, e.g. Negate(1) == -1.
|
||||||
|
Negate(Box<GenericCalcNode<L>>),
|
||||||
/// A sum node, representing `a + b + c` where a, b, and c are the
|
/// A sum node, representing `a + b + c` where a, b, and c are the
|
||||||
/// arguments.
|
/// arguments.
|
||||||
Sum(crate::OwnedSlice<GenericCalcNode<L>>),
|
Sum(crate::OwnedSlice<GenericCalcNode<L>>),
|
||||||
|
@ -247,10 +249,80 @@ pub trait CalcNodeLeaf: Clone + Sized + PartialOrd + PartialEq + ToCss {
|
||||||
fn sort_key(&self) -> SortKey;
|
fn sort_key(&self) -> SortKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The level of any argument being serialized in `to_css_impl`.
|
||||||
|
enum ArgumentLevel {
|
||||||
|
/// The root of a calculation tree.
|
||||||
|
CalculationRoot,
|
||||||
|
/// The root of an operand node's argument, e.g. `min(10, 20)`, `10` and `20` will have this
|
||||||
|
/// level, but min in this case will have `TopMost`.
|
||||||
|
ArgumentRoot,
|
||||||
|
/// Any other values serialized in the tree.
|
||||||
|
Nested,
|
||||||
|
}
|
||||||
|
|
||||||
impl<L: CalcNodeLeaf> CalcNode<L> {
|
impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
/// Negates the leaf.
|
/// Negate the node inline. If the node is distributive, it is replaced by the result,
|
||||||
fn negate(&mut self) {
|
/// otherwise the node is wrapped in a [`Negate`] node.
|
||||||
self.map(std::ops::Neg::neg);
|
pub fn negate(&mut self) {
|
||||||
|
match *self {
|
||||||
|
CalcNode::Leaf(ref mut leaf) => leaf.map(|l| l.neg()),
|
||||||
|
CalcNode::Negate(ref mut value) => {
|
||||||
|
// Don't negate the value here. Replace `self` with it's child.
|
||||||
|
let result = mem::replace(
|
||||||
|
value.as_mut(),
|
||||||
|
Self::MinMax(Default::default(), MinMaxOp::Max),
|
||||||
|
);
|
||||||
|
*self = result;
|
||||||
|
},
|
||||||
|
CalcNode::Sum(ref mut children) => {
|
||||||
|
for child in children.iter_mut() {
|
||||||
|
child.negate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CalcNode::MinMax(ref mut children, ref mut op) => {
|
||||||
|
for child in children.iter_mut() {
|
||||||
|
child.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negating min-max means the operation is swapped.
|
||||||
|
*op = match *op {
|
||||||
|
MinMaxOp::Min => MinMaxOp::Max,
|
||||||
|
MinMaxOp::Max => MinMaxOp::Min,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
CalcNode::Clamp {
|
||||||
|
ref mut min,
|
||||||
|
ref mut center,
|
||||||
|
ref mut max,
|
||||||
|
} => {
|
||||||
|
min.negate();
|
||||||
|
center.negate();
|
||||||
|
max.negate();
|
||||||
|
|
||||||
|
mem::swap(min, max);
|
||||||
|
},
|
||||||
|
CalcNode::Round {
|
||||||
|
ref mut value,
|
||||||
|
ref mut step,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
value.negate();
|
||||||
|
step.negate();
|
||||||
|
},
|
||||||
|
CalcNode::ModRem {
|
||||||
|
ref mut dividend,
|
||||||
|
ref mut divisor,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
dividend.negate();
|
||||||
|
divisor.negate();
|
||||||
|
},
|
||||||
|
CalcNode::Hypot(ref mut children) => {
|
||||||
|
for child in children.iter_mut() {
|
||||||
|
child.negate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_key(&self) -> SortKey {
|
fn sort_key(&self) -> SortKey {
|
||||||
|
@ -296,6 +368,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
fn map_internal<L: CalcNodeLeaf>(node: &mut CalcNode<L>, op: &mut impl FnMut(f32) -> f32) {
|
fn map_internal<L: CalcNodeLeaf>(node: &mut CalcNode<L>, op: &mut impl FnMut(f32) -> f32) {
|
||||||
match node {
|
match node {
|
||||||
CalcNode::Leaf(l) => l.map(op),
|
CalcNode::Leaf(l) => l.map(op),
|
||||||
|
CalcNode::Negate(v) => map_internal(v, op),
|
||||||
CalcNode::Sum(children) => {
|
CalcNode::Sum(children) => {
|
||||||
for node in &mut **children {
|
for node in &mut **children {
|
||||||
map_internal(node, op);
|
map_internal(node, op);
|
||||||
|
@ -363,6 +436,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Self::Leaf(ref l) => CalcNode::Leaf(map(l)),
|
Self::Leaf(ref l) => CalcNode::Leaf(map(l)),
|
||||||
|
Self::Negate(ref c) => CalcNode::Negate(Box::new(c.map_leaves_internal(map))),
|
||||||
Self::Sum(ref c) => CalcNode::Sum(map_children(c, map)),
|
Self::Sum(ref c) => CalcNode::Sum(map_children(c, map)),
|
||||||
Self::MinMax(ref c, op) => CalcNode::MinMax(map_children(c, map), op),
|
Self::MinMax(ref c, op) => CalcNode::MinMax(map_children(c, map), op),
|
||||||
Self::Clamp {
|
Self::Clamp {
|
||||||
|
@ -440,6 +514,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
{
|
{
|
||||||
Ok(match *self {
|
Ok(match *self {
|
||||||
Self::Leaf(ref l) => return leaf_to_output_fn(l),
|
Self::Leaf(ref l) => return leaf_to_output_fn(l),
|
||||||
|
Self::Negate(ref c) => c.resolve_internal(leaf_to_output_fn)?.neg(),
|
||||||
Self::Sum(ref c) => {
|
Self::Sum(ref c) => {
|
||||||
let mut result = Zero::zero();
|
let mut result = Zero::zero();
|
||||||
for child in &**c {
|
for child in &**c {
|
||||||
|
@ -631,6 +706,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
pub fn mul_by(&mut self, scalar: f32) {
|
pub fn mul_by(&mut self, scalar: f32) {
|
||||||
match *self {
|
match *self {
|
||||||
Self::Leaf(ref mut l) => l.map(|v| v * scalar),
|
Self::Leaf(ref mut l) => l.map(|v| v * scalar),
|
||||||
|
Self::Negate(ref mut value) => value.mul_by(scalar),
|
||||||
// Multiplication is distributive across this.
|
// Multiplication is distributive across this.
|
||||||
Self::Sum(ref mut children) => {
|
Self::Sum(ref mut children) => {
|
||||||
for node in &mut **children {
|
for node in &mut **children {
|
||||||
|
@ -733,6 +809,9 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
child.visit_depth_first_internal(f);
|
child.visit_depth_first_internal(f);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Self::Negate(ref mut value) => {
|
||||||
|
value.visit_depth_first_internal(f);
|
||||||
|
},
|
||||||
Self::Leaf(..) => {},
|
Self::Leaf(..) => {},
|
||||||
}
|
}
|
||||||
f(self);
|
f(self);
|
||||||
|
@ -746,6 +825,8 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
/// This is only needed if it's going to be preserved after parsing (so, for
|
/// This is only needed if it's going to be preserved after parsing (so, for
|
||||||
/// `<length-percentage>`). Otherwise we can just evaluate it using
|
/// `<length-percentage>`). Otherwise we can just evaluate it using
|
||||||
/// `resolve()`, and we'll come up with a simplified value anyways.
|
/// `resolve()`, and we'll come up with a simplified value anyways.
|
||||||
|
///
|
||||||
|
/// <https://drafts.csswg.org/css-values-4/#calc-simplification>
|
||||||
pub fn simplify_and_sort_direct_children(&mut self) {
|
pub fn simplify_and_sort_direct_children(&mut self) {
|
||||||
macro_rules! replace_self_with {
|
macro_rules! replace_self_with {
|
||||||
($slot:expr) => {{
|
($slot:expr) => {{
|
||||||
|
@ -1062,6 +1143,24 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
|
|
||||||
replace_self_with!(&mut result);
|
replace_self_with!(&mut result);
|
||||||
},
|
},
|
||||||
|
Self::Negate(ref mut child) => {
|
||||||
|
// Step 6.
|
||||||
|
match &mut **child {
|
||||||
|
CalcNode::Leaf(_) => {
|
||||||
|
// 1. If root’s child is a numeric value, return an equivalent numeric value, but
|
||||||
|
// with the value negated (0 - value).
|
||||||
|
child.negate();
|
||||||
|
replace_self_with!(&mut **child);
|
||||||
|
},
|
||||||
|
CalcNode::Negate(value) => {
|
||||||
|
// 2. If root’s child is a Negate node, return the child’s child.
|
||||||
|
replace_self_with!(&mut **value);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
// 3. Return root.
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
Self::Leaf(ref mut l) => {
|
Self::Leaf(ref mut l) => {
|
||||||
l.simplify();
|
l.simplify();
|
||||||
},
|
},
|
||||||
|
@ -1073,7 +1172,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
self.visit_depth_first(|node| node.simplify_and_sort_direct_children())
|
self.visit_depth_first(|node| node.simplify_and_sort_direct_children())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_css_impl<W>(&self, dest: &mut CssWriter<W>, is_outermost: bool) -> fmt::Result
|
fn to_css_impl<W>(&self, dest: &mut CssWriter<W>, level: ArgumentLevel) -> fmt::Result
|
||||||
where
|
where
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
|
@ -1111,11 +1210,34 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
dest.write_str("hypot(")?;
|
dest.write_str("hypot(")?;
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
_ => {
|
Self::Negate(_) => {
|
||||||
if is_outermost {
|
// We never generate a [`Negate`] node as the root of a calculation, only inside
|
||||||
|
// [`Sum`] nodes as a child. Because negate nodes are handled by the [`Sum`] node
|
||||||
|
// directly (see below), this node will never be serialized.
|
||||||
|
debug_assert!(
|
||||||
|
false,
|
||||||
|
"We never serialize Negate nodes as they are handled inside Sum nodes."
|
||||||
|
);
|
||||||
|
dest.write_str("(-1 * ")?;
|
||||||
|
true
|
||||||
|
},
|
||||||
|
Self::Sum(_) => match level {
|
||||||
|
ArgumentLevel::CalculationRoot => {
|
||||||
dest.write_str("calc(")?;
|
dest.write_str("calc(")?;
|
||||||
}
|
true
|
||||||
is_outermost
|
},
|
||||||
|
ArgumentLevel::ArgumentRoot => false,
|
||||||
|
ArgumentLevel::Nested => {
|
||||||
|
dest.write_str("(")?;
|
||||||
|
true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Self::Leaf(_) => match level {
|
||||||
|
ArgumentLevel::CalculationRoot => {
|
||||||
|
dest.write_str("calc(")?;
|
||||||
|
true
|
||||||
|
},
|
||||||
|
ArgumentLevel::ArgumentRoot | ArgumentLevel::Nested => false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1127,25 +1249,38 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
dest.write_str(", ")?;
|
dest.write_str(", ")?;
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
child.to_css_impl(dest, false)?;
|
child.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Self::Negate(ref value) => value.to_css_impl(dest, ArgumentLevel::Nested)?,
|
||||||
Self::Sum(ref children) => {
|
Self::Sum(ref children) => {
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for child in &**children {
|
for child in &**children {
|
||||||
if !first {
|
if !first {
|
||||||
if child.is_negative_leaf() {
|
match child {
|
||||||
|
Self::Leaf(l) => {
|
||||||
|
if l.is_negative() {
|
||||||
dest.write_str(" - ")?;
|
dest.write_str(" - ")?;
|
||||||
let mut c = child.clone();
|
let mut negated = l.clone();
|
||||||
c.negate();
|
negated.negate();
|
||||||
c.to_css_impl(dest, false)?;
|
negated.to_css(dest)?;
|
||||||
} else {
|
} else {
|
||||||
dest.write_str(" + ")?;
|
dest.write_str(" + ")?;
|
||||||
child.to_css_impl(dest, false)?;
|
l.to_css(dest)?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::Negate(n) => {
|
||||||
|
dest.write_str(" - ")?;
|
||||||
|
n.to_css_impl(dest, ArgumentLevel::Nested)?;
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
dest.write_str(" + ")?;
|
||||||
|
child.to_css_impl(dest, ArgumentLevel::Nested)?;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
first = false;
|
first = false;
|
||||||
child.to_css_impl(dest, false)?;
|
child.to_css_impl(dest, ArgumentLevel::Nested)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1154,29 +1289,29 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
ref center,
|
ref center,
|
||||||
ref max,
|
ref max,
|
||||||
} => {
|
} => {
|
||||||
min.to_css_impl(dest, false)?;
|
min.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
|
||||||
dest.write_str(", ")?;
|
dest.write_str(", ")?;
|
||||||
center.to_css_impl(dest, false)?;
|
center.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
|
||||||
dest.write_str(", ")?;
|
dest.write_str(", ")?;
|
||||||
max.to_css_impl(dest, false)?;
|
max.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
|
||||||
},
|
},
|
||||||
Self::Round {
|
Self::Round {
|
||||||
ref value,
|
ref value,
|
||||||
ref step,
|
ref step,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
value.to_css_impl(dest, false)?;
|
value.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
|
||||||
dest.write_str(", ")?;
|
dest.write_str(", ")?;
|
||||||
step.to_css_impl(dest, false)?;
|
step.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
|
||||||
},
|
},
|
||||||
Self::ModRem {
|
Self::ModRem {
|
||||||
ref dividend,
|
ref dividend,
|
||||||
ref divisor,
|
ref divisor,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
dividend.to_css_impl(dest, false)?;
|
dividend.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
|
||||||
dest.write_str(", ")?;
|
dest.write_str(", ")?;
|
||||||
divisor.to_css_impl(dest, false)?;
|
divisor.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
|
||||||
},
|
},
|
||||||
Self::Leaf(ref l) => l.to_css(dest)?,
|
Self::Leaf(ref l) => l.to_css(dest)?,
|
||||||
}
|
}
|
||||||
|
@ -1203,6 +1338,6 @@ impl<L: CalcNodeLeaf> ToCss for CalcNode<L> {
|
||||||
where
|
where
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
self.to_css_impl(dest, /* is_outermost = */ true)
|
self.to_css_impl(dest, ArgumentLevel::CalculationRoot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -724,7 +724,7 @@ impl CalcNode {
|
||||||
},
|
},
|
||||||
Token::Delim('-') => {
|
Token::Delim('-') => {
|
||||||
let mut rhs = Self::parse_product(context, input, allowed_units)?;
|
let mut rhs = Self::parse_product(context, input, allowed_units)?;
|
||||||
rhs.mul_by(-1.0);
|
rhs.negate();
|
||||||
sum.push(rhs);
|
sum.push(rhs);
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue