
public class PSMyGraphics
    extends MyGraphics
{
    public PSMyGraphics(java.io.PrintWriter writer,
                        java.awt.Dimension gsize,
                        double x0, double x1, double y0, double y1)
    {
        super(null, gsize, x0, x1, y0, y1);
        this.writer = writer;
        writePrologue();
    }
    public PSMyGraphics(java.io.PrintWriter writer,
                        java.awt.Dimension gsize)
    {
        super(null, gsize);
        this.writer = writer;
        writePrologue();
    }

    private void writePrologue()
    {
        String dateString = (new java.text.SimpleDateFormat("EEE MMM dd H:MM:ss yyyy")).format(new java.util.Date());

        writer.println("%!PS-Adobe-2.0 EPSF-2.0");
        writer.println("%%Title: template");
        writer.println("%%Creator: HyperbolicApplet Version foo Patchlevel bar");

        writer.println("%%CreationDate: "+dateString);
        writer.println("%%For: rdlyons@probable.math.indiana.edu (Russell Lyons)");
        writer.println("%%BoundingBox: 0 0 421 421");
        writer.println("%%Magnification: 1.0000");
        writer.println("%%EndComments");
        writer.println();
        if (false)
        {
            writer.println("/paperwidth 8.5 def  % in inches");
            writer.println("/paperheight 11 def  % in inches");
            writer.println("72.0 72.0 scale  % so we can think in inches");
            writer.println("paperwidth 2 div paperheight 2 div translate  % center at center of page");
            writer.println("paperwidth 2 div paperwidth 2 div scale  % so local -1..1 is width of paper");
            writer.println(".0025 setlinewidth % XXX argh, need to do something more sensible");
            writer.println();
        }
        else
        {




            writer.println();
            writer.println("/n {newpath} bind def");
            writer.println("/s {stroke} bind def");
            writer.println("/m {moveto} bind def");
            writer.println("/l {lineto} bind def");
            writer.println("/a {arc} bind def");
            writer.println();
            writer.println("421 2 div 421 2 div translate");
            writer.println(".5  420 2 div  div setlinewidth");
            writer.println("420 2 div 420 2 div scale");
            writer.println();
            writer.println("% We are now in a local coordinate system");
            writer.println("% in which the circle will be centered at 0,0 and have radius 1.");

        }
    }

    public void fillWindow()
    {



        endPathIfInPath();
        writer.println("n -1.1 -1.1 m 1.1 -1.1 l 1.1 1.1 l -1.1 1.1 l fill s  % fill background");

    }

    public void drawLineNonClipped(double x0, double y0, double x1, double y1,
                                   boolean antiAlias)
    {

        newPathIfNotInPath();
        writer.println(""
                      +(float)(x0 * scaleX + translateX)
                      +" "
                      +(float)(y0 * scaleY + translateY)
                      +" m"
                      +" "
                      +(float)(x1 * scaleX + translateX)
                      +" "
                      +(float)(y1 * scaleY + translateY)
                      +" l");

    }

    public void drawPoint(double x, double y, int nPixels)
    {
        int x0 = (int)(x * scaleX + translateX - .5*nPixels);
        int y0 = (int)(y * scaleY + translateY - .5*nPixels);
        if (verbose >= 2) System.out.println("in drawPoint("+x+","+y+","+nPixels+") -> "+x0+","+y0);


        if (x0 < -nPixels
         || x0 > gsize.width+nPixels
         || y0 < -nPixels
         || y0 > gsize.width+nPixels)
            return;





        { if (!(false)) HyperbolicUtils.set_exception("PSMyGraphics.java","drawPoint" , "x0 et y0 hors ecran" ); };
    }
    public void drawString(String s, double x, double y)
    {




        { if (!(false)) HyperbolicUtils.set_exception("PSMyGraphics.java","drawString" ,"false" ); };
    }
    public void drawString(String s, double x, double y,
                                     double xJustify,
                                     double yJustify)
    {

        { if (!(false)) HyperbolicUtils.set_exception("PSMyGraphics.java","drawString" , "false" ); };
    }

    public double getFontHeight()
    {




        { if (!(false)) HyperbolicUtils.set_exception("PSMyGraphics.java","getFontHeight" , "false" ); };
        return 0.;
    }



    public void smartDrawArcNonClipped(
        double focusX, double focusY,
        double focusAngleRadians,
        double c,
        double start, double end,
        boolean antiAlias)
    {






        if (c > 1e-5)
        {
            double r = 1./c;
            double centerX = focusX - r*Math.cos(focusAngleRadians);
            double centerY = focusY - r*Math.sin(focusAngleRadians);
            double startRadians = focusAngleRadians + start*c;
            double endRadians = focusAngleRadians + end*c;


            double temp;
            if (endRadians < startRadians)
                {temp=(startRadians);startRadians=(endRadians);endRadians=(temp);};



            endPathIfInPath();
            writer.println("n"
                         +" "+(float)centerX
                         +" "+(float)centerY
                         +" "+(float)r
                         +" "+(float)((startRadians)*(180./Math.PI))
                         +" "+(float)((endRadians)*(180./Math.PI))
                         +" a"
                         +" s");
            if (false)
                System.out.print(".");
            return;
        }
        else if (true)
        {
            double cosFocusAngle = Math.cos(focusAngleRadians);
            double sinFocusAngle = Math.sin(focusAngleRadians);
            double normalAndTangent[][] = {
                { cosFocusAngle, sinFocusAngle},
                {-sinFocusAngle, cosFocusAngle},
            };
            double startX, startY;
            double endX, endY;
            {
                double point[] = new double[2];
                double scratch[] = new double[2];

                findPointOnArc(start, focusX, focusY, c, normalAndTangent, scratch, point);
                startX = point[0];
                startY = point[1];

                findPointOnArc(end, focusX, focusY, c, normalAndTangent, scratch, point);
                endX = point[0];
                endY = point[1];
            }
            endPathIfInPath();
            writer.println(""
                          +"n "
                          +(float)startX
                          +" "
                          +(float)startY
                          +" m"
                          +" "
                          +(float)endX
                          +" "
                          +(float)endY
                          +" l s");
            if (false)
                System.out.print("!");
            return;
        }

        final boolean useSegments = true;

        if (useSegments)
        {
            int nSegs = ((10)>=((int)((((end-start)*c) < 0 ? -((end-start)*c) : ((end-start)*c)) / (1*(Math.PI/180))))?(10):((int)((((end-start)*c) < 0 ? -((end-start)*c) : ((end-start)*c)) / (1*(Math.PI/180)))));


            double cosFocusAngle = Math.cos(focusAngleRadians);
            double sinFocusAngle = Math.sin(focusAngleRadians);
            double normalAndTangent[][] = {
                { cosFocusAngle, sinFocusAngle},
                {-sinFocusAngle, cosFocusAngle},
            };

            double prevPoint[] = new double[2];
            double point[] = new double[2];
            double scratch[] = new double[2];
            for (int i = 0; i < nSegs+1; ++i)
            {
                double t = ((start) + ((double)i/nSegs)*((end)-(start)));
                findPointOnArc(t, focusX, focusY, c, normalAndTangent, scratch, point);

                if (i > 0)
                    this.drawLineNonClipped(prevPoint[0], prevPoint[1],
                                            point[0], point[1],
                                            antiAlias);

                System.arraycopy(point, 0, prevPoint, 0, 2);
            }
            return;
        }





        if (c*Math.abs(start-end) > .75*Math.PI)
        {
            smartDrawArcNonClipped(focusX, focusY, focusAngleRadians, c,
                                   start, (start+end)*.5,
                                   antiAlias);
            smartDrawArcNonClipped(focusX, focusY, focusAngleRadians, c,
                                   (start+end)*.5, end,
                                   antiAlias);

            return;
        }






        {
            focusX = focusX * scaleX + translateX;
            focusY = focusY * scaleY + translateY;
            if (scaleY < 0.)
            {
                focusAngleRadians *= -1.;
                start *= -1.;
                end *= -1.;
            }
            double scale = .5 * (Math.abs(scaleX) + Math.abs(scaleY));
            c /= scale;
            start *= scale;
            end *= scale;
        }






        {
            if (c < 0.)
            {
                c *= -1.;
                focusAngleRadians += Math.PI;
                start *= -1.;
                end *= -1.;
            }
            if (start > end)
            {
                double temp = start;
                start = end;
                end = temp;
            }
        }



        double normalX = Math.cos(focusAngleRadians);
        double normalY = Math.sin(focusAngleRadians);
        double normalAndTangent[][] = {
            { normalX, normalY},
            {-normalY, normalX},
        };





        double startX, startY;
        double endX, endY;
        {
            double point[] = new double[2];
            double scratch[] = new double[2];

            findPointOnArc(start, focusX, focusY, c, normalAndTangent, scratch, point);
            startX = point[0];
            startY = point[1];

            findPointOnArc(end, focusX, focusY, c, normalAndTangent, scratch, point);
            endX = point[0];
            endY = point[1];
        }


        int maxPointsToDraw = (int)Math.ceil(Math.abs(end-start)) + 1;

        int nPointsDrawn = 0;
        int x = (int)Math.round(startX), y = (int)Math.round(startY);
        double X = x - focusX;
        double Y = y - focusY;




        newPathIfNotInPath();
        writer.println(""+(float)startX+" "+(float)startY+" moveto");

        nPointsDrawn++;

        if (c < 1)
        {
{System.out.print("    ");System.out.println("404" + " = " + (404));};
            while (true)
            {
{System.out.print("    ");System.out.println("407" + " = " + (407));};
                final int quantizedDirVectors[][] = {
                    {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0}, {-1,-1}, {0,-1}, {1,-1}
                };

                int dir;
                {


                    double thisNormalX = c*X + normalX;
                    double thisNormalY = c*Y + normalY;
                    double thisTangentX = -thisNormalY;
                    double thisTangentY = thisNormalX;
                    int quantizedDirX = (thisTangentX < 0. ? -1 : thisTangentX > 0. ? 1 : 0);
                    int quantizedDirY = (thisTangentY < 0. ? -1 : thisTangentY > 0. ? 1 : 0);
                    double absThisTangentX = Math.abs(thisTangentX);
                    double absThisTangentY = Math.abs(thisTangentY);
                    if (absThisTangentX > 2*absThisTangentY)
                        quantizedDirY = 0;
                    else if (absThisTangentY > 2*absThisTangentX)
                        quantizedDirX = 0;

                    for (dir = 0; dir < 8; ++dir)
                    {
                        if (quantizedDirVectors[dir][0] == quantizedDirX
                         && quantizedDirVectors[dir][1] == quantizedDirY)
                            break;
                    }
                    if (dir == 8)
                    {
                        System.out.println("huh?????");
                        break;
                    }
                }

                x += quantizedDirVectors[dir][0];
                y += quantizedDirVectors[dir][1];
                X = x - focusX;
                Y = y - focusY;



                double signedDistanceOutsideCircle =
                    (c * (X*X + Y*Y) + 2 * (X*normalX + Y*normalY))
                  / (MyMath.hypot(c*X + normalX, c*Y + normalY) + 1.);





                double inflation;
                {
                    double thisNormalX = c*X + normalX;
                    double thisNormalY = c*Y + normalY;
                    inflation = MyMath.hypot(thisNormalX, thisNormalY)
                              / Math.max(Math.abs(thisNormalX), Math.abs(thisNormalY));
                }

                signedDistanceOutsideCircle *= inflation;


                if (signedDistanceOutsideCircle > .5001)
                {
                    x -= quantizedDirVectors[dir][0];
                    y -= quantizedDirVectors[dir][1];

                    dir = (dir + 1) % 8;
                    x += quantizedDirVectors[dir][0];
                    y += quantizedDirVectors[dir][1];
                    X = x - focusX;
                    Y = y - focusY;
                }
                else if (signedDistanceOutsideCircle < -.5001)
                {
                    x -= quantizedDirVectors[dir][0];
                    y -= quantizedDirVectors[dir][1];

                    dir = (dir - 1 + 8) % 8;
                    x += quantizedDirVectors[dir][0];
                    y += quantizedDirVectors[dir][1];
                    X = x - focusX;
                    Y = y - focusY;
                }

                {
                    double thisNormalX = c*X + normalX;
                    double thisNormalY = c*Y + normalY;
                    signedDistanceOutsideCircle =
                        (c * (X*X + Y*Y) + 2 * (X*normalX + Y*normalY))
                      / (MyMath.hypot(c*X + normalX, c*Y + normalY) + 1.);
                    double arcPointX = focusX + X - signedDistanceOutsideCircle * thisNormalX;
                    double arcPointY = focusY + Y - signedDistanceOutsideCircle * thisNormalY;
                    double startToArcPoint = MyMath.hypot(arcPointX-startX,
                                                          arcPointY-startY);
                    double arcDistance = startToArcPoint * MyMath.asin_over_x(c*startToArcPoint*.5);
                    if (false)
                    {
                        System.out.println("    nPointsDrawn = "+nPointsDrawn);
                        System.out.println("        signedDistanceOutsideCircle = "+signedDistanceOutsideCircle);
                        System.out.println("        startX = "+startX);
                        System.out.println("        startY = "+startY);
                        System.out.println("        arcPointX = "+arcPointX);
                        System.out.println("        arcPointY = "+arcPointY);
                        System.out.println("        startToArcPoint = "+startToArcPoint);
                        System.out.println("        arcDistance = "+arcDistance);
                        System.out.println("        start = "+start);
                        System.out.println("        end = "+end);
                        System.out.println("        c = "+c);
                    }
                    if (arcDistance >= end-start)
                        break;
                }






                writer.println(""+(float)((x-translateX)/scaleX)+" "+(float)((y-translateY)/scaleY)+" lineto  % sorry this is quantized");

                nPointsDrawn++;




                if (nPointsDrawn >= maxPointsToDraw-1)
                    break;
            }





            nPointsDrawn++;
        }




            writer.println(""+(float)endX+" "+(float)endY+" lineto");


    }





    public void drawThickArc(double x, double y, double width, double height,
                             double startRadians, double arcRadians,
                             double lineThicknessInPixels,
                             boolean antiAlias)
    {

        double centerX = x + width*.5;
        double centerY = y + height*.5;
        double radius = .25*(width+height);
        double curvature = 1./radius;
        double focusAngleRadians = startRadians;
        double start = 0.;
        double end = arcRadians * radius;
        double focusX = centerX + radius * Math.cos(focusAngleRadians);
        double focusY = centerY + radius * Math.sin(focusAngleRadians);
        smartDrawThickArc(lineThicknessInPixels,
                          focusX, focusY,
                          focusAngleRadians,
                          curvature,
                          start, end,
                          antiAlias);

        return;
    }

    public java.awt.Color getColor()
    {



        return currentColor;

    }
    public void setColor(java.awt.Color color)
    {



        currentColor = color;

        int colorRGB = color.getRGB();
        int rbyte = (colorRGB >> 16) & 0xff;
        int gbyte = (colorRGB >> 8) & 0xff;
        int bbyte = (colorRGB >> 0) & 0xff;
        double r = rbyte/255.;
        double g = gbyte/255.;
        double b = bbyte/255.;
        if (false)
        {
            if (r == 1. && g == 1. && b == 1.)
                r = g = b = 0.;
            else if (r == 0. && g == 0. && b == 0.)
                r = g = b = 1.;
        }
        if (true)
        {






            double hsl[] = new double[3];
            RGBtoHSL(r,g,b, hsl);
            double rgb[] = new double[3];
            HSLtoRGB(hsl[0],hsl[1],1.-hsl[2], rgb);
            r = rgb[0];
            g = rgb[1];
            b = rgb[2];

        }
        endPathIfInPath();
        writer.println(""+(float)r+" "+(float)g+" "+(float)b+" setrgbcolor");

    }

    private java.io.PrintWriter writer;
    private java.awt.Color currentColor;
    private boolean inPath = false;

    private java.awt.Dimension gsize;
    private double scaleX = 1;
    private double scaleY = 1;
    private double translateX = 0;
    private double translateY = 0;
    public int verbose = 0;


        private static void findPointOnArc(double t,
                                           double focusX, double focusY,
                                           double c,
                                           double normalAndTangent[][],
                                           double scratch[ ],
                                           double result[ ])
        {

            scratch[0] = -MyMath.cosf1_over_x(t*c) * t;

            scratch[1] = MyMath.sin_over_x(t*c) * t;
            VecMath.vxm(result, scratch, normalAndTangent);
            result[0] += focusX;
            result[1] += focusY;
        }


    public void flush()
    {


        endPathIfInPath();
        writer.println("showpage");
        writer.flush();

    }



    private void newPathIfNotInPath()
    {
        if (!inPath)
        {
            writer.println("n");
            inPath = true;
        }
    }
    private void endPathIfInPath()
    {
        if (inPath)
        {
            writer.println("s");
            inPath = false;
        }
    }

    public static void HSVtoRGB(double h, double s, double v, double rgb[ ])
    {
        while (h < 0)
            h++;
        while (h >= 1)
            h--;

        int segment = (int)(h*6);

        double frac = (6*h)-segment;
        if (segment % 2 == 1)
            frac = 1 - frac;

        double major = v;
        double minor = (1-s)*v;
        double middle = frac * major + (1-frac) * minor;

        rgb[(segment+1)/2 % 3] = major;
        rgb[(segment+4)/2 % 3] = minor;
        rgb[(7-segment) % 3] = middle;
    }
    public static void RGBtoHSV(double r, double g, double b, double hsv[ ])
    {
        double rgb[] = {r,g,b};
        int majorI = ((r)>=(g)?(r)>=(b)?0:2 :(g)>=(b)?1:2);
        int minorI = ((r)<=(g)?(r)<=(b)?0:2 :(g)<=(b)?1:2);
        int middleI = 3-majorI-minorI;
        int segment = (minorI+1)%3 * 2
                    + ((majorI-middleI+3)%3 == 1 ? 1 : 0);
        double v = rgb[majorI];
        double s = (v==0. ? 0. : 1. - (rgb[minorI] / v));
        double h;
        if (rgb[majorI] != rgb[minorI])
        {
            double frac = (rgb[middleI]-rgb[minorI]) / (rgb[majorI]-rgb[minorI]);
            h = (segment + (segment % 2 == 0 ? frac : 1.-frac)) * (1./6.);
        }
        else
            h = 0.;
        hsv[0] = h;
        hsv[1] = s;
        hsv[2] = v;
    }

    public static void RGBtoHSL(double r, double g, double b, double hsl[ ])
    {


        double h,s_of_hsv,v;
        {
            double hsv[] = hsl;
            RGBtoHSV(r,g,b, hsv);
            h = hsv[0];
            s_of_hsv = hsv[1];
            v = hsv[2];
        }

        hsl[0] = h;

        double l = .3*r + .59*g + .11*b;

        hsl[2] = l;




        double major = v;
        double minor = ((v) + (s_of_hsv)*((0)-(v)));
        double s_of_hsl = l==0.
                       || l==1. ? 0. : (((major-l)/(1.-l))>=((minor-l)/(0.-l))?((major-l)/(1.-l)):((minor-l)/(0.-l)))
                                                            ;

        hsl[1] = s_of_hsl;

    }
    public static void HSLtoRGB(double h, double s, double l, double rgb[ ])
    {






        double hr, hg, hb;
        {
            HSVtoRGB(h,1.,1., rgb);
            hr = rgb[0];
            hg = rgb[1];
            hb = rgb[2];
        }





        double hl = .3*hr + .59*hg + .11*hb;

        double delta = (l - hl)/3.;

        double hlr = hr + (l-hl);
        double hlg = hg + (l-hl);
        double hlb = hb + (l-hl);


        double hlmajor = ((hlr)>=(hlg)?((hlr)>=(hlb)?(hlr):(hlb)):((hlg)>=(hlb)?(hlg):(hlb)));
        double hlminor = ((hlr)<=(hlg)?((hlr)<=(hlb)?(hlr):(hlb)):((hlg)<=(hlb)?(hlg):(hlb)));

        double t1 = (((1.-l)/(hlmajor-l))<=((0.-l)/(hlminor-l))?((1.-l)/(hlmajor-l)):((0.-l)/(hlminor-l)))
                                           ;
        double t = s * t1;
        rgb[0] = ((l) + (t)*((hlr)-(l)));
        rgb[1] = ((l) + (t)*((hlg)-(l)));
        rgb[2] = ((l) + (t)*((hlb)-(l)));

    }

    public static void main(String args[])
    {
        double r = (double)Math.random();
        double g = (double)Math.random();
        double b = (double)Math.random();
        r = 0.8;
        g = 0.7;
        b = 0.6;

        double hsl[] = new double[3];
        RGBtoHSL(r, g, b, hsl);

        double rgb[] = new double[3];
        HSLtoRGB(hsl[0], hsl[1], hsl[2], rgb);

    }


}
