123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- from functools import singledispatch
- from sympy.core.numbers import pi
- from sympy.functions.elementary.trigonometric import tan
- from sympy.simplify import trigsimp
- from sympy.core import Basic, Tuple
- from sympy.core.symbol import _symbol
- from sympy.solvers import solve
- from sympy.geometry import Point, Segment, Curve, Ellipse, Polygon
- from sympy.vector import ImplicitRegion
- class ParametricRegion(Basic):
- """
- Represents a parametric region in space.
- Examples
- ========
- >>> from sympy import cos, sin, pi
- >>> from sympy.abc import r, theta, t, a, b, x, y
- >>> from sympy.vector import ParametricRegion
- >>> ParametricRegion((t, t**2), (t, -1, 2))
- ParametricRegion((t, t**2), (t, -1, 2))
- >>> ParametricRegion((x, y), (x, 3, 4), (y, 5, 6))
- ParametricRegion((x, y), (x, 3, 4), (y, 5, 6))
- >>> ParametricRegion((r*cos(theta), r*sin(theta)), (r, -2, 2), (theta, 0, pi))
- ParametricRegion((r*cos(theta), r*sin(theta)), (r, -2, 2), (theta, 0, pi))
- >>> ParametricRegion((a*cos(t), b*sin(t)), t)
- ParametricRegion((a*cos(t), b*sin(t)), t)
- >>> circle = ParametricRegion((r*cos(theta), r*sin(theta)), r, (theta, 0, pi))
- >>> circle.parameters
- (r, theta)
- >>> circle.definition
- (r*cos(theta), r*sin(theta))
- >>> circle.limits
- {theta: (0, pi)}
- Dimension of a parametric region determines whether a region is a curve, surface
- or volume region. It does not represent its dimensions in space.
- >>> circle.dimensions
- 1
- Parameters
- ==========
- definition : tuple to define base scalars in terms of parameters.
- bounds : Parameter or a tuple of length 3 to define parameter and corresponding lower and upper bound.
- """
- def __new__(cls, definition, *bounds):
- parameters = ()
- limits = {}
- if not isinstance(bounds, Tuple):
- bounds = Tuple(*bounds)
- for bound in bounds:
- if isinstance(bound, (tuple, Tuple)):
- if len(bound) != 3:
- raise ValueError("Tuple should be in the form (parameter, lowerbound, upperbound)")
- parameters += (bound[0],)
- limits[bound[0]] = (bound[1], bound[2])
- else:
- parameters += (bound,)
- if not isinstance(definition, (tuple, Tuple)):
- definition = (definition,)
- obj = super().__new__(cls, Tuple(*definition), *bounds)
- obj._parameters = parameters
- obj._limits = limits
- return obj
- @property
- def definition(self):
- return self.args[0]
- @property
- def limits(self):
- return self._limits
- @property
- def parameters(self):
- return self._parameters
- @property
- def dimensions(self):
- return len(self.limits)
- @singledispatch
- def parametric_region_list(reg):
- """
- Returns a list of ParametricRegion objects representing the geometric region.
- Examples
- ========
- >>> from sympy.abc import t
- >>> from sympy.vector import parametric_region_list
- >>> from sympy.geometry import Point, Curve, Ellipse, Segment, Polygon
- >>> p = Point(2, 5)
- >>> parametric_region_list(p)
- [ParametricRegion((2, 5))]
- >>> c = Curve((t**3, 4*t), (t, -3, 4))
- >>> parametric_region_list(c)
- [ParametricRegion((t**3, 4*t), (t, -3, 4))]
- >>> e = Ellipse(Point(1, 3), 2, 3)
- >>> parametric_region_list(e)
- [ParametricRegion((2*cos(t) + 1, 3*sin(t) + 3), (t, 0, 2*pi))]
- >>> s = Segment(Point(1, 3), Point(2, 6))
- >>> parametric_region_list(s)
- [ParametricRegion((t + 1, 3*t + 3), (t, 0, 1))]
- >>> p1, p2, p3, p4 = [(0, 1), (2, -3), (5, 3), (-2, 3)]
- >>> poly = Polygon(p1, p2, p3, p4)
- >>> parametric_region_list(poly)
- [ParametricRegion((2*t, 1 - 4*t), (t, 0, 1)), ParametricRegion((3*t + 2, 6*t - 3), (t, 0, 1)),\
- ParametricRegion((5 - 7*t, 3), (t, 0, 1)), ParametricRegion((2*t - 2, 3 - 2*t), (t, 0, 1))]
- """
- raise ValueError("SymPy cannot determine parametric representation of the region.")
- @parametric_region_list.register(Point)
- def _(obj):
- return [ParametricRegion(obj.args)]
- @parametric_region_list.register(Curve) # type: ignore
- def _(obj):
- definition = obj.arbitrary_point(obj.parameter).args
- bounds = obj.limits
- return [ParametricRegion(definition, bounds)]
- @parametric_region_list.register(Ellipse) # type: ignore
- def _(obj, parameter='t'):
- definition = obj.arbitrary_point(parameter).args
- t = _symbol(parameter, real=True)
- bounds = (t, 0, 2*pi)
- return [ParametricRegion(definition, bounds)]
- @parametric_region_list.register(Segment) # type: ignore
- def _(obj, parameter='t'):
- t = _symbol(parameter, real=True)
- definition = obj.arbitrary_point(t).args
- for i in range(0, 3):
- lower_bound = solve(definition[i] - obj.points[0].args[i], t)
- upper_bound = solve(definition[i] - obj.points[1].args[i], t)
- if len(lower_bound) == 1 and len(upper_bound) == 1:
- bounds = t, lower_bound[0], upper_bound[0]
- break
- definition_tuple = obj.arbitrary_point(parameter).args
- return [ParametricRegion(definition_tuple, bounds)]
- @parametric_region_list.register(Polygon) # type: ignore
- def _(obj, parameter='t'):
- l = [parametric_region_list(side, parameter)[0] for side in obj.sides]
- return l
- @parametric_region_list.register(ImplicitRegion) # type: ignore
- def _(obj, parameters=('t', 's')):
- definition = obj.rational_parametrization(parameters)
- bounds = []
- for i in range(len(obj.variables) - 1):
- # Each parameter is replaced by its tangent to simplify intergation
- parameter = _symbol(parameters[i], real=True)
- definition = [trigsimp(elem.subs(parameter, tan(parameter/2))) for elem in definition]
- bounds.append((parameter, 0, 2*pi),)
- definition = Tuple(*definition)
- return [ParametricRegion(definition, *bounds)]
|