geometry2d.py
from math import pi,sqrt,atan2;
from enum import Enum;

#------------------------------------------------------------------------------#

twicePi=2.0*pi;
halfPi=0.5*pi;

r2d=180.0/pi;
d2r=pi/180.0;

#------------------------------------------------------------------------------#

maxLength=10000.0; # area extension
epsilon=0.005
deltaAngles=epsilon/maxLength; # radian
deltaCoordinates=epsilon;

def areCoordinatesEqual(c1,c2:float)->bool:
    return abs(c2-c1)<deltaCoordinates;

def areLengthsEqual(l1,l2:float)->bool:
    return abs(l2-l1)<deltaCoordinates;

def areAnglesEqual(a1,a2:float)->bool:
    return abs(a2-a1)<deltaAngles;

#------------------------------------------------------------------------------#

class BadError(Exception):
    def __init__(self,data):
        self.Data=data;

class InvalidDataError(Exception):
    def __init__(self,data):
        self.Data=data;

class InfinityPoints(Exception):
    def __init__(self): pass;
    
class NoPoint(Exception):
    def __init__(self): pass;

#------------------------------------------------------------------------------#

class IntersectionKind(Enum):
    onePoint=1;
    noPoint=2;
    infinityPoints=3;
# end IntersectionKind

#------------------------------------------------------------------------------#

class Vectors(object):

    def __init__(self,x=0.0,y=0.0):
        self.X=x; self.Y=y;

    def toClone(self)->'Vectors':
        return Vectors(self.X,self.Y);

    def __str__(self)->str:
        return str(self.X)+', '+str(self.Y);

    def __eq__(self,vc:'Vectors')->bool:
        return (areCoordinatesEqual(self.X,vc.X) and
                areCoordinatesEqual(self.Y,vc.Y));

    def lengthOf(self)->float:
        return sqrt(self.X*self.X+self.Y*self.Y);

    def toUnitVector(self)->'Vectors':
        lg=sqrt(self.X*self.X+self.Y*self.Y);
        return Vectors(self.X/lg,self.Y/lg);
    # end toUnitVector

    def __add__(self,vc:'Vectors')->'Vectors':
        return Vectors(self.X+vc.X,self.Y+vc.Y);

    def __sub__(self,vc:'Vectors')->'Vectors':
        return Vectors(self.X-vc.X,self.Y-vc.Y);  

##    def __mul__(self,vc): # to float
##        return self.X*vc.X + self.Y*vc.Y;

    def __mul__(self,sc:float)->'Vectors': # with scalar
        return Vectors(self.X*sc,self.Y*sc); 

    def smul(self,sc:float)->None: # with scalar to self
        self.X*=sc; self.Y*=sc; 

    def __rmul__(self,sc:float)->'Vectors': # with scalar
        return Vectors(self.X*sc,self.Y*sc);

    def __truediv__(self,sc:float)->'Vectors': # with scalar
        return Vectors(self.X/sc,self.Y/sc);

    def sdiv(self,sc:float)->None: # with scalar to self
        self.X/=sc; self.Y/=sc;

    def dotOp(self,vc:'Vectors')->float:
        return self.X*vc.X + self.Y*vc.Y;

    def crossOp(self,vc:'Vectors')->float:
        return self.X*vc.Y - self.Y*vc.X;

    def dirAngleOf(self)->float:
        angle=atan2(self.Y,self.X);
        if angle<0.0:
            return angle+twicePi;
        else:
            return angle;
    # end directionAngle

    def sameDirectionOf(self,withV:'Vectors')->float:
        angleDiff=abs(withV.dirAngleOf()-self.dirAngleOf());
        if angleDiff<pi:
            return angleDiff<deltaAngles;
        else:
            return (twicePi-angleDiff)<deltaAngles;
    # end sameDirectionOf
    
# end Vectors

#------------------------------------------------------------------------------#

