Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implemented Shape class that can construct closed 2D Figure and methods area, centroid, second_moment_of_area #14434

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
9 changes: 9 additions & 0 deletions sympy/core/tests/test_args.py
Expand Up @@ -3545,6 +3545,15 @@ def test_sympy__geometry__parabola__Parabola():
assert _test_args(Parabola((0, 0), Line((2, 3), (4, 3))))


def test_sympy__geometry__shape__Shape():
from sympy.geometry.shape import Shape
from sympy.geometry.line import Line
from sympy.geometry.ellipse import Ellipse, Circle
e = Ellipse((0, 0), 4, 2)
l = Line((0, 0), (0, 2))
assert _test_args(Shape(e, l))


@SKIP("abstract class")
def test_sympy__geometry__line__LinearEntity():
pass
Expand Down
1 change: 1 addition & 0 deletions sympy/geometry/__init__.py
Expand Up @@ -21,3 +21,4 @@
from sympy.geometry.exceptions import GeometryError
from sympy.geometry.curve import Curve
from sympy.geometry.parabola import Parabola
from sympy.geometry.shape import Shape
8 changes: 5 additions & 3 deletions sympy/geometry/parabola.py
Expand Up @@ -15,6 +15,7 @@
from sympy.geometry.line import Line, Line2D, Ray2D, Segment2D, LinearEntity3D
from sympy.geometry.ellipse import Ellipse

from sympy import Abs, sqrt

class Parabola(GeometrySet):
"""A parabolic GeometryEntity.
Expand Down Expand Up @@ -407,9 +408,10 @@ def vertex(self):

"""
focus = self.focus
if (self.axis_of_symmetry.slope == 0):
vertex = Point(focus.args[0] - self.p_parameter, focus.args[1])

if(self.axis_of_symmetry.slope == 0):
vertex = Point((focus.args[0] + self.directrix.p1[0])/2, focus.args[1])
else:
vertex = Point(focus.args[0], focus.args[1] - self.p_parameter)
vertex = Point(focus.args[0], (focus.args[1] + self.directrix.p1[1])/2)

return vertex
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes #14461

318 changes: 318 additions & 0 deletions sympy/geometry/shape.py
@@ -0,0 +1,318 @@
""" 2D geometrical entity constructed by boolean operations
of various geometric elements.

"""

from __future__ import division, print_function
from sympy.core import S, pi, sympify
from sympy.core.compatibility import ordered
from sympy.core.symbol import _symbol
from sympy.core.numbers import Rational, oo
from sympy import symbols, simplify, solve
from sympy.geometry.entity import GeometryEntity, GeometrySet
from sympy.geometry.point import Point, Point2D
from sympy.geometry.line import Line, Line2D, Ray2D, Segment2D, LinearEntity3D
from sympy.geometry.ellipse import Ellipse, Circle
from sympy.geometry.parabola import Parabola
from sympy.functions.elementary.trigonometric import asin

from sympy import Abs, sqrt

class Shape(GeometrySet):
"""
Parameters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a description to the top of the docstring describing what the class is.

Copy link
Contributor Author

@rushyam rushyam Mar 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok. I will add

==========

conic : Ellipse, Circle, Parabola
line : Line

Attributes
==========

area
centroid
second moment of area
product moment of area

Raises
======

TypeError
Wrong type of argument were put
When The generated shape is not a closed figure
NotImplementedError
When `line` is neither horizontal nor vertical.

Notes
=====

If the conic figure is Circle or Ellipse and if the line is vertical,
then the shape will be segment that belongs to right side of the line.
And if the line is horizontal, then the shape will be above the line.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really clear what this class is supposed to represent, but this seems confusing to me. Is there a better way to construct such shapes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

screenshot from 2018-03-23 17-38-31
screenshot from 2018-03-23 17-33-52

It will give the property of the shaded region and this is form by the intersection of the ellipse with vertical and horizontal lines.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can form similar type of close shape using straight line and (parabola, ellipse, circle)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really clear what this class is supposed to represent, but this seems confusing to me. Is there a better way to construct such shapes?

These might better be termed "segments" of conics (and I would prefer that to "shape" which is a much more general thing). They could perhaps be constructed by giving a line, width, and height and then another parameter that would determine (when width != height) whether the segment is for a parabola or an ellipse. But using the conic itself doesn't seem too bad.


Examples
========

>>> from sympy import Point, Line, Parabola, Ellipse, Circle, Shape
>>> e = Ellipse((0, 0), 4, 2)
>>> p = Parabola((2, 0), Line((-2, 0), (-2, 2)))
>>> c = Circle((0, 0), 4)
>>> l = Line((2, 0), (2, 2))
>>> Shape(e, l)
Shape(Ellipse(Point2D(0, 0), 4, 2), Line2D(Point2D(2, 0), Point2D(2, 2)))
>>> Shape(p, l)
Shape(Parabola(Point2D(2, 0), Line2D(Point2D(-2, 0), Point2D(-2, 2))), Line2D(Point2D(2, 0), Point2D(2, 2)))
>>> Shape(c, l)
Shape(Circle(Point2D(0, 0), 4), Line2D(Point2D(2, 0), Point2D(2, 2)))

"""

