Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
433 changes: 0 additions & 433 deletions src/shapepy/bool2d/boolalg.py

This file was deleted.

92 changes: 7 additions & 85 deletions src/shapepy/bool2d/boolean.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@
from __future__ import annotations

from copy import copy
from typing import Dict, Iterable, Tuple, Union
from typing import Iterable, Tuple, Union

from shapepy.geometry.jordancurve import JordanCurve

from ..geometry.intersection import GeometricIntersectionCurves
from ..geometry.unparam import USegment
from ..loggers import debug
from ..tools import CyclicContainer, Is, NotExpectedError
from . import boolalg
from ..tools import CyclicContainer, Is
from .base import EmptyShape, Future, SubSetR2, WholeShape
from .config import Config
from .curve import SingleCurve
from .lazy import LazyAnd, LazyNot, LazyOr, RecipeLazy, is_lazy
from .lazy import LazyAnd, LazyNot, LazyOr, RecipeLazy
from .point import SinglePoint
from .shape import ConnectedShape, DisjointShape, SimpleShape

Expand All @@ -38,7 +37,7 @@ def invert_bool2d(subset: SubSetR2) -> SubSetR2:
SubSetR2
The complementar subset
"""
return Boolalg.clean(RecipeLazy.invert(subset))
return RecipeLazy.invert(subset)


@debug("shapepy.bool2d.boolean")
Expand All @@ -56,8 +55,7 @@ def unite_bool2d(subsets: Iterable[SubSetR2]) -> SubSetR2:
SubSetR2
The united subset
"""
union = RecipeLazy.unite(subsets)
return Boolalg.clean(union)
return RecipeLazy.unite(subsets)


@debug("shapepy.bool2d.boolean")
Expand All @@ -75,8 +73,7 @@ def intersect_bool2d(subsets: Iterable[SubSetR2]) -> SubSetR2:
SubSetR2
The intersection subset
"""
intersection = RecipeLazy.intersect(subsets)
return Boolalg.clean(intersection)
return RecipeLazy.intersect(subsets)


@debug("shapepy.bool2d.boolean")
Expand All @@ -94,8 +91,7 @@ def xor_bool2d(subsets: Iterable[SubSetR2]) -> SubSetR2:
SubSetR2
The intersection subset
"""
subset = RecipeLazy.xor(subsets)
return Boolalg.clean(subset)
return RecipeLazy.xor(subsets)


