| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883 | from sympy.core import Expr, S, oo, pi, sympifyfrom sympy.core.evalf import Nfrom sympy.core.sorting import default_sort_key, orderedfrom sympy.core.symbol import _symbol, Dummy, Symbolfrom sympy.functions.elementary.complexes import signfrom sympy.functions.elementary.piecewise import Piecewisefrom sympy.functions.elementary.trigonometric import cos, sin, tanfrom .ellipse import Circlefrom .entity import GeometryEntity, GeometrySetfrom .exceptions import GeometryErrorfrom .line import Line, Segment, Rayfrom .point import Pointfrom sympy.logic import Andfrom sympy.matrices import Matrixfrom sympy.simplify.simplify import simplifyfrom sympy.solvers.solvers import solvefrom sympy.utilities.iterables import has_dups, has_variety, uniq, rotate_left, least_rotationfrom sympy.utilities.misc import as_int, func_namefrom mpmath.libmp.libmpf import prec_to_dpsimport warningsx, y, T = [Dummy('polygon_dummy', real=True) for i in range(3)]class Polygon(GeometrySet):    """A two-dimensional polygon.    A simple polygon in space. Can be constructed from a sequence of points    or from a center, radius, number of sides and rotation angle.    Parameters    ==========    vertices        A sequence of points.    n : int, optional        If $> 0$, an n-sided RegularPolygon is created.        Default value is $0$.    Attributes    ==========    area    angles    perimeter    vertices    centroid    sides    Raises    ======    GeometryError        If all parameters are not Points.    See Also    ========    sympy.geometry.point.Point, sympy.geometry.line.Segment, Triangle    Notes    =====    Polygons are treated as closed paths rather than 2D areas so    some calculations can be be negative or positive (e.g., area)    based on the orientation of the points.    Any consecutive identical points are reduced to a single point    and any points collinear and between two points will be removed    unless they are needed to define an explicit intersection (see examples).    A Triangle, Segment or Point will be returned when there are 3 or    fewer points provided.    Examples    ========    >>> from sympy import Polygon, pi    >>> p1, p2, p3, p4, p5 = [(0, 0), (1, 0), (5, 1), (0, 1), (3, 0)]    >>> Polygon(p1, p2, p3, p4)    Polygon(Point2D(0, 0), Point2D(1, 0), Point2D(5, 1), Point2D(0, 1))    >>> Polygon(p1, p2)    Segment2D(Point2D(0, 0), Point2D(1, 0))    >>> Polygon(p1, p2, p5)    Segment2D(Point2D(0, 0), Point2D(3, 0))    The area of a polygon is calculated as positive when vertices are    traversed in a ccw direction. When the sides of a polygon cross the    area will have positive and negative contributions. The following    defines a Z shape where the bottom right connects back to the top    left.    >>> Polygon((0, 2), (2, 2), (0, 0), (2, 0)).area    0    When the keyword `n` is used to define the number of sides of the    Polygon then a RegularPolygon is created and the other arguments are    interpreted as center, radius and rotation. The unrotated RegularPolygon    will always have a vertex at Point(r, 0) where `r` is the radius of the    circle that circumscribes the RegularPolygon. Its method `spin` can be    used to increment that angle.    >>> p = Polygon((0,0), 1, n=3)    >>> p    RegularPolygon(Point2D(0, 0), 1, 3, 0)    >>> p.vertices[0]    Point2D(1, 0)    >>> p.args[0]    Point2D(0, 0)    >>> p.spin(pi/2)    >>> p.vertices[0]    Point2D(0, 1)    """    __slots__ = ()    def __new__(cls, *args, n = 0, **kwargs):        if n:            args = list(args)            # return a virtual polygon with n sides            if len(args) == 2:  # center, radius                args.append(n)            elif len(args) == 3:  # center, radius, rotation                args.insert(2, n)            return RegularPolygon(*args, **kwargs)        vertices = [Point(a, dim=2, **kwargs) for a in args]        # remove consecutive duplicates        nodup = []        for p in vertices:            if nodup and p == nodup[-1]:                continue            nodup.append(p)        if len(nodup) > 1 and nodup[-1] == nodup[0]:            nodup.pop()  # last point was same as first        # remove collinear points        i = -3        while i < len(nodup) - 3 and len(nodup) > 2:            a, b, c = nodup[i], nodup[i + 1], nodup[i + 2]            if Point.is_collinear(a, b, c):                nodup.pop(i + 1)                if a == c:                    nodup.pop(i)            else:                i += 1        vertices = list(nodup)        if len(vertices) > 3:            return GeometryEntity.__new__(cls, *vertices, **kwargs)        elif len(vertices) == 3:            return Triangle(*vertices, **kwargs)        elif len(vertices) == 2:            return Segment(*vertices, **kwargs)        else:            return Point(*vertices, **kwargs)    @property    def area(self):        """        The area of the polygon.        Notes        =====        The area calculation can be positive or negative based on the        orientation of the points. If any side of the polygon crosses        any other side, there will be areas having opposite signs.        See Also        ========        sympy.geometry.ellipse.Ellipse.area        Examples        ========        >>> from sympy import Point, Polygon        >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])        >>> poly = Polygon(p1, p2, p3, p4)        >>> poly.area        3        In the Z shaped polygon (with the lower right connecting back        to the upper left) the areas cancel out:        >>> Z = Polygon((0, 1), (1, 1), (0, 0), (1, 0))        >>> Z.area        0        In the M shaped polygon, areas do not cancel because no side        crosses any other (though there is a point of contact).        >>> M = Polygon((0, 0), (0, 1), (2, 0), (3, 1), (3, 0))        >>> M.area        -3/2        """        area = 0        args = self.args        for i in range(len(args)):            x1, y1 = args[i - 1].args            x2, y2 = args[i].args            area += x1*y2 - x2*y1        return simplify(area) / 2    @staticmethod    def _isright(a, b, c):        """Return True/False for cw/ccw orientation.        Examples        ========        >>> from sympy import Point, Polygon        >>> a, b, c = [Point(i) for i in [(0, 0), (1, 1), (1, 0)]]        >>> Polygon._isright(a, b, c)        True        >>> Polygon._isright(a, c, b)        False        """        ba = b - a        ca = c - a        t_area = simplify(ba.x*ca.y - ca.x*ba.y)        res = t_area.is_nonpositive        if res is None:            raise ValueError("Can't determine orientation")        return res    @property    def angles(self):        """The internal angle at each vertex.        Returns        =======        angles : dict            A dictionary where each key is a vertex and each value is the            internal angle at that vertex. The vertices are represented as            Points.        See Also        ========        sympy.geometry.point.Point, sympy.geometry.line.LinearEntity.angle_between        Examples        ========        >>> from sympy import Point, Polygon        >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])        >>> poly = Polygon(p1, p2, p3, p4)        >>> poly.angles[p1]        pi/2        >>> poly.angles[p2]        acos(-4*sqrt(17)/17)        """        # Determine orientation of points        args = self.vertices        cw = self._isright(args[-1], args[0], args[1])        ret = {}        for i in range(len(args)):            a, b, c = args[i - 2], args[i - 1], args[i]            ang = Ray(b, a).angle_between(Ray(b, c))            if cw ^ self._isright(a, b, c):                ret[b] = 2*S.Pi - ang            else:                ret[b] = ang        return ret    @property    def ambient_dimension(self):        return self.vertices[0].ambient_dimension    @property    def perimeter(self):        """The perimeter of the polygon.        Returns        =======        perimeter : number or Basic instance        See Also        ========        sympy.geometry.line.Segment.length        Examples        ========        >>> from sympy import Point, Polygon        >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])        >>> poly = Polygon(p1, p2, p3, p4)        >>> poly.perimeter        sqrt(17) + 7        """        p = 0        args = self.vertices        for i in range(len(args)):            p += args[i - 1].distance(args[i])        return simplify(p)    @property    def vertices(self):        """The vertices of the polygon.        Returns        =======        vertices : list of Points        Notes        =====        When iterating over the vertices, it is more efficient to index self        rather than to request the vertices and index them. Only use the        vertices when you want to process all of them at once. This is even        more important with RegularPolygons that calculate each vertex.        See Also        ========        sympy.geometry.point.Point        Examples        ========        >>> from sympy import Point, Polygon        >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])        >>> poly = Polygon(p1, p2, p3, p4)        >>> poly.vertices        [Point2D(0, 0), Point2D(1, 0), Point2D(5, 1), Point2D(0, 1)]        >>> poly.vertices[0]        Point2D(0, 0)        """        return list(self.args)    @property    def centroid(self):        """The centroid of the polygon.        Returns        =======        centroid : Point        See Also        ========        sympy.geometry.point.Point, sympy.geometry.util.centroid        Examples        ========        >>> from sympy import Point, Polygon        >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])        >>> poly = Polygon(p1, p2, p3, p4)        >>> poly.centroid        Point2D(31/18, 11/18)        """        A = 1/(6*self.area)        cx, cy = 0, 0        args = self.args        for i in range(len(args)):            x1, y1 = args[i - 1].args            x2, y2 = args[i].args            v = x1*y2 - x2*y1            cx += v*(x1 + x2)            cy += v*(y1 + y2)        return Point(simplify(A*cx), simplify(A*cy))    def second_moment_of_area(self, point=None):        """Returns the second moment and product moment of area of a two dimensional polygon.        Parameters        ==========        point : Point, two-tuple of sympifyable 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 polygon.        Returns        =======        I_xx, I_yy, I_xy : number or SymPy expression                           I_xx, I_yy are second moment of area of a two dimensional polygon.                           I_xy is product moment of area of a two dimensional polygon.        Examples        ========        >>> from sympy import Polygon, symbols        >>> a, b = symbols('a, b')        >>> p1, p2, p3, p4, p5 = [(0, 0), (a, 0), (a, b), (0, b), (a/3, b/3)]        >>> rectangle = Polygon(p1, p2, p3, p4)        >>> rectangle.second_moment_of_area()        (a*b**3/12, a**3*b/12, 0)        >>> rectangle.second_moment_of_area(p5)        (a*b**3/9, a**3*b/9, a**2*b**2/36)        References        ==========        .. [1] https://en.wikipedia.org/wiki/Second_moment_of_area        """        I_xx, I_yy, I_xy = 0, 0, 0        args = self.vertices        for i in range(len(args)):            x1, y1 = args[i-1].args            x2, y2 = args[i].args            v = x1*y2 - x2*y1            I_xx += (y1**2 + y1*y2 + y2**2)*v            I_yy += (x1**2 + x1*x2 + x2**2)*v            I_xy += (x1*y2 + 2*x1*y1 + 2*x2*y2 + x2*y1)*v        A = self.area        c_x = self.centroid[0]        c_y = self.centroid[1]        # parallel axis theorem        I_xx_c = (I_xx/12) - (A*(c_y**2))        I_yy_c = (I_yy/12) - (A*(c_x**2))        I_xy_c = (I_xy/24) - (A*(c_x*c_y))        if point is None:            return I_xx_c, I_yy_c, I_xy_c        I_xx = (I_xx_c + A*((point[1]-c_y)**2))        I_yy = (I_yy_c + A*((point[0]-c_x)**2))        I_xy = (I_xy_c + A*((point[0]-c_x)*(point[1]-c_y)))        return I_xx, I_yy, I_xy    def first_moment_of_area(self, point=None):        """        Returns the first moment of area of a two-dimensional polygon with        respect to a certain point of interest.        First moment of area is a measure of the distribution of the area        of a polygon in relation to an axis. The first moment of area of        the entire polygon about its own centroid is always zero. Therefore,        here it is calculated for an area, above or below a certain point        of interest, that makes up a smaller portion of the polygon. This        area is bounded by the point of interest and the extreme end        (top or bottom) of the polygon. The first moment for this area is        is then determined about the centroidal axis of the initial polygon.        References        ==========        .. [1] https://skyciv.com/docs/tutorials/section-tutorials/calculating-the-statical-or-first-moment-of-area-of-beam-sections/?cc=BMD        .. [2] https://mechanicalc.com/reference/cross-sections        Parameters        ==========        point: Point, two-tuple of sympifyable objects, or None (default=None)            point is the point above or below which the area of interest lies            If ``point=None`` then the centroid acts as the point of interest.        Returns        =======        Q_x, Q_y: number or SymPy expressions            Q_x is the first moment of area about the x-axis            Q_y is the first moment of area about the y-axis            A negative sign indicates that the section modulus is            determined for a section below (or left of) the centroidal axis        Examples        ========        >>> from sympy import Point, Polygon        >>> a, b = 50, 10        >>> p1, p2, p3, p4 = [(0, b), (0, 0), (a, 0), (a, b)]        >>> p = Polygon(p1, p2, p3, p4)        >>> p.first_moment_of_area()        (625, 3125)        >>> p.first_moment_of_area(point=Point(30, 7))        (525, 3000)        """        if point:            xc, yc = self.centroid        else:            point = self.centroid            xc, yc = point        h_line = Line(point, slope=0)        v_line = Line(point, slope=S.Infinity)        h_poly = self.cut_section(h_line)        v_poly = self.cut_section(v_line)        poly_1 = h_poly[0] if h_poly[0].area <= h_poly[1].area else h_poly[1]        poly_2 = v_poly[0] if v_poly[0].area <= v_poly[1].area else v_poly[1]        Q_x = (poly_1.centroid.y - yc)*poly_1.area        Q_y = (poly_2.centroid.x - xc)*poly_2.area        return Q_x, Q_y    def polar_second_moment_of_area(self):        """Returns the polar modulus of a two-dimensional polygon        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 Polygon, symbols        >>> a, b = symbols('a, b')        >>> rectangle = Polygon((0, 0), (a, 0), (a, b), (0, b))        >>> rectangle.polar_second_moment_of_area()        a**3*b/12 + a*b**3/12        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 a two-dimensional        polygon.        Section modulus is a geometric property of a polygon defined as the        ratio of second moment of area to the distance of the extreme end of        the polygon 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" it will be calculated for the point farthest from the            centroidal axis of the polygon.        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 symbols, Polygon, Point        >>> a, b = symbols('a, b', positive=True)        >>> rectangle = Polygon((0, 0), (a, 0), (a, b), (0, b))        >>> rectangle.section_modulus()        (a*b**2/6, a**2*b/6)        >>> rectangle.section_modulus(Point(a/4, b/4))        (-a*b**2/3, -a**2*b/3)        References        ==========        .. [1] https://en.wikipedia.org/wiki/Section_modulus        """        x_c, y_c = self.centroid        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 centroid            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    @property    def sides(self):        """The directed line segments that form the sides of the polygon.        Returns        =======        sides : list of sides            Each side is a directed Segment.        See Also        ========        sympy.geometry.point.Point, sympy.geometry.line.Segment        Examples        ========        >>> from sympy import Point, Polygon        >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])        >>> poly = Polygon(p1, p2, p3, p4)        >>> poly.sides        [Segment2D(Point2D(0, 0), Point2D(1, 0)),        Segment2D(Point2D(1, 0), Point2D(5, 1)),        Segment2D(Point2D(5, 1), Point2D(0, 1)), Segment2D(Point2D(0, 1), Point2D(0, 0))]        """        res = []        args = self.vertices        for i in range(-len(args), 0):            res.append(Segment(args[i], args[i + 1]))        return res    @property    def bounds(self):        """Return a tuple (xmin, ymin, xmax, ymax) representing the bounding        rectangle for the geometric figure.        """        verts = self.vertices        xs = [p.x for p in verts]        ys = [p.y for p in verts]        return (min(xs), min(ys), max(xs), max(ys))    def is_convex(self):        """Is the polygon convex?        A polygon is convex if all its interior angles are less than 180        degrees and there are no intersections between sides.        Returns        =======        is_convex : boolean            True if this polygon is convex, False otherwise.        See Also        ========        sympy.geometry.util.convex_hull        Examples        ========        >>> from sympy import Point, Polygon        >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])        >>> poly = Polygon(p1, p2, p3, p4)        >>> poly.is_convex()        True        """        # Determine orientation of points        args = self.vertices        cw = self._isright(args[-2], args[-1], args[0])        for i in range(1, len(args)):            if cw ^ self._isright(args[i - 2], args[i - 1], args[i]):                return False        # check for intersecting sides        sides = self.sides        for i, si in enumerate(sides):            pts = si.args            # exclude the sides connected to si            for j in range(1 if i == len(sides) - 1 else 0, i - 1):                sj = sides[j]                if sj.p1 not in pts and sj.p2 not in pts:                    hit = si.intersection(sj)                    if hit:                        return False        return True    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, sympy.geometry.ellipse.Ellipse.encloses_point        Examples        ========        >>> from sympy import Polygon, Point        >>> p = Polygon((0, 0), (4, 0), (4, 4))        >>> p.encloses_point(Point(2, 1))        True        >>> p.encloses_point(Point(2, 2))        False        >>> p.encloses_point(Point(5, 5))        False        References        ==========        .. [1] http://paulbourke.net/geometry/polygonmesh/#insidepoly        """        p = Point(p, dim=2)        if p in self.vertices or any(p in s for s in self.sides):            return False        # move to p, checking that the result is numeric        lit = []        for v in self.vertices:            lit.append(v - p)  # the difference is simplified            if lit[-1].free_symbols:                return None        poly = Polygon(*lit)        # polygon closure is assumed in the following test but Polygon removes duplicate pts so        # the last point has to be added so all sides are computed. Using Polygon.sides is        # not good since Segments are unordered.        args = poly.args        indices = list(range(-len(args), 1))        if poly.is_convex():            orientation = None            for i in indices:                a = args[i]                b = args[i + 1]                test = ((-a.y)*(b.x - a.x) - (-a.x)*(b.y - a.y)).is_negative                if orientation is None:                    orientation = test                elif test is not orientation:                    return False            return True        hit_odd = False        p1x, p1y = args[0].args        for i in indices[1:]:            p2x, p2y = args[i].args            if 0 > min(p1y, p2y):                if 0 <= max(p1y, p2y):                    if 0 <= max(p1x, p2x):                        if p1y != p2y:                            xinters = (-p1y)*(p2x - p1x)/(p2y - p1y) + p1x                            if p1x == p2x or 0 <= xinters:                                hit_odd = not hit_odd            p1x, p1y = p2x, p2y        return hit_odd    def arbitrary_point(self, parameter='t'):        """A parameterized point on the polygon.        The parameter, varying from 0 to 1, assigns points to the position on        the perimeter that is that fraction of the total perimeter. So the        point evaluated at t=1/2 would return the point from the first vertex        that is 1/2 way around the polygon.        Parameters        ==========        parameter : str, optional            Default value is 't'.        Returns        =======        arbitrary_point : Point        Raises        ======        ValueError            When `parameter` already appears in the Polygon's definition.        See Also        ========        sympy.geometry.point.Point        Examples        ========        >>> from sympy import Polygon, Symbol        >>> t = Symbol('t', real=True)        >>> tri = Polygon((0, 0), (1, 0), (1, 1))        >>> p = tri.arbitrary_point('t')        >>> perimeter = tri.perimeter        >>> s1, s2 = [s.length for s in tri.sides[:2]]        >>> p.subs(t, (s1 + s2/2)/perimeter)        Point2D(1, 1/2)        """        t = _symbol(parameter, real=True)        if t.name in (f.name for f in self.free_symbols):            raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name)        sides = []        perimeter = self.perimeter        perim_fraction_start = 0        for s in self.sides:            side_perim_fraction = s.length/perimeter            perim_fraction_end = perim_fraction_start + side_perim_fraction            pt = s.arbitrary_point(parameter).subs(                t, (t - perim_fraction_start)/side_perim_fraction)            sides.append(                (pt, (And(perim_fraction_start <= t, t < perim_fraction_end))))            perim_fraction_start = perim_fraction_end        return Piecewise(*sides)    def parameter_value(self, other, t):        if not isinstance(other,GeometryEntity):            other = Point(other, dim=self.ambient_dimension)        if not isinstance(other,Point):            raise ValueError("other must be a point")        if other.free_symbols:            raise NotImplementedError('non-numeric coordinates')        unknown = False        p = self.arbitrary_point(T)        for pt, cond in p.args:            sol = solve(pt - other, T, dict=True)            if not sol:                continue            value = sol[0][T]            if simplify(cond.subs(T, value)) == True:                return {t: value}            unknown = True        if unknown:            raise ValueError("Given point may not be on %s" % func_name(self))        raise ValueError("Given point is not on %s" % func_name(self))    def plot_interval(self, parameter='t'):        """The plot interval for the default geometric plot of the polygon.        Parameters        ==========        parameter : str, optional            Default value is 't'.        Returns        =======        plot_interval : list (plot interval)            [parameter, lower_bound, upper_bound]        Examples        ========        >>> from sympy import Polygon        >>> p = Polygon((0, 0), (1, 0), (1, 1))        >>> p.plot_interval()        [t, 0, 1]        """        t = Symbol(parameter, real=True)        return [t, 0, 1]    def intersection(self, o):        """The intersection of polygon and geometry entity.        The intersection may be empty and can contain individual Points and        complete Line Segments.        Parameters        ==========        other: GeometryEntity        Returns        =======        intersection : list            The list of Segments and Points        See Also        ========        sympy.geometry.point.Point, sympy.geometry.line.Segment        Examples        ========        >>> from sympy import Point, Polygon, Line        >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])        >>> poly1 = Polygon(p1, p2, p3, p4)        >>> p5, p6, p7 = map(Point, [(3, 2), (1, -1), (0, 2)])        >>> poly2 = Polygon(p5, p6, p7)        >>> poly1.intersection(poly2)        [Point2D(1/3, 1), Point2D(2/3, 0), Point2D(9/5, 1/5), Point2D(7/3, 1)]        >>> poly1.intersection(Line(p1, p2))        [Segment2D(Point2D(0, 0), Point2D(1, 0))]        >>> poly1.intersection(p1)        [Point2D(0, 0)]        """        intersection_result = []        k = o.sides if isinstance(o, Polygon) else [o]        for side in self.sides:            for side1 in k:                intersection_result.extend(side.intersection(side1))        intersection_result = list(uniq(intersection_result))        points = [entity for entity in intersection_result if isinstance(entity, Point)]        segments = [entity for entity in intersection_result if isinstance(entity, Segment)]        if points and segments:            points_in_segments = list(uniq([point for point in points for segment in segments if point in segment]))            if points_in_segments:                for i in points_in_segments:                    points.remove(i)            return list(ordered(segments + points))        else:            return list(ordered(intersection_result))    def cut_section(self, line):        """        Returns a tuple of two polygon segments that lie above and below        the intersecting line respectively.        Parameters        ==========        line: Line object of geometry module            line which cuts the Polygon. The part of the Polygon that lies            above and below this line is returned.        Returns        =======        upper_polygon, lower_polygon: Polygon objects or None            upper_polygon is the polygon that lies above the given line.            lower_polygon is the polygon that lies below the given line.            upper_polygon and lower polygon are ``None`` when no polygon            exists above the line or below the line.        Raises        ======        ValueError: When the line does not intersect the polygon        Examples        ========        >>> from sympy import Polygon, Line        >>> a, b = 20, 10        >>> p1, p2, p3, p4 = [(0, b), (0, 0), (a, 0), (a, b)]        >>> rectangle = Polygon(p1, p2, p3, p4)        >>> t = rectangle.cut_section(Line((0, 5), slope=0))        >>> t        (Polygon(Point2D(0, 10), Point2D(0, 5), Point2D(20, 5), Point2D(20, 10)),        Polygon(Point2D(0, 5), Point2D(0, 0), Point2D(20, 0), Point2D(20, 5)))        >>> upper_segment, lower_segment = t        >>> upper_segment.area        100        >>> upper_segment.centroid        Point2D(10, 15/2)        >>> lower_segment.centroid        Point2D(10, 5/2)        References        ==========        .. [1] https://github.com/sympy/sympy/wiki/A-method-to-return-a-cut-section-of-any-polygon-geometry        """        intersection_points = self.intersection(line)        if not intersection_points:            raise ValueError("This line does not intersect the polygon")        points = list(self.vertices)        points.append(points[0])        eq = line.equation(x, y)        # considering equation of line to be `ax +by + c`        a = eq.coeff(x)        b = eq.coeff(y)        upper_vertices = []        lower_vertices = []        # prev is true when previous point is above the line        prev = True        prev_point = None        for point in points:            # when coefficient of y is 0, right side of the line is            # considered            compare = eq.subs({x: point.x, y: point.y})/b if b \                    else eq.subs(x, point.x)/a            # if point lies above line            if compare > 0:                if not prev:                    # if previous point lies below the line, the intersection                    # point of the polygon edge and the line has to be included                    edge = Line(point, prev_point)                    new_point = edge.intersection(line)                    upper_vertices.append(new_point[0])                    lower_vertices.append(new_point[0])                upper_vertices.append(point)                prev = True            else:                if prev and prev_point:                    edge = Line(point, prev_point)                    new_point = edge.intersection(line)                    upper_vertices.append(new_point[0])                    lower_vertices.append(new_point[0])                lower_vertices.append(point)                prev = False            prev_point = point        upper_polygon, lower_polygon = None, None        if upper_vertices and isinstance(Polygon(*upper_vertices), Polygon):            upper_polygon = Polygon(*upper_vertices)        if lower_vertices and isinstance(Polygon(*lower_vertices), Polygon):            lower_polygon = Polygon(*lower_vertices)        return upper_polygon, lower_polygon    def distance(self, o):        """        Returns the shortest distance between self and o.        If o is a point, then self does not need to be convex.        If o is another polygon self and o must be convex.        Examples        ========        >>> from sympy import Point, Polygon, RegularPolygon        >>> p1, p2 = map(Point, [(0, 0), (7, 5)])        >>> poly = Polygon(*RegularPolygon(p1, 1, 3).vertices)        >>> poly.distance(p2)        sqrt(61)        """        if isinstance(o, Point):            dist = oo            for side in self.sides:                current = side.distance(o)                if current == 0:                    return S.Zero                elif current < dist:                    dist = current            return dist        elif isinstance(o, Polygon) and self.is_convex() and o.is_convex():            return self._do_poly_distance(o)        raise NotImplementedError()    def _do_poly_distance(self, e2):        """        Calculates the least distance between the exteriors of two        convex polygons e1 and e2. Does not check for the convexity        of the polygons as this is checked by Polygon.distance.        Notes        =====            - Prints a warning if the two polygons possibly intersect as the return              value will not be valid in such a case. For a more through test of              intersection use intersection().        See Also        ========        sympy.geometry.point.Point.distance        Examples        ========        >>> from sympy import Point, Polygon        >>> square = Polygon(Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0))        >>> triangle = Polygon(Point(1, 2), Point(2, 2), Point(2, 1))        >>> square._do_poly_distance(triangle)        sqrt(2)/2        Description of method used        ==========================        Method:        [1] https://web.archive.org/web/20150509035744/http://cgm.cs.mcgill.ca/~orm/mind2p.html        Uses rotating calipers:        [2] https://en.wikipedia.org/wiki/Rotating_calipers        and antipodal points:        [3] https://en.wikipedia.org/wiki/Antipodal_point        """        e1 = self        '''Tests for a possible intersection between the polygons and outputs a warning'''        e1_center = e1.centroid        e2_center = e2.centroid        e1_max_radius = S.Zero        e2_max_radius = S.Zero        for vertex in e1.vertices:            r = Point.distance(e1_center, vertex)            if e1_max_radius < r:                e1_max_radius = r        for vertex in e2.vertices:            r = Point.distance(e2_center, vertex)            if e2_max_radius < r:                e2_max_radius = r        center_dist = Point.distance(e1_center, e2_center)        if center_dist <= e1_max_radius + e2_max_radius:            warnings.warn("Polygons may intersect producing erroneous output",                          stacklevel=3)        '''        Find the upper rightmost vertex of e1 and the lowest leftmost vertex of e2        '''        e1_ymax = Point(0, -oo)        e2_ymin = Point(0, oo)        for vertex in e1.vertices:            if vertex.y > e1_ymax.y or (vertex.y == e1_ymax.y and vertex.x > e1_ymax.x):                e1_ymax = vertex        for vertex in e2.vertices:            if vertex.y < e2_ymin.y or (vertex.y == e2_ymin.y and vertex.x < e2_ymin.x):                e2_ymin = vertex        min_dist = Point.distance(e1_ymax, e2_ymin)        '''        Produce a dictionary with vertices of e1 as the keys and, for each vertex, the points        to which the vertex is connected as its value. The same is then done for e2.        '''        e1_connections = {}        e2_connections = {}        for side in e1.sides:            if side.p1 in e1_connections:                e1_connections[side.p1].append(side.p2)            else:                e1_connections[side.p1] = [side.p2]            if side.p2 in e1_connections:                e1_connections[side.p2].append(side.p1)            else:                e1_connections[side.p2] = [side.p1]        for side in e2.sides:            if side.p1 in e2_connections:                e2_connections[side.p1].append(side.p2)            else:                e2_connections[side.p1] = [side.p2]            if side.p2 in e2_connections:                e2_connections[side.p2].append(side.p1)            else:                e2_connections[side.p2] = [side.p1]        e1_current = e1_ymax        e2_current = e2_ymin        support_line = Line(Point(S.Zero, S.Zero), Point(S.One, S.Zero))        '''        Determine which point in e1 and e2 will be selected after e2_ymin and e1_ymax,        this information combined with the above produced dictionaries determines the        path that will be taken around the polygons        '''        point1 = e1_connections[e1_ymax][0]        point2 = e1_connections[e1_ymax][1]        angle1 = support_line.angle_between(Line(e1_ymax, point1))        angle2 = support_line.angle_between(Line(e1_ymax, point2))        if angle1 < angle2:            e1_next = point1        elif angle2 < angle1:            e1_next = point2        elif Point.distance(e1_ymax, point1) > Point.distance(e1_ymax, point2):            e1_next = point2        else:            e1_next = point1        point1 = e2_connections[e2_ymin][0]        point2 = e2_connections[e2_ymin][1]        angle1 = support_line.angle_between(Line(e2_ymin, point1))        angle2 = support_line.angle_between(Line(e2_ymin, point2))        if angle1 > angle2:            e2_next = point1        elif angle2 > angle1:            e2_next = point2        elif Point.distance(e2_ymin, point1) > Point.distance(e2_ymin, point2):            e2_next = point2        else:            e2_next = point1        '''        Loop which determines the distance between anti-podal pairs and updates the        minimum distance accordingly. It repeats until it reaches the starting position.        '''        while True:            e1_angle = support_line.angle_between(Line(e1_current, e1_next))            e2_angle = pi - support_line.angle_between(Line(                e2_current, e2_next))            if (e1_angle < e2_angle) is True:                support_line = Line(e1_current, e1_next)                e1_segment = Segment(e1_current, e1_next)                min_dist_current = e1_segment.distance(e2_current)                if min_dist_current.evalf() < min_dist.evalf():                    min_dist = min_dist_current                if e1_connections[e1_next][0] != e1_current:                    e1_current = e1_next                    e1_next = e1_connections[e1_next][0]                else:                    e1_current = e1_next                    e1_next = e1_connections[e1_next][1]            elif (e1_angle > e2_angle) is True:                support_line = Line(e2_next, e2_current)                e2_segment = Segment(e2_current, e2_next)                min_dist_current = e2_segment.distance(e1_current)                if min_dist_current.evalf() < min_dist.evalf():                    min_dist = min_dist_current                if e2_connections[e2_next][0] != e2_current:                    e2_current = e2_next                    e2_next = e2_connections[e2_next][0]                else:                    e2_current = e2_next                    e2_next = e2_connections[e2_next][1]            else:                support_line = Line(e1_current, e1_next)                e1_segment = Segment(e1_current, e1_next)                e2_segment = Segment(e2_current, e2_next)                min1 = e1_segment.distance(e2_next)                min2 = e2_segment.distance(e1_next)                min_dist_current = min(min1, min2)                if min_dist_current.evalf() < min_dist.evalf():                    min_dist = min_dist_current                if e1_connections[e1_next][0] != e1_current:                    e1_current = e1_next                    e1_next = e1_connections[e1_next][0]                else:                    e1_current = e1_next                    e1_next = e1_connections[e1_next][1]                if e2_connections[e2_next][0] != e2_current:                    e2_current = e2_next                    e2_next = e2_connections[e2_next][0]                else:                    e2_current = e2_next                    e2_next = e2_connections[e2_next][1]            if e1_current == e1_ymax and e2_current == e2_ymin:                break        return min_dist    def _svg(self, scale_factor=1., fill_color="#66cc99"):        """Returns SVG path element for the Polygon.        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".        """        verts = map(N, self.vertices)        coords = ["{},{}".format(p.x, p.y) for p in verts]        path = "M {} L {} z".format(coords[0], " L ".join(coords[1:]))        return (            '<path fill-rule="evenodd" fill="{2}" stroke="#555555" '            'stroke-width="{0}" opacity="0.6" d="{1}" />'            ).format(2. * scale_factor, path, fill_color)    def _hashable_content(self):        D = {}        def ref_list(point_list):            kee = {}            for i, p in enumerate(ordered(set(point_list))):                kee[p] = i                D[i] = p            return [kee[p] for p in point_list]        S1 = ref_list(self.args)        r_nor = rotate_left(S1, least_rotation(S1))        S2 = ref_list(list(reversed(self.args)))        r_rev = rotate_left(S2, least_rotation(S2))        if r_nor < r_rev:            r = r_nor        else:            r = r_rev        canonical_args = [ D[order] for order in r ]        return tuple(canonical_args)    def __contains__(self, o):        """        Return True if o is contained within the boundary lines of self.altitudes        Parameters        ==========        other : GeometryEntity        Returns        =======        contained in : bool            The points (and sides, if applicable) are contained in self.        See Also        ========        sympy.geometry.entity.GeometryEntity.encloses        Examples        ========        >>> from sympy import Line, Segment, Point        >>> p = Point(0, 0)        >>> q = Point(1, 1)        >>> s = Segment(p, q*2)        >>> l = Line(p, q)        >>> p in q        False        >>> p in s        True        >>> q*3 in s        False        >>> s in l        True        """        if isinstance(o, Polygon):            return self == o        elif isinstance(o, Segment):            return any(o in s for s in self.sides)        elif isinstance(o, Point):            if o in self.vertices:                return True            for side in self.sides:                if o in side:                    return True        return False    def bisectors(p, prec=None):        """Returns angle bisectors of a polygon. If prec is given        then approximate the point defining the ray to that precision.        The distance between the points defining the bisector ray is 1.        Examples        ========        >>> from sympy import Polygon, Point        >>> p = Polygon(Point(0, 0), Point(2, 0), Point(1, 1), Point(0, 3))        >>> p.bisectors(2)        {Point2D(0, 0): Ray2D(Point2D(0, 0), Point2D(0.71, 0.71)),         Point2D(0, 3): Ray2D(Point2D(0, 3), Point2D(0.23, 2.0)),         Point2D(1, 1): Ray2D(Point2D(1, 1), Point2D(0.19, 0.42)),         Point2D(2, 0): Ray2D(Point2D(2, 0), Point2D(1.1, 0.38))}        """        b = {}        pts = list(p.args)        pts.append(pts[0])  # close it        cw = Polygon._isright(*pts[:3])        if cw:            pts = list(reversed(pts))        for v, a in p.angles.items():            i = pts.index(v)            p1, p2 = Point._normalize_dimension(pts[i], pts[i + 1])            ray = Ray(p1, p2).rotate(a/2, v)            dir = ray.direction            ray = Ray(ray.p1, ray.p1 + dir/dir.distance((0, 0)))            if prec is not None:                ray = Ray(ray.p1, ray.p2.n(prec))            b[v] = ray        return bclass RegularPolygon(Polygon):    """    A regular polygon.    Such a polygon has all internal angles equal and all sides the same length.    Parameters    ==========    center : Point    radius : number or Basic instance        The distance from the center to a vertex    n : int        The number of sides    Attributes    ==========    vertices    center    radius    rotation    apothem    interior_angle    exterior_angle    circumcircle    incircle    angles    Raises    ======    GeometryError        If the `center` is not a Point, or the `radius` is not a number or Basic        instance, or the number of sides, `n`, is less than three.    Notes    =====    A RegularPolygon can be instantiated with Polygon with the kwarg n.    Regular polygons are instantiated with a center, radius, number of sides    and a rotation angle. Whereas the arguments of a Polygon are vertices, the    vertices of the RegularPolygon must be obtained with the vertices method.    See Also    ========    sympy.geometry.point.Point, Polygon    Examples    ========    >>> from sympy import RegularPolygon, Point    >>> r = RegularPolygon(Point(0, 0), 5, 3)    >>> r    RegularPolygon(Point2D(0, 0), 5, 3, 0)    >>> r.vertices[0]    Point2D(5, 0)    """    __slots__ = ('_n', '_center', '_radius', '_rot')    def __new__(self, c, r, n, rot=0, **kwargs):        r, n, rot = map(sympify, (r, n, rot))        c = Point(c, dim=2, **kwargs)        if not isinstance(r, Expr):            raise GeometryError("r must be an Expr object, not %s" % r)        if n.is_Number:            as_int(n)  # let an error raise if necessary            if n < 3:                raise GeometryError("n must be a >= 3, not %s" % n)        obj = GeometryEntity.__new__(self, c, r, n, **kwargs)        obj._n = n        obj._center = c        obj._radius = r        obj._rot = rot % (2*S.Pi/n) if rot.is_number else rot        return obj    def _eval_evalf(self, prec=15, **options):        c, r, n, a = self.args        dps = prec_to_dps(prec)        c, r, a = [i.evalf(n=dps, **options) for i in (c, r, a)]        return self.func(c, r, n, a)    @property    def args(self):        """        Returns the center point, the radius,        the number of sides, and the orientation angle.        Examples        ========        >>> from sympy import RegularPolygon, Point        >>> r = RegularPolygon(Point(0, 0), 5, 3)        >>> r.args        (Point2D(0, 0), 5, 3, 0)        """        return self._center, self._radius, self._n, self._rot    def __str__(self):        return 'RegularPolygon(%s, %s, %s, %s)' % tuple(self.args)    def __repr__(self):        return 'RegularPolygon(%s, %s, %s, %s)' % tuple(self.args)    @property    def area(self):        """Returns the area.        Examples        ========        >>> from sympy import RegularPolygon        >>> square = RegularPolygon((0, 0), 1, 4)        >>> square.area        2        >>> _ == square.length**2        True        """        c, r, n, rot = self.args        return sign(r)*n*self.length**2/(4*tan(pi/n))    @property    def length(self):        """Returns the length of the sides.        The half-length of the side and the apothem form two legs        of a right triangle whose hypotenuse is the radius of the        regular polygon.        Examples        ========        >>> from sympy import RegularPolygon        >>> from sympy import sqrt        >>> s = square_in_unit_circle = RegularPolygon((0, 0), 1, 4)        >>> s.length        sqrt(2)        >>> sqrt((_/2)**2 + s.apothem**2) == s.radius        True        """        return self.radius*2*sin(pi/self._n)    @property    def center(self):        """The center of the RegularPolygon        This is also the center of the circumscribing circle.        Returns        =======        center : Point        See Also        ========        sympy.geometry.point.Point, sympy.geometry.ellipse.Ellipse.center        Examples        ========        >>> from sympy import RegularPolygon, Point        >>> rp = RegularPolygon(Point(0, 0), 5, 4)        >>> rp.center        Point2D(0, 0)        """        return self._center    centroid = center    @property    def circumcenter(self):        """        Alias for center.        Examples        ========        >>> from sympy import RegularPolygon, Point        >>> rp = RegularPolygon(Point(0, 0), 5, 4)        >>> rp.circumcenter        Point2D(0, 0)        """        return self.center    @property    def radius(self):        """Radius of the RegularPolygon        This is also the radius of the circumscribing circle.        Returns        =======        radius : number or instance of Basic        See Also        ========        sympy.geometry.line.Segment.length, sympy.geometry.ellipse.Circle.radius        Examples        ========        >>> from sympy import Symbol        >>> from sympy import RegularPolygon, Point        >>> radius = Symbol('r')        >>> rp = RegularPolygon(Point(0, 0), radius, 4)        >>> rp.radius        r        """        return self._radius    @property    def circumradius(self):        """        Alias for radius.        Examples        ========        >>> from sympy import Symbol        >>> from sympy import RegularPolygon, Point        >>> radius = Symbol('r')        >>> rp = RegularPolygon(Point(0, 0), radius, 4)        >>> rp.circumradius        r        """        return self.radius    @property    def rotation(self):        """CCW angle by which the RegularPolygon is rotated        Returns        =======        rotation : number or instance of Basic        Examples        ========        >>> from sympy import pi        >>> from sympy.abc import a        >>> from sympy import RegularPolygon, Point        >>> RegularPolygon(Point(0, 0), 3, 4, pi/4).rotation        pi/4        Numerical rotation angles are made canonical:        >>> RegularPolygon(Point(0, 0), 3, 4, a).rotation        a        >>> RegularPolygon(Point(0, 0), 3, 4, pi).rotation        0        """        return self._rot    @property    def apothem(self):        """The inradius of the RegularPolygon.        The apothem/inradius is the radius of the inscribed circle.        Returns        =======        apothem : number or instance of Basic        See Also        ========        sympy.geometry.line.Segment.length, sympy.geometry.ellipse.Circle.radius        Examples        ========        >>> from sympy import Symbol        >>> from sympy import RegularPolygon, Point        >>> radius = Symbol('r')        >>> rp = RegularPolygon(Point(0, 0), radius, 4)        >>> rp.apothem        sqrt(2)*r/2        """        return self.radius * cos(S.Pi/self._n)    @property    def inradius(self):        """        Alias for apothem.        Examples        ========        >>> from sympy import Symbol        >>> from sympy import RegularPolygon, Point        >>> radius = Symbol('r')        >>> rp = RegularPolygon(Point(0, 0), radius, 4)        >>> rp.inradius        sqrt(2)*r/2        """        return self.apothem    @property    def interior_angle(self):        """Measure of the interior angles.        Returns        =======        interior_angle : number        See Also        ========        sympy.geometry.line.LinearEntity.angle_between        Examples        ========        >>> from sympy import RegularPolygon, Point        >>> rp = RegularPolygon(Point(0, 0), 4, 8)        >>> rp.interior_angle        3*pi/4        """        return (self._n - 2)*S.Pi/self._n    @property    def exterior_angle(self):        """Measure of the exterior angles.        Returns        =======        exterior_angle : number        See Also        ========        sympy.geometry.line.LinearEntity.angle_between        Examples        ========        >>> from sympy import RegularPolygon, Point        >>> rp = RegularPolygon(Point(0, 0), 4, 8)        >>> rp.exterior_angle        pi/4        """        return 2*S.Pi/self._n    @property    def circumcircle(self):        """The circumcircle of the RegularPolygon.        Returns        =======        circumcircle : Circle        See Also        ========        circumcenter, sympy.geometry.ellipse.Circle        Examples        ========        >>> from sympy import RegularPolygon, Point        >>> rp = RegularPolygon(Point(0, 0), 4, 8)        >>> rp.circumcircle        Circle(Point2D(0, 0), 4)        """        return Circle(self.center, self.radius)    @property    def incircle(self):        """The incircle of the RegularPolygon.        Returns        =======        incircle : Circle        See Also        ========        inradius, sympy.geometry.ellipse.Circle        Examples        ========        >>> from sympy import RegularPolygon, Point        >>> rp = RegularPolygon(Point(0, 0), 4, 7)        >>> rp.incircle        Circle(Point2D(0, 0), 4*cos(pi/7))        """        return Circle(self.center, self.apothem)    @property    def angles(self):        """        Returns a dictionary with keys, the vertices of the Polygon,        and values, the interior angle at each vertex.        Examples        ========        >>> from sympy import RegularPolygon, Point        >>> r = RegularPolygon(Point(0, 0), 5, 3)        >>> r.angles        {Point2D(-5/2, -5*sqrt(3)/2): pi/3,         Point2D(-5/2, 5*sqrt(3)/2): pi/3,         Point2D(5, 0): pi/3}        """        ret = {}        ang = self.interior_angle        for v in self.vertices:            ret[v] = ang        return ret    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.        The general Polygon.encloses_point method is called only if        a point is not within or beyond the incircle or circumcircle,        respectively.        Parameters        ==========        p : Point        Returns        =======        encloses_point : True, False or None        See Also        ========        sympy.geometry.ellipse.Ellipse.encloses_point        Examples        ========        >>> from sympy import RegularPolygon, S, Point, Symbol        >>> p = RegularPolygon((0, 0), 3, 4)        >>> p.encloses_point(Point(0, 0))        True        >>> r, R = p.inradius, p.circumradius        >>> p.encloses_point(Point((r + R)/2, 0))        True        >>> p.encloses_point(Point(R/2, R/2 + (R - r)/10))        False        >>> t = Symbol('t', real=True)        >>> p.encloses_point(p.arbitrary_point().subs(t, S.Half))        False        >>> p.encloses_point(Point(5, 5))        False        """        c = self.center        d = Segment(c, p).length        if d >= self.radius:            return False        elif d < self.inradius:            return True        else:            # now enumerate the RegularPolygon like a general polygon.            return Polygon.encloses_point(self, p)    def spin(self, angle):        """Increment *in place* the virtual Polygon's rotation by ccw angle.        See also: rotate method which moves the center.        >>> from sympy import Polygon, Point, pi        >>> r = Polygon(Point(0,0), 1, n=3)        >>> r.vertices[0]        Point2D(1, 0)        >>> r.spin(pi/6)        >>> r.vertices[0]        Point2D(sqrt(3)/2, 1/2)        See Also        ========        rotation        rotate : Creates a copy of the RegularPolygon rotated about a Point        """        self._rot += angle    def rotate(self, angle, pt=None):        """Override GeometryEntity.rotate to first rotate the RegularPolygon        about its center.        >>> from sympy import Point, RegularPolygon, pi        >>> t = RegularPolygon(Point(1, 0), 1, 3)        >>> t.vertices[0] # vertex on x-axis        Point2D(2, 0)        >>> t.rotate(pi/2).vertices[0] # vertex on y axis now        Point2D(0, 2)        See Also        ========        rotation        spin : Rotates a RegularPolygon in place        """        r = type(self)(*self.args)  # need a copy or else changes are in-place        r._rot += angle        return GeometryEntity.rotate(r, angle, pt)    def scale(self, x=1, y=1, pt=None):        """Override GeometryEntity.scale since it is the radius that must be        scaled (if x == y) or else a new Polygon must be returned.        >>> from sympy import RegularPolygon        Symmetric scaling returns a RegularPolygon:        >>> RegularPolygon((0, 0), 1, 4).scale(2, 2)        RegularPolygon(Point2D(0, 0), 2, 4, 0)        Asymmetric scaling returns a kite as a Polygon:        >>> RegularPolygon((0, 0), 1, 4).scale(2, 1)        Polygon(Point2D(2, 0), Point2D(0, 1), Point2D(-2, 0), Point2D(0, -1))        """        if pt:            pt = Point(pt, dim=2)            return self.translate(*(-pt).args).scale(x, y).translate(*pt.args)        if x != y:            return Polygon(*self.vertices).scale(x, y)        c, r, n, rot = self.args        r *= x        return self.func(c, r, n, rot)    def reflect(self, line):        """Override GeometryEntity.reflect since this is not made of only        points.        Examples        ========        >>> from sympy import RegularPolygon, Line        >>> RegularPolygon((0, 0), 1, 4).reflect(Line((0, 1), slope=-2))        RegularPolygon(Point2D(4/5, 2/5), -1, 4, atan(4/3))        """        c, r, n, rot = self.args        v = self.vertices[0]        d = v - c        cc = c.reflect(line)        vv = v.reflect(line)        dd = vv - cc        # calculate rotation about the new center        # which will align the vertices        l1 = Ray((0, 0), dd)        l2 = Ray((0, 0), d)        ang = l1.closing_angle(l2)        rot += ang        # change sign of radius as point traversal is reversed        return self.func(cc, -r, n, rot)    @property    def vertices(self):        """The vertices of the RegularPolygon.        Returns        =======        vertices : list            Each vertex is a Point.        See Also        ========        sympy.geometry.point.Point        Examples        ========        >>> from sympy import RegularPolygon, Point        >>> rp = RegularPolygon(Point(0, 0), 5, 4)        >>> rp.vertices        [Point2D(5, 0), Point2D(0, 5), Point2D(-5, 0), Point2D(0, -5)]        """        c = self._center        r = abs(self._radius)        rot = self._rot        v = 2*S.Pi/self._n        return [Point(c.x + r*cos(k*v + rot), c.y + r*sin(k*v + rot))                for k in range(self._n)]    def __eq__(self, o):        if not isinstance(o, Polygon):            return False        elif not isinstance(o, RegularPolygon):            return Polygon.__eq__(o, self)        return self.args == o.args    def __hash__(self):        return super().__hash__()class Triangle(Polygon):    """    A polygon with three vertices and three sides.    Parameters    ==========    points : sequence of Points    keyword: asa, sas, or sss to specify sides/angles of the triangle    Attributes    ==========    vertices    altitudes    orthocenter    circumcenter    circumradius    circumcircle    inradius    incircle    exradii    medians    medial    nine_point_circle    Raises    ======    GeometryError        If the number of vertices is not equal to three, or one of the vertices        is not a Point, or a valid keyword is not given.    See Also    ========    sympy.geometry.point.Point, Polygon    Examples    ========    >>> from sympy import Triangle, Point    >>> Triangle(Point(0, 0), Point(4, 0), Point(4, 3))    Triangle(Point2D(0, 0), Point2D(4, 0), Point2D(4, 3))    Keywords sss, sas, or asa can be used to give the desired    side lengths (in order) and interior angles (in degrees) that    define the triangle:    >>> Triangle(sss=(3, 4, 5))    Triangle(Point2D(0, 0), Point2D(3, 0), Point2D(3, 4))    >>> Triangle(asa=(30, 1, 30))    Triangle(Point2D(0, 0), Point2D(1, 0), Point2D(1/2, sqrt(3)/6))    >>> Triangle(sas=(1, 45, 2))    Triangle(Point2D(0, 0), Point2D(2, 0), Point2D(sqrt(2)/2, sqrt(2)/2))    """    def __new__(cls, *args, **kwargs):        if len(args) != 3:            if 'sss' in kwargs:                return _sss(*[simplify(a) for a in kwargs['sss']])            if 'asa' in kwargs:                return _asa(*[simplify(a) for a in kwargs['asa']])            if 'sas' in kwargs:                return _sas(*[simplify(a) for a in kwargs['sas']])            msg = "Triangle instantiates with three points or a valid keyword."            raise GeometryError(msg)        vertices = [Point(a, dim=2, **kwargs) for a in args]        # remove consecutive duplicates        nodup = []        for p in vertices:            if nodup and p == nodup[-1]:                continue            nodup.append(p)        if len(nodup) > 1 and nodup[-1] == nodup[0]:            nodup.pop()  # last point was same as first        # remove collinear points        i = -3        while i < len(nodup) - 3 and len(nodup) > 2:            a, b, c = sorted(                [nodup[i], nodup[i + 1], nodup[i + 2]], key=default_sort_key)            if Point.is_collinear(a, b, c):                nodup[i] = a                nodup[i + 1] = None                nodup.pop(i + 1)            i += 1        vertices = list(filter(lambda x: x is not None, nodup))        if len(vertices) == 3:            return GeometryEntity.__new__(cls, *vertices, **kwargs)        elif len(vertices) == 2:            return Segment(*vertices, **kwargs)        else:            return Point(*vertices, **kwargs)    @property    def vertices(self):        """The triangle's vertices        Returns        =======        vertices : tuple            Each element in the tuple is a Point        See Also        ========        sympy.geometry.point.Point        Examples        ========        >>> from sympy import Triangle, Point        >>> t = Triangle(Point(0, 0), Point(4, 0), Point(4, 3))        >>> t.vertices        (Point2D(0, 0), Point2D(4, 0), Point2D(4, 3))        """        return self.args    def is_similar(t1, t2):        """Is another triangle similar to this one.        Two triangles are similar if one can be uniformly scaled to the other.        Parameters        ==========        other: Triangle        Returns        =======        is_similar : boolean        See Also        ========        sympy.geometry.entity.GeometryEntity.is_similar        Examples        ========        >>> from sympy import Triangle, Point        >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3))        >>> t2 = Triangle(Point(0, 0), Point(-4, 0), Point(-4, -3))        >>> t1.is_similar(t2)        True        >>> t2 = Triangle(Point(0, 0), Point(-4, 0), Point(-4, -4))        >>> t1.is_similar(t2)        False        """        if not isinstance(t2, Polygon):            return False        s1_1, s1_2, s1_3 = [side.length for side in t1.sides]        s2 = [side.length for side in t2.sides]        def _are_similar(u1, u2, u3, v1, v2, v3):            e1 = simplify(u1/v1)            e2 = simplify(u2/v2)            e3 = simplify(u3/v3)            return bool(e1 == e2) and bool(e2 == e3)        # There's only 6 permutations, so write them out        return _are_similar(s1_1, s1_2, s1_3, *s2) or \            _are_similar(s1_1, s1_3, s1_2, *s2) or \            _are_similar(s1_2, s1_1, s1_3, *s2) or \            _are_similar(s1_2, s1_3, s1_1, *s2) or \            _are_similar(s1_3, s1_1, s1_2, *s2) or \            _are_similar(s1_3, s1_2, s1_1, *s2)    def is_equilateral(self):        """Are all the sides the same length?        Returns        =======        is_equilateral : boolean        See Also        ========        sympy.geometry.entity.GeometryEntity.is_similar, RegularPolygon        is_isosceles, is_right, is_scalene        Examples        ========        >>> from sympy import Triangle, Point        >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3))        >>> t1.is_equilateral()        False        >>> from sympy import sqrt        >>> t2 = Triangle(Point(0, 0), Point(10, 0), Point(5, 5*sqrt(3)))        >>> t2.is_equilateral()        True        """        return not has_variety(s.length for s in self.sides)    def is_isosceles(self):        """Are two or more of the sides the same length?        Returns        =======        is_isosceles : boolean        See Also        ========        is_equilateral, is_right, is_scalene        Examples        ========        >>> from sympy import Triangle, Point        >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(2, 4))        >>> t1.is_isosceles()        True        """        return has_dups(s.length for s in self.sides)    def is_scalene(self):        """Are all the sides of the triangle of different lengths?        Returns        =======        is_scalene : boolean        See Also        ========        is_equilateral, is_isosceles, is_right        Examples        ========        >>> from sympy import Triangle, Point        >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(1, 4))        >>> t1.is_scalene()        True        """        return not has_dups(s.length for s in self.sides)    def is_right(self):        """Is the triangle right-angled.        Returns        =======        is_right : boolean        See Also        ========        sympy.geometry.line.LinearEntity.is_perpendicular        is_equilateral, is_isosceles, is_scalene        Examples        ========        >>> from sympy import Triangle, Point        >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3))        >>> t1.is_right()        True        """        s = self.sides        return Segment.is_perpendicular(s[0], s[1]) or \            Segment.is_perpendicular(s[1], s[2]) or \            Segment.is_perpendicular(s[0], s[2])    @property    def altitudes(self):        """The altitudes of the triangle.        An altitude of a triangle is a segment through a vertex,        perpendicular to the opposite side, with length being the        height of the vertex measured from the line containing the side.        Returns        =======        altitudes : dict            The dictionary consists of keys which are vertices and values            which are Segments.        See Also        ========        sympy.geometry.point.Point, sympy.geometry.line.Segment.length        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)        >>> t = Triangle(p1, p2, p3)        >>> t.altitudes[p1]        Segment2D(Point2D(0, 0), Point2D(1/2, 1/2))        """        s = self.sides        v = self.vertices        return {v[0]: s[1].perpendicular_segment(v[0]),                v[1]: s[2].perpendicular_segment(v[1]),                v[2]: s[0].perpendicular_segment(v[2])}    @property    def orthocenter(self):        """The orthocenter of the triangle.        The orthocenter is the intersection of the altitudes of a triangle.        It may lie inside, outside or on the triangle.        Returns        =======        orthocenter : Point        See Also        ========        sympy.geometry.point.Point        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)        >>> t = Triangle(p1, p2, p3)        >>> t.orthocenter        Point2D(0, 0)        """        a = self.altitudes        v = self.vertices        return Line(a[v[0]]).intersection(Line(a[v[1]]))[0]    @property    def circumcenter(self):        """The circumcenter of the triangle        The circumcenter is the center of the circumcircle.        Returns        =======        circumcenter : Point        See Also        ========        sympy.geometry.point.Point        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)        >>> t = Triangle(p1, p2, p3)        >>> t.circumcenter        Point2D(1/2, 1/2)        """        a, b, c = [x.perpendicular_bisector() for x in self.sides]        return a.intersection(b)[0]    @property    def circumradius(self):        """The radius of the circumcircle of the triangle.        Returns        =======        circumradius : number of Basic instance        See Also        ========        sympy.geometry.ellipse.Circle.radius        Examples        ========        >>> from sympy import Symbol        >>> from sympy import Point, Triangle        >>> a = Symbol('a')        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, a)        >>> t = Triangle(p1, p2, p3)        >>> t.circumradius        sqrt(a**2/4 + 1/4)        """        return Point.distance(self.circumcenter, self.vertices[0])    @property    def circumcircle(self):        """The circle which passes through the three vertices of the triangle.        Returns        =======        circumcircle : Circle        See Also        ========        sympy.geometry.ellipse.Circle        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)        >>> t = Triangle(p1, p2, p3)        >>> t.circumcircle        Circle(Point2D(1/2, 1/2), sqrt(2)/2)        """        return Circle(self.circumcenter, self.circumradius)    def bisectors(self):        """The angle bisectors of the triangle.        An angle bisector of a triangle is a straight line through a vertex        which cuts the corresponding angle in half.        Returns        =======        bisectors : dict            Each key is a vertex (Point) and each value is the corresponding            bisector (Segment).        See Also        ========        sympy.geometry.point.Point, sympy.geometry.line.Segment        Examples        ========        >>> from sympy import Point, Triangle, Segment        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)        >>> t = Triangle(p1, p2, p3)        >>> from sympy import sqrt        >>> t.bisectors()[p2] == Segment(Point(1, 0), Point(0, sqrt(2) - 1))        True        """        # use lines containing sides so containment check during        # intersection calculation can be avoided, thus reducing        # the processing time for calculating the bisectors        s = [Line(l) for l in self.sides]        v = self.vertices        c = self.incenter        l1 = Segment(v[0], Line(v[0], c).intersection(s[1])[0])        l2 = Segment(v[1], Line(v[1], c).intersection(s[2])[0])        l3 = Segment(v[2], Line(v[2], c).intersection(s[0])[0])        return {v[0]: l1, v[1]: l2, v[2]: l3}    @property    def incenter(self):        """The center of the incircle.        The incircle is the circle which lies inside the triangle and touches        all three sides.        Returns        =======        incenter : Point        See Also        ========        incircle, sympy.geometry.point.Point        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)        >>> t = Triangle(p1, p2, p3)        >>> t.incenter        Point2D(1 - sqrt(2)/2, 1 - sqrt(2)/2)        """        s = self.sides        l = Matrix([s[i].length for i in [1, 2, 0]])        p = sum(l)        v = self.vertices        x = simplify(l.dot(Matrix([vi.x for vi in v]))/p)        y = simplify(l.dot(Matrix([vi.y for vi in v]))/p)        return Point(x, y)    @property    def inradius(self):        """The radius of the incircle.        Returns        =======        inradius : number of Basic instance        See Also        ========        incircle, sympy.geometry.ellipse.Circle.radius        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(4, 0), Point(0, 3)        >>> t = Triangle(p1, p2, p3)        >>> t.inradius        1        """        return simplify(2 * self.area / self.perimeter)    @property    def incircle(self):        """The incircle of the triangle.        The incircle is the circle which lies inside the triangle and touches        all three sides.        Returns        =======        incircle : Circle        See Also        ========        sympy.geometry.ellipse.Circle        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(2, 0), Point(0, 2)        >>> t = Triangle(p1, p2, p3)        >>> t.incircle        Circle(Point2D(2 - sqrt(2), 2 - sqrt(2)), 2 - sqrt(2))        """        return Circle(self.incenter, self.inradius)    @property    def exradii(self):        """The radius of excircles of a triangle.        An excircle of the triangle is a circle lying outside the triangle,        tangent to one of its sides and tangent to the extensions of the        other two.        Returns        =======        exradii : dict        See Also        ========        sympy.geometry.polygon.Triangle.inradius        Examples        ========        The exradius touches the side of the triangle to which it is keyed, e.g.        the exradius touching side 2 is:        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(6, 0), Point(0, 2)        >>> t = Triangle(p1, p2, p3)        >>> t.exradii[t.sides[2]]        -2 + sqrt(10)        References        ==========        .. [1] https://mathworld.wolfram.com/Exradius.html        .. [2] https://mathworld.wolfram.com/Excircles.html        """        side = self.sides        a = side[0].length        b = side[1].length        c = side[2].length        s = (a+b+c)/2        area = self.area        exradii = {self.sides[0]: simplify(area/(s-a)),                   self.sides[1]: simplify(area/(s-b)),                   self.sides[2]: simplify(area/(s-c))}        return exradii    @property    def excenters(self):        """Excenters of the triangle.        An excenter is the center of a circle that is tangent to a side of the        triangle and the extensions of the other two sides.        Returns        =======        excenters : dict        Examples        ========        The excenters are keyed to the side of the triangle to which their corresponding        excircle is tangent: The center is keyed, e.g. the excenter of a circle touching        side 0 is:        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(6, 0), Point(0, 2)        >>> t = Triangle(p1, p2, p3)        >>> t.excenters[t.sides[0]]        Point2D(12*sqrt(10), 2/3 + sqrt(10)/3)        See Also        ========        sympy.geometry.polygon.Triangle.exradii        References        ==========        .. [1] https://mathworld.wolfram.com/Excircles.html        """        s = self.sides        v = self.vertices        a = s[0].length        b = s[1].length        c = s[2].length        x = [v[0].x, v[1].x, v[2].x]        y = [v[0].y, v[1].y, v[2].y]        exc_coords = {            "x1": simplify(-a*x[0]+b*x[1]+c*x[2]/(-a+b+c)),            "x2": simplify(a*x[0]-b*x[1]+c*x[2]/(a-b+c)),            "x3": simplify(a*x[0]+b*x[1]-c*x[2]/(a+b-c)),            "y1": simplify(-a*y[0]+b*y[1]+c*y[2]/(-a+b+c)),            "y2": simplify(a*y[0]-b*y[1]+c*y[2]/(a-b+c)),            "y3": simplify(a*y[0]+b*y[1]-c*y[2]/(a+b-c))        }        excenters = {            s[0]: Point(exc_coords["x1"], exc_coords["y1"]),            s[1]: Point(exc_coords["x2"], exc_coords["y2"]),            s[2]: Point(exc_coords["x3"], exc_coords["y3"])        }        return excenters    @property    def medians(self):        """The medians of the triangle.        A median of a triangle is a straight line through a vertex and the        midpoint of the opposite side, and divides the triangle into two        equal areas.        Returns        =======        medians : dict            Each key is a vertex (Point) and each value is the median (Segment)            at that point.        See Also        ========        sympy.geometry.point.Point.midpoint, sympy.geometry.line.Segment.midpoint        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)        >>> t = Triangle(p1, p2, p3)        >>> t.medians[p1]        Segment2D(Point2D(0, 0), Point2D(1/2, 1/2))        """        s = self.sides        v = self.vertices        return {v[0]: Segment(v[0], s[1].midpoint),                v[1]: Segment(v[1], s[2].midpoint),                v[2]: Segment(v[2], s[0].midpoint)}    @property    def medial(self):        """The medial triangle of the triangle.        The triangle which is formed from the midpoints of the three sides.        Returns        =======        medial : Triangle        See Also        ========        sympy.geometry.line.Segment.midpoint        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)        >>> t = Triangle(p1, p2, p3)        >>> t.medial        Triangle(Point2D(1/2, 0), Point2D(1/2, 1/2), Point2D(0, 1/2))        """        s = self.sides        return Triangle(s[0].midpoint, s[1].midpoint, s[2].midpoint)    @property    def nine_point_circle(self):        """The nine-point circle of the triangle.        Nine-point circle is the circumcircle of the medial triangle, which        passes through the feet of altitudes and the middle points of segments        connecting the vertices and the orthocenter.        Returns        =======        nine_point_circle : Circle        See also        ========        sympy.geometry.line.Segment.midpoint        sympy.geometry.polygon.Triangle.medial        sympy.geometry.polygon.Triangle.orthocenter        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)        >>> t = Triangle(p1, p2, p3)        >>> t.nine_point_circle        Circle(Point2D(1/4, 1/4), sqrt(2)/4)        """        return Circle(*self.medial.vertices)    @property    def eulerline(self):        """The Euler line of the triangle.        The line which passes through circumcenter, centroid and orthocenter.        Returns        =======        eulerline : Line (or Point for equilateral triangles in which case all                    centers coincide)        Examples        ========        >>> from sympy import Point, Triangle        >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)        >>> t = Triangle(p1, p2, p3)        >>> t.eulerline        Line2D(Point2D(0, 0), Point2D(1/2, 1/2))        """        if self.is_equilateral():            return self.orthocenter        return Line(self.orthocenter, self.circumcenter)def rad(d):    """Return the radian value for the given degrees (pi = 180 degrees)."""    return d*pi/180def deg(r):    """Return the degree value for the given radians (pi = 180 degrees)."""    return r/pi*180def _slope(d):    rv = tan(rad(d))    return rvdef _asa(d1, l, d2):    """Return triangle having side with length l on the x-axis."""    xy = Line((0, 0), slope=_slope(d1)).intersection(        Line((l, 0), slope=_slope(180 - d2)))[0]    return Triangle((0, 0), (l, 0), xy)def _sss(l1, l2, l3):    """Return triangle having side of length l1 on the x-axis."""    c1 = Circle((0, 0), l3)    c2 = Circle((l1, 0), l2)    inter = [a for a in c1.intersection(c2) if a.y.is_nonnegative]    if not inter:        return None    pt = inter[0]    return Triangle((0, 0), (l1, 0), pt)def _sas(l1, d, l2):    """Return triangle having side with length l2 on the x-axis."""    p1 = Point(0, 0)    p2 = Point(l2, 0)    p3 = Point(cos(rad(d))*l1, sin(rad(d))*l1)    return Triangle(p1, p2, p3)
 |