public bool Contains(Point p)
            {
                Euler   euler = new Euler();
                Point3D spt   = new Point3D();

                euler.phi     = -psi;
                euler.theta   = -theta;
                euler.psi     = -phi;
                euler.psi_a   = Euler.AXIS_Z;
                euler.theta_a = Euler.AXIS_X;
                euler.phi_a   = Euler.AXIS_Z;
                euler.transform(spt, new Point3D(p));
                Point sp = spt.toSpherePoint();

                return(FP.zero(sp.dec) && FP.ge(sp.ra, 0.0) && FP.le(sp.ra, length));
            }
            public Line(Point beg, Point end)
            {
                double l = beg.Distance(end);

                if (FP.eq(l, Math.PI))
                {
                    Debug.Assert(FP.eq(beg.ra, end.ra));
                    phi    = -Math.PI / 2;
                    theta  = Math.PI / 2;
                    psi    = beg.ra < 0.0 ? Math.PI * 2 + beg.ra : beg.ra;
                    length = Math.PI;
                    return;
                }
                if (beg.Equals(end))
                {
                    phi    = Math.PI / 2;
                    theta  = beg.dec;
                    psi    = beg.ra - Math.PI / 2;
                    length = 0.0;
                }
                else
                {
                    Point3D beg3d = new Point3D(beg);
                    Point3D end3d = new Point3D(end);
                    Point3D tp    = new Point3D();
                    Point   spt   = beg3d.cross(end3d).toSpherePoint();
                    Euler   euler = new Euler();
                    euler.phi     = -spt.ra - Math.PI / 2;
                    euler.theta   = spt.dec - Math.PI / 2;
                    euler.psi     = 0.0;
                    euler.psi_a   = Euler.AXIS_Z;
                    euler.theta_a = Euler.AXIS_X;
                    euler.phi_a   = Euler.AXIS_Z;
                    euler.transform(tp, beg3d);
                    spt = tp.toSpherePoint();

                    // invert
                    phi    = spt.ra;
                    theta  = -euler.theta;
                    psi    = -euler.phi;
                    length = l;
                }
            }
         public RectangleRn WrappingRectangle() { 
            Euler euler = new Euler();
            euler.phi      = phi;
            euler.theta    = theta;
            euler.psi      = psi;
            euler.psi_a    = Euler.AXIS_Z ;
            euler.theta_a  = Euler.AXIS_X;
            euler.phi_a    = Euler.AXIS_Z;
            
            if (FP.zero(length)) {
                Point3D beg3d = new Point3D();
                Point3D end3d = new Point3D();
                euler.transform(beg3d, new Point3D(0.0, 0.0));
                euler.transform(end3d, new Point3D(length, 0.0));
                RectangleRn r = beg3d.toRectangle();
                end3d.addToRectangle(r);
                return r;
            } else { 
                double l, ls, lc;
                Point3D[] v = new Point3D[4];
                Point3D tv = new Point3D();
                l  = length / 2.0; 
                ls = Math.Sin(l);
                lc = Math.Cos(l);
                euler.phi += l;
                
                v[0] = new Point3D(lc,  lc<0 ? -1.0 : -ls, 0.0);
                v[1] = new Point3D(1.0, lc<0 ? -1.0 : -ls, 0.0);
                v[2] = new Point3D(lc,  lc<0 ? +1.0 : +ls, 0.0);
                v[3] = new Point3D(1.0, lc<0 ? +1.0 : +ls, 0.0) ;
 
                Point3D min = new Point3D(1.0, 1.0, 1.0);
                Point3D max = new Point3D(-1.0, -1.0, -1.0);
 
                for (int i=0; i<4; i++) {
                    euler.transform(tv, v[i]);
                    if (tv.x < -1.0) {
                        min.x = -1.0;
                    } else if (tv.x > 1.0) {
                        max.x = 1.0;
                    } else { 
                        if (tv.x < min.x) { 
                            min.x = tv.x;
                        }
                        if (tv.x > max.x) { 
                            max.x = tv.x;
                        }
                    }
                    
                    if (tv.y < -1.0) {
                        min.y = -1.0;
                    } else if ( tv.y > 1.0 ) {
                        max.y = 1.0;
                    } else {
                        if (tv.y < min.y) { 
                            min.y = tv.y;
                        }
                        if (tv.y > max.y) { 
                            max.y = tv.y;
                        }
                    }
                    if (tv.z < -1.0) {
                        min.z = -1.0;
                    } else if (tv.z > 1.0) {
                        max.z = 1.0;
                    } else { 
                        if (tv.z < min.z) { 
                            min.z = tv.z;
                        }
                        if (tv.z > max.z) { 
                            max.z = tv.z;
                        }
                    } 
                }  
                return new RectangleRn(new double[]{min.x, min.y, min.z, max.x, max.y, max.z});
            }
        }
         public Line(Point beg, Point end) { 
             double l = beg.Distance(end);
             if (FP.eq(l, Math.PI)) {
                 Debug.Assert(FP.eq(beg.ra, end.ra));
                 phi = -Math.PI/2;
                 theta = Math.PI/2;
                 psi = beg.ra < 0.0 ? Math.PI*2 + beg.ra : beg.ra;
                 length = Math.PI;
                 return;
             }
             if (beg.Equals(end)) { 
                 phi    = Math.PI/2;
                 theta  = beg.dec;
                 psi    = beg.ra - Math.PI/2;
                 length = 0.0;
             } else { 
                 Point3D beg3d = new Point3D(beg);
                 Point3D end3d = new Point3D(end);
                 Point3D tp = new Point3D();
                 Point spt = beg3d.cross(end3d).toSpherePoint();
                 Euler euler = new Euler();
                 euler.phi     = - spt.ra - Math.PI/2;
                 euler.theta   =   spt.dec - Math.PI/2;
                 euler.psi     =   0.0 ;
                 euler.psi_a   = Euler.AXIS_Z;
                 euler.theta_a = Euler.AXIS_X;
                 euler.phi_a   = Euler.AXIS_Z;
                 euler.transform(tp, beg3d);
                 spt = tp.toSpherePoint();
 
                 // invert
                 phi = spt.ra;
                 theta = -euler.theta;
                 psi = -euler.phi;
                 length = l;
             }
         }
 public bool Contains(Point p) { 
     Euler euler = new Euler();
     Point3D spt = new Point3D();
     euler.phi     = -psi;
     euler.theta   = -theta;
     euler.psi     = -phi;
     euler.psi_a   = Euler.AXIS_Z;
     euler.theta_a = Euler.AXIS_X;
     euler.phi_a   = Euler.AXIS_Z;
     euler.transform(spt, new Point3D(p));
     Point sp = spt.toSpherePoint();
     return FP.zero(sp.dec) && FP.ge(sp.ra, 0.0) && FP.le(sp.ra, length);
 }                      
         public RectangleRn WrappingRectangle() { 
             Point3D[] v = new Point3D[8];
             Point3D tv = new Point3D();
             Euler euler = new Euler();
 
             double r0 = Math.Sin(rad0);
             double r1 = Math.Sin(rad1);
             double d = Math.Cos(rad1);
 
             v[0] = new Point3D(d, -r0, -r1);
             v[1] = new Point3D(d, +r0, -r1);
             v[2] = new Point3D(d, -r0, r1);
             v[3] = new Point3D(d, +r0, r1);
             v[4] = new Point3D(1.0, -r0, -r1);
             v[5] = new Point3D(1.0, +r0, -r1);
             v[6] = new Point3D(1.0, -r0, r1);
             v[7] = new Point3D(1.0, +r0, r1);
             
             euler.psi_a    = Euler.AXIS_Z ;
             euler.theta_a  = Euler.AXIS_Y;
             euler.phi_a    = Euler.AXIS_X;
             euler.phi      = phi;
             euler.theta    = theta;
             euler.psi      = psi;
 
             Point3D min = new Point3D(1.0, 1.0, 1.0);
             Point3D max = new Point3D(-1.0, -1.0, -1.0);
 
             for (int i=0; i<8; i++) {
                 euler.transform(tv, v[i]);
                 if (tv.x < -1.0) {
                     min.x = -1.0;
                 } else if (tv.x > 1.0) {
                     max.x = 1.0;
                 } else { 
                     if (tv.x < min.x) { 
                         min.x = tv.x;
                     }
                     if (tv.x > max.x) { 
                         max.x = tv.x;
                     }
                 }
       
                 if (tv.y < -1.0) {
                     min.y = -1.0;
                 } else if ( tv.y > 1.0 ) {
                     max.y = 1.0;
                 } else {
                     if (tv.y < min.y) { 
                         min.y = tv.y;
                     }
                     if (tv.y > max.y) { 
                         max.y = tv.y;
                     }
                 }
                 if (tv.z < -1.0) {
                     min.z = -1.0;
                 }  else if (tv.z > 1.0) {
                     max.z = 1.0;
                 } else { 
                     if (tv.z < min.z) { 
                         min.z = tv.z;
                     }
                     if (tv.z > max.z) { 
                         max.z = tv.z;
                     }
                 } 
             }
             return new RectangleRn(new double[]{min.x, min.y, min.z, max.x, max.y, max.z});
         }
            public RectangleRn WrappingRectangle()
            {
                Euler euler = new Euler();

                euler.phi     = phi;
                euler.theta   = theta;
                euler.psi     = psi;
                euler.psi_a   = Euler.AXIS_Z;
                euler.theta_a = Euler.AXIS_X;
                euler.phi_a   = Euler.AXIS_Z;

                if (FP.zero(length))
                {
                    Point3D beg3d = new Point3D();
                    Point3D end3d = new Point3D();
                    euler.transform(beg3d, new Point3D(0.0, 0.0));
                    euler.transform(end3d, new Point3D(length, 0.0));
                    RectangleRn r = beg3d.toRectangle();
                    end3d.addToRectangle(r);
                    return(r);
                }
                else
                {
                    double    l, ls, lc;
                    Point3D[] v  = new Point3D[4];
                    Point3D   tv = new Point3D();
                    l          = length / 2.0;
                    ls         = Math.Sin(l);
                    lc         = Math.Cos(l);
                    euler.phi += l;

                    v[0] = new Point3D(lc, lc < 0 ? -1.0 : -ls, 0.0);
                    v[1] = new Point3D(1.0, lc < 0 ? -1.0 : -ls, 0.0);
                    v[2] = new Point3D(lc, lc < 0 ? +1.0 : +ls, 0.0);
                    v[3] = new Point3D(1.0, lc < 0 ? +1.0 : +ls, 0.0);

                    Point3D min = new Point3D(1.0, 1.0, 1.0);
                    Point3D max = new Point3D(-1.0, -1.0, -1.0);

                    for (int i = 0; i < 4; i++)
                    {
                        euler.transform(tv, v[i]);
                        if (tv.x < -1.0)
                        {
                            min.x = -1.0;
                        }
                        else if (tv.x > 1.0)
                        {
                            max.x = 1.0;
                        }
                        else
                        {
                            if (tv.x < min.x)
                            {
                                min.x = tv.x;
                            }
                            if (tv.x > max.x)
                            {
                                max.x = tv.x;
                            }
                        }

                        if (tv.y < -1.0)
                        {
                            min.y = -1.0;
                        }
                        else if (tv.y > 1.0)
                        {
                            max.y = 1.0;
                        }
                        else
                        {
                            if (tv.y < min.y)
                            {
                                min.y = tv.y;
                            }
                            if (tv.y > max.y)
                            {
                                max.y = tv.y;
                            }
                        }
                        if (tv.z < -1.0)
                        {
                            min.z = -1.0;
                        }
                        else if (tv.z > 1.0)
                        {
                            max.z = 1.0;
                        }
                        else
                        {
                            if (tv.z < min.z)
                            {
                                min.z = tv.z;
                            }
                            if (tv.z > max.z)
                            {
                                max.z = tv.z;
                            }
                        }
                    }
                    return(new RectangleRn(new double[] { min.x, min.y, min.z, max.x, max.y, max.z }));
                }
            }
            public RectangleRn WrappingRectangle()
            {
                Point3D[] v     = new Point3D[8];
                Point3D   tv    = new Point3D();
                Euler     euler = new Euler();

                double r0 = Math.Sin(rad0);
                double r1 = Math.Sin(rad1);
                double d  = Math.Cos(rad1);

                v[0] = new Point3D(d, -r0, -r1);
                v[1] = new Point3D(d, +r0, -r1);
                v[2] = new Point3D(d, -r0, r1);
                v[3] = new Point3D(d, +r0, r1);
                v[4] = new Point3D(1.0, -r0, -r1);
                v[5] = new Point3D(1.0, +r0, -r1);
                v[6] = new Point3D(1.0, -r0, r1);
                v[7] = new Point3D(1.0, +r0, r1);

                euler.psi_a   = Euler.AXIS_Z;
                euler.theta_a = Euler.AXIS_Y;
                euler.phi_a   = Euler.AXIS_X;
                euler.phi     = phi;
                euler.theta   = theta;
                euler.psi     = psi;

                Point3D min = new Point3D(1.0, 1.0, 1.0);
                Point3D max = new Point3D(-1.0, -1.0, -1.0);

                for (int i = 0; i < 8; i++)
                {
                    euler.transform(tv, v[i]);
                    if (tv.x < -1.0)
                    {
                        min.x = -1.0;
                    }
                    else if (tv.x > 1.0)
                    {
                        max.x = 1.0;
                    }
                    else
                    {
                        if (tv.x < min.x)
                        {
                            min.x = tv.x;
                        }
                        if (tv.x > max.x)
                        {
                            max.x = tv.x;
                        }
                    }

                    if (tv.y < -1.0)
                    {
                        min.y = -1.0;
                    }
                    else if (tv.y > 1.0)
                    {
                        max.y = 1.0;
                    }
                    else
                    {
                        if (tv.y < min.y)
                        {
                            min.y = tv.y;
                        }
                        if (tv.y > max.y)
                        {
                            max.y = tv.y;
                        }
                    }
                    if (tv.z < -1.0)
                    {
                        min.z = -1.0;
                    }
                    else if (tv.z > 1.0)
                    {
                        max.z = 1.0;
                    }
                    else
                    {
                        if (tv.z < min.z)
                        {
                            min.z = tv.z;
                        }
                        if (tv.z > max.z)
                        {
                            max.z = tv.z;
                        }
                    }
                }
                return(new RectangleRn(new double[] { min.x, min.y, min.z, max.x, max.y, max.z }));
            }