123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782 |
- """Elliptical geometrical entities.
- Contains
- * Ellipse
- * Circle
- """
- from sympy.core.expr import Expr
- from sympy.core.relational import Eq
- from sympy.core import S, pi, sympify
- from sympy.core.evalf import N
- from sympy.core.parameters import global_parameters
- from sympy.core.logic import fuzzy_bool
- from sympy.core.numbers import Rational, oo
- from sympy.core.sorting import ordered
- from sympy.core.symbol import Dummy, uniquely_named_symbol, _symbol
- from sympy.simplify import simplify, trigsimp
- from sympy.functions.elementary.miscellaneous import sqrt, Max
- from sympy.functions.elementary.trigonometric import cos, sin
- from sympy.functions.special.elliptic_integrals import elliptic_e
- from .entity import GeometryEntity, GeometrySet
- from .exceptions import GeometryError
- from .line import Line, Segment, Ray2D, Segment2D, Line2D, LinearEntity3D
- from .point import Point, Point2D, Point3D
- from .util import idiff, find
- from sympy.polys import DomainError, Poly, PolynomialError
- from sympy.polys.polyutils import _not_a_coeff, _nsort
- from sympy.solvers import solve
- from sympy.solvers.solveset import linear_coeffs
- from sympy.utilities.misc import filldedent, func_name
- from mpmath.libmp.libmpf import prec_to_dps
- import random
- class Ellipse(GeometrySet):
- """An elliptical GeometryEntity.
- Parameters
- ==========
- center : Point, optional
- Default value is Point(0, 0)
- hradius : number or SymPy expression, optional
- vradius : number or SymPy expression, optional
- eccentricity : number or SymPy expression, optional
- Two of `hradius`, `vradius` and `eccentricity` must be supplied to
- create an Ellipse. The third is derived from the two supplied.
- Attributes
- ==========
- center
- hradius
- vradius
- area
- circumference
- eccentricity
- periapsis
- apoapsis
- focus_distance
- foci
- Raises
- ======
- GeometryError
- When `hradius`, `vradius` and `eccentricity` are incorrectly supplied
- as parameters.
- TypeError
- When `center` is not a Point.
- See Also
- ========
- Circle
- Notes
- -----
- Constructed from a center and two radii, the first being the horizontal
- radius (along the x-axis) and the second being the vertical radius (along
- the y-axis).
- When symbolic value for hradius and vradius are used, any calculation that
- refers to the foci or the major or minor axis will assume that the ellipse
- has its major radius on the x-axis. If this is not true then a manual
- rotation is necessary.
- Examples
- ========
- >>> from sympy import Ellipse, Point, Rational
- >>> e1 = Ellipse(Point(0, 0), 5, 1)
- >>> e1.hradius, e1.vradius
- (5, 1)
- >>> e2 = Ellipse(Point(3, 1), hradius=3, eccentricity=Rational(4, 5))
- >>> e2
- Ellipse(Point2D(3, 1), 3, 9/5)
- """
- def __contains__(self, o):
- if isinstance(o, Point):
- x = Dummy('x', real=True)
- y = Dummy('y', real=True)
- res = self.equation(x, y).subs({x: o.x, y: o.y})
- return trigsimp(simplify(res)) is S.Zero
- elif isinstance(o, Ellipse):
- return self == o
- return False
- def __eq__(self, o):
- """Is the other GeometryEntity the same as this ellipse?"""
- return isinstance(o, Ellipse) and (self.center == o.center and
- self.hradius == o.hradius and
- self.vradius == o.vradius)
- def __hash__(self):
- return super().__hash__()
- def __new__(
- cls, center=None, hradius=None, vradius=None, eccentricity=None, **kwargs):
- hradius = sympify(hradius)
- vradius = sympify(vradius)
- eccentricity = sympify(eccentricity)
- if center is None:
- center = Point(0, 0)
- else:
- center = Point(center, dim=2)
- if len(center) != 2:
- raise ValueError('The center of "{}" must be a two dimensional point'.format(cls))
- if len(list(filter(lambda x: x is not None, (hradius, vradius, eccentricity)))) != 2:
- raise ValueError(filldedent('''
- Exactly two arguments of "hradius", "vradius", and
- "eccentricity" must not be None.'''))
- if eccentricity is not None:
- if eccentricity.is_negative:
- raise GeometryError("Eccentricity of ellipse/circle should lie between [0, 1)")
- elif hradius is None:
- hradius = vradius / sqrt(1 - eccentricity**2)
- elif vradius is None:
- vradius = hradius * sqrt(1 - eccentricity**2)
- if hradius == vradius:
- return Circle(center, hradius, **kwargs)
- if S.Zero in (hradius, vradius):
- return Segment(Point(center[0] - hradius, center[1] - vradius), Point(center[0] + hradius, center[1] + vradius))
- if hradius.is_real is False or vradius.is_real is False:
- raise GeometryError("Invalid value encountered when computing hradius / vradius.")
- return GeometryEntity.__new__(cls, center, hradius, vradius, **kwargs)
- def _svg(self, scale_factor=1., fill_color="#66cc99"):
- """Returns SVG ellipse element for the Ellipse.
- Parameters
- ==========
- scale_factor : float
- Multiplication factor for the SVG stroke-width. Default is 1.
- fill_color : str, optional
- Hex string for fill color. Default is "#66cc99".
- """
- c = N(self.center)
- h, v = N(self.hradius), N(self.vradius)
- return (
- '<ellipse fill="{1}" stroke="#555555" '
- 'stroke-width="{0}" opacity="0.6" cx="{2}" cy="{3}" rx="{4}" ry="{5}"/>'
- ).format(2. * scale_factor, fill_color, c.x, c.y, h, v)
- @property
- def ambient_dimension(self):
- return 2
- @property
- def apoapsis(self):
- """The apoapsis of the ellipse.
- The greatest distance between the focus and the contour.
- Returns
- =======
- apoapsis : number
- See Also
- ========
- periapsis : Returns shortest distance between foci and contour
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.apoapsis
- 2*sqrt(2) + 3
- """
- return self.major * (1 + self.eccentricity)
- def arbitrary_point(self, parameter='t'):
- """A parameterized point on the ellipse.
- Parameters
- ==========
- parameter : str, optional
- Default value is 't'.
- Returns
- =======
- arbitrary_point : Point
- Raises
- ======
- ValueError
- When `parameter` already appears in the functions.
- See Also
- ========
- sympy.geometry.point.Point
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> e1 = Ellipse(Point(0, 0), 3, 2)
- >>> e1.arbitrary_point()
- Point2D(3*cos(t), 2*sin(t))
- """
- t = _symbol(parameter, real=True)
- if t.name in (f.name for f in self.free_symbols):
- raise ValueError(filldedent('Symbol %s already appears in object '
- 'and cannot be used as a parameter.' % t.name))
- return Point(self.center.x + self.hradius*cos(t),
- self.center.y + self.vradius*sin(t))
- @property
- def area(self):
- """The area of the ellipse.
- Returns
- =======
- area : number
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.area
- 3*pi
- """
- return simplify(S.Pi * self.hradius * self.vradius)
- @property
- def bounds(self):
- """Return a tuple (xmin, ymin, xmax, ymax) representing the bounding
- rectangle for the geometric figure.
- """
- h, v = self.hradius, self.vradius
- return (self.center.x - h, self.center.y - v, self.center.x + h, self.center.y + v)
- @property
- def center(self):
- """The center of the ellipse.
- Returns
- =======
- center : number
- See Also
- ========
- sympy.geometry.point.Point
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.center
- Point2D(0, 0)
- """
- return self.args[0]
- @property
- def circumference(self):
- """The circumference of the ellipse.
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.circumference
- 12*elliptic_e(8/9)
- """
- if self.eccentricity == 1:
- # degenerate
- return 4*self.major
- elif self.eccentricity == 0:
- # circle
- return 2*pi*self.hradius
- else:
- return 4*self.major*elliptic_e(self.eccentricity**2)
- @property
- def eccentricity(self):
- """The eccentricity of the ellipse.
- Returns
- =======
- eccentricity : number
- Examples
- ========
- >>> from sympy import Point, Ellipse, sqrt
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, sqrt(2))
- >>> e1.eccentricity
- sqrt(7)/3
- """
- return self.focus_distance / self.major
- def encloses_point(self, p):
- """
- Return True if p is enclosed by (is inside of) self.
- Notes
- -----
- Being on the border of self is considered False.
- Parameters
- ==========
- p : Point
- Returns
- =======
- encloses_point : True, False or None
- See Also
- ========
- sympy.geometry.point.Point
- Examples
- ========
- >>> from sympy import Ellipse, S
- >>> from sympy.abc import t
- >>> e = Ellipse((0, 0), 3, 2)
- >>> e.encloses_point((0, 0))
- True
- >>> e.encloses_point(e.arbitrary_point(t).subs(t, S.Half))
- False
- >>> e.encloses_point((4, 0))
- False
- """
- p = Point(p, dim=2)
- if p in self:
- return False
- if len(self.foci) == 2:
- # if the combined distance from the foci to p (h1 + h2) is less
- # than the combined distance from the foci to the minor axis
- # (which is the same as the major axis length) then p is inside
- # the ellipse
- h1, h2 = [f.distance(p) for f in self.foci]
- test = 2*self.major - (h1 + h2)
- else:
- test = self.radius - self.center.distance(p)
- return fuzzy_bool(test.is_positive)
- def equation(self, x='x', y='y', _slope=None):
- """
- Returns the equation of an ellipse aligned with the x and y axes;
- when slope is given, the equation returned corresponds to an ellipse
- with a major axis having that slope.
- Parameters
- ==========
- x : str, optional
- Label for the x-axis. Default value is 'x'.
- y : str, optional
- Label for the y-axis. Default value is 'y'.
- _slope : Expr, optional
- The slope of the major axis. Ignored when 'None'.
- Returns
- =======
- equation : SymPy expression
- See Also
- ========
- arbitrary_point : Returns parameterized point on ellipse
- Examples
- ========
- >>> from sympy import Point, Ellipse, pi
- >>> from sympy.abc import x, y
- >>> e1 = Ellipse(Point(1, 0), 3, 2)
- >>> eq1 = e1.equation(x, y); eq1
- y**2/4 + (x/3 - 1/3)**2 - 1
- >>> eq2 = e1.equation(x, y, _slope=1); eq2
- (-x + y + 1)**2/8 + (x + y - 1)**2/18 - 1
- A point on e1 satisfies eq1. Let's use one on the x-axis:
- >>> p1 = e1.center + Point(e1.major, 0)
- >>> assert eq1.subs(x, p1.x).subs(y, p1.y) == 0
- When rotated the same as the rotated ellipse, about the center
- point of the ellipse, it will satisfy the rotated ellipse's
- equation, too:
- >>> r1 = p1.rotate(pi/4, e1.center)
- >>> assert eq2.subs(x, r1.x).subs(y, r1.y) == 0
- References
- ==========
- .. [1] https://math.stackexchange.com/questions/108270/what-is-the-equation-of-an-ellipse-that-is-not-aligned-with-the-axis
- .. [2] https://en.wikipedia.org/wiki/Ellipse#Equation_of_a_shifted_ellipse
- """
- x = _symbol(x, real=True)
- y = _symbol(y, real=True)
- dx = x - self.center.x
- dy = y - self.center.y
- if _slope is not None:
- L = (dy - _slope*dx)**2
- l = (_slope*dy + dx)**2
- h = 1 + _slope**2
- b = h*self.major**2
- a = h*self.minor**2
- return l/b + L/a - 1
- else:
- t1 = (dx/self.hradius)**2
- t2 = (dy/self.vradius)**2
- return t1 + t2 - 1
- def evolute(self, x='x', y='y'):
- """The equation of evolute of the ellipse.
- Parameters
- ==========
- x : str, optional
- Label for the x-axis. Default value is 'x'.
- y : str, optional
- Label for the y-axis. Default value is 'y'.
- Returns
- =======
- equation : SymPy expression
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> e1 = Ellipse(Point(1, 0), 3, 2)
- >>> e1.evolute()
- 2**(2/3)*y**(2/3) + (3*x - 3)**(2/3) - 5**(2/3)
- """
- if len(self.args) != 3:
- raise NotImplementedError('Evolute of arbitrary Ellipse is not supported.')
- x = _symbol(x, real=True)
- y = _symbol(y, real=True)
- t1 = (self.hradius*(x - self.center.x))**Rational(2, 3)
- t2 = (self.vradius*(y - self.center.y))**Rational(2, 3)
- return t1 + t2 - (self.hradius**2 - self.vradius**2)**Rational(2, 3)
- @property
- def foci(self):
- """The foci of the ellipse.
- Notes
- -----
- The foci can only be calculated if the major/minor axes are known.
- Raises
- ======
- ValueError
- When the major and minor axis cannot be determined.
- See Also
- ========
- sympy.geometry.point.Point
- focus_distance : Returns the distance between focus and center
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.foci
- (Point2D(-2*sqrt(2), 0), Point2D(2*sqrt(2), 0))
- """
- c = self.center
- hr, vr = self.hradius, self.vradius
- if hr == vr:
- return (c, c)
- # calculate focus distance manually, since focus_distance calls this
- # routine
- fd = sqrt(self.major**2 - self.minor**2)
- if hr == self.minor:
- # foci on the y-axis
- return (c + Point(0, -fd), c + Point(0, fd))
- elif hr == self.major:
- # foci on the x-axis
- return (c + Point(-fd, 0), c + Point(fd, 0))
- @property
- def focus_distance(self):
- """The focal distance of the ellipse.
- The distance between the center and one focus.
- Returns
- =======
- focus_distance : number
- See Also
- ========
- foci
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.focus_distance
- 2*sqrt(2)
- """
- return Point.distance(self.center, self.foci[0])
- @property
- def hradius(self):
- """The horizontal radius of the ellipse.
- Returns
- =======
- hradius : number
- See Also
- ========
- vradius, major, minor
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.hradius
- 3
- """
- return self.args[1]
- def intersection(self, o):
- """The intersection of this ellipse and another geometrical entity
- `o`.
- Parameters
- ==========
- o : GeometryEntity
- Returns
- =======
- intersection : list of GeometryEntity objects
- Notes
- -----
- Currently supports intersections with Point, Line, Segment, Ray,
- Circle and Ellipse types.
- See Also
- ========
- sympy.geometry.entity.GeometryEntity
- Examples
- ========
- >>> from sympy import Ellipse, Point, Line
- >>> e = Ellipse(Point(0, 0), 5, 7)
- >>> e.intersection(Point(0, 0))
- []
- >>> e.intersection(Point(5, 0))
- [Point2D(5, 0)]
- >>> e.intersection(Line(Point(0,0), Point(0, 1)))
- [Point2D(0, -7), Point2D(0, 7)]
- >>> e.intersection(Line(Point(5,0), Point(5, 1)))
- [Point2D(5, 0)]
- >>> e.intersection(Line(Point(6,0), Point(6, 1)))
- []
- >>> e = Ellipse(Point(-1, 0), 4, 3)
- >>> e.intersection(Ellipse(Point(1, 0), 4, 3))
- [Point2D(0, -3*sqrt(15)/4), Point2D(0, 3*sqrt(15)/4)]
- >>> e.intersection(Ellipse(Point(5, 0), 4, 3))
- [Point2D(2, -3*sqrt(7)/4), Point2D(2, 3*sqrt(7)/4)]
- >>> e.intersection(Ellipse(Point(100500, 0), 4, 3))
- []
- >>> e.intersection(Ellipse(Point(0, 0), 3, 4))
- [Point2D(3, 0), Point2D(-363/175, -48*sqrt(111)/175), Point2D(-363/175, 48*sqrt(111)/175)]
- >>> e.intersection(Ellipse(Point(-1, 0), 3, 4))
- [Point2D(-17/5, -12/5), Point2D(-17/5, 12/5), Point2D(7/5, -12/5), Point2D(7/5, 12/5)]
- """
- # TODO: Replace solve with nonlinsolve, when nonlinsolve will be able to solve in real domain
- x = Dummy('x', real=True)
- y = Dummy('y', real=True)
- if isinstance(o, Point):
- if o in self:
- return [o]
- else:
- return []
- elif isinstance(o, (Segment2D, Ray2D)):
- ellipse_equation = self.equation(x, y)
- result = solve([ellipse_equation, Line(o.points[0], o.points[1]).equation(x, y)], [x, y])
- return list(ordered([Point(i) for i in result if i in o]))
- elif isinstance(o, Polygon):
- return o.intersection(self)
- elif isinstance(o, (Ellipse, Line2D)):
- if o == self:
- return self
- else:
- ellipse_equation = self.equation(x, y)
- return list(ordered([Point(i) for i in solve([ellipse_equation, o.equation(x, y)], [x, y])]))
- elif isinstance(o, LinearEntity3D):
- raise TypeError('Entity must be two dimensional, not three dimensional')
- else:
- raise TypeError('Intersection not handled for %s' % func_name(o))
- def is_tangent(self, o):
- """Is `o` tangent to the ellipse?
- Parameters
- ==========
- o : GeometryEntity
- An Ellipse, LinearEntity or Polygon
- Raises
- ======
- NotImplementedError
- When the wrong type of argument is supplied.
- Returns
- =======
- is_tangent: boolean
- True if o is tangent to the ellipse, False otherwise.
- See Also
- ========
- tangent_lines
- Examples
- ========
- >>> from sympy import Point, Ellipse, Line
- >>> p0, p1, p2 = Point(0, 0), Point(3, 0), Point(3, 3)
- >>> e1 = Ellipse(p0, 3, 2)
- >>> l1 = Line(p1, p2)
- >>> e1.is_tangent(l1)
- True
- """
- if isinstance(o, Point2D):
- return False
- elif isinstance(o, Ellipse):
- intersect = self.intersection(o)
- if isinstance(intersect, Ellipse):
- return True
- elif intersect:
- return all((self.tangent_lines(i)[0]).equals(o.tangent_lines(i)[0]) for i in intersect)
- else:
- return False
- elif isinstance(o, Line2D):
- hit = self.intersection(o)
- if not hit:
- return False
- if len(hit) == 1:
- return True
- # might return None if it can't decide
- return hit[0].equals(hit[1])
- elif isinstance(o, Ray2D):
- intersect = self.intersection(o)
- if len(intersect) == 1:
- return intersect[0] != o.source and not self.encloses_point(o.source)
- else:
- return False
- elif isinstance(o, (Segment2D, Polygon)):
- all_tangents = False
- segments = o.sides if isinstance(o, Polygon) else [o]
- for segment in segments:
- intersect = self.intersection(segment)
- if len(intersect) == 1:
- if not any(intersect[0] in i for i in segment.points) \
- and not any(self.encloses_point(i) for i in segment.points):
- all_tangents = True
- continue
- else:
- return False
- else:
- return all_tangents
- return all_tangents
- elif isinstance(o, (LinearEntity3D, Point3D)):
- raise TypeError('Entity must be two dimensional, not three dimensional')
- else:
- raise TypeError('Is_tangent not handled for %s' % func_name(o))
- @property
- def major(self):
- """Longer axis of the ellipse (if it can be determined) else hradius.
- Returns
- =======
- major : number or expression
- See Also
- ========
- hradius, vradius, minor
- Examples
- ========
- >>> from sympy import Point, Ellipse, Symbol
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.major
- 3
- >>> a = Symbol('a')
- >>> b = Symbol('b')
- >>> Ellipse(p1, a, b).major
- a
- >>> Ellipse(p1, b, a).major
- b
- >>> m = Symbol('m')
- >>> M = m + 1
- >>> Ellipse(p1, m, M).major
- m + 1
- """
- ab = self.args[1:3]
- if len(ab) == 1:
- return ab[0]
- a, b = ab
- o = b - a < 0
- if o == True:
- return a
- elif o == False:
- return b
- return self.hradius
- @property
- def minor(self):
- """Shorter axis of the ellipse (if it can be determined) else vradius.
- Returns
- =======
- minor : number or expression
- See Also
- ========
- hradius, vradius, major
- Examples
- ========
- >>> from sympy import Point, Ellipse, Symbol
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.minor
- 1
- >>> a = Symbol('a')
- >>> b = Symbol('b')
- >>> Ellipse(p1, a, b).minor
- b
- >>> Ellipse(p1, b, a).minor
- a
- >>> m = Symbol('m')
- >>> M = m + 1
- >>> Ellipse(p1, m, M).minor
- m
- """
- ab = self.args[1:3]
- if len(ab) == 1:
- return ab[0]
- a, b = ab
- o = a - b < 0
- if o == True:
- return a
- elif o == False:
- return b
- return self.vradius
- def normal_lines(self, p, prec=None):
- """Normal lines between `p` and the ellipse.
- Parameters
- ==========
- p : Point
- Returns
- =======
- normal_lines : list with 1, 2 or 4 Lines
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> e = Ellipse((0, 0), 2, 3)
- >>> c = e.center
- >>> e.normal_lines(c + Point(1, 0))
- [Line2D(Point2D(0, 0), Point2D(1, 0))]
- >>> e.normal_lines(c)
- [Line2D(Point2D(0, 0), Point2D(0, 1)), Line2D(Point2D(0, 0), Point2D(1, 0))]
- Off-axis points require the solution of a quartic equation. This
- often leads to very large expressions that may be of little practical
- use. An approximate solution of `prec` digits can be obtained by
- passing in the desired value:
- >>> e.normal_lines((3, 3), prec=2)
- [Line2D(Point2D(-0.81, -2.7), Point2D(0.19, -1.2)),
- Line2D(Point2D(1.5, -2.0), Point2D(2.5, -2.7))]
- Whereas the above solution has an operation count of 12, the exact
- solution has an operation count of 2020.
- """
- p = Point(p, dim=2)
- # XXX change True to something like self.angle == 0 if the arbitrarily
- # rotated ellipse is introduced.
- # https://github.com/sympy/sympy/issues/2815)
- if True:
- rv = []
- if p.x == self.center.x:
- rv.append(Line(self.center, slope=oo))
- if p.y == self.center.y:
- rv.append(Line(self.center, slope=0))
- if rv:
- # at these special orientations of p either 1 or 2 normals
- # exist and we are done
- return rv
- # find the 4 normal points and construct lines through them with
- # the corresponding slope
- x, y = Dummy('x', real=True), Dummy('y', real=True)
- eq = self.equation(x, y)
- dydx = idiff(eq, y, x)
- norm = -1/dydx
- slope = Line(p, (x, y)).slope
- seq = slope - norm
- # TODO: Replace solve with solveset, when this line is tested
- yis = solve(seq, y)[0]
- xeq = eq.subs(y, yis).as_numer_denom()[0].expand()
- if len(xeq.free_symbols) == 1:
- try:
- # this is so much faster, it's worth a try
- xsol = Poly(xeq, x).real_roots()
- except (DomainError, PolynomialError, NotImplementedError):
- # TODO: Replace solve with solveset, when these lines are tested
- xsol = _nsort(solve(xeq, x), separated=True)[0]
- points = [Point(i, solve(eq.subs(x, i), y)[0]) for i in xsol]
- else:
- raise NotImplementedError(
- 'intersections for the general ellipse are not supported')
- slopes = [norm.subs(zip((x, y), pt.args)) for pt in points]
- if prec is not None:
- points = [pt.n(prec) for pt in points]
- slopes = [i if _not_a_coeff(i) else i.n(prec) for i in slopes]
- return [Line(pt, slope=s) for pt, s in zip(points, slopes)]
- @property
- def periapsis(self):
- """The periapsis of the ellipse.
- The shortest distance between the focus and the contour.
- Returns
- =======
- periapsis : number
- See Also
- ========
- apoapsis : Returns greatest distance between focus and contour
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.periapsis
- 3 - 2*sqrt(2)
- """
- return self.major * (1 - self.eccentricity)
- @property
- def semilatus_rectum(self):
- """
- Calculates the semi-latus rectum of the Ellipse.
- Semi-latus rectum is defined as one half of the chord through a
- focus parallel to the conic section directrix of a conic section.
- Returns
- =======
- semilatus_rectum : number
- See Also
- ========
- apoapsis : Returns greatest distance between focus and contour
- periapsis : The shortest distance between the focus and the contour
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.semilatus_rectum
- 1/3
- References
- ==========
- .. [1] http://mathworld.wolfram.com/SemilatusRectum.html
- .. [2] https://en.wikipedia.org/wiki/Ellipse#Semi-latus_rectum
- """
- return self.major * (1 - self.eccentricity ** 2)
- def auxiliary_circle(self):
- """Returns a Circle whose diameter is the major axis of the ellipse.
- Examples
- ========
- >>> from sympy import Ellipse, Point, symbols
- >>> c = Point(1, 2)
- >>> Ellipse(c, 8, 7).auxiliary_circle()
- Circle(Point2D(1, 2), 8)
- >>> a, b = symbols('a b')
- >>> Ellipse(c, a, b).auxiliary_circle()
- Circle(Point2D(1, 2), Max(a, b))
- """
- return Circle(self.center, Max(self.hradius, self.vradius))
- def director_circle(self):
- """
- Returns a Circle consisting of all points where two perpendicular
- tangent lines to the ellipse cross each other.
- Returns
- =======
- Circle
- A director circle returned as a geometric object.
- Examples
- ========
- >>> from sympy import Ellipse, Point, symbols
- >>> c = Point(3,8)
- >>> Ellipse(c, 7, 9).director_circle()
- Circle(Point2D(3, 8), sqrt(130))
- >>> a, b = symbols('a b')
- >>> Ellipse(c, a, b).director_circle()
- Circle(Point2D(3, 8), sqrt(a**2 + b**2))
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Director_circle
- """
- return Circle(self.center, sqrt(self.hradius**2 + self.vradius**2))
- def plot_interval(self, parameter='t'):
- """The plot interval for the default geometric plot of the Ellipse.
- Parameters
- ==========
- parameter : str, optional
- Default value is 't'.
- Returns
- =======
- plot_interval : list
- [parameter, lower_bound, upper_bound]
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> e1 = Ellipse(Point(0, 0), 3, 2)
- >>> e1.plot_interval()
- [t, -pi, pi]
- """
- t = _symbol(parameter, real=True)
- return [t, -S.Pi, S.Pi]
- def random_point(self, seed=None):
- """A random point on the ellipse.
- Returns
- =======
- point : Point
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> e1 = Ellipse(Point(0, 0), 3, 2)
- >>> e1.random_point() # gives some random point
- Point2D(...)
- >>> p1 = e1.random_point(seed=0); p1.n(2)
- Point2D(2.1, 1.4)
- Notes
- =====
- When creating a random point, one may simply replace the
- parameter with a random number. When doing so, however, the
- random number should be made a Rational or else the point
- may not test as being in the ellipse:
- >>> from sympy.abc import t
- >>> from sympy import Rational
- >>> arb = e1.arbitrary_point(t); arb
- Point2D(3*cos(t), 2*sin(t))
- >>> arb.subs(t, .1) in e1
- False
- >>> arb.subs(t, Rational(.1)) in e1
- True
- >>> arb.subs(t, Rational('.1')) in e1
- True
- See Also
- ========
- sympy.geometry.point.Point
- arbitrary_point : Returns parameterized point on ellipse
- """
- t = _symbol('t', real=True)
- x, y = self.arbitrary_point(t).args
- # get a random value in [-1, 1) corresponding to cos(t)
- # and confirm that it will test as being in the ellipse
- if seed is not None:
- rng = random.Random(seed)
- else:
- rng = random
- # simplify this now or else the Float will turn s into a Float
- r = Rational(rng.random())
- c = 2*r - 1
- s = sqrt(1 - c**2)
- return Point(x.subs(cos(t), c), y.subs(sin(t), s))
- def reflect(self, line):
- """Override GeometryEntity.reflect since the radius
- is not a GeometryEntity.
- Examples
- ========
- >>> from sympy import Circle, Line
- >>> Circle((0, 1), 1).reflect(Line((0, 0), (1, 1)))
- Circle(Point2D(1, 0), -1)
- >>> from sympy import Ellipse, Line, Point
- >>> Ellipse(Point(3, 4), 1, 3).reflect(Line(Point(0, -4), Point(5, 0)))
- Traceback (most recent call last):
- ...
- NotImplementedError:
- General Ellipse is not supported but the equation of the reflected
- Ellipse is given by the zeros of: f(x, y) = (9*x/41 + 40*y/41 +
- 37/41)**2 + (40*x/123 - 3*y/41 - 364/123)**2 - 1
- Notes
- =====
- Until the general ellipse (with no axis parallel to the x-axis) is
- supported a NotImplemented error is raised and the equation whose
- zeros define the rotated ellipse is given.
- """
- if line.slope in (0, oo):
- c = self.center
- c = c.reflect(line)
- return self.func(c, -self.hradius, self.vradius)
- else:
- x, y = [uniquely_named_symbol(
- name, (self, line), modify=lambda s: '_' + s, real=True)
- for name in 'xy']
- expr = self.equation(x, y)
- p = Point(x, y).reflect(line)
- result = expr.subs(zip((x, y), p.args
- ), simultaneous=True)
- raise NotImplementedError(filldedent(
- 'General Ellipse is not supported but the equation '
- 'of the reflected Ellipse is given by the zeros of: ' +
- "f(%s, %s) = %s" % (str(x), str(y), str(result))))
- def rotate(self, angle=0, pt=None):
- """Rotate ``angle`` radians counterclockwise about Point ``pt``.
- Note: since the general ellipse is not supported, only rotations that
- are integer multiples of pi/2 are allowed.
- Examples
- ========
- >>> from sympy import Ellipse, pi
- >>> Ellipse((1, 0), 2, 1).rotate(pi/2)
- Ellipse(Point2D(0, 1), 1, 2)
- >>> Ellipse((1, 0), 2, 1).rotate(pi)
- Ellipse(Point2D(-1, 0), 2, 1)
- """
- if self.hradius == self.vradius:
- return self.func(self.center.rotate(angle, pt), self.hradius)
- if (angle/S.Pi).is_integer:
- return super().rotate(angle, pt)
- if (2*angle/S.Pi).is_integer:
- return self.func(self.center.rotate(angle, pt), self.vradius, self.hradius)
- # XXX see https://github.com/sympy/sympy/issues/2815 for general ellipes
- raise NotImplementedError('Only rotations of pi/2 are currently supported for Ellipse.')
- def scale(self, x=1, y=1, pt=None):
- """Override GeometryEntity.scale since it is the major and minor
- axes which must be scaled and they are not GeometryEntities.
- Examples
- ========
- >>> from sympy import Ellipse
- >>> Ellipse((0, 0), 2, 1).scale(2, 4)
- Circle(Point2D(0, 0), 4)
- >>> Ellipse((0, 0), 2, 1).scale(2)
- Ellipse(Point2D(0, 0), 4, 1)
- """
- c = self.center
- if pt:
- pt = Point(pt, dim=2)
- return self.translate(*(-pt).args).scale(x, y).translate(*pt.args)
- h = self.hradius
- v = self.vradius
- return self.func(c.scale(x, y), hradius=h*x, vradius=v*y)
- def tangent_lines(self, p):
- """Tangent lines between `p` and the ellipse.
- If `p` is on the ellipse, returns the tangent line through point `p`.
- Otherwise, returns the tangent line(s) from `p` to the ellipse, or
- None if no tangent line is possible (e.g., `p` inside ellipse).
- Parameters
- ==========
- p : Point
- Returns
- =======
- tangent_lines : list with 1 or 2 Lines
- Raises
- ======
- NotImplementedError
- Can only find tangent lines for a point, `p`, on the ellipse.
- See Also
- ========
- sympy.geometry.point.Point, sympy.geometry.line.Line
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> e1 = Ellipse(Point(0, 0), 3, 2)
- >>> e1.tangent_lines(Point(3, 0))
- [Line2D(Point2D(3, 0), Point2D(3, -12))]
- """
- p = Point(p, dim=2)
- if self.encloses_point(p):
- return []
- if p in self:
- delta = self.center - p
- rise = (self.vradius**2)*delta.x
- run = -(self.hradius**2)*delta.y
- p2 = Point(simplify(p.x + run),
- simplify(p.y + rise))
- return [Line(p, p2)]
- else:
- if len(self.foci) == 2:
- f1, f2 = self.foci
- maj = self.hradius
- test = (2*maj -
- Point.distance(f1, p) -
- Point.distance(f2, p))
- else:
- test = self.radius - Point.distance(self.center, p)
- if test.is_number and test.is_positive:
- return []
- # else p is outside the ellipse or we can't tell. In case of the
- # latter, the solutions returned will only be valid if
- # the point is not inside the ellipse; if it is, nan will result.
- x, y = Dummy('x'), Dummy('y')
- eq = self.equation(x, y)
- dydx = idiff(eq, y, x)
- slope = Line(p, Point(x, y)).slope
- # TODO: Replace solve with solveset, when this line is tested
- tangent_points = solve([slope - dydx, eq], [x, y])
- # handle horizontal and vertical tangent lines
- if len(tangent_points) == 1:
- if tangent_points[0][
- 0] == p.x or tangent_points[0][1] == p.y:
- return [Line(p, p + Point(1, 0)), Line(p, p + Point(0, 1))]
- else:
- return [Line(p, p + Point(0, 1)), Line(p, tangent_points[0])]
- # others
- return [Line(p, tangent_points[0]), Line(p, tangent_points[1])]
- @property
- def vradius(self):
- """The vertical radius of the ellipse.
- Returns
- =======
- vradius : number
- See Also
- ========
- hradius, major, minor
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.vradius
- 1
- """
- return self.args[2]
- def second_moment_of_area(self, point=None):
- """Returns the second moment and product moment area of an ellipse.
- 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 ellipse.
- Returns
- =======
- I_xx, I_yy, I_xy : number or SymPy expression
- I_xx, I_yy are second moment of area of an ellise.
- I_xy is product moment of area of an ellipse.
- Examples
- ========
- >>> from sympy import Point, Ellipse
- >>> p1 = Point(0, 0)
- >>> e1 = Ellipse(p1, 3, 1)
- >>> e1.second_moment_of_area()
- (3*pi/4, 27*pi/4, 0)
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/List_of_second_moments_of_area
- """
- I_xx = (S.Pi*(self.hradius)*(self.vradius**3))/4
- I_yy = (S.Pi*(self.hradius**3)*(self.vradius))/4
- I_xy = 0
- if point is None:
- return I_xx, I_yy, I_xy
- # parallel axis theorem
- I_xx = I_xx + self.area*((point[1] - self.center.y)**2)
- I_yy = I_yy + self.area*((point[0] - self.center.x)**2)
- I_xy = I_xy + self.area*(point[0] - self.center.x)*(point[1] - self.center.y)
- return I_xx, I_yy, I_xy
- def polar_second_moment_of_area(self):
- """Returns the polar second moment of area of an Ellipse
- It is a constituent of the second moment of area, linked through
- the perpendicular axis theorem. While the planar second moment of
- area describes an object's resistance to deflection (bending) when
- subjected to a force applied to a plane parallel to the central
- axis, the polar second moment of area describes an object's
- resistance to deflection when subjected to a moment applied in a
- plane perpendicular to the object's central axis (i.e. parallel to
- the cross-section)
- Examples
- ========
- >>> from sympy import symbols, Circle, Ellipse
- >>> c = Circle((5, 5), 4)
- >>> c.polar_second_moment_of_area()
- 128*pi
- >>> a, b = symbols('a, b')
- >>> e = Ellipse((0, 0), a, b)
- >>> e.polar_second_moment_of_area()
- pi*a**3*b/4 + pi*a*b**3/4
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Polar_moment_of_inertia
- """
- second_moment = self.second_moment_of_area()
- return second_moment[0] + second_moment[1]
- def section_modulus(self, point=None):
- """Returns a tuple with the section modulus of an ellipse
- Section modulus is a geometric property of an ellipse defined as the
- ratio of second moment of area to the distance of the extreme end of
- the ellipse from the centroidal axis.
- Parameters
- ==========
- point : Point, two-tuple of sympifyable objects, or None(default=None)
- point is the point at which section modulus is to be found.
- If "point=None" section modulus will be calculated for the
- point farthest from the centroidal axis of the ellipse.
- Returns
- =======
- S_x, S_y: numbers or SymPy expressions
- S_x is the section modulus with respect to the x-axis
- S_y is the section modulus with respect to the y-axis
- A negative sign indicates that the section modulus is
- determined for a point below the centroidal axis.
- Examples
- ========
- >>> from sympy import Symbol, Ellipse, Circle, Point2D
- >>> d = Symbol('d', positive=True)
- >>> c = Circle((0, 0), d/2)
- >>> c.section_modulus()
- (pi*d**3/32, pi*d**3/32)
- >>> e = Ellipse(Point2D(0, 0), 2, 4)
- >>> e.section_modulus()
- (8*pi, 4*pi)
- >>> e.section_modulus((2, 2))
- (16*pi, 4*pi)
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Section_modulus
- """
- x_c, y_c = self.center
- if point is None:
- # taking x and y as maximum distances from centroid
- x_min, y_min, x_max, y_max = self.bounds
- y = max(y_c - y_min, y_max - y_c)
- x = max(x_c - x_min, x_max - x_c)
- else:
- # taking x and y as distances of the given point from the center
- point = Point2D(point)
- y = point.y - y_c
- x = point.x - x_c
- second_moment = self.second_moment_of_area()
- S_x = second_moment[0]/y
- S_y = second_moment[1]/x
- return S_x, S_y
- class Circle(Ellipse):
- """A circle in space.
- Constructed simply from a center and a radius, from three
- non-collinear points, or the equation of a circle.
- Parameters
- ==========
- center : Point
- radius : number or SymPy expression
- points : sequence of three Points
- equation : equation of a circle
- Attributes
- ==========
- radius (synonymous with hradius, vradius, major and minor)
- circumference
- equation
- Raises
- ======
- GeometryError
- When the given equation is not that of a circle.
- When trying to construct circle from incorrect parameters.
- See Also
- ========
- Ellipse, sympy.geometry.point.Point
- Examples
- ========
- >>> from sympy import Point, Circle, Eq
- >>> from sympy.abc import x, y, a, b
- A circle constructed from a center and radius:
- >>> c1 = Circle(Point(0, 0), 5)
- >>> c1.hradius, c1.vradius, c1.radius
- (5, 5, 5)
- A circle constructed from three points:
- >>> c2 = Circle(Point(0, 0), Point(1, 1), Point(1, 0))
- >>> c2.hradius, c2.vradius, c2.radius, c2.center
- (sqrt(2)/2, sqrt(2)/2, sqrt(2)/2, Point2D(1/2, 1/2))
- A circle can be constructed from an equation in the form
- `a*x**2 + by**2 + gx + hy + c = 0`, too:
- >>> Circle(x**2 + y**2 - 25)
- Circle(Point2D(0, 0), 5)
- If the variables corresponding to x and y are named something
- else, their name or symbol can be supplied:
- >>> Circle(Eq(a**2 + b**2, 25), x='a', y=b)
- Circle(Point2D(0, 0), 5)
- """
- def __new__(cls, *args, **kwargs):
- evaluate = kwargs.get('evaluate', global_parameters.evaluate)
- if len(args) == 1 and isinstance(args[0], (Expr, Eq)):
- x = kwargs.get('x', 'x')
- y = kwargs.get('y', 'y')
- equation = args[0]
- if isinstance(equation, Eq):
- equation = equation.lhs - equation.rhs
- x = find(x, equation)
- y = find(y, equation)
- try:
- a, b, c, d, e = linear_coeffs(equation, x**2, y**2, x, y)
- except ValueError:
- raise GeometryError("The given equation is not that of a circle.")
- if S.Zero in (a, b) or a != b:
- raise GeometryError("The given equation is not that of a circle.")
- center_x = -c/a/2
- center_y = -d/b/2
- r2 = (center_x**2) + (center_y**2) - e
- return Circle((center_x, center_y), sqrt(r2), evaluate=evaluate)
- else:
- c, r = None, None
- if len(args) == 3:
- args = [Point(a, dim=2, evaluate=evaluate) for a in args]
- t = Triangle(*args)
- if not isinstance(t, Triangle):
- return t
- c = t.circumcenter
- r = t.circumradius
- elif len(args) == 2:
- # Assume (center, radius) pair
- c = Point(args[0], dim=2, evaluate=evaluate)
- r = args[1]
- # this will prohibit imaginary radius
- try:
- r = Point(r, 0, evaluate=evaluate).x
- except ValueError:
- raise GeometryError("Circle with imaginary radius is not permitted")
- if not (c is None or r is None):
- if r == 0:
- return c
- return GeometryEntity.__new__(cls, c, r, **kwargs)
- raise GeometryError("Circle.__new__ received unknown arguments")
- def _eval_evalf(self, prec=15, **options):
- pt, r = self.args
- dps = prec_to_dps(prec)
- pt = pt.evalf(n=dps, **options)
- r = r.evalf(n=dps, **options)
- return self.func(pt, r, evaluate=False)
- @property
- def circumference(self):
- """The circumference of the circle.
- Returns
- =======
- circumference : number or SymPy expression
- Examples
- ========
- >>> from sympy import Point, Circle
- >>> c1 = Circle(Point(3, 4), 6)
- >>> c1.circumference
- 12*pi
- """
- return 2 * S.Pi * self.radius
- def equation(self, x='x', y='y'):
- """The equation of the circle.
- Parameters
- ==========
- x : str or Symbol, optional
- Default value is 'x'.
- y : str or Symbol, optional
- Default value is 'y'.
- Returns
- =======
- equation : SymPy expression
- Examples
- ========
- >>> from sympy import Point, Circle
- >>> c1 = Circle(Point(0, 0), 5)
- >>> c1.equation()
- x**2 + y**2 - 25
- """
- x = _symbol(x, real=True)
- y = _symbol(y, real=True)
- t1 = (x - self.center.x)**2
- t2 = (y - self.center.y)**2
- return t1 + t2 - self.major**2
- def intersection(self, o):
- """The intersection of this circle with another geometrical entity.
- Parameters
- ==========
- o : GeometryEntity
- Returns
- =======
- intersection : list of GeometryEntities
- Examples
- ========
- >>> from sympy import Point, Circle, Line, Ray
- >>> p1, p2, p3 = Point(0, 0), Point(5, 5), Point(6, 0)
- >>> p4 = Point(5, 0)
- >>> c1 = Circle(p1, 5)
- >>> c1.intersection(p2)
- []
- >>> c1.intersection(p4)
- [Point2D(5, 0)]
- >>> c1.intersection(Ray(p1, p2))
- [Point2D(5*sqrt(2)/2, 5*sqrt(2)/2)]
- >>> c1.intersection(Line(p2, p3))
- []
- """
- return Ellipse.intersection(self, o)
- @property
- def radius(self):
- """The radius of the circle.
- Returns
- =======
- radius : number or SymPy expression
- See Also
- ========
- Ellipse.major, Ellipse.minor, Ellipse.hradius, Ellipse.vradius
- Examples
- ========
- >>> from sympy import Point, Circle
- >>> c1 = Circle(Point(3, 4), 6)
- >>> c1.radius
- 6
- """
- return self.args[1]
- def reflect(self, line):
- """Override GeometryEntity.reflect since the radius
- is not a GeometryEntity.
- Examples
- ========
- >>> from sympy import Circle, Line
- >>> Circle((0, 1), 1).reflect(Line((0, 0), (1, 1)))
- Circle(Point2D(1, 0), -1)
- """
- c = self.center
- c = c.reflect(line)
- return self.func(c, -self.radius)
- def scale(self, x=1, y=1, pt=None):
- """Override GeometryEntity.scale since the radius
- is not a GeometryEntity.
- Examples
- ========
- >>> from sympy import Circle
- >>> Circle((0, 0), 1).scale(2, 2)
- Circle(Point2D(0, 0), 2)
- >>> Circle((0, 0), 1).scale(2, 4)
- Ellipse(Point2D(0, 0), 2, 4)
- """
- c = self.center
- if pt:
- pt = Point(pt, dim=2)
- return self.translate(*(-pt).args).scale(x, y).translate(*pt.args)
- c = c.scale(x, y)
- x, y = [abs(i) for i in (x, y)]
- if x == y:
- return self.func(c, x*self.radius)
- h = v = self.radius
- return Ellipse(c, hradius=h*x, vradius=v*y)
- @property
- def vradius(self):
- """
- This Ellipse property is an alias for the Circle's radius.
- Whereas hradius, major and minor can use Ellipse's conventions,
- the vradius does not exist for a circle. It is always a positive
- value in order that the Circle, like Polygons, will have an
- area that can be positive or negative as determined by the sign
- of the hradius.
- Examples
- ========
- >>> from sympy import Point, Circle
- >>> c1 = Circle(Point(3, 4), 6)
- >>> c1.vradius
- 6
- """
- return abs(self.radius)
- from .polygon import Polygon, Triangle
|