parametricregion.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. from functools import singledispatch
  2. from sympy.core.numbers import pi
  3. from sympy.functions.elementary.trigonometric import tan
  4. from sympy.simplify import trigsimp
  5. from sympy.core import Basic, Tuple
  6. from sympy.core.symbol import _symbol
  7. from sympy.solvers import solve
  8. from sympy.geometry import Point, Segment, Curve, Ellipse, Polygon
  9. from sympy.vector import ImplicitRegion
  10. class ParametricRegion(Basic):
  11. """
  12. Represents a parametric region in space.
  13. Examples
  14. ========
  15. >>> from sympy import cos, sin, pi
  16. >>> from sympy.abc import r, theta, t, a, b, x, y
  17. >>> from sympy.vector import ParametricRegion
  18. >>> ParametricRegion((t, t**2), (t, -1, 2))
  19. ParametricRegion((t, t**2), (t, -1, 2))
  20. >>> ParametricRegion((x, y), (x, 3, 4), (y, 5, 6))
  21. ParametricRegion((x, y), (x, 3, 4), (y, 5, 6))
  22. >>> ParametricRegion((r*cos(theta), r*sin(theta)), (r, -2, 2), (theta, 0, pi))
  23. ParametricRegion((r*cos(theta), r*sin(theta)), (r, -2, 2), (theta, 0, pi))
  24. >>> ParametricRegion((a*cos(t), b*sin(t)), t)
  25. ParametricRegion((a*cos(t), b*sin(t)), t)
  26. >>> circle = ParametricRegion((r*cos(theta), r*sin(theta)), r, (theta, 0, pi))
  27. >>> circle.parameters
  28. (r, theta)
  29. >>> circle.definition
  30. (r*cos(theta), r*sin(theta))
  31. >>> circle.limits
  32. {theta: (0, pi)}
  33. Dimension of a parametric region determines whether a region is a curve, surface
  34. or volume region. It does not represent its dimensions in space.
  35. >>> circle.dimensions
  36. 1
  37. Parameters
  38. ==========
  39. definition : tuple to define base scalars in terms of parameters.
  40. bounds : Parameter or a tuple of length 3 to define parameter and corresponding lower and upper bound.
  41. """
  42. def __new__(cls, definition, *bounds):
  43. parameters = ()
  44. limits = {}
  45. if not isinstance(bounds, Tuple):
  46. bounds = Tuple(*bounds)
  47. for bound in bounds:
  48. if isinstance(bound, (tuple, Tuple)):
  49. if len(bound) != 3:
  50. raise ValueError("Tuple should be in the form (parameter, lowerbound, upperbound)")
  51. parameters += (bound[0],)
  52. limits[bound[0]] = (bound[1], bound[2])
  53. else:
  54. parameters += (bound,)
  55. if not isinstance(definition, (tuple, Tuple)):
  56. definition = (definition,)
  57. obj = super().__new__(cls, Tuple(*definition), *bounds)
  58. obj._parameters = parameters
  59. obj._limits = limits
  60. return obj
  61. @property
  62. def definition(self):
  63. return self.args[0]
  64. @property
  65. def limits(self):
  66. return self._limits
  67. @property
  68. def parameters(self):
  69. return self._parameters
  70. @property
  71. def dimensions(self):
  72. return len(self.limits)
  73. @singledispatch
  74. def parametric_region_list(reg):
  75. """
  76. Returns a list of ParametricRegion objects representing the geometric region.
  77. Examples
  78. ========
  79. >>> from sympy.abc import t
  80. >>> from sympy.vector import parametric_region_list
  81. >>> from sympy.geometry import Point, Curve, Ellipse, Segment, Polygon
  82. >>> p = Point(2, 5)
  83. >>> parametric_region_list(p)
  84. [ParametricRegion((2, 5))]
  85. >>> c = Curve((t**3, 4*t), (t, -3, 4))
  86. >>> parametric_region_list(c)
  87. [ParametricRegion((t**3, 4*t), (t, -3, 4))]
  88. >>> e = Ellipse(Point(1, 3), 2, 3)
  89. >>> parametric_region_list(e)
  90. [ParametricRegion((2*cos(t) + 1, 3*sin(t) + 3), (t, 0, 2*pi))]
  91. >>> s = Segment(Point(1, 3), Point(2, 6))
  92. >>> parametric_region_list(s)
  93. [ParametricRegion((t + 1, 3*t + 3), (t, 0, 1))]
  94. >>> p1, p2, p3, p4 = [(0, 1), (2, -3), (5, 3), (-2, 3)]
  95. >>> poly = Polygon(p1, p2, p3, p4)
  96. >>> parametric_region_list(poly)
  97. [ParametricRegion((2*t, 1 - 4*t), (t, 0, 1)), ParametricRegion((3*t + 2, 6*t - 3), (t, 0, 1)),\
  98. ParametricRegion((5 - 7*t, 3), (t, 0, 1)), ParametricRegion((2*t - 2, 3 - 2*t), (t, 0, 1))]
  99. """
  100. raise ValueError("SymPy cannot determine parametric representation of the region.")
  101. @parametric_region_list.register(Point)
  102. def _(obj):
  103. return [ParametricRegion(obj.args)]
  104. @parametric_region_list.register(Curve) # type: ignore
  105. def _(obj):
  106. definition = obj.arbitrary_point(obj.parameter).args
  107. bounds = obj.limits
  108. return [ParametricRegion(definition, bounds)]
  109. @parametric_region_list.register(Ellipse) # type: ignore
  110. def _(obj, parameter='t'):
  111. definition = obj.arbitrary_point(parameter).args
  112. t = _symbol(parameter, real=True)
  113. bounds = (t, 0, 2*pi)
  114. return [ParametricRegion(definition, bounds)]
  115. @parametric_region_list.register(Segment) # type: ignore
  116. def _(obj, parameter='t'):
  117. t = _symbol(parameter, real=True)
  118. definition = obj.arbitrary_point(t).args
  119. for i in range(0, 3):
  120. lower_bound = solve(definition[i] - obj.points[0].args[i], t)
  121. upper_bound = solve(definition[i] - obj.points[1].args[i], t)
  122. if len(lower_bound) == 1 and len(upper_bound) == 1:
  123. bounds = t, lower_bound[0], upper_bound[0]
  124. break
  125. definition_tuple = obj.arbitrary_point(parameter).args
  126. return [ParametricRegion(definition_tuple, bounds)]
  127. @parametric_region_list.register(Polygon) # type: ignore
  128. def _(obj, parameter='t'):
  129. l = [parametric_region_list(side, parameter)[0] for side in obj.sides]
  130. return l
  131. @parametric_region_list.register(ImplicitRegion) # type: ignore
  132. def _(obj, parameters=('t', 's')):
  133. definition = obj.rational_parametrization(parameters)
  134. bounds = []
  135. for i in range(len(obj.variables) - 1):
  136. # Each parameter is replaced by its tangent to simplify intergation
  137. parameter = _symbol(parameters[i], real=True)
  138. definition = [trigsimp(elem.subs(parameter, tan(parameter/2))) for elem in definition]
  139. bounds.append((parameter, 0, 2*pi),)
  140. definition = Tuple(*definition)
  141. return [ParametricRegion(definition, *bounds)]