class Points(object):

    def __init__(self,x=0.0,y=0.0):
        self.X=x; self.Y=y;

    def toClone(self)->'Points':
        return Points(self.X,self.Y);

    def updateOf(self,withP:'Points')->None:
        self.X=withP.X;
        self.Y=withP.Y;
    # end updateOf

    def __str__(self)->str:
        return str(self.X)+', '+str(self.Y);

    def __eq__(self,pt: 'Points')-> bool:
        return (areCoordinatesEqual(self.X,pt.X) and
                areCoordinatesEqual(self.Y,pt.Y));

    def __add__(self,vc:'Vectors')->'Points': # point with vector to point
        return Points(self.X+vc.X,self.Y+vc.Y);

    def __sub__(self,pt:'Points')->'Vectors': # point with point to vector
        return Vectors(self.X-pt.X,self.Y-pt.Y);

    def distanceOf(self,toPoint:'Points')->float:
        return Vectors.lengthOf(toPoint-self); 

    def areaOf(self,p1,p2:'Points')->float: # of triangle
        return 0.5*Vectors.crossOp(p1-self,p2-self);

#end Points

#------------------------------------------------------------------------------#

class Lines(object):
    # Implicite line equation is A*x+B*y=C with sqrt(A*A+B*B)=1

    def __init__(self,a,b,c:float):
        d=sqrt(a*a+b*b);
        self.A=a/d; self.B=b/d; self.C=c/d;
        if self.A==0.0: #!!!#
            xlp=0.0;
            ylp=self.C/self.B;
            xdir=1.0;
            ydir=0.0;
        elif self.B==0: #!!!#
            xlp=self.C/self.A;
            ylp=0.0;
            xdir=0.0;
            ydir=1.0;
        else:
            xlp=0.0;
            ylp=self.C/self.B;
            c=sqrt(self.A*self.A+self.B*self.B);
            xdir=self.B/c;
            ydir=-self.A/c;
        # end if
        self.LP=Points(xlp,ylp);
        self.DV=Vectors(xdir,ydir);
    # end __init__

    def toClone(self)->'Lines':
        return Lines(self.A,self.B,self.C);

    def __str__(self)->str:
        return ('ABC: '+str(self.A)+' '+str(self.B)+' '+str(self.C)+
                '\n\tLP: '+str(self.LP)+' | DV: '+str(self.DV));

    def __eq__(self,ln:'Lines')->bool:
        return (self.LP==ln.LP) and ((self.DV==ln.DV) or (self.DV==(-1.0*ln.DV)));

    @staticmethod
    def toLine(p1,p2:'Points')->'Lines':
        a=p2.Y-p1.Y;
        b=p1.X-p2.X;
        c=a*p1.X + b*p1.Y;
        return Lines(a,b,c);
    # end toLine

    @staticmethod
    def toLineWithDir(p1:'Points',dv:'Vectors')-> 'Lines':
        p2: Points=p1+dv;
        return Lines.toLine(p1,p2);
    # end toLineWithDir

    def distanceOf(self,pt:'Points')->float: # signed distance
        return self.A*pt.X + self.B*pt.Y - self.C; 

    def nearestPointOf(self,fromP:'Points')->'Points':
        lP=self.LP;
        dV=self.DV;
        t=Vectors.dotOp(dV,fromP-lP);
        lP=lP+t*dV;
        return lP;
    # end nearestPointOf

    def isPointInside(self,pt:'Points')->bool:
        np=self.nearestPointOf(pt);
        return pt==np;
    # end isPointInside

    def areParallel(self,withLine:'Lines')->bool:
            # Vektorvergleich?
        an1=self.DV.dirAngleOf();
        an2=withLine.DV.dirAngleOf();
        sameDir=areAnglesEqual(an1,an2) or areAnglesEqual(an1,an2+pi);
        return sameDir and not (self.LP==withLine.LP);
    # end areParallel

    def intersectionOf(self,withLine:'Lines')->'Points':
        # Precondition: Lines are not parallel
        L1=self;
        L2=withLine;
        if L1==L2:
            raise InfinityPoints;
        if L1.areParallel(L2):
            raise NoPoint;
        lP1=L1.LP.toClone();
        lP2=L2.LP;
        dV1=L1.DV;
        dV2Ortho=Vectors(-L2.DV.Y,L2.DV.X);      
        q=Vectors.dotOp(dV1,dV2Ortho);
        ts=Vectors.dotOp(dV2Ortho,lP2-lP1);
        ts/=q;
        lP1=lP1+ts*dV1;
        return lP1;
    # end intersectionOf
    
# end Lines

#------------------------------------------------------------------------------#