# pylint: disable=too-many-return-statements
Expand All @@ -114,7 +110,6 @@ def clean_bool2d(subset: SubSetR2) -> SubSetR2:
SubSetR2
The intersection subset
"""
subset = Boolalg.clean(subset)
if not Is.lazy(subset):
return subset
if Is.instance(subset, LazyNot):
Expand Down Expand Up @@ -213,79 +208,6 @@ def contains_bool2d(subseta: SubSetR2, subsetb: SubSetR2) -> bool:
)


class Boolalg:
"""Static methods to clean a SubSetR2 using algebraic simplifier"""

alphabet = "abcdefghijklmnop"
sub2var: Dict[SubSetR2, str] = {}

@staticmethod
def clean(subset: SubSetR2) -> SubSetR2:
"""Simplifies the subset"""

if not Is.lazy(subset):
return subset
Boolalg.sub2var.clear()
original = Boolalg.subset2expression(subset)
simplified = boolalg.simplify(original)
if simplified != original:
subset = Boolalg.expression2subset(simplified)
Boolalg.sub2var.clear()
return subset

@staticmethod
def get_variable(subset: SubSetR2) -> str:
"""Gets the variable represeting the subset"""
if subset not in Boolalg.sub2var:
index = len(Boolalg.sub2var)
if index > len(Boolalg.alphabet):
raise ValueError("Too many variables")
Boolalg.sub2var[subset] = Boolalg.alphabet[index]
return Boolalg.sub2var[subset]

@staticmethod
def subset2expression(subset: SubSetR2) -> str:
"""Converts a SubSetR2 into a boolean expression"""
if not is_lazy(subset):
if Is.instance(subset, (EmptyShape, WholeShape)):
raise NotExpectedError("Lazy does not contain these")
return Boolalg.get_variable(subset)
if Is.instance(subset, LazyNot):
return boolalg.Formatter.invert_str(
Boolalg.subset2expression(~subset)
)
internals = map(Boolalg.subset2expression, subset)
if Is.instance(subset, LazyAnd):
return boolalg.Formatter.mult_strs(internals, boolalg.AND)
if Is.instance(subset, LazyOr):
return boolalg.Formatter.mult_strs(internals, boolalg.OR)
raise NotExpectedError

@staticmethod
def expression2subset(expression: str) -> SubSetR2:
"""Converts a boolean expression into a SubSetR2"""
if expression == boolalg.TRUE:
return WholeShape()
if expression == boolalg.FALSE:
return EmptyShape()
for subset, variable in Boolalg.sub2var.items():
if expression == variable:
return subset
expression = boolalg.remove_parentesis(expression)
operator = boolalg.find_operator(expression)
if operator == boolalg.NOT:
subexpr = boolalg.extract(expression, boolalg.NOT)
inverted = Boolalg.expression2subset(subexpr)
return RecipeLazy.invert(inverted)
subexprs = boolalg.extract(expression, operator)
subsets = map(Boolalg.expression2subset, subexprs)
if operator == boolalg.OR:
return RecipeLazy.unite(subsets)
if operator == boolalg.AND:
return RecipeLazy.intersect(subsets)
raise NotExpectedError(f"Invalid expression: {expression}")


def divide_connecteds(
simples: Tuple[SimpleShape],
) -> Tuple[Union[SimpleShape, ConnectedShape]]:
Expand Down
104 changes: 52 additions & 52 deletions src/shapepy/bool2d/lazy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,86 +2,86 @@

from __future__ import annotations

from collections import Counter
from copy import deepcopy
from typing import Iterable, Iterator, Type

from typing import Iterable, Iterator, Union

from ..boolalg.simplify import simplify_tree
from ..boolalg.tree import (
BoolTree,
Operators,
false_tree,
items2tree,
true_tree,
)
from ..loggers import debug
from ..tools import Is
from ..tools import Is, NotExpectedError
from .base import EmptyShape, SubSetR2, WholeShape
from .density import intersect_densities, unite_densities


def subset2tree(subset: SubSetR2) -> Union[SubSetR2, BoolTree]:
"""Converts a subset into a tree equivalent"""
if Is.instance(subset, EmptyShape):
return false_tree()
if Is.instance(subset, WholeShape):
return true_tree()
if Is.instance(subset, LazyNot):
return items2tree((subset2tree(-subset),), Operators.NOT)
if Is.instance(subset, LazyAnd):
return items2tree(map(subset2tree, subset), Operators.AND)
if Is.instance(subset, LazyOr):
return items2tree(map(subset2tree, subset), Operators.OR)
return subset


def tree2subset(tree: Union[SubSetR2, BoolTree]) -> SubSetR2:
"""Converts a tree into the subset equivalent"""
if not Is.instance(tree, BoolTree):
return tree
if len(tree) == 0:
return WholeShape() if tree.operator == Operators.AND else EmptyShape()
if tree.operator == Operators.NOT:
return LazyNot(tree2subset(tuple(tree)[0]))
if tree.operator == Operators.AND:
return LazyAnd(map(tree2subset, tree))
if tree.operator == Operators.OR:
return LazyOr(map(tree2subset, tree))
raise NotExpectedError(f"Operator {tree.operator}")


@debug("shapepy.bool2d.lazy")
def operate(subsets: Iterable[SubSetR2], operator: Operators) -> SubSetR2:
"""Computes the operation of the items, such as union, intersection"""
tree = items2tree(map(subset2tree, subsets), operator)
return tree2subset(simplify_tree(tree))


class RecipeLazy:
"""Contains static methods that gives lazy recipes"""

@staticmethod
def flatten(subsets: Iterable[SubSetR2], typo: Type) -> Iterator[SubSetR2]:
"""Flattens the subsets"""
for subset in subsets:
if Is.instance(subset, typo):
yield from subset
else:
yield subset

@staticmethod
@debug("shapepy.bool2d.lazy")
def invert(subset: SubSetR2) -> SubSetR2:
"""Gives the complementar of the given subset"""
if Is.instance(subset, (EmptyShape, WholeShape, LazyNot)):
return -subset
return LazyNot(subset)
return operate((subset,), Operators.NOT)

@staticmethod
@debug("shapepy.bool2d.lazy")
def intersect(subsets: Iterable[SubSetR2]) -> SubSetR2:
"""Gives the recipe for the intersection of given subsets"""
subsets = RecipeLazy.flatten(subsets, LazyAnd)
subsets = frozenset(
s for s in subsets if not Is.instance(s, WholeShape)
)
if len(subsets) == 0:
return WholeShape()
if any(Is.instance(s, EmptyShape) for s in subsets):
return EmptyShape()
if len(subsets) == 1:
return tuple(subsets)[0]
return LazyAnd(subsets)
return operate(subsets, Operators.AND)

@staticmethod
@debug("shapepy.bool2d.contain")
def unite(subsets: Iterable[SubSetR2]) -> SubSetR2:
"""Gives the recipe for the union of given subsets"""
subsets = RecipeLazy.flatten(subsets, LazyOr)
subsets = frozenset(
s for s in subsets if not Is.instance(s, EmptyShape)
)
if len(subsets) == 0:
return EmptyShape()
if any(Is.instance(s, WholeShape) for s in subsets):
return WholeShape()
if len(subsets) == 1:
return tuple(subsets)[0]
return LazyOr(subsets)
return operate(subsets, Operators.OR)

@staticmethod
@debug("shapepy.bool2d.contain")
def xor(subsets: Iterable[SubSetR2]) -> SubSetR2:
"""Gives the exclusive or of the given subsets"""
subsets = tuple(subsets)
dictids = dict(Counter(map(id, subsets)))
subsets = tuple(s for s in subsets if dictids[id(s)] % 2)
length = len(subsets)
if length == 0:
return EmptyShape()
if length == 1:
return subsets[0]
mid = length // 2
aset = RecipeLazy.xor(subsets[:mid])
bset = RecipeLazy.xor(subsets[mid:])
left = RecipeLazy.intersect((aset, RecipeLazy.invert(bset)))
righ = RecipeLazy.intersect((RecipeLazy.invert(aset), bset))
return RecipeLazy.unite((left, righ))
return operate(subsets, Operators.XOR)


class LazyNot(SubSetR2):
Expand Down
Empty file added src/shapepy/boolalg/__init__.py
Empty file.
Loading