diff --git a/src/shapepy/analytic/base.py b/src/shapepy/analytic/base.py index 4bab6689..ad98a92d 100644 --- a/src/shapepy/analytic/base.py +++ b/src/shapepy/analytic/base.py @@ -9,8 +9,7 @@ from functools import lru_cache from typing import Iterable, Set, Union -from rbool import SubSetR1, Whole, from_any - +from ..rbool import SubSetR1, WholeR1, from_any from ..scalar.reals import Real from ..tools import Is @@ -127,7 +126,7 @@ class BaseAnalytic(IAnalytic): Base class parent of Analytic classes """ - def __init__(self, coefs: Iterable[Real], domain: SubSetR1 = Whole()): + def __init__(self, coefs: Iterable[Real], domain: SubSetR1 = WholeR1()): if not Is.iterable(coefs): raise TypeError("Expected an iterable of coefficients") coefs = tuple(coefs) @@ -185,7 +184,7 @@ def __rmul__(self, other: Real) -> IAnalytic: return self.__mul__(other) def __repr__(self) -> str: - if self.domain is Whole(): + if self.domain is WholeR1(): return str(self) return f"{self.domain}: {self}" diff --git a/src/shapepy/analytic/bezier.py b/src/shapepy/analytic/bezier.py index d6587624..310e2267 100644 --- a/src/shapepy/analytic/bezier.py +++ b/src/shapepy/analytic/bezier.py @@ -7,9 +7,8 @@ from functools import lru_cache from typing import Iterable, Tuple, Union -from rbool import SubSetR1, Whole - from ..loggers import debug +from ..rbool import SubSetR1, WholeR1 from ..scalar.quadrature import inner from ..scalar.reals import Math, Rational, Real from ..tools import Is, NotExpectedError, To @@ -80,7 +79,7 @@ class Bezier(BaseAnalytic): such as adding, subtracting, multiplying, etc """ - def __init__(self, coefs: Iterable[Real], domain: SubSetR1 = Whole()): + def __init__(self, coefs: Iterable[Real], domain: SubSetR1 = WholeR1()): super().__init__(coefs, domain) self.__polynomial = bezier2polynomial(self) diff --git a/src/shapepy/analytic/polynomial.py b/src/shapepy/analytic/polynomial.py index 1e4f4e9c..e1a30e52 100644 --- a/src/shapepy/analytic/polynomial.py +++ b/src/shapepy/analytic/polynomial.py @@ -7,9 +7,8 @@ from numbers import Real from typing import Iterable, List, Union -from rbool import move, scale - from ..loggers import debug +from ..rbool import scale, shift from ..scalar.reals import Math from ..tools import Is, To, vectorize from .base import BaseAnalytic, IAnalytic @@ -177,7 +176,7 @@ def shift(self, amount: Real) -> Polynomial: if (i + j) % 2: value *= -1 newcoefs[j] += coef * value - return Polynomial(newcoefs, move(self.domain, amount)) + return Polynomial(newcoefs, shift(self.domain, amount)) @debug("shapepy.analytic.polynomial") def integrate(self, times: int = 1) -> Polynomial: diff --git a/src/shapepy/analytic/tools.py b/src/shapepy/analytic/tools.py index 4797c7d1..dfcb4cb4 100644 --- a/src/shapepy/analytic/tools.py +++ b/src/shapepy/analytic/tools.py @@ -5,17 +5,17 @@ from typing import Union import numpy as np -from rbool import ( - Empty, - SingleValue, + +from ..loggers import debug +from ..rbool import ( + EmptyR1, + SingleR1, SubSetR1, - Whole, + WholeR1, extract_knots, from_any, unite, ) - -from ..loggers import debug from ..scalar.reals import Math, Real from ..tools import Is, NotExpectedError, To from .base import IAnalytic, derivate_analytic @@ -24,7 +24,7 @@ def find_polynomial_roots( - polynomial: Polynomial, domain: SubSetR1 = Whole() + polynomial: Polynomial, domain: SubSetR1 = WholeR1() ) -> SubSetR1: """ Finds all the values of t* such p(t*) = 0 inside given domain @@ -33,15 +33,15 @@ def find_polynomial_roots( polynomial = polynomial.clean() domain &= polynomial.domain if polynomial.degree == 0: - return domain if polynomial[0] == 0 else Empty() + return domain if polynomial[0] == 0 else EmptyR1() if polynomial.degree == 1: numerator = -To.rational(1, 1) * polynomial[0] - return SingleValue(numerator / polynomial[1]) + return SingleR1(numerator / polynomial[1]) if polynomial.degree == 2: c, b, a = polynomial delta = b * b - 4 * a * c if delta < 0: - return Empty() + return EmptyR1() sqrtdelta = Math.sqrt(delta) half = To.rational(1, 2) x0 = half * (-b - sqrtdelta) / a @@ -53,7 +53,7 @@ def find_polynomial_roots( def where_minimum_polynomial( - polynomial: Polynomial, domain: SubSetR1 = Whole() + polynomial: Polynomial, domain: SubSetR1 = WholeR1() ) -> SubSetR1: """ Finds the value of t* such poly(t*) is minimal @@ -62,8 +62,8 @@ def where_minimum_polynomial( domain &= polynomial.domain if polynomial.degree == 0: return domain - if domain == Whole() and polynomial.degree % 2: - return Empty() + if domain == WholeR1() and polynomial.degree % 2: + return EmptyR1() relation = {knot: polynomial(knot) for knot in extract_knots(domain)} critical = find_roots(derivate_analytic(polynomial), domain) for knot in extract_knots(critical): @@ -78,7 +78,7 @@ def where_minimum_polynomial( def find_minimum_polynomial( - polynomial: Polynomial, domain: SubSetR1 = Whole() + polynomial: Polynomial, domain: SubSetR1 = WholeR1() ) -> Union[Real, None]: """ Finds the minimal value of p(t) in the given domain @@ -90,7 +90,7 @@ def find_minimum_polynomial( assert Is.instance(polynomial, Polynomial) if polynomial.degree == 0: return polynomial[0] - if domain == Whole() and polynomial.degree % 2: + if domain == WholeR1() and polynomial.degree % 2: return Math.NEGINF relation = {} relation = {knot: polynomial(knot) for knot in extract_knots(domain)} @@ -103,7 +103,7 @@ def find_minimum_polynomial( @debug("shapepy.analytic.tools") -def find_roots(analytic: IAnalytic, domain: SubSetR1 = Whole()) -> SubSetR1: +def find_roots(analytic: IAnalytic, domain: SubSetR1 = WholeR1()) -> SubSetR1: """ Finds the values of roots of the Analytic function """ @@ -117,7 +117,9 @@ def find_roots(analytic: IAnalytic, domain: SubSetR1 = Whole()) -> SubSetR1: @debug("shapepy.analytic.tools") -def where_minimum(analytic: IAnalytic, domain: SubSetR1 = Whole()) -> SubSetR1: +def where_minimum( + analytic: IAnalytic, domain: SubSetR1 = WholeR1() +) -> SubSetR1: """ Finds the parameters (t*) such the analytic function is minimum """ @@ -131,7 +133,9 @@ def where_minimum(analytic: IAnalytic, domain: SubSetR1 = Whole()) -> SubSetR1: @debug("shapepy.analytic.tools") -def find_minimum(analytic: IAnalytic, domain: SubSetR1 = Whole()) -> SubSetR1: +def find_minimum( + analytic: IAnalytic, domain: SubSetR1 = WholeR1() +) -> SubSetR1: """ Finds the minimal value for the given analytic in the given domain """ diff --git a/src/shapepy/geometry/intersection.py b/src/shapepy/geometry/intersection.py index 9924b393..21620bac 100644 --- a/src/shapepy/geometry/intersection.py +++ b/src/shapepy/geometry/intersection.py @@ -14,15 +14,14 @@ from fractions import Fraction from typing import Dict, Iterable, Set, Tuple, Union -from rbool import ( - Empty, - Interval, - SingleValue, +from ..rbool import ( + EmptyR1, + IntervalR1, + SingleR1, SubSetR1, extract_knots, from_any, ) - from ..scalar.nodes_sample import NodeSampleFactory from ..scalar.reals import Real from ..tools import Is, NotExpectedError @@ -150,7 +149,7 @@ def evaluate(self): for curve in self.curves: knots = curve.parametrize().knots self.__all_knots[id(curve)] = set(knots) - self.__all_subsets[id(curve)] = Empty() + self.__all_subsets[id(curve)] = EmptyR1() for i, j in self.pairs: self.__evaluate_two(self.curves[i], self.curves[j]) @@ -159,7 +158,7 @@ def __evaluate_two(self, curvea: IGeometricCurve, curveb: IGeometricCurve): Private function two compute the intersection between two curves """ subseta, subsetb = self.__compute_two(curvea, curveb) - if Is.instance(subseta, Empty): + if Is.instance(subseta, EmptyR1): return self.all_subsets[id(curvea)] |= subseta self.all_knots[id(curvea)] |= set(extract_knots(subseta)) @@ -170,10 +169,10 @@ def __compute_two( self, curvea: IGeometricCurve, curveb: IGeometricCurve ) -> Tuple[SubSetR1, SubSetR1]: if curvea.box() & curveb.box() is None: - return Empty(), Empty() + return EmptyR1(), EmptyR1() if id(curvea) == id(curveb): # Check if curves are equal curvea = curvea.parametrize() - subset = Interval(curvea.knots[0], curvea.knots[-1]) + subset = IntervalR1(curvea.knots[0], curvea.knots[-1]) return subset, subset return curve_and_curve(curvea, curveb) @@ -191,7 +190,7 @@ def __or__( return GeometricIntersectionCurves(newcurves, newparis) def __bool__(self): - return all(v == Empty() for v in self.all_subsets.values()) + return all(v == EmptyR1() for v in self.all_subsets.values()) def curve_and_curve( @@ -226,9 +225,9 @@ def segment_and_segment( assert Is.instance(curvea, Segment) assert Is.instance(curveb, Segment) if curvea.box() & curveb.box() is None: - return Empty(), Empty() + return EmptyR1(), EmptyR1() if curvea == curveb: - return Interval(0, 1), Interval(0, 1) + return IntervalR1(0, 1), IntervalR1(0, 1) if segment_is_linear(curvea) and segment_is_linear(curveb): return IntersectionSegments.lines(curvea, curveb) nptsa = max(curvea.xfunc.degree, curvea.yfunc.degree) + 4 @@ -270,7 +269,7 @@ class IntersectionSegments: @staticmethod def lines(curvea: Segment, curveb: Segment) -> Tuple[SubSetR1, SubSetR1]: """Finds the intersection of two line segments""" - empty = Empty() + empty = EmptyR1() A0, A1 = curvea(0), curvea(1) B0, B1 = curveb(0), curveb(1) dA = A1 - A0 @@ -284,7 +283,7 @@ def lines(curvea: Segment, curveb: Segment) -> Tuple[SubSetR1, SubSetR1]: u0 = cross(B0mA0, dA) / dAxdB if u0 < 0 or 1 < u0: return empty, empty - return SingleValue(t0), SingleValue(u0) + return SingleR1(t0), SingleR1(u0) # Lines are parallel if cross(dA, B0mA0) != 0: return empty, empty # Parallel, but not colinear @@ -307,8 +306,8 @@ def lines(curvea: Segment, curveb: Segment) -> Tuple[SubSetR1, SubSetR1]: u0 = min(max(0, u0), 1) u1 = min(max(0, u1), 1) if t0 == t1 or u0 == u1: - return SingleValue(t0), SingleValue(u1) - return Interval(t0, t1), Interval(u0, u1) + return SingleR1(t0), SingleR1(u1) + return IntervalR1(t0, t1), IntervalR1(u0, u1) # pylint: disable=too-many-locals @staticmethod @@ -469,7 +468,7 @@ def intersect_piecewises( assert Is.piecewise(curvea) assert Is.piecewise(curveb) - subseta, subsetb = Empty(), Empty() + subseta, subsetb = EmptyR1(), EmptyR1() for ai, sbezier in enumerate(curvea): for bj, obezier in enumerate(curveb): suba, subb = segment_and_segment(sbezier, obezier) diff --git a/src/shapepy/geometry/segment.py b/src/shapepy/geometry/segment.py index ade51249..4156f30b 100644 --- a/src/shapepy/geometry/segment.py +++ b/src/shapepy/geometry/segment.py @@ -15,11 +15,10 @@ from copy import copy from typing import Iterable, Optional, Tuple, Union -from rbool import Interval, from_any - from ..analytic.base import IAnalytic from ..analytic.tools import find_minimum from ..loggers import debug +from ..rbool import IntervalR1, from_any from ..scalar.angle import Angle from ..scalar.quadrature import AdaptativeIntegrator, IntegratorFactory from ..scalar.reals import Math, Real @@ -152,10 +151,10 @@ def split(self, nodes: Iterable[Real]) -> Tuple[Segment, ...]: nodes = sorted(set(nodes) | set(self.knots)) return tuple(self.extract([ka, kb]) for ka, kb in pairs(nodes)) - def extract(self, interval: Interval) -> Segment: + def extract(self, interval: IntervalR1) -> Segment: """Extracts a subsegment from the given segment""" interval = from_any(interval) - if not Is.instance(interval, Interval): + if not Is.instance(interval, IntervalR1): raise TypeError knota, knotb = interval[0], interval[1] denom = 1 / (knotb - knota) diff --git a/src/shapepy/rbool.py b/src/shapepy/rbool.py new file mode 100644 index 00000000..bde18596 --- /dev/null +++ b/src/shapepy/rbool.py @@ -0,0 +1,16 @@ +"""Wraps the rbool library and add some useful functions for this package""" + +from typing import Any, Callable, Iterator, Type + +import rbool + +EmptyR1: Type = rbool.Empty +IntervalR1: Type = rbool.Interval +SingleR1: Type = rbool.SingleValue +SubSetR1: Type = rbool.SubSetR1 +WholeR1: Type = rbool.Whole +extract_knots: Callable[[Any], Iterator[Any]] = rbool.extract_knots +from_any: Callable[[Any], object] = rbool.from_any +shift = rbool.move +scale = rbool.scale +unite = rbool.unite diff --git a/tests/analytic/test_bezier.py b/tests/analytic/test_bezier.py index abadcf78..c82e679b 100644 --- a/tests/analytic/test_bezier.py +++ b/tests/analytic/test_bezier.py @@ -1,8 +1,5 @@ -from fractions import Fraction as frac - import numpy as np import pytest -from rbool import Interval from shapepy.analytic.bezier import ( Bezier, @@ -73,7 +70,7 @@ def test_matrices(): @pytest.mark.order(4) @pytest.mark.dependency(depends=["test_build", "test_degree", "test_matrices"]) def test_compare(): - domain = Interval(0, 1) + domain = [0, 1] bezier = Bezier([1], domain) assert bezier == Polynomial([1], domain) assert bezier == 1 diff --git a/tests/analytic/test_tools.py b/tests/analytic/test_tools.py index e83ddf4a..47ee188c 100644 --- a/tests/analytic/test_tools.py +++ b/tests/analytic/test_tools.py @@ -3,11 +3,11 @@ from fractions import Fraction import pytest -from rbool import Empty, Whole from shapepy.analytic.bezier import Bezier from shapepy.analytic.polynomial import Polynomial from shapepy.analytic.tools import find_minimum, find_roots, where_minimum +from shapepy.rbool import EmptyR1, WholeR1 from shapepy.scalar.reals import Math @@ -26,11 +26,11 @@ def test_begin(): @pytest.mark.dependency(depends=["test_begin"]) def test_polynomial_roots(): poly = Polynomial([0]) - assert find_roots(poly) == Whole() + assert find_roots(poly) == WholeR1() for const in range(-10, 11): if const != 0: poly = Polynomial([const]) - assert find_roots(poly) == Empty() + assert find_roots(poly) == EmptyR1() ntests = 100 x = Polynomial([0, 1]) @@ -47,7 +47,7 @@ def test_polynomial_roots(): assert find_roots((x - x0) * (x - x1)) == {x0, x1} poly = Polynomial([1, 0, 1]) # p(x) = 1 + x^2 - assert find_roots(poly) == Empty() + assert find_roots(poly) == EmptyR1() # Finds the roots of p(x) = a * x^2 + b * x + c for _ in range(ntests): @@ -57,7 +57,7 @@ def test_polynomial_roots(): poly = a * x * x + b * x + c delta = b * b - 4 * a * c if delta < 0: - assert find_roots(poly) == Empty() + assert find_roots(poly) == EmptyR1() elif delta == 0: assert find_roots(poly) == {-b / 2 * a} else: @@ -70,7 +70,7 @@ def test_polynomial_roots(): @pytest.mark.dependency(depends=["test_begin"]) def test_bezier_roots(): bezier = Bezier([0]) - assert find_roots(bezier) == Whole() + assert find_roots(bezier) == WholeR1() assert find_roots(bezier, [0, 1]) == [0, 1] bezier = Bezier([1, -1]) @@ -83,7 +83,7 @@ def test_bezier_roots(): def test_where_minimal_polynomial(): for const in range(-10, 11): poly = Polynomial([const]) - assert where_minimum(poly) == Whole() + assert where_minimum(poly) == WholeR1() x = Polynomial([0, 1]) ntests = 100 @@ -91,8 +91,8 @@ def test_where_minimal_polynomial(): for _ in range(ntests): a = random.randint(1, 10) b = random.randint(-10, 10) - assert where_minimum(a * x + b) == Empty() - assert where_minimum(-a * x + b) == Empty() + assert where_minimum(a * x + b) == EmptyR1() + assert where_minimum(-a * x + b) == EmptyR1() # Finds the minimum of p(x) = a * x + b, in a closed interval for _ in range(ntests): @@ -110,8 +110,8 @@ def test_where_minimal_polynomial(): # Finds minimum of f(x) = (x + 1) * x * (x - 1) poly = x * (x - 1) * (x + 1) - assert where_minimum(poly) == Empty() - assert where_minimum(-poly) == Empty() + assert where_minimum(poly) == EmptyR1() + assert where_minimum(-poly) == EmptyR1() # Finds minimum of f(x) = (x + 2) * (x + 1) * (x - 1) * (x - 2) poly = (x + 2) * (x + 1) * (x - 1) * (x - 2)