def __new__(cls, conic, line):
if (isinstance(conic, Circle) == 0 and isinstance(conic, Parabola) == 0 and isinstance(conic, Ellipse) == 0):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use if not isinstance(conic, (Circle, Parabola, Ellipse)). Don't use == 0 to compare booleans (see PEP 8).

raise TypeError('Wrong type of argument were put')

if (line.slope != 0 and line.slope != S.Infinity):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if line.slope not in {0, oo}

raise NotImplementedError('The line must be a horizontal'
' or vertical line')

intersection = conic.intersection(line)

if(len(intersection) < 2):
if(isinstance(conic, Parabola)):
raise TypeError('The shape is not a closed figure')
else:
raise TypeError('The conic-section is not cut by the line')

return GeometryEntity.__new__(cls, conic, line)

@property
def name(self):
if isinstance(self.conic, Circle):
return 'Circle'
if isinstance(self.conic, Parabola):
return 'Parabola'
if isinstance(self.conic, Ellipse):
return 'Ellipse'

@property
def conic(self):
return self.args[0]

@property
def line(self):
return self.args[1]

@property
def area(self):
"""The area of the generated shape.

See Also
========

sympy.geometry.ellipse.Ellipse.area, sympy.geometry.ellipse.Circle.area

Examples
========

>>> from sympy import Point, Line, Parabola, Ellipse, Circle, Shape
>>> e = Ellipse((0, 0), 4, 2)
>>> p = Parabola((1, 0), Line((-3, 0), (-3, 2)))
>>> c = Circle((0, 0), 4)
>>> l = Line((0, 0), (0, 2))
>>> Shape(e, l).area
4*pi
>>> Shape(p, l).area
8*sqrt(2)/3
>>> Shape(c, l).area
8*pi

"""
if(self.name == 'Parabola'):
f_l = self.conic.focal_length

if(self.line.slope == 0):
l = Abs(self.line.p1[1] - self.conic.vertex[1])
return ((8*sqrt(f_l)*sqrt(l)**3))/3

if(self.line.slope == S.Infinity):
l = Abs(self.line.p1[0] - self.conic.vertex[0])
return ((8*sqrt(f_l)*sqrt(l)**3))/3
else:
a = self.conic.hradius
b = self.conic.vradius

if(self.line.slope == 0):
l = (self.line.p1[1] - self.conic.center[1])
return a*b*((S.Pi/2) - ((l/b)*sqrt(1 - (l/b)**2) + asin(l/b)))

if(self.line.slope == S.Infinity):
l = (self.line.p1[0] - self.conic.center[0])
return a*b*((S.Pi/2) - ((l/a)*sqrt(1 - (l/a)**2) + asin(l/a)))

@property
def centroid(self):
"""The centroid of generated shape.

Returns
=======

centroid : Point

See Also
========

sympy.geometry.point.Point, sympy.geometry.util.centroid

Examples
========

>>> from sympy import Point, Line, Parabola, Ellipse, Circle, Shape
>>> e = Ellipse((0, 0), 4, 2)
>>> p = Parabola((1, 0), Line((-3, 0), (-3, 2)))
>>> c = Circle((0, 0), 4)
>>> l = Line((0, 0), (0, 2))
>>> Shape(e, l).centroid
Point2D(16/(3*pi), 0)
>>> Shape(p, l).centroid
Point2D(-2/5, 0)
>>> Shape(c, l).centroid
Point2D(16/(3*pi), 0)

"""
if(self.name == 'Parabola'):
if(self.conic.directrix.slope == 0):
return Point(self.conic.vertex[0], (2*self.conic.vertex[1] + 3*self.line.p1[1])/5)

if(self.conic.directrix.slope == S.Infinity):
return Point((2*self.conic.vertex[0] + 3*self.line.p1[0])/5, self.conic.vertex[1])

else:
a = self.conic.hradius
b = self.conic.vradius
if(self.line.slope == 0):
l = (self.line.p1[1] - self.conic.center[1])
y = (2*a*(b**2)/3)*((sqrt(1 - (l/b)**2))**3)
return Point(self.conic.center[0], y/self.area)

if(self.line.slope == S.Infinity):
l = (self.line.p1[0] - self.conic.center[0])
x = (2*b*(a**2)/3)*((sqrt(1 - (l/a)**2))**3)
return Point( x/self.area, self.conic.center[1])