class Rays(object):
    
    def __init__(self,startPoint:'Points',direction:'Vectors'):
        self.StartPoint=startPoint;
        self.Direction=direction.toUnitVector();
    # end __init__

    def toClone(self)->'Rays':
        return Rays(self.StartPoint,self.Direction);

    def __str__(self)->str:
        return 'SP: '+str(self.StartPoint)+' | DV: '+str(self.Direction);

    def __eq__(self,withRay:'Rays')->bool:
        return ((self.StartPoint==withRay.StartPoint) and
                 self.Direction==withRay.Direction);

    @staticmethod
    def toRay(pt1,pt2:'Points')->'Rays':
        return Rays(pt1,pt2-pt1);
    
    def toLine(self)->'Lines':
        return (Lines.toLine(self.StartPoint,
                self.StartPoint+self.Direction));

    def nearestPointOf(self,fromP:'Points')->'Points':
        lP=self.StartPoint.toClone();
        dV=self.Direction;
        t=Vectors.dotOp(dV,fromP-lP);
        if t>=0.0:      
            lP=lP+t*dV;
        return lP;
    # end nearestPointOf

    def isPointInside(self,pt:'Points')->bool:
        np=self.nearestPointOf(pt);
        if np==self.StartPoint:
            return False;
        else:
            return np==pt;
    # end isPointInside

    def intersectionOf(self,withRay:'Rays')->'Points': # or exceptions - TBD
        ry1=self;
        ry2=withRay;
        ln1=ry1.toLine();
        ln2=ry2.toLine();
        if Lines.areParallel(ln1,ln2):
            raise NoPoint;
        if ln1==ln2:
            if (ry1.isPointInside(ry2.StartPoint) or
                ry2.isPointInside(ry1.StartPoint)):
                raise InfinityPoints;
            elif ((ry1.StartPoint==ry2.StartPoint) and
                  (ry1.Direction==ry2.Direction)):
                raise InfinityPoints;
            else:
                raise NoPoint;
        # end if
        ip=Lines.intersectionOf(ln1,ln2);
        if (ry1.isPointInside(ip) and
            ry2.isPointInside(ip)):
            return ip;
        else:
            raise NoPoint;
        # end if
    # end intersectionOf
    
# end Rays

#------------------------------------------------------------------------------#

class Sectors(object):

    def __init__(self,startPoint:'Points',direction1,direction2:'Vectors'):
        self.StartPoint=startPoint;
        self.Direction1=direction1.toUnitVector();
        self.Direction2=direction2.toUnitVector();
    # end __init__

    def toClone(self)->'Sectors':
        return Sectors(self.StartPoint,self.Direction1,self.Direction2);

    def __str__(self)->str:
        return ('SP: '+str(self.StartPoint)+
                ' | DV1: '+str(self.Direction1)+
                ' | DV2: '+str(self.Direction2));

    def __eq__(self,withSector:'Sectors')->bool:
        return ((self.StartPoint==withSector.StartPoint) and
                 ((self.Direction1==withSector.Direction1) and
                 (self.Direction2==withSector.Direction2)) or
                 ((self.Direction1==withSector.Direction2) and
                 (self.Direction2==withSector.Direction1)));

    @staticmethod
    def toSector(pt0,pt1,pt2:'Points')->'Sectors':
        return Sectors(pt0,pt1-pt0,pt2-pt0);

    def openingAngleOf(self)->float: # no orientation, Öffnungswinkel
        return abs(self.Direction2.dirAngleOf()-self.Direction1.dirAngleOf());
        
# end Sectors

#------------------------------------------------------------------------------#

class Segments(object):
    
    def __init__(self,p1,p2:'Points'):
        self.P1=p1; self.P2=p2;

    @staticmethod
    def toSegmentWithDir(mp:'Points',dv:'Vectors',L:float)-> 'Segments':
        Lh=0.5*L;
        p1: Points=mp+(+Lh*dv);
        p2: Points=mp+(-Lh*dv);
        return Segments(p1,p2);
    # end toSegmentWithDir

    def toClone(self)->'Segments':
        return Segments(self.P1,self.P2);

    def update(self,withSegment:'Segments')->None:
        self.P1=withSegment.P1;
        self.P2=withSegment.P2;
    # end update
        
    def __str__(self)->str:
        return str(self.P1)+' | '+str(self.P2);

    def toLine(self)->'Lines':
        return Lines.toLine(self.P1,self.P2);

    def isPointInside(self,pt:'Points')->bool:
        ps1=self.P1;
        ps2=self.P2;
        rs1=Rays.toRay(ps1,ps2);
        rs2=Rays.toRay(ps2,ps1);
        return rs1.isPointInside(pt) and rs2.isPointInside(pt);
    # end isPointInside

    def distanceOf(self,pt:'Points')->float: # signed distance
        From=pt; To=self;
        if (Vectors.dotOp(From-To.P2,To.P2-To.P1)>0.0):
            return From.distanceOf(To.P2);
        elif (Vectors.dotOp(From-To.P1,To.P1-To.P2)>0.0):
            return From.distanceOf(To.P1);
        else:
            ln=To.toLine();
            return ln.distanceOf(From);
        # end if
    # end distanceOf

    def isIntersectionOf_withLine(self,withLine: 'Lines') -> bool:
        # or exceptions TBD; Abstand = 0 TBD
        ps1=self.P1;
        ps2=self.P2;
        # Orientierter (vorzeichenbehafteter) Abstand der beiden Endpunkte
        # der Nadel von der Dielengerade
        d1=withLine.distanceOf(ps1); assert(d1!=0.0);
        d2=withLine.distanceOf(ps2); assert(d2!=0.0);
        # Prüfen, ob die Nadel die Dielengerade kreuzt
        return ((d1>=0.0>=d2) or (d1<0.0<d2));
    # end isIntersectionOf_withLine


    def intersectionOf_withRay(self,withRay:'Rays')->'Points': # or exceptions - TBD
        ps1=self.P1;
        ps2=self.P2;
        rs1=Rays.toRay(ps1,ps2);
        rs2=Rays.toRay(ps2,ps1);
        
        intersectionKind1=IntersectionKind.onePoint;
        intersectionKind2=IntersectionKind.onePoint;
        
        try:
            ip1=withRay.intersectionOf(rs1);
            if (ip1==ps1) or (ip1==ps2):
                raise NoPoint;
        except NoPoint:
            intersectionKind1=IntersectionKind.noPoint;
        except InfinityPoints:
            intersectionKind1=IntersectionKind.infinityPoints;
        # end try
        
        try:
            ip2=withRay.intersectionOf(rs2);
            if (ip2==ps1) or (ip2==ps2):
                raise NoPoint;
        except NoPoint:
            intersectionKind2=IntersectionKind.noPoint;
        except InfinityPoints:
            intersectionKind1=IntersectionKind.infinityPoints;
        # end try

        if ((intersectionKind1==IntersectionKind.onePoint) and
            (intersectionKind2==IntersectionKind.onePoint)):
            assert(ip1==ip2); # TBD
            return ip1;
        elif ((intersectionKind1==IntersectionKind.noPoint) and
              (intersectionKind2==IntersectionKind.noPoint)):
            raise NoPoint;
        elif ((intersectionKind1==IntersectionKind.noPoint) and
              (intersectionKind2==IntersectionKind.onePoint)):
            raise NoPoint;
        elif ((intersectionKind2==IntersectionKind.noPoint) and
              (intersectionKind1==IntersectionKind.onePoint)):
            raise NoPoint;
        else:
            raise InfinityPoints; # TBD
        # end if
    # end intersectionOf_withRay

    def intersectionOf(self,withSegment:'Segments')->'Points': # or exceptions - TBD
        ps1=self.P1;
        ps2=self.P2;
        rs1=Rays.toRay(ps1,ps2);
        rs2=Rays.toRay(ps2,ps1);
        try:
            ip1=withSegment.intersectionOf_withRay(rs1);
            ip2=withSegment.intersectionOf_withRay(rs2);
        except InfinityPoints:
            if (withSegment.isPointInside(ps1) or
                withSegment.isPointInside(ps2)):
                raise InfinityPoints;
            else:
                raise NoPoint;
            # end if
        except NoPoint:
            raise NoPoint;
        else:
            assert ip1==ip2;
            return ip1;
        # end try
    # end intersectionOf

    def bisectorOf(self)->'Lines':
            # Return the bisector to self,
            # i.e. the line that is perpendicular
            # to self and goes through its middle
        ln=self.toLine();
        xMid=(self.P1.X+self.P2.X)/2.0;
        yMid=(self.P1.Y+self.P2.Y)/2.0;
        return Lines(-ln.B,
                      ln.A,
                     -ln.B*xMid + ln.A*yMid);
    # end bisectorOf

# end Segments

#------------------------------------------------------------------------------#

class Triangles(object):

    def __init__(self,p0,p1,p2:'Points'):
        self.P0=p0; self.P1=p1; self.P2=p2;

    def __str__(self)->str:
        return str(self.P0)+' | '+str(self.P1)+' | '+str(self.P2);

    def areaOf(self)->float:
        return Points.areaOf(self.P0,self.P1,self.P2);

    def isPointInside(self,pt:'Points')->bool:
        areaT=Points.areaOf(self.P0,self.P1,self.P2);
            # Barycentric coordinates with (alpha+beta+gamma)=1
            # alpha=Points.areaOf(pt,self.P1,self.P2)/areaT;
        beta =Points.areaOf(pt,self.P2,self.P0)/areaT;
        gamma=Points.areaOf(pt,self.P0,self.P1)/areaT;
        
        return (0.0<beta<1.0) and (0.0<gamma<1.0);
            #! No use of some delta
    # end isPointInside

# end Triangles

#------------------------------------------------------------------------------#

class Polygons(object):

    def __init__(self,pointList: list['Points']):
        self.NbPoints=len(pointList);
        self.Points=[];
        self.Points+=pointList;
    # end __init__

    def toClone(self)->'Polygons':
        return Polygons(self.Points);

    def __str__(self)->str:
        output='';
        for pt in self.Points:
            output+=str(pt)+' | ';

        return(output);          
    # end __str__

    def distanceOf(self,pt:'Points')->float:
        dist=-1;
        for Ix in range(0,self.NbPoints-1):
            p1=self.Points[Ix];
            p2=self.Points[Ix+1];
            sgm=Segments(p1,p2);
            dist=min(maxLength,sgm.distanceOf(pt));           
        # end for
        p1=self.Points[1];
        p2=self.Points[self.NbPoints-1];
        sgm=Segments(p1,p2);
        dist=min(dist,sgm.distanceOf(pt));
        return dist;
    # end distanceOf

    def areaOf(self)->float: # signed area
        # Precondition: convex polygons only
        p0=self.Points[0];
        area=0.0;
        for Ix in range(1,self.NbPoints-1):
            p1=self.Points[Ix];
            p2=self.Points[Ix+1];
            area+=Vectors.crossOp(p1-p0,p2-p0);
        # end for
        return 0.5*area;
    # end areaOf

# end Polygons

#------------------------------------------------------------------------------#

class Circles(object):

    class NoCircle(Exception):
        def __init__(self): pass; 

    def __init__(self,point:'Points',radius:float):
        self.C=point;
        self.R=radius;
    # end def

    def toClone(self)->'Circles':
        return Circles(self.C,self.R);

    def __str__(self)->str:
        return str(self.C)+' | '+str(self.R);

    @staticmethod
    def toCircle(pt1,pt2,pt3:'Points')->'Circles': # or exceptions - TBD
        sgm1=Segments(pt1,pt2);
        sgm2=Segments(pt2,pt3);
        bis1=sgm1.bisectorOf();
        bis2=sgm2.bisectorOf();
        try:
            center=Lines.intersectionOf(bis1,bis2);
        except (NoPoint,InfinityPoints):
            raise Circles.NoCircle;
        # end try
        radius=pt1.distanceOf(center);
        return Circles(center,radius);
    # end toCircle

    def areaOf(self)->float:
        return pi*self.R*self.R;

    def distanceOf(self,pt:'Points')->float: # signed distance
        d=Points.distanceOf(self.C,pt);
        r=self.R;
        return d-r;
    # end distanceOf

    def isPointInside(self,pt:'Points')->bool:
        dist=self.distanceOf(pt);
        return (dist<0) and not areLengthsEqual(dist,0);
    # end isPointInside

# end Circles

#------------------------------------------------------------------------------#
#------------------------------------------------------------------------------#

if __name__ == '__main__':
    print('\n# Beginning Test ...');

#------------------------------------------------------------------------------#
#---Vectors--------------------------------------------------------------------#

    if True:
        print('\nVectors ...');
        vc1=Vectors(1,2);
        assert str(vc1)=='1, 2';
        assert vc1==Vectors(1,2);
        assert vc1==Vectors(1,2.001);
        assert not vc1==Vectors(1,2.01);
        assert not vc1==Vectors(1,3);
        vc2=Vectors(2,4);
        assert vc1+vc1==vc2;
        assert vc1*2==Vectors(2,4);
        assert 2*vc1==Vectors(2,4);
        vc3=vc2/2.0;
        assert vc3==vc1;
        vc1.smul(2);
        assert vc1==vc2;
        vc1.sdiv(2);
        assert vc1==vc1;
        assert Vectors.dotOp(vc1,vc1)==5;
        assert Vectors.dotOp(Vectors(1,1),Vectors(-1,1))==0;      
        assert Vectors.crossOp(vc1,vc1)==0;
        assert Vectors.crossOp(vc1,Vectors(1,3))==1;
        vc3=vc1.toUnitVector();
        assert not vc3.lengthOf()==1;
        assert abs(vc3.lengthOf()-1) <epsilon;
        vc4=Vectors(1,1);
        assert vc4.dirAngleOf()*r2d==45;
        vc4=Vectors(-1,1);
        assert vc4.dirAngleOf()*r2d==135;
        vc4=Vectors(-1,-1);
        assert vc4.dirAngleOf()*r2d==225;
        assert vc1.sameDirectionOf(vc1);
        assert vc1.sameDirectionOf(Vectors(1,2.000001));
        assert not vc1.sameDirectionOf(Vectors(1,2.00001));
    # end if

#---Points---------------------------------------------------------------------#

    if True:
        print('\nPoints ...');
        pt1=Points(1,2);
        assert pt1==pt1;
        assert str(pt1)=='1, 2';
        assert pt1.distanceOf(pt1)==0.0;
        pt2=Points(3,2);
        assert pt1.distanceOf(pt2)==2.0;
        assert pt2.distanceOf(pt1)==2.0;
        vc1=Vectors(1,1);
        assert (pt2-pt1)==Vectors(2,0);
        assert (pt1+vc1)==Points(2,3);
        pt3=Points(0,0); pt4=Points(2,0); pt5=Points(1,1);
        assert pt3.areaOf(pt4,pt5)==1.0;
        assert pt3.areaOf(pt5,pt4)==-1.0;
    # end if

#---Lines----------------------------------------------------------------------#

    if True:
        print('\nLines ...');
        pt1=Points(0,0); pt2=Points(2,2);
        pt3=Points(0,2); pt4=Points(2,0);
        L12=Lines.toLine(pt1,pt2); 
        L34=Lines.toLine(pt3,pt4); 
        assert L12==L12;
        assert not L12==L34;
        ipt=Lines.intersectionOf(L12,L34);
        pt5=Points(1,1);
        assert ipt==pt5;
        try:
            ipt=Lines.intersectionOf(L12,L12);
        except InfinityPoints:
            pass;
        assert L12.nearestPointOf(pt5)==pt5;
        assert L12.nearestPointOf(Points(0,2))==pt5;
        pt6=Points(3,3);
        assert Lines.isPointInside(L12,pt6); 
        assert not Lines.isPointInside(L12,pt3); 
        assert abs(abs(Lines.distanceOf(L12,pt3))-sqrt(2))<epsilon;
        assert not abs(Lines.distanceOf(L12,pt3)-sqrt(2))<epsilon;
        assert not Lines.areParallel(L12,L12);
        pt7=Points(1,0); pt8=Points(3,2);
        L78=Lines.toLine(pt7,pt8);
        assert Lines.areParallel(L12,L78);
        try:
            ipt=Lines.intersectionOf(L12,L78);
        except NoPoint:
            pass;
    # end if

#---Rays-----------------------------------------------------------------------#

    if True:
        print('\nRays ...');
        sp1=Points(1,1); dir1=Vectors(1,1);
        ray1=Rays(sp1,dir1);
        assert ray1==ray1;
        assert str(ray1)=='SP: 1, 1 | DV: 0.7071067811865475, 0.7071067811865475';
        ln1=ray1.toLine(); 
        ln2=Lines(0.707106781186545,-0.7071067811865475,0.0);
        assert ln1==ln2;
        sp2=Points(3,1); dir2=Vectors(-1,1);
        ray2=Rays(sp2,dir2);
        ip12=Rays.intersectionOf(ray1,ray2);
        assert ip12==Points(2,2);
        sp3=Points(1,1); dir3=Vectors(-1,1);
        ray3=Rays(sp3,dir3);
        try:
            ip13=Rays.intersectionOf(ray1,ray3);
        except NoPoint:
            pass;
        try:
            ip13=Rays.intersectionOf(ray1,ray1);
        except InfinityPoints:
            pass;
        sp4=Points(1,1); dir4=Vectors(-1,-1);
        ray4=Rays(sp4,dir4);
        try:
            ip44=Rays.intersectionOf(ray4,ray1);
        except NoPoint:
            pass;
    # end if

#---Sectors -------------------------------------------------------------------#

    if True:
        print('\nSectors ...');
        pt0=Points(0,0);
        pt1=Points(1,1);
        pt2=Points(-1,1);
        sec=Sectors.toSector(pt0,pt1,pt2);
        assert sec==sec;
        sp1=Points(2,0);
        dv1=Vectors(-0.7071067811865475,0.7071067811865475);
        dv3=Vectors(-0.7071067811865476,0.7071067811865476);
        dv2=Vectors(0.4472135954999579, 0.8944271909999159);
        dv4=Vectors(0.447213595499958,  0.894427190999916);
        sc1=Sectors(sp1,dv1,dv2);
        sc2=Sectors(sp1,dv3,dv4);
        assert sc1==sc2;
        sc3=Sectors(sp1,dv1,dv2);
        sc4=Sectors(sp1,dv4,dv3);
        assert sc3==sc4;
    # end if

#---Segments-------------------------------------------------------------------#

    if True:
        print('\nSegments ...');
        pt1=Points(1,1);
        pt2=Points(2,1);
        sgm=Segments(pt1,pt2);
        assert sgm==sgm;
        assert str(sgm)=='1, 1 | 2, 1';
        sp1=Points(2.5,0); dir1=Vectors(-1,1);
        ray1=Rays(sp1,dir1);
        ip1=sgm.intersectionOf_withRay(ray1);
        assert ip1==Points(1.5,1.0);     
        assert sgm.distanceOf(pt2)==0.0;
        try:
            pt0=Points(0,0); dir0=Vectors(1,1);
            ray0=Rays(pt0,dir0);
            ip0=sgm.intersectionOf_withRay(ray0);
        except NoPoint:
            pass;
        else:
            raise BadError('### in intersectionOf_withRay ###');
        # end try
        pt3=Points(1.5,2);
        assert sgm.distanceOf(pt3)==-1.0;
        pt3=Points(1.5,0);
        assert sgm.distanceOf(pt3)==1.0;
        pt1a=Points(1.4,0);
        pt2a=Points(1.4,2);
        sgma=Segments(pt1a,pt2a);
        ipa=sgm.intersectionOf(sgma);
        assert ipa==Points(1.4,1);
        pt1b=Points(1.3,0);
        pt2b=Points(4,3);
        sgmb=Segments(pt1a,pt2b);
        try:
            ipb=sgm.intersectionOf(sgmb);
        except NoPoint: pass;
    # end if

#---Polygons-------------------------------------------------------------------#

    if True:
        print('\nPolygons ...');
        pt1=Points(1,0); pt2=Points(2,1); pt3=Points(0,1);
        pts1=[pt1,pt2,pt3];
        pg1=Polygons(pts1);
        assert pg1.areaOf()==1.0;
        pts2=[pt1,pt3,pt2];
        pg2=Polygons(pts2);
        assert pg2.areaOf()==-1.0;
        pt4=Points(2,2);
        assert pg1.distanceOf(pt4)==1.0;
    # end if

#---Triangles------------------------------------------------------------------#

    if True:
        print('\nTriangles ...');
        pt1=Points(1,0); pt2=Points(2,1); pt3=Points(0,1);
        tri=Triangles(pt1,pt2,pt3);
        assert tri.areaOf()==1.0; 
        p4=Points(3,3);
        assert not tri.isPointInside(p4);
        p5=Points(1,0.5);
        assert tri.isPointInside(p5);
    # end if

#---Circles----------------------  --------------------------------------------#

    if True:
        print('\nCircles ...');
        pt1=Points(2,2);
        cc1=Circles(pt1,1);
        assert not cc1.isPointInside(Points(1,1));
        assert cc1.isPointInside(Points(1.5,2)); 
        pt2=Points(1,1); pt3=Points(2,0); pt4=Points(2,2);
        cc2=Circles.toCircle(pt2,pt3,pt4);
        assert str(cc2)=='2.0, 1.0 | 1.0';
        pt5=Points(1,1); pt6=Points(2,1); pt7=Points(3,1);
        try:
            cc3=Circles.toCircle(pt5,pt6,pt7);
        except Circles.NoCircle:
            pass;
    # end if

#------------------------------------------------------------------------------#
#------------------------------------------------------------------------------#
    print('\n# Finished Test.\n');
# end if main