Make Constraint a protocol

So constraint and multi-constraint can be interchangeable.
This commit is contained in:
Arjan Molenaar 2020-12-11 22:59:30 +01:00
parent 447279d823
commit 81a76750e6
No known key found for this signature in database
GPG Key ID: BF977B918996CB13
7 changed files with 40 additions and 33 deletions

View File

@ -100,9 +100,7 @@ class Port:
"""Get glue point on the port and distance to the port."""
raise NotImplementedError("Glue method not implemented")
def constraint(
self, item: Item, handle: Handle, glue_item: Item
) -> Union[Constraint, MultiConstraint]:
def constraint(self, item: Item, handle: Handle, glue_item: Item) -> Constraint:
"""Create connection constraint between item's handle and glue item."""
raise NotImplementedError("Constraint method not implemented")
@ -131,9 +129,7 @@ class LinePort(Port):
)
return pl, d
def constraint(
self, item: Item, handle: Handle, glue_item: Item
) -> Union[Constraint, MultiConstraint]:
def constraint(self, item: Item, handle: Handle, glue_item: Item) -> Constraint:
"""Create connection line constraint between item's handle and the
port."""
start = MatrixProjection(self.start, glue_item.matrix_i2c)

View File

@ -31,7 +31,7 @@ import math
from typing import Dict, Optional, Tuple
from gaphas.position import Position
from gaphas.solver import Constraint, Variable
from gaphas.solver import BaseConstraint, Constraint, Variable
log = logging.getLogger(__name__)
@ -102,7 +102,7 @@ def _update(variable, value):
variable.value = value
class EqualsConstraint(Constraint):
class EqualsConstraint(BaseConstraint):
"""Constraint, which ensures that two arguments ``a`` and ``b`` are equal:
a + delta = b
@ -140,7 +140,7 @@ class EqualsConstraint(Constraint):
)
class CenterConstraint(Constraint):
class CenterConstraint(BaseConstraint):
"""Simple Constraint, takes three arguments: 'a', 'b' and center. When
solved, the constraint ensures 'center' is located in the middle of 'a' and
'b'.
@ -174,7 +174,7 @@ class CenterConstraint(Constraint):
_update(self.center, v)
class LessThanConstraint(Constraint):
class LessThanConstraint(BaseConstraint):
"""Ensure ``smaller`` is less than ``bigger``. The variable that is passed
as to-be-solved is left alone (cause it is the variable that has not been
moved lately). Instead the other variable is solved.
@ -219,7 +219,7 @@ class LessThanConstraint(Constraint):
ITERLIMIT = 1000 # iteration limit
class EquationConstraint(Constraint):
class EquationConstraint(BaseConstraint):
"""Equation solver using attributes and introspection.
Takes a function, named arg value (opt.) and returns a Constraint
@ -349,7 +349,7 @@ class EquationConstraint(Constraint):
return x1
class BalanceConstraint(Constraint):
class BalanceConstraint(BaseConstraint):
"""Ensure that a variable ``v`` is between values specified by ``band`` and
in distance proportional from ``band[0]``.
@ -398,7 +398,7 @@ class BalanceConstraint(Constraint):
_update(var, value)
class LineConstraint(Constraint):
class LineConstraint(BaseConstraint):
"""Ensure a point is kept on a line.
Attributes:
@ -476,7 +476,7 @@ class LineConstraint(Constraint):
_update(py, y)
class PositionConstraint(Constraint):
class PositionConstraint(BaseConstraint):
"""Ensure that point is always in origin position.
Attributes:
@ -498,7 +498,7 @@ class PositionConstraint(Constraint):
_update(self._point[1], y)
class LineAlignConstraint(Constraint):
class LineAlignConstraint(BaseConstraint):
"""Ensure a point is kept on a line in position specified by align and
padding information.

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from typing import SupportsFloat, Tuple, Union
from gaphas.matrix import Matrix
from gaphas.solver import NORMAL, Constraint, Variable
from gaphas.solver import NORMAL, BaseConstraint, Variable
from gaphas.types import SupportsFloatPos, TypedProperty
@ -76,7 +76,7 @@ class Position:
return self[0] < other[0] and self[1] < other[1]
class MatrixProjection(Constraint):
class MatrixProjection(BaseConstraint):
def __init__(self, pos: Position, matrix: Matrix):
proj_pos = Position(0, 0, pos.strength)
super().__init__(proj_pos.x, proj_pos.y, pos.x, pos.y)

