Created
September 16, 2021 15:47
-
-
Save rvodden/c5d7d6ecea734006469d8dc758f9d1ae to your computer and use it in GitHub Desktop.
PySketcher Geometric Solver Recap of Part 1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC | |
class Constraint(ABC): | |
"""Used to restrict that value of a ```ConstrainedValue```.""" | |
pass | |
class FixedValueConstraint(Constraint): | |
def __init__(self, value): | |
self._value = value | |
@property | |
def value(self): | |
return self._value | |
def __repr__(self): | |
return f"{self.__class__.__name__}<{self.value}>" | |
class LinkedValueConstraint(Constraint): | |
def __init__(self, constraint_set): | |
self._constraint_set = constraint_set | |
@property | |
def constraint_set(self): | |
return self._constraint_set | |
def __repr__(self): | |
return f"{self.__class__.__name__}<{self.constraint_set}>" | |
class ConstraintSet: | |
def __init__(self, name=""): | |
self._constraints = [] | |
self._name = name | |
def constrain_with(self, constraint): | |
"""Add a constraint to this objects list of constraints.""" | |
self._constraints.append(constraint) | |
def reset_constraints(self): | |
"""Removes the existing constraints from the constraint set""" | |
self._constraints = [] | |
def resolve(self): | |
"""Naive implementation to aid testing""" | |
for constraint in self._constraints: | |
if isinstance(constraint, FixedValueConstraint): | |
return constraint.value | |
if isinstance(constraint, LinkedValueConstraint): | |
return constraint.constraint_set.resolve() | |
raise UnderconstrainedError("Fixed Value has not been provided.") | |
def __repr__(self): | |
retval = "ConstraintSet(" | |
if len(self._constraints) == 0: | |
retval += ")" | |
return retval | |
for constraint in self._constraints: | |
retval += f"\n {constraint}" | |
retval += "\n)" | |
return retval | |
def __str__(self): | |
return self._name | |
class UnderConstrainedError(RuntimeError): | |
pass | |
class ConstrainedValue: | |
"""An object which can be passed around to represent a value.""" | |
def __set_name__(self, owner, name): | |
self.public_name = name | |
self.private_name = f"_{name}" | |
def __get__(self, instance, typ=None): | |
# grab the ConstraintSet from the instance | |
constraint_set = getattr(instance, self.private_name, None) | |
# If the instance didn't have an initialized ConstraintSet then | |
# give it one | |
if constraint_set is None: | |
constraint_set = ConstraintSet(f"{instance.name}.{self.public_name}") | |
setattr(instance, self.private_name, constraint_set) | |
return constraint_set | |
def __set__(self, instance, value): | |
# Grab the ConstraintSet from the instance | |
constraint_set = self.__get__(instance, None) | |
constraint_set.reset_constraints() | |
# if the value we've been asked to assign is a ConstraintSet | |
# then add a LinkedValueConstraint: | |
if isinstance(value, ConstraintSet): | |
constraint_set.constrain_with(LinkedValueConstraint(value)) | |
return | |
# otherwise use a FixedValueConstraint to constrain to the provided | |
# value | |
constraint_set.constrain_with(FixedValueConstraint(value)) | |
class Point: | |
x = ConstrainedValue() | |
y = ConstrainedValue() | |
def __init__(self, name="", x=None, y=None): | |
self._name = name | |
if x is not None: | |
self.x = x | |
if y is not None : | |
self.y = y | |
@property | |
def name(self): | |
return self._name | |
p = Point('p', 1,2) | |
q = Point('q') | |
print(f"p.x is {p.x}") | |
print(f"p.x resolves to {p.x.resolve()}") | |
q.x = p.x | |
print(f"q.x is {repr(q.x)}") | |
print(f"q.x resolves to {q.x.resolve()}") | |
p.x = 2 | |
print(f"Now p.x has changed, q.x resolves to {q.x.resolve()}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment