mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-10-05 08:27:22 +03:00
747 lines
24 KiB
Python
747 lines
24 KiB
Python
# coding: utf8
|
|
"""
|
|
weasyprint.layout.variable_margin_dimesion
|
|
------------------------------------------
|
|
|
|
Partial implementation of the Varibale Dimension algorithm
|
|
for margin boxes.
|
|
|
|
http://dev.w3.org/csswg/css3-page/#margin-dimension
|
|
|
|
:copyright: Copyright 2011-2012 Simon Sapin and contributors, see AUTHORS.
|
|
:license: BSD, see LICENSE for details.
|
|
|
|
"""
|
|
|
|
from __future__ import division, unicode_literals
|
|
|
|
|
|
def with_rule_2(side_boxes, outer_sum):
|
|
"""Margin box variable dimension computation, in the case where
|
|
rule 2 applies.
|
|
"""
|
|
box_a, box_b, box_c = side_boxes
|
|
seen = set()
|
|
while 1:
|
|
implementation, swap = IMPLEMENTATIONS[
|
|
tuple(box.inner == 'auto' for box in side_boxes),
|
|
tuple('auto' in [box.margin_a, box.margin_b] for box in side_boxes),
|
|
]
|
|
assert implementation not in seen # break loops
|
|
seen.add(implementation)
|
|
if swap:
|
|
box_a, box_c = swap_ac(box_a, box_c)
|
|
result = implementation(box_a, box_b, box_c, outer_sum)
|
|
if swap:
|
|
box_a, box_c = swap_ac(box_a, box_c)
|
|
|
|
if result == 'ok':
|
|
return
|
|
|
|
# XXX
|
|
if result is NotImplemented:
|
|
for box in side_boxes:
|
|
if box.margin_a == 'auto':
|
|
box.margin_a = 0
|
|
if box.margin_b == 'auto':
|
|
box.margin_b = 0
|
|
|
|
# Try again with less constraints
|
|
box_a.margin_b = 'auto'
|
|
box_c.margin_a = 'auto'
|
|
|
|
|
|
def swap_ac(box_a, box_c):
|
|
"""Swap margins A and B in each box, and return ``box_c, box_a``."""
|
|
# TODO: find a better way to do this?
|
|
for box in [box_a, box_c]:
|
|
box.margin_a, box.margin_b = box.margin_b, box.margin_a
|
|
return box_c, box_a
|
|
|
|
|
|
IMPLEMENTATIONS = {}
|
|
|
|
|
|
def register(auto_inners, auto_margins):
|
|
"""Register an implementation for with_rule_2()
|
|
|
|
:param auto_inners:
|
|
A tuple of 3 booleans. True if box A, B and C (respectively) have a
|
|
value of 'auto' for the inner dimension.
|
|
:param auto_margins:
|
|
A tuple of 3 booleans. True if the box has any margin with
|
|
the value 'auto'.
|
|
|
|
"""
|
|
def decorator(function):
|
|
key_1 = auto_inners, auto_margins
|
|
keys = [(key_1, False)]
|
|
# symmetry:
|
|
key_2 = auto_inners[::-1], auto_margins[::-1]
|
|
if key_2 != key_1:
|
|
keys.append((key_2, True))
|
|
for key, swap_ac in keys:
|
|
assert key not in IMPLEMENTATIONS, (
|
|
'redundant implementations for %r: %r' % (key, function))
|
|
IMPLEMENTATIONS[key] = function, swap_ac
|
|
return function
|
|
return decorator
|
|
|
|
|
|
def outer(box, ignore_auto=''):
|
|
"""Return the outer dimension of a box, or raise if any value is auto."""
|
|
result = box.padding_plus_border
|
|
for value, type_ in [
|
|
(box.inner, 'I'),
|
|
(box.margin_a, 'M'),
|
|
(box.margin_b, 'M'),
|
|
]:
|
|
if value == 'auto':
|
|
if type_ not in ignore_auto:
|
|
assert False
|
|
else:
|
|
result += value
|
|
return result
|
|
|
|
|
|
def distribute_margins(box, new_outer):
|
|
"""Set 'auto' margins on ``box`` so that the outer dimension
|
|
is ``new_outer``.
|
|
|
|
"""
|
|
num_auto = [box.margin_a, box.margin_b].count('auto')
|
|
# Raises if num_auto == 0:
|
|
each_auto = (new_outer - outer(box, ignore_auto='M')) / num_auto
|
|
if box.margin_a == 'auto':
|
|
box.margin_a = each_auto
|
|
if box.margin_b == 'auto':
|
|
box.margin_b = each_auto
|
|
|
|
|
|
def distribute_margins_and_inner(box, new_outer):
|
|
"""Set 'auto' values when both inner and some margins are 'auto'."""
|
|
remaining = new_outer - outer(box, ignore_auto='MI')
|
|
set_inner_within_range(box, remaining)
|
|
distribute_margins(box, new_outer)
|
|
|
|
|
|
def set_inner_within_range(box, optimal_inner):
|
|
"""Set ``box.inner`` as close to ``optimal_inner`` as possible with
|
|
range of its intrinsic bounds.
|
|
|
|
"""
|
|
min_inner = box.minimum
|
|
if optimal_inner < min_inner:
|
|
box.inner = min_inner
|
|
else:
|
|
max_inner = box.preferred
|
|
if optimal_inner > max_inner:
|
|
box.inner = max_inner
|
|
else:
|
|
box.inner = optimal_inner
|
|
|
|
|
|
@register(auto_inners=(0, 0, 0), auto_margins=(0, 0, 0))
|
|
def implementation_1(box_a, box_b, box_c, outer_sum):
|
|
# If the previous values matched the constraints,
|
|
# they will end up unchanged
|
|
box_a.margin_b = 'auto'
|
|
box_c.margin_a = 'auto'
|
|
target_outer_ac = (outer_sum - outer(box_b)) / 2
|
|
distribute_margins(box_a, target_outer_ac)
|
|
distribute_margins(box_c, target_outer_ac)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(0, 0, 1), auto_margins=(0, 0, 0))
|
|
def implementation_2(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_b = outer(box_b)
|
|
if outer_a == (outer_sum - outer_b) / 2: # Rules 1 and 2
|
|
outer_c = outer_a # Rule 2
|
|
inner_c = outer_c - outer(box_c, ignore_auto='I')
|
|
min_c = box_c.minimum
|
|
if inner_c >= min_c:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop that constraint.
|
|
box_c.inner = inner_c
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(0, 1, 0), auto_margins=(0, 0, 0))
|
|
def implementation_3(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer(box_c)
|
|
if outer_a == outer_c: # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
inner_b = outer_b - outer(box_b, ignore_auto='I')
|
|
min_b = box_b.minimum
|
|
if inner_b >= min_b:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop that constraint.
|
|
box_b.inner = inner_b
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(0, 1, 1), auto_margins=(0, 0, 0))
|
|
def implementation_4(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer_a # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
|
|
inner_c = outer_c - outer(box_c, ignore_auto='I')
|
|
inner_b = outer_b - outer(box_b, ignore_auto='I')
|
|
|
|
min_c = box_c.minimum
|
|
min_b = box_b.minimum
|
|
if inner_b >= min_b and inner_c >= min_c:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop these constraints.
|
|
box_b.inner = inner_b
|
|
box_c.inner = inner_c
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(1, 0, 1), auto_margins=(0, 0, 0))
|
|
def implementation_5(box_a, box_b, box_c, outer_sum):
|
|
outer_b = outer(box_b)
|
|
outer_ac = outer_sum - outer_b # Rule 1
|
|
outer_a = outer_ac / 2 # Rule 2
|
|
outer_c = outer_ac / 2 # Rule 2
|
|
|
|
inner_a = outer_a - outer(box_a, ignore_auto='I')
|
|
inner_c = outer_c - outer(box_c, ignore_auto='I')
|
|
|
|
min_a = box_a.minimum
|
|
min_c = box_c.minimum
|
|
if inner_a >= min_a and inner_c >= min_c:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop these constraints.
|
|
box_a.inner = inner_a
|
|
box_c.inner = inner_c
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(1, 1, 1), auto_margins=(0, 0, 0))
|
|
def implementation_6(box_a, box_b, box_c, outer_sum):
|
|
constants_a = outer(box_a, ignore_auto='I')
|
|
constants_b = outer(box_b, ignore_auto='I')
|
|
constants_c = outer(box_c, ignore_auto='I')
|
|
|
|
min_outer_a = box_a.minimum + constants_a
|
|
min_outer_b = box_b.minimum + constants_b
|
|
min_outer_c = box_c.minimum + constants_c
|
|
|
|
max_a = box_a.preferred
|
|
max_b = box_b.preferred
|
|
max_c = box_c.preferred
|
|
max_outer_a = max_a + constants_a
|
|
max_outer_b = max_b + constants_b
|
|
max_outer_c = max_c + constants_c
|
|
|
|
# Rule 2: Same outer for A and C, rule 2
|
|
# So their common min/max is the most restrictive
|
|
min_outer_ac = max(min_outer_a, min_outer_c)
|
|
max_outer_ac = min(max_outer_a, max_outer_c)
|
|
# Condition 1
|
|
if outer_sum >= (2 * min_outer_ac + min_outer_b):
|
|
weight_ac = max(max_a, max_c)
|
|
weight_b = max_b
|
|
total_weight = 2 * weight_ac + weight_b # Rule 1
|
|
if total_weight in (0, float('inf')):
|
|
normalized_weight_b = 1 / 3
|
|
else:
|
|
normalized_weight_b = weight_b / total_weight
|
|
inner_sum = outer_sum - constants_a - constants_b - constants_c
|
|
inner_b = inner_sum * normalized_weight_b
|
|
outer_b = inner_b + constants_b
|
|
|
|
new_max_outer_b = outer_sum - 2 * min_outer_ac
|
|
# min_outer_b <= max_outer_b is ensured by Condition 1
|
|
if (
|
|
outer_sum <= 2 * max_outer_ac + max_outer_b and
|
|
max_outer_a >= min_outer_c and
|
|
max_outer_c >= min_outer_a
|
|
):
|
|
# outer_ac <= max_outer_ac
|
|
# => (outer_sum - outer_b) / 2 <= max_outer_ac
|
|
# => outer_b >= outer_sum - 2 * max_outer_ac
|
|
min_outer_b = max(min_outer_b, outer_sum - 2 * max_outer_ac)
|
|
max_outer_b = min(max_outer_b, new_max_outer_b)
|
|
else:
|
|
# Drop max constraints
|
|
max_outer_b = new_max_outer_b
|
|
assert min_outer_b <= max_outer_b
|
|
outer_b = max(outer_b, min_outer_b)
|
|
outer_b = min(outer_b, max_outer_b)
|
|
# Rule 1 and 2
|
|
outer_ac = (outer_sum - outer_b) / 2
|
|
box_a.inner = outer_ac - constants_a
|
|
box_b.inner = outer_b - constants_b
|
|
box_c.inner = outer_ac - constants_c
|
|
|
|
return 'ok'
|
|
|
|
# Over-constrained by minimums.
|
|
# Margins that become 'auto' will end up negative.
|
|
# Choose inner so that margins are as small (close to 0) as possible.
|
|
box_a.inner = min_outer_ac - constants_a
|
|
box_b.inner = min_outer_b - constants_b
|
|
box_c.inner = min_outer_ac - constants_c
|
|
|
|
|
|
@register(auto_inners=(0, 0, 0), auto_margins=(0, 0, 1))
|
|
def implementation_7(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_b = outer(box_b)
|
|
if outer_a == (outer_sum - outer_b) / 2: # Rules 1 and 2
|
|
outer_c = outer_a # Rule 2
|
|
distribute_margins(box_c, outer_c)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(0, 0, 1), auto_margins=(0, 0, 1))
|
|
def implementation_8(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_b = outer(box_b)
|
|
if outer_a == (outer_sum - outer_b) / 2: # Rules 1 and 2
|
|
outer_c = outer_a # Rule 2
|
|
distribute_margins_and_inner(box_c, outer_c)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(0, 1, 0), auto_margins=(0, 0, 1))
|
|
def implementation_9(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer_a # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
|
|
inner_b = outer_b - outer(box_b, ignore_auto='I')
|
|
min_b = box_b.minimum
|
|
if inner_b >= min_b:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop that constraints.
|
|
box_b.inner = inner_b
|
|
distribute_margins(box_c, outer_c)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(0, 1, 1), auto_margins=(0, 0, 1))
|
|
def implementation_10(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer_a # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
|
|
inner_b = outer_b - outer(box_b, ignore_auto='I')
|
|
|
|
min_b = box_b.minimum
|
|
if inner_b >= min_b:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop these constraints.
|
|
box_b.inner = inner_b
|
|
distribute_margins_and_inner(box_c, outer_c)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(1, 0, 0), auto_margins=(0, 0, 1))
|
|
def implementation_11(box_a, box_b, box_c, outer_sum):
|
|
outer_b = outer(box_b)
|
|
outer_ac = outer_sum - outer_b # Rule 1
|
|
outer_a = outer_ac / 2 # Rule 2
|
|
outer_c = outer_ac / 2 # Rule 2
|
|
|
|
inner_a = outer_a - outer(box_a, ignore_auto='I')
|
|
min_a = box_a.minimum
|
|
if inner_a >= min_a:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop these constraints.
|
|
box_a.inner = inner_a
|
|
distribute_margins(box_c, outer_c)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(1, 0, 1), auto_margins=(0, 0, 1))
|
|
def implementation_12(box_a, box_b, box_c, outer_sum):
|
|
outer_b = outer(box_b)
|
|
outer_ac = outer_sum - outer_b # Rule 1
|
|
outer_a = outer_ac / 2 # Rule 2
|
|
outer_c = outer_ac / 2 # Rule 2
|
|
|
|
inner_a = outer_a - outer(box_a, ignore_auto='I')
|
|
min_a = box_a.minimum
|
|
if inner_a >= min_a:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop these constraints.
|
|
box_a.inner = inner_a
|
|
|
|
remaining_c = outer_c - outer(box_c, ignore_auto='MI')
|
|
set_inner_within_range(box_c, remaining_c)
|
|
distribute_margins(box_c, outer_c)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(1, 1, 0), auto_margins=(0, 0, 1))
|
|
def implementation_13(box_a, box_b, box_c, outer_sum):
|
|
constants_a = outer(box_a, ignore_auto='I')
|
|
constants_b = outer(box_b, ignore_auto='I')
|
|
constants_c = outer(box_c, ignore_auto='M')
|
|
|
|
min_outer_a = box_a.minimum + constants_a
|
|
min_outer_b = box_b.minimum + constants_b
|
|
|
|
# C can have any margin, so it is unconstrained other than rule 2.
|
|
if outer_sum >= min_outer_b + 2 * min_outer_a:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop these constraints.
|
|
|
|
# Rule 2: Same outer for A and C, rule 2
|
|
# So their common min/max is the most restrictive
|
|
min_outer_ac = min_outer_a
|
|
max_outer_ac = (outer_sum - min_outer_b) / 2
|
|
|
|
# The goal function is minimized (auto margins == 0) for this value:
|
|
optimal_outer_ac = constants_c
|
|
|
|
# Within bounds
|
|
outer_ac = min(max_outer_ac, max(min_outer_ac, optimal_outer_ac))
|
|
outer_b = outer_sum - 2 * outer_ac
|
|
|
|
box_a.inner = outer_ac - constants_a
|
|
box_b.inner = outer_b - constants_b
|
|
distribute_margins(box_c, outer_ac)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(1, 1, 1), auto_margins=(0, 0, 1))
|
|
def implementation_14(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(0, 0, 0), auto_margins=(0, 1, 0))
|
|
def implementation_15(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer(box_c)
|
|
if outer_a == outer_c: # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
distribute_margins(box_b, outer_b)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(0, 0, 1), auto_margins=(0, 1, 0))
|
|
def implementation_16(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer_a # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
|
|
inner_c = outer_c - outer(box_c, ignore_auto='I')
|
|
min_c = box_c.minimum
|
|
if inner_c >= min_c:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop these constraints.
|
|
box_c.inner = inner_c
|
|
distribute_margins(box_b, outer_b)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(0, 1, 0), auto_margins=(0, 1, 0))
|
|
def implementation_17(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer(box_c)
|
|
if outer_a == outer_c: # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
distribute_margins_and_inner(box_b, outer_b)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(0, 1, 1), auto_margins=(0, 1, 0))
|
|
def implementation_18(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer_a # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
|
|
inner_c = outer_c - outer(box_c, ignore_auto='I')
|
|
min_c = box_c.minimum
|
|
if inner_c >= min_c:
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop these constraints.
|
|
box_c.inner = inner_c
|
|
distribute_margins_and_inner(box_b, outer_b)
|
|
return 'ok'
|
|
# else: Over-constrained
|
|
|
|
|
|
@register(auto_inners=(1, 0, 1), auto_margins=(0, 1, 0))
|
|
def implementation_19(box_a, box_b, box_c, outer_sum):
|
|
constants_a = outer(box_a, ignore_auto='I')
|
|
constants_b = outer(box_b, ignore_auto='M')
|
|
constants_c = outer(box_c, ignore_auto='I')
|
|
|
|
min_outer_a = box_a.minimum + constants_a
|
|
min_outer_c = box_c.minimum + constants_c
|
|
|
|
# Ignore the preferred/maximum as the next best solution
|
|
# is to drop these constraints.
|
|
|
|
# Rule 2: Same outer for A and C, rule 2
|
|
# So their common min is the most restrictive
|
|
min_outer_ac = max(min_outer_a, min_outer_c)
|
|
|
|
# The goal function is minimized (auto margins == 0) for this value:
|
|
optimal_outer_ac = (outer_sum - constants_b) / 2
|
|
|
|
# Within bounds
|
|
outer_ac = max(min_outer_ac, optimal_outer_ac)
|
|
outer_b = outer_sum - 2 * outer_ac
|
|
|
|
box_a.inner = outer_ac - constants_a
|
|
box_c.inner = outer_ac - constants_c
|
|
distribute_margins(box_b, outer_b)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(1, 1, 1), auto_margins=(0, 1, 0))
|
|
def implementation_20(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(0, 0, 0), auto_margins=(0, 1, 1))
|
|
def implementation_21(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer_a # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
|
|
distribute_margins(box_b, outer_b)
|
|
distribute_margins(box_c, outer_c)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(0, 0, 1), auto_margins=(0, 1, 1))
|
|
def implementation_22(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer_a # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
|
|
distribute_margins(box_b, outer_b)
|
|
distribute_margins_and_inner(box_c, outer_c)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(0, 1, 0), auto_margins=(0, 1, 1))
|
|
def implementation_23(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer_a # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
|
|
distribute_margins_and_inner(box_b, outer_b)
|
|
distribute_margins(box_c, outer_c)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(0, 1, 1), auto_margins=(0, 1, 1))
|
|
def implementation_24(box_a, box_b, box_c, outer_sum):
|
|
outer_a = outer(box_a)
|
|
outer_c = outer_a # Rule 2
|
|
outer_b = outer_sum - outer_a - outer_c # Rule 1
|
|
|
|
distribute_margins_and_inner(box_b, outer_b)
|
|
distribute_margins_and_inner(box_c, outer_c)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(1, 0, 0), auto_margins=(0, 1, 1))
|
|
def implementation_25(box_a, box_b, box_c, outer_sum):
|
|
constants_a = outer(box_a, ignore_auto='I')
|
|
constants_b = outer(box_b, ignore_auto='M')
|
|
constants_c = outer(box_c, ignore_auto='M')
|
|
|
|
n_margins_b = [box_b.margin_a, box_b.margin_b].count('auto')
|
|
n_margins_c = [box_c.margin_a, box_c.margin_b].count('auto')
|
|
|
|
# Math:
|
|
# Use rule 1 and 2 to rewrite the goal function in terms of outer_ac
|
|
# The result is still quadratic but with only one variable:
|
|
# Goal = A * outer_ac**2 + B * outer_ac + C
|
|
# The minimum of a quadratic function is at
|
|
# outer_ac = -B / (2 * A)
|
|
optimal_outer_ac = (
|
|
constants_c / n_margins_c
|
|
+ 2 * (outer_sum - constants_b) / n_margins_b
|
|
) / (
|
|
1 / n_margins_c
|
|
+ 4 / n_margins_b
|
|
)
|
|
optimal_inner_a = optimal_outer_ac - constants_a
|
|
|
|
set_inner_within_range(box_a, optimal_inner_a)
|
|
|
|
outer_ac = box_a.inner + constants_a
|
|
outer_b = outer_sum - 2 * outer_ac
|
|
distribute_margins(box_b, outer_b)
|
|
distribute_margins(box_c, outer_ac)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(1, 0, 1), auto_margins=(0, 1, 1))
|
|
def implementation_26(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(1, 1, 0), auto_margins=(0, 1, 1))
|
|
def implementation_27(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(1, 1, 1), auto_margins=(0, 1, 1))
|
|
def implementation_28(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(0, 0, 0), auto_margins=(1, 0, 1))
|
|
def implementation_29(box_a, box_b, box_c, outer_sum):
|
|
new_outer_ac = (outer_sum - outer(box_b)) / 2
|
|
distribute_margins(box_a, new_outer_ac)
|
|
distribute_margins(box_c, new_outer_ac)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(0, 0, 1), auto_margins=(1, 0, 1))
|
|
def implementation_30(box_a, box_b, box_c, outer_sum):
|
|
outer_b = outer(box_b)
|
|
outer_ac = outer_sum - outer_b # Rule 1
|
|
outer_a = outer_ac / 2 # Rule 2
|
|
outer_c = outer_ac / 2 # Rule 2
|
|
|
|
distribute_margins(box_a, outer_a)
|
|
distribute_margins_and_inner(box_c, outer_c)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(0, 1, 0), auto_margins=(1, 0, 1))
|
|
def implementation_31(box_a, box_b, box_c, outer_sum):
|
|
constants_a = outer(box_a, ignore_auto='M')
|
|
constants_b = outer(box_b, ignore_auto='I')
|
|
constants_c = outer(box_c, ignore_auto='M')
|
|
|
|
n_margins_a = [box_a.margin_a, box_a.margin_b].count('auto')
|
|
n_margins_c = [box_c.margin_a, box_c.margin_b].count('auto')
|
|
|
|
# Math:
|
|
# Use rule 1 and 2 to rewrite the goal function in terms of outer_ac
|
|
# The result is still quadratic but with only one variable:
|
|
# Goal = A * outer_ac**2 + B * outer_ac + C
|
|
# The minimum of a quadratic function is at
|
|
# outer_ac = -B / (2 * A)
|
|
optimal_outer_ac = (
|
|
constants_a / n_margins_a
|
|
+ constants_c / n_margins_c
|
|
) / (
|
|
1 / n_margins_a
|
|
+ 1 / n_margins_c
|
|
)
|
|
optimal_outer_b = outer_sum - 2 * optimal_outer_ac
|
|
optimal_inner_b = optimal_outer_b - constants_b
|
|
|
|
set_inner_within_range(box_b, optimal_inner_b)
|
|
|
|
outer_b = box_b.inner + constants_b
|
|
outer_ac = (outer_sum - outer_b) / 2
|
|
distribute_margins(box_a, outer_ac)
|
|
distribute_margins(box_c, outer_ac)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(0, 1, 1), auto_margins=(1, 0, 1))
|
|
def implementation_32(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(1, 0, 1), auto_margins=(1, 0, 1))
|
|
def implementation_33(box_a, box_b, box_c, outer_sum):
|
|
outer_b = outer(box_b)
|
|
outer_ac = outer_sum - outer_b # Rule 1
|
|
outer_a = outer_ac / 2 # Rule 2
|
|
outer_c = outer_ac / 2 # Rule 2
|
|
|
|
distribute_margins_and_inner(box_a, outer_a)
|
|
distribute_margins_and_inner(box_c, outer_c)
|
|
return 'ok'
|
|
|
|
|
|
@register(auto_inners=(1, 1, 1), auto_margins=(1, 0, 1))
|
|
def implementation_34(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(0, 0, 0), auto_margins=(1, 1, 1))
|
|
def implementation_35(box_a, box_b, box_c, outer_sum):
|
|
constants_a = outer(box_a, ignore_auto='M')
|
|
constants_b = outer(box_b, ignore_auto='M')
|
|
constants_c = outer(box_c, ignore_auto='M')
|
|
|
|
n_margins_a = [box_a.margin_a, box_a.margin_b].count('auto')
|
|
n_margins_b = [box_b.margin_a, box_b.margin_b].count('auto')
|
|
n_margins_c = [box_c.margin_a, box_c.margin_b].count('auto')
|
|
|
|
# Math:
|
|
# Use rule 1 and 2 to rewrite the goal function in terms of outer_ac
|
|
# The result is still quadratic but with only one variable:
|
|
# Goal = A * outer_ac**2 + B * outer_ac + C
|
|
# The minimum of a quadratic function is at
|
|
# outer_ac = -B / (2 * A)
|
|
outer_ac = (
|
|
constants_a / n_margins_a
|
|
+ constants_c / n_margins_c
|
|
+ 2 * (outer_sum - constants_b) / n_margins_b
|
|
) / (
|
|
1 / n_margins_a
|
|
+ 1 / n_margins_b
|
|
+ 1 / n_margins_c
|
|
)
|
|
outer_b = outer_sum - 2 * outer_ac
|
|
distribute_margins(box_a, outer_ac)
|
|
distribute_margins(box_b, outer_b)
|
|
distribute_margins(box_c, outer_ac)
|
|
return 'ok'
|
|
|
|
|
|
|
|
@register(auto_inners=(0, 0, 1), auto_margins=(1, 1, 1))
|
|
def implementation_36(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(0, 1, 0), auto_margins=(1, 1, 1))
|
|
def implementation_37(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(0, 1, 1), auto_margins=(1, 1, 1))
|
|
def implementation_38(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(1, 0, 1), auto_margins=(1, 1, 1))
|
|
def implementation_39(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
|
|
@register(auto_inners=(1, 1, 1), auto_margins=(1, 1, 1))
|
|
def implementation_40(box_a, box_b, box_c, outer_sum):
|
|
return NotImplemented
|
|
|
|
assert len(IMPLEMENTATIONS) == 64
|