def second_moment_of_area(self, point=None):
"""Returns the second moment and product moment of area of generated shape.

Parameters
==========

point : Point, two-tuple of sympifiable objects, or None(default=None)
point is the point about which second moment of area is to be found.
If "point=None" it will be calculated about the axis passing through the
centroid of the generated shape.

Returns
=======

I_xx, I_yy, I_xy : number or sympy expression
I_xx, I_yy are second moment of area of generated shape.
I_xy is product moment of area of generated shape.

Examples
========

>>> from sympy import Point, Line, Parabola, Ellipse, Circle, Shape
>>> e = Ellipse((0, 0), 4, 2)
>>> p = Parabola((1, 0), Line((-3, 0), (-3, 2)))
>>> c = Circle((0, 0), 4)
>>> l = Line((0, 0), (0, 2))
>>> Shape(e, l).second_moment_of_area()
(4*pi, -1024/(9*pi) + 16*pi, 0)
>>> Shape(p, l).second_moment_of_area()
(64*sqrt(2)/15, 32*sqrt(2)/175, 0)
>>> Shape(c, l).second_moment_of_area()
(32*pi, -2048/(9*pi) + 32*pi, 0)

"""
if(self.name == 'Parabola'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use parentheses with if.

a = self.conic.focal_length
I_xy_v = 0;
c = self.centroid
Ar = self.area
if(self.conic.directrix.slope == 0):
I_xx_v = 32*sqrt((a**3))*sqrt(Abs((self.conic.vertex[1] - self.line.p1[1])**5))/15
I_yy_v = 8*sqrt(a*(Abs((self.conic.vertex[1] - self.line.p1[1]))**7))/7

I_xx_c = I_yy_v + Ar*((c[1] - self.conic.vertex[1])**2)
I_yy_c = I_xx_v
I_xy_c = I_xy_v
if point is None:
return I_xx_c, I_yy_c, I_xy_c

I_xx = I_xx_c + Ar*((c[1] - point[1])**2)
I_yy = I_yy_c + Ar*((c[0] - point[0])**2)
I_xy = I_xy_c + Ar*((point[0]-c[0])*(point[1]-c[1]))

return I_xx, I_yy, I_xy

if(self.conic.directrix.slope == S.Infinity):
I_xx_v = 32*sqrt((a**3))*sqrt((Abs((self.conic.vertex[0] - self.line.p1[0]))**5))/15
I_yy_v = 8*sqrt(a*(Abs((self.conic.vertex[0] - self.line.p1[0]))**7))/7

I_xx_c = I_xx_v
I_yy_c = I_yy_v - Ar*((c[0] - self.conic.vertex[0])**2)
I_xy_c = I_xy_v
if point is None:
return I_xx_c, I_yy_c, I_xy_c

I_xx = I_xx_c + Ar*((c[1] - point[1])**2)
I_yy = I_yy_c + Ar*((c[0] - point[0])**2)
I_xy = I_xy_c + Ar*((point[0]-c[0])*(point[1]-c[1]))

return I_xx, I_yy, I_xy

else:
a = self.conic.hradius
b = self.conic.vradius
I_xy_v = 0
c = self.centroid
Ar = self.area
if(self.line.slope == 0):
l = (self.line.p1[1] - self.conic.center[1])

z = (l/b)*sqrt(1 - (l/b)**2)*(1 - 2*(l/b)**2)

I_yy_v = b*(a**3)*(asin(sqrt(1 - (l/b)**2) + z))/4 - 2*l*(a*(sqrt(1 - (l/b)**2)))**3/3
I_xx_v = a*(b**3)*((S.Pi/2) - asin(l/b) + z)/4

I_xx_c = I_yy_v + Ar*((c[1] - self.conic.center[1])**2)
I_yy_c = I_xx_v
I_xy_c = I_xy_v
if point is None:
return I_xx_c, I_yy_c, I_xy_c

I_xx = I_xx_c + Ar*((c[1] - point[1])**2)
I_yy = I_yy_c + Ar*((c[0] - point[0])**2)
I_xy = I_xy_c + Ar*((point[0]-c[0])*(point[1]-c[1]))

return I_xx, I_yy, I_xy

if(self.line.slope == S.Infinity):
l = (self.line.p1[0] - self.conic.center[0])

z = (l/a)*sqrt(1 - (l/a)**2)*(1 - 2*(l/a)**2)

I_yy_v = b*(a**3)*((S.Pi/2) - asin(l/a) + z)/4
I_xx_v = a*(b**3)*(asin(sqrt(1 - (l/a)**2)) + z)/4 - 2*l*(b*(sqrt(1 - (l/a)**2)))**3/3

I_xx_c = I_xx_v
I_yy_c = I_yy_v - Ar*((c[0] - self.conic.center[0])**2)
I_xy_c = I_xy_v

if point is None:
return I_xx_c, I_yy_c, I_xy_c

I_xx = I_xx_c + Ar*((c[1] - point[1])**2)
I_yy = I_yy_c + Ar*((c[0] - point[0])**2)
I_xy = I_xy_c + Ar*((point[0]-c[0])*(point[1]-c[1]))

return I_xx, I_yy, I_xy