View File

@ -1,4 +1,4 @@
from gaphas.solver.constraint import Constraint, MultiConstraint
from gaphas.solver.constraint import BaseConstraint, Constraint, MultiConstraint
from gaphas.solver.solver import JuggleError, Solver
from gaphas.solver.variable import (
NORMAL,

View File

@ -2,10 +2,23 @@ from __future__ import annotations
from typing import Callable, Set
from typing_extensions import Protocol
from gaphas.solver.variable import Variable
class Constraint:
class Constraint(Protocol):
def add_handler(self, handler: Callable[[Constraint], None]) -> None:
...
def remove_handler(self, handler: Callable[[Constraint], None]) -> None:
...
def solve(self) -> None:
...
class BaseConstraint:
"""Constraint base class.
- variables - list of all variables

View File

@ -32,9 +32,9 @@ every constraint is being asked to solve itself
(`constraint.Constraint.solve_for()` method) changing appropriate
variables to make the constraint valid again.
"""
from typing import Collection, List, Set, Union
from typing import Collection, List, Set
from gaphas.solver.constraint import Constraint, MultiConstraint
from gaphas.solver.constraint import Constraint
from gaphas.state import observed, reversible_pair
@ -46,18 +46,16 @@ class Solver:
def __init__(self) -> None:
# a dict of constraint -> name/variable mappings
self._constraints: Set[Union[Constraint, MultiConstraint]] = set()
self._marked_cons: List[Union[Constraint, MultiConstraint]] = []
self._constraints: Set[Constraint] = set()
self._marked_cons: List[Constraint] = []
self._solving = False
@property
def constraints(self) -> Collection[Union[Constraint, MultiConstraint]]:
def constraints(self) -> Collection[Constraint]:
return self._constraints
@observed
def add_constraint(
self, constraint: Union[Constraint, MultiConstraint]
) -> Union[Constraint, MultiConstraint]:
def add_constraint(self, constraint: Constraint) -> Constraint:
"""Add a constraint. The actual constraint is returned, so the
constraint can be removed later on.
@ -84,7 +82,7 @@ class Solver:
return constraint
@observed
def remove_constraint(self, constraint: Union[Constraint, MultiConstraint]) -> None:
def remove_constraint(self, constraint: Constraint) -> None:
"""Remove a constraint from the solver.
>>> from gaphas.constraint import EquationConstraint

View File

@ -1,6 +1,6 @@
import pytest
from gaphas.solver import Constraint, MultiConstraint, Variable
from gaphas.solver import BaseConstraint, MultiConstraint, Variable
@pytest.fixture
@ -16,7 +16,7 @@ def handler():
def test_constraint_propagates_variable_changed(handler):
v = Variable()
c = Constraint(v)
c = BaseConstraint(v)
c.add_handler(handler)
v.value = 3
@ -26,7 +26,7 @@ def test_constraint_propagates_variable_changed(handler):
def test_multi_constraint(handler):
v = Variable()
c = Constraint(v)
c = BaseConstraint(v)
m = MultiConstraint(c)
m.add_handler(handler)
@ -37,7 +37,7 @@ def test_multi_constraint(handler):
def test_default_constraint_can_not_solve():
v = Variable()
c = Constraint(v)
c = BaseConstraint(v)
with pytest.raises(NotImplementedError):
c.solve()
@ -45,7 +45,7 @@ def test_default_constraint_can_not_solve():
def test_constraint_handlers_are_set_just_in_time():
v = Variable()
c = Constraint(v)
c = BaseConstraint(v)
def handler(c):
pass