package com.didi.codeProfiles;

import java.applet.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;

public class CodeProfiles extends Applet implements Runnable {

    int programLineIndexAsWritten[] = new int[256];  int i11 = line(80);
    long programLineExecutionCount[] = new long[256];  int i16 = line(132);
    int programTrace[] = new int[4096], lastLineCallInProgram;  int i1 = line(1);
    Line programLines[] = null;  int i6 = line(42);


    //============================================================================//   
    //                                                                            //   
    //   CodeProfiles was written August 9-15 2002 for CODeDOC; commissioned by   //  
    //   Christiane Paul, Curator at the Whitney Museum of American Art, NY USA   //  
    //                                                                            //  
    //   The assignment was to write code that moves three points in space. The   //  
    //   artists shared the code with one another and the public, to focus upon   //   
    //   the code and process as well as artist comments on one another's code.   //  
    //                                                                            //   
    //   This code reads in its own source and displays it in a tiny font, then   //   
    //   moves three points in "code space." It essentially comments on itself.   //
    //                                                                            //   
    //   The white Insertion Point traces the code in the order it was written.   //   
    //   The amber Fixation Point traces word by word as someone might read it.   //   
    //   The green Execution Point shows a sample of how the computer reads it.   //   
    //   The code lines themselves gradually get brighter as they execute more.   //   
    //                                                                            //   
    //   This code was written in my un-optimized prototyping style; built with   //   
    //   meaning, visuals, usability, and fun taking priority over engineering.   //   
    //   Thank you Greta Peterman and Marek Walczak for your valuable critique.   //   
    //                                                                            //   
    //   W. Bradford Paley                                        brad@didi.com   //   
    //   Copyright (c) 2002, all rights reserved    related work at textarc.org   //   
    //                                                                            //   
    //============================================================================//   



    //==============================================================================   
    class Line {
        String s;
        int leadingPixels = -1, x, y, lineIndex, lineAsWrittenIndex, i1 = line(16);
        boolean hasLineCall = false;  int i2 = line(101);
        double brightness;  int i17 = line(134);

        public Line(String s) {  line(17);
            this.s = s;  line(18);
        }
    }


    //==============================================================================   
    class Cubic {
        double a, b, c, d = line(151);

        public Cubic (double a, double b, double c, double d) {
            this.a = a; this.b = b; this.c = c; this.d = d;  line(152);
        }

        public double evaluate (double u) { line(182);
            return (((d*u) + c) * u + b) * u + a;
        }
    }


    //==============================================================================   
    public CodeProfiles () {  line(2);
        setBackground(Color.black);  line(79);
    }


    //==============================================================================   
    int drawnInsertionLine, drawnInsertionLineBackup, drawnInsertionPoint,
        drawnFixationLine, drawnFixationPoint, drawnPC = line(75);
    Line lineDrawnLarger, lastLineDrawnLarger;  int i14 = line(117);
    double lineDrawnLargerRatio = 1./3.;  int i25 = line(244);
    void step () {

        //  start all three points we're moving where the mouse was clicked 
        if (lineToStartTraces != null) {  line(251);

            //  point the fixation point at this line   
            drawnFixationPoint = 0;  line(230);
            drawnFixationLine = lineUnderMouseIndex;  line(224);

            //  point the insertion point at this line   
            drawnInsertionPoint = drawnInsertionLineBackup = 0;  line(232);

            //  if it has a line() call we're all set 
            if (lineToStartTraces.hasLineCall) {
                drawnInsertionLine = lineToStartTraces.lineAsWrittenIndex;
                line(231);

            //  otherwise find a line() call 
            } else {

                //  start where the mouse was clicked 
                int i = lineToStartTraces.lineIndex;  line(235);

                //  go forward until we see a line() call 
                while (i != 0 && ! programLines[i].hasLineCall) {
                    i = ++i % programLines.length;  line(252);
                    drawnInsertionLineBackup--; line(234);
                }

                if (i == 0) drawnInsertionLine = lastLineCallInProgram;
                else drawnInsertionLine = programLines[i].lineAsWrittenIndex;
                line(233);
            }
            resetTraceVectors = true;  line(225);

            //  have drawnPC wait until this line is executed, then trace   
            drawnPCWaitTargetLine = drawnInsertionLine;  line(236);

            lineToStartTraces = null;  line(253);
        }


        //  increase a ratio (used to reveal cropped lines) if we're lingering  
        if (lastLineDrawnLarger == lineDrawnLarger)  { line(246);
            lineDrawnLargerRatio = Math.min(1., lineDrawnLargerRatio * 1.05);
        } else {
            lineDrawnLargerRatio = 1./3.;  line(247);
            lastLineDrawnLarger = lineDrawnLarger;  line(245);
        }

        //--------------------------------------------------------------------------   
        //  step the pseudo-fixation point to show how a person might read the code   
        //  (word by word: quickly)   

        //  first check to see if we need to step to the next line   
        Line lastLine = programLines[drawnFixationLine];  line(206);

        if (programLines[drawnFixationLine] == null ||
            drawnFixationPoint >= programLines[drawnFixationLine].s.length()-1) {
            line(95);

            //  step to the next line to be read   
            do {
                drawnFixationLine = (++drawnFixationLine) % programLines.length;
                line(83);
            } while (programLines[drawnFixationLine] == null ||
                     programLines[drawnFixationLine].s.trim().length() == 0);
                     line(98);

            //  and reset the fixation point   
            drawnFixationPoint = 0;  line(96);
        }

        //  step the fixation point past the next word   
        drawnFixationPoint = programLines[drawnFixationLine].s.
            indexOf(" ", drawnFixationPoint);  line(93);
        if (programLines[drawnFixationLine].s.
            indexOf("              ", drawnFixationPoint) != -1)
            drawnFixationPoint += 14;  line(216);
        if (drawnFixationPoint == -1) drawnFixationPoint =
            programLines[drawnFixationLine].s.length()-2;  line(94);
        drawnFixationPoint++;  line(97);

        //  follow this point if we catch it   
        if (lineDrawnLarger == lastLine)
            lineDrawnLarger = programLines[drawnFixationLine]; line(205);

        //--------------------------------------------------------------------------   
        //  step the pseudo-insertion point to show how the code was written   
        //  (character by character: slowly)   

        //  increment the insertion point, and if it's at the end of the line   
        lastLine = programLines[programLineIndexAsWritten[drawnInsertionLine]+
            drawnInsertionLineBackup];  line(210);
        if (++drawnInsertionPoint >
            programLines[programLineIndexAsWritten[drawnInsertionLine]+
                drawnInsertionLineBackup].s.trim().length()) {  line(91);

            //  either back up less (in code space)   
            if (drawnInsertionLineBackup < 0) {
                drawnInsertionLineBackup++;  line(106);

            } else {  line(107);

                //  or step to the next line in the order that they were written   
                do { drawnInsertionLine = (++drawnInsertionLine) %
                        programLineIndexAsWritten.length;  line(84);
                } while (programLineIndexAsWritten[drawnInsertionLine] == 0);

                //  then back up in the actual code 'til there's another line() call   
                drawnInsertionLineBackup = 0;  line(104);
                do {
                    drawnInsertionLineBackup--;
                } while (drawnInsertionLine + drawnInsertionLineBackup > 0 &&
                    programLineIndexAsWritten[drawnInsertionLine]+
                        drawnInsertionLineBackup > 0 &&
                    programLines[programLineIndexAsWritten[drawnInsertionLine]+
                        drawnInsertionLineBackup].s.indexOf("//") != 0 &&
                    programLines[programLineIndexAsWritten[drawnInsertionLine]+
                        drawnInsertionLineBackup].hasLineCall == false);  line(105);

                drawnInsertionLineBackup++;  line(108);
            }

            //  and always reset the insertion point to the beginning of the line   
            drawnInsertionPoint = 0;  line(92);
        }

        //  follow this point if we catch it   
        if (lineDrawnLarger == lastLine)
            lineDrawnLarger = programLines[programLineIndexAsWritten[drawnInsertionLine]+
                    drawnInsertionLineBackup]; line(209);

        //--------------------------------------------------------------------------   
        //  step the pseudo-program counter to show how a profiler reads the code    
        //  (line by line: very quickly)   
        if (drawnPCWaitTargetLine == -1) {
            lastLine = programLines[programLineIndexAsWritten[
                programTrace[drawnPC]]];  line(207);
            drawnPC = (++drawnPC) % programTrace.length;  line(74);

            //  get a fresh sample of execution trace once we've shown the full array   
            //  (use the current insertion point to hash the sample a little)   
            if (drawnPC == 0) pc = -drawnInsertionLine * 10;  line(99);

            //  follow this point if we catch it   
            if (lineDrawnLarger == lastLine)
                lineDrawnLarger = programLines[programLineIndexAsWritten[
                    programTrace[drawnPC]]]; line(208);
        }
    }


    //==============================================================================   
    long lastUpdateTime = 0;  int i10 = line(69);
    boolean go = true;  int i23 = line(243);
    Line lineToStartTraces = null;  int i26 = line(254);
    public void run () {

        programLines = readFile(getParameter("fileName"));  line(32);

        addMouseMotionListener(new MouseMotionAdapter() {
            public void mouseMoved(MouseEvent e) {
                lineDrawnLarger = findLineUnderMouse(e);  line(116);
            }});

        addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                lineDrawnLarger = findLineUnderMouse(e);  line(250);
            }
            public void mouseClicked(MouseEvent e) {
                lineToStartTraces = findLineUnderMouse(e);  line(248);
            }});

        while (go) {

            //  step forward in the state of the world model   
            step();  line(73);

            //  draw the whole world again   
            drawAll(getGraphics());  line(72);

            //  insure at least 15 milliseconds between draws   
            try {
                Thread.sleep(Math.max(0,
                    15 - (System.currentTimeMillis() - lastUpdateTime)));  line(70);
            } catch (Exception e) { e.printStackTrace();  }
            lastUpdateTime = System.currentTimeMillis();  line(71);
        }
    }


    //==============================================================================   
    int lineUnderMouseIndex;  int i22 = line(227);
    public Line findLineUnderMouse (MouseEvent e) {  line(114);

        Line lineUnderMouse = null;  line(119);
        lineUnderMouseIndex = -1;  line(226);
        for (int i = 0; i < programLines.length; i++) {
            if (e.getX() > (programLines[i].x - programLines[i].leadingPixels) &&
                e.getX() < (programLines[i].x -
                    programLines[i].leadingPixels + columnWidth) &&
                e.getY() > programLines[i].y - 3 &&
                e.getY() < programLines[i].y) {
                    lineUnderMouse = programLines[lineUnderMouseIndex = i];
                    line(118);
                    break;
                }

        }  line(115);

        return lineUnderMouse;
    }


    //==============================================================================   
    int pc = 0, drawnPCWaitTargetLine = -1;  int i5 = line(38);
    int line (int i) { return line(i, false); }
    int line (int i, boolean recur) {  if (recur) line(3);

        //  if a click made us wait for this line to execute, stop waiting and trace   
        if (drawnPCWaitTargetLine == i) {
            drawnPC = pc = 0;  if (recur) line(237);
            drawnPCWaitTargetLine = -1;  if (recur) line(240);
        }

        //  trace execution   
        if (programTrace != null && pc < programTrace.length &&
            drawnPCWaitTargetLine == -1)
            programTrace[Math.max(0, Math.min(pc++, programTrace.length-1))] = i;
        if (recur) line(39);

        //  count the number of times this line is executed   
        if (programLineExecutionCount != null)
            programLineExecutionCount[i]++;  if (recur) line(133);

        return i;
    }


    //==============================================================================   
    void brightenExecutedLines (Graphics g, boolean all) {  line(135);

        //  loop through all lines as written and update their brightness   
        for (int i = 0; i < programLineIndexAsWritten.length; i++) {  line(140);

            //  calculate a new brightness level   
            double newBrightness =
                (Math.log((double) programLineExecutionCount[i] + 1));  line(141);

            //  if it's different enough   
            if (all || (programLines != null && (Math.abs(newBrightness -
                programLines[programLineIndexAsWritten[i]].brightness) > .4)) &&
                programLineIndexAsWritten[i] != 0) {
                line(143);

                //  find the line we want to check   
                int index = programLineIndexAsWritten[i], backup = 0;  line(137);

                //  set the new level   
                programLines[index].brightness = newBrightness;  line(142);

                //  set the new color   
                int greenComponent = (int) Math.min(150,
                    70 + programLines[index].brightness * 2);  line(149);
                g.setColor(
                    new Color(greenComponent/3, greenComponent, greenComponent/3));
                    line(136);

                //  draw the line again (and those before it until another line() call)   
                if (programLines != null && programLines[index] != null) {

                    do {
                        g.drawString(programLines[index+backup].s,
                            programLines[index+backup].x,
                            programLines[index+backup].y);  line(138);

                        backup--;

                    } while (index+backup > 0 &&
                        programLines[index+backup].s.indexOf("//") != 0 &&
                        programLines[index+backup].hasLineCall == false); line(139);
                 }
            }
        }
    }


    //==============================================================================   
    Line[] readFile (String fileName) {  line(4);

        Vector v = new Vector();  line(10);

        try {  line(7);

            //  open the file   
            InputStream is = (new URL(getCodeBase() + fileName)).openStream();
            line(5);
            BufferedReader br = new BufferedReader (new InputStreamReader(is));
            line(19);

            //  read every line in it   
            Line l;  line(12);
            do {  line(13);
                l = readLine(br);  line(9);
                if (l != null) v.addElement(l);  line(11);
            } while (l != null);  line(14);

            is.close();  line(6);

        } catch (Exception e) { System.err.println(e); }  line(8);

        //  put all the lines we read into an array of Line objects   
        Line lines[] = new Line[v.size()];  line(33);
        for (int i = 0; i < v.size(); i++) {
            lines[i] = (Line) v.elementAt(i);  line(48);

            //  remember where we are in the program   
            lines[i].lineIndex = i;

            //  if there's a call to line() here put the index of this Line in    
            //  programLineIndexAsWritten   
            int lineCallPos = lines[i].s.indexOf("line(");  line(85);
            int lineCallEndPos = lines[i].s.indexOf(")", lineCallPos);  line(82);
            if (lineCallPos != -1 &&
                Character.isDigit(lines[i].s.charAt(lineCallEndPos-1))) {

                //  remember if there's a call to line() in this line   
                lines[i].hasLineCall = true;  line(103);

                //  this array finds the line index from the time it was written    
                //  (the parameter to line() )   
                programLineIndexAsWritten[lastLineCallInProgram =
                    lines[i].lineAsWrittenIndex = Integer.parseInt(
                    lines[i].s.substring(lineCallPos + 5, lineCallEndPos))] = i;
                    line(81);

            }
        }
        return lines;
    }


    //==============================================================================   
    byte buffer[] = new byte[1000]; int i3 = line(31);
    Line readLine (BufferedReader br) {  line(15);
        int length = 0;  line(20);
        int ch = 0;  line(21);

        try {  line(22);
            while ((ch = br.read()) != '\n' && ch != '\r' && ch != -1)
                buffer[length++] = (byte) ch;  line(23);

            //  -1 means EOF   
            if (ch == -1) return null;  line(24);

            //  skip a line feed if this was a carriage return   
            if (ch == '\r') {  line(25);
                ch = br.read();  line(26);
            }  line(27);

        } catch (Exception e) { return null; }  line(28);

        if (length == 0) return new Line("");  line(29);

        return new Line(new String(buffer, 0, length + line(30) * 0) + " ");

    }


    //==============================================================================   
    Image cacheImage, offscreenImage;  int i7 = line(54);
    Graphics cacheGraphics, offscreenGraphics;  int i8 = line(55);
    boolean drawCacheNeeded = true, resetTraceVectors = false; int i9 = line(62);
    Vector pastPCPositions = null;  int i13 = line(109);
    Vector pastFixationPositions = null;  int i18 = line(177);
    Line lastFixationLine;  int i21 = line(200);
    Vector pastInsertionPositions = null;  int i19 = line(184);
    Line lastInsertionLine;  int i20 = line(197);
    public synchronized void drawAll (Graphics g) {  line(53);

        //  don't even try if we don't have a full-sized window yet 
        if (size().width < 21 || size().height < 21) {  line(255);
            return;
        }

        //--------------------------------------------------------------------------   
        //  get an offscreen buffer: double-buffering for smooth animation   
        if (offscreenImage == null ||
            size().width != offscreenImage.getWidth(this) ||
            size().height != offscreenImage.getHeight(this)) {  line(56);

            setFont(new Font("Courier", Font.PLAIN,
                Math.max(1, size().width / 320)));  line(78);

            offscreenImage = createImage(size().width, size().height);  line(57);
            offscreenGraphics = offscreenImage.getGraphics();  line(58);
            resetTraceVectors = true;  line(228);
        }

        //  get a cache buffer: so we don't have to draw everything every frame   
        if (cacheImage == null ||
            size().width != cacheImage.getWidth(this) ||
            size().height != cacheImage.getHeight(this)) {  line(59);
            cacheImage = createImage(size().width, size().height);  line(60);
            cacheGraphics = cacheImage.getGraphics();  line(61);
            drawCacheNeeded = true;  line(63);
        }

        //  be safe--check to see if we got what we wanted above   
        if (cacheGraphics == null || offscreenGraphics == null)
            return; line(131);


        //--------------------------------------------------------------------------   
        //  draw the cache if it needs refreshing   
        if (drawCacheNeeded) drawCache(cacheGraphics);  line(66);

        //  fix the cache if there are changes in it   
        if (programLines != null)
            brightenExecutedLines(cacheGraphics, false);  line(144);

        //  copy the graphics cache into the back buffer   
        offscreenGraphics.drawImage(cacheImage, 0, 0, this);  line(67);


        //--------------------------------------------------------------------------   
        //  draw the traces   
        if (resetTraceVectors) {  line(229);
            pastPCPositions = new Vector();  line(217);
            pastFixationPositions = new Vector();  line(218);
            pastInsertionPositions = new Vector();  line(219);
            resetTraceVectors = false;
        }
        drawFixationTrace(g);
        drawInsertionTrace(g);
        drawExecutionTrace(g);


        //--------------------------------------------------------------------------   
        //  draw the line under the mouse or stuck to a point larger and brighter   
        Line lineRecentlyDrawnLarger = lineDrawnLarger;  line(124);
        if (lineRecentlyDrawnLarger != null) { line(121);
            offscreenGraphics.setColor(new Color(30, 255, 30));  line(120);
            offscreenGraphics.setFont(
                new Font(getFont().getName(), getFont().getStyle(), 12));  line(122);
            offscreenGraphics.drawString(lineRecentlyDrawnLarger.s,  (int) Math.min(
                size().width - lineDrawnLargerRatio *
                    getFontMetrics(offscreenGraphics.getFont()).
                    stringWidth(lineRecentlyDrawnLarger.s.trim()),
                lineRecentlyDrawnLarger.x), lineRecentlyDrawnLarger.y);  line(123);
            offscreenGraphics.setFont(getFont());  line(125);
        }


        //--------------------------------------------------------------------------   
        //  draw the line the pseudo-program counter is pointing to   
        offscreenGraphics.setColor(Color.green);  line(85);
        int index = programLineIndexAsWritten[programTrace[drawnPC]],
            backup = 0;  line(100);

        if (drawnPCWaitTargetLine == -1 && programTrace[drawnPC] != 0 &&
            programLines != null && programLines[index] != null) {

            do {
                offscreenGraphics.drawString(programLines[index+backup].s,
                    programLines[index+backup].x, programLines[index+backup].y);
                    line(86);

                //  remember the position if it's not a blank line   
                if (programLines[index+backup].s.trim().length() != 0 &&
                    programLines[index+backup].y > 1 && drawnPC < pc) {
                    pastPCPositions.insertElementAt(
                        new Point(programLines[index+backup-1].x,
                                  programLines[index+backup-1].y),
                        Math.max(0, pastPCPositions.size() + backup));
                }  line(110);

                backup--;

            } while (index+backup > 0 &&
                programLines[index+backup].s.indexOf("//") != 0 &&
                programLines[index+backup].hasLineCall == false);  line(102);
        }


        //--------------------------------------------------------------------------   
        //  draw the fixation point   
        if (programLines != null) {  line(113);
            offscreenGraphics.setColor(new Color(255, 255*7/8, 0));  line(87);

            //  if it's the attended line draw it larger and brighter   
            if (lineRecentlyDrawnLarger == programLines[drawnFixationLine])
                offscreenGraphics.setFont(new Font(getFont().getName(),
                    getFont().getStyle(), 12));  line(126);

            offscreenGraphics.drawString(
                programLines[drawnFixationLine].s.substring(0, drawnFixationPoint),
                    (int) Math.min(size().width - lineDrawnLargerRatio *
                    getFontMetrics(offscreenGraphics.
                    getFont()).stringWidth(programLines[drawnFixationLine].s.trim()),
                programLines[drawnFixationLine].x),
                programLines[drawnFixationLine].y);  line(88);

            //  remember the position if it's not a blank line   
            if (lastFixationLine == null ||
                lastFixationLine != programLines[drawnFixationLine] &&
                programLines[drawnFixationLine].s.trim().length() != 0 &&
                programLines[drawnFixationLine].y > 1) {
                pastFixationPositions.addElement(
                    new Point(programLines[drawnFixationLine].x +
                              getFontMetrics(getFont()).stringWidth(
                              programLines[drawnFixationLine].s.trim()),
                              programLines[drawnFixationLine].y));  line(178);
                pastFixationPositions.addElement(
                    new Point(programLines[drawnFixationLine].x,
                        programLines[drawnFixationLine].y));  line(201);
            }
            lastFixationLine = programLines[drawnFixationLine];  line(202);

            //  if it's the line drawn larger put the font back to normal   
            if (lineRecentlyDrawnLarger == programLines[drawnFixationLine])
                offscreenGraphics.setFont(getFont());  line(127);

        }


        //--------------------------------------------------------------------------   
        //  draw the insertion point   
        offscreenGraphics.setColor(Color.white);  line(89);
        if (programLineIndexAsWritten[drawnInsertionLine] != 0) {

            Line insertionLine = programLines[programLineIndexAsWritten[
                drawnInsertionLine]+drawnInsertionLineBackup];  line(148);

            //  if it's the attended line draw it larger and brighter   
            if (lineRecentlyDrawnLarger == insertionLine)
                offscreenGraphics.setFont(new Font(getFont().getName(),
                    getFont().getStyle(), 12));  line(146);

            offscreenGraphics.drawString(
                insertionLine.s.substring(0, drawnInsertionPoint),
                (int) Math.min(size().width - lineDrawnLargerRatio *
                    getFontMetrics(offscreenGraphics.getFont()).
                    stringWidth(insertionLine.s.trim()),
                    insertionLine.x), insertionLine.y);  line(90);

            //  remember the position if it's not a blank line   
            if (lastInsertionLine == null ||
                lastInsertionLine != insertionLine &&
                insertionLine.s.trim().length() != 0 &&
                insertionLine.y > 1) {
                pastInsertionPositions.addElement(
                    new Point(insertionLine.x, insertionLine.y));  line(199);
                pastInsertionPositions.addElement(
                    new Point(insertionLine.x +
                              getFontMetrics(getFont()).stringWidth(
                              insertionLine.s.trim()),
                              insertionLine.y));  line(185);
            }
            lastInsertionLine = insertionLine;  line(198);

            //  if it's the line drawn larger put the font back to normal   
            if (lineRecentlyDrawnLarger == insertionLine)
                offscreenGraphics.setFont(getFont());  line(147);
        }


        //--------------------------------------------------------------------------   
        //  copy the graphics cache into the back buffer   
        g.drawImage(offscreenImage, 0, 0, this);  line(68);
    }


    //==============================================================================   
    void drawExecutionTrace (Graphics g) {

        //  if we just started a new sample of trace, start a new trace line   
        if (drawnPC == 0) pastPCPositions = new Vector();  line(150);

        //  trim the line to trace only the last 200 PC positions   
        while (pastPCPositions.size() > 200)
            pastPCPositions.removeElementAt(0);  line(111);

        //  draw from the tail forward   
        for (int i = 1; i < pastPCPositions.size(); i++) {

            //  fade it out slowly   
            int lineBrightness = Math.min(140, 20 + i +
                //  adjust to let short lines glow brightly    
                Math.max(0, 200 - pastPCPositions.size()));  line(211);

            offscreenGraphics.setColor(
                new Color(lineBrightness/8, lineBrightness, lineBrightness/8));

            offscreenGraphics.drawLine(
                ((Point) pastPCPositions.elementAt(i-1)).x,
                ((Point) pastPCPositions.elementAt(i-1)).y,
                ((Point) pastPCPositions.elementAt(i)).x,
                ((Point) pastPCPositions.elementAt(i)).y);  line(112);
        }
    }


    //==============================================================================   
    void drawInsertionTrace (Graphics g) {

        //  trim the line to trace only the last 30 insertion positions   
        while (pastInsertionPositions.size() > 30)
            pastInsertionPositions.removeElementAt(0);  line(186);

        if (pastInsertionPositions.size() > 1) {  line(187);
            Cubic[][] insertionSpline = calculateNaturalCubic(pastInsertionPositions);
            line(188);

            Line insertionLine = programLines[programLineIndexAsWritten[
                drawnInsertionLine]+drawnInsertionLineBackup];  line(212);
            double lastStepRatio = (insertionLine.s.trim().length() == 0) ? 1. :
                (double) drawnInsertionPoint / insertionLine.s.trim().length();
                line(213);

            //  draw from the tail forward   
            double steps = 40;  line(189);
            for (int i = 0;
                i < insertionSpline.length -
                    (lastStepRatio < .5 && lastStepRatio != 0. ? 2 : 1); i++) {

                //  fade it out slowly   
                int lineBrightness = Math.min(140, 20 + i*5 +
                    Math.max(0, 30 - insertionSpline.length)*5);  line(204);
                offscreenGraphics.setColor(
                    new Color(lineBrightness, lineBrightness, lineBrightness));

                //  figure out where to stop   
                int lastStep = (int) steps;  line(214);
                if (lastStepRatio < .5 && lastStepRatio != 0. &&
                    i == insertionSpline.length - 3)
                    lastStep = (int) (lastStepRatio * 2 * steps);
                else if (lastStepRatio >= .5 && i == insertionSpline.length - 2)
                    lastStep = (int) ((lastStepRatio - .5) * 2 * steps);  line(215);

                //  do the drawing   
                Point p2, p1 = new Point(
                    (int) Math.round(insertionSpline[i][0].evaluate(0)),
                    (int) Math.round(insertionSpline[i][1].evaluate(0)));  line(194);

                for (int j = 1; j <= lastStep; j++) {
                    p2 = new Point(
                        (int) Math.round(insertionSpline[i][0].evaluate(j / steps)),
                        (int) Math.round(insertionSpline[i][1].evaluate(j / steps)));
                        line(195);
                    if (p1 != null)
                        offscreenGraphics.drawLine(p1.x, p1.y, p2.x, p2.y);
                        line(190);
                    p1 = p2;  line(196);
                }
            }
        }
    }


    //==============================================================================   
    void drawFixationTrace (Graphics g) {

        //  trim the line to trace only the last 80 fixation positions   
        while (pastFixationPositions.size() > 80)
            pastFixationPositions.removeElementAt(0);  line(179);

        if (pastFixationPositions.size() > 1) {  line(183);
            Cubic[][] fixationSpline = calculateNaturalCubic(pastFixationPositions);
            line(180);

            //  draw from the tail forward   
            for (int i = 0; i < fixationSpline.length - 1; i++) {

                //  fade it out slowly   
                int lineBrightness = Math.min(120, 20 + (int) (i*1.5) +
                    Math.max(0, 80 - fixationSpline.length)*2);  line(203);
                offscreenGraphics.setColor(
                    new Color(lineBrightness, lineBrightness*7/8, lineBrightness/4));

                double steps = 13;  line(157);
                Point p2, p1 = new Point(
                    (int) Math.round(fixationSpline[i][0].evaluate(0)),
                    (int) Math.round(fixationSpline[i][1].evaluate(0)));  line(191);

                for (int j = 1; j <= steps; j++) {
                    p2 = new Point(
                        (int) Math.round(fixationSpline[i][0].evaluate(j / steps)),
                        (int) Math.round(fixationSpline[i][1].evaluate(j / steps)));
                        line(192);
                    if (p1 != null)
                        offscreenGraphics.drawLine(p1.x, p1.y, p2.x, p2.y);
                        line(181);
                    p1 = p2;  line(193);
                }
            }
        }
    }


    //==============================================================================   
    void drawCache (Graphics g) {

        //  start with a "no-electron CRT" black background   
        g.setColor(Color.black);  line(44);
        g.fillRect(0, 0, size().width, size().height);  line(45);

        if (programLines != null && cacheImage.getHeight(this) != 0) {
            drawCode(g, cacheImage.getWidth(this), cacheImage.getHeight(this));
            line(64);

            //  we drew it   
            drawCacheNeeded = false;  line(65);
        }
    }


    //==============================================================================   
    int columnWidth;  int i27 = line(249);
    public void drawCode (Graphics g, int w, int h) {  line(41);
        int columns = 4, linesPerCol = (programLines.length+columns-1) / columns,
            margin = w/8, colSpacing = columnWidth = (w - margin*2 - getFontMetrics(
            g.getFont()).stringWidth(" ")*100) / (columns - 1);  line(49);
        double lineHeight = (h - margin*2) / (double) (linesPerCol+2);  line(50);

        //  draw the code in "early CRT phosphor" green   
        g.setColor(new Color(20, 80, 20));  line(46);

        //  draw every line of the code we have read   
        for (int i = 0; i < programLines.length; i++) {  line(43);
            if (programLines[i] != null) {

                //  trim leading spaces the first time we draw the line   
                if (programLines[i].leadingPixels == -1) {  line(128);
                    programLines[i].leadingPixels =
                        programLines[i].s.indexOf(programLines[i].s.trim()) *
                        getFontMetrics(g.getFont()).stringWidth(" ");  line(129);

                    //  trailing spaces make us pause in the word-by-word reading mode   
                    programLines[i].s = programLines[i].s.trim() + "             ";
                    line(130);
                }

                programLines[i].x = margin + programLines[i].leadingPixels +
                    (i / linesPerCol) * colSpacing;  line(76);
                programLines[i].y =
                    margin + (int) ((i % linesPerCol) * lineHeight);  line(77);
                g.drawString(
                    programLines[i].s, programLines[i].x, programLines[i].y);  line(47);
            }
        }

        brightenExecutedLines(g, true);  line(145);
    }


    //==============================================================================   
    Thread thread; int i4 = line(34);
    public void start() {  line(35);
        thread = new Thread(this);  line(36);
        thread.start();  line(37);
    }


    //==============================================================================   
    public void stop() {  line(241);
        go = false;  line(242);
    }


    //==============================================================================   
    public void update (Graphics g) { paint(g); line(40); }


    //==============================================================================   
    public void paint (Graphics g) {  line(51);
        drawAll(g);  line(52);
    }


    //==============================================================================   
    Cubic[][] calculateNaturalCubic (Vector v) {  line(153);
        int i, n = v.size()-1;  line(159);
        double gammas[] = new double[n+1];  line(154);
        double deltaXs[] = new double[n+1];  line(155);
        double DXs[] = new double[n+1];  line(156);
        double deltaYs[] = new double[n+1];  line(175);
        double DYs[] = new double[n+1];  line(176);


        //--------------------------------------------------------------------------   
        //  gammas   
        gammas[0] = 1/2d;  line(158);
        for (i = 1; i < n; i++)
            gammas[i] = 1/(4-gammas[i-1]);  line(160);
        gammas[n] = 1/(2-gammas[n-1]);  line(161);


        //--------------------------------------------------------------------------   
        //  deltas for x   
        deltaXs[0] = 3 * (((Point) v.elementAt(1)).x - ((Point) v.elementAt(0)).x) *
            gammas[0];  line(162);
        for (i = 1; i < n; i++)
            deltaXs[i] =
                (3 * (((Point) v.elementAt(i+1)).x - ((Point) v.elementAt(i-1)).x) -
                deltaXs[i-1]) * gammas[i];  line(163);
        deltaXs[n] = (3 * (((Point) v.elementAt(n)).x - ((Point) v.elementAt(n-1)).x) -
            deltaXs[n-1]) * gammas[n];  line(164);

        //  Ds for x   
        DXs[n] = deltaXs[n];  line(165);
        for (i = n-1; i >= 0; i--)
            DXs[i] = deltaXs[i] - gammas[i] * DXs[i+1];   line(166);


        //--------------------------------------------------------------------------   
        //  deltas for y   
        deltaYs[0] = 3 * (((Point) v.elementAt(1)).y - ((Point) v.elementAt(0)).y) *
            gammas[0];  line(167);
        for (i = 1; i < n; i++)
            deltaYs[i] =
                (3 * (((Point) v.elementAt(i+1)).y - ((Point) v.elementAt(i-1)).y) -
                deltaYs[i-1]) * gammas[i];  line(168);
        deltaYs[n] = (3 * (((Point) v.elementAt(n)).y - ((Point) v.elementAt(n-1)).y) -
            deltaYs[n-1]) * gammas[n];  line(169);

        //  Ds for y   
        DYs[n] = deltaYs[n];  line(170);
        for (i = n-1; i >= 0; i--)
            DYs[i] = deltaYs[i] - gammas[i] * DYs[i+1];   line(171);


        //--------------------------------------------------------------------------   
        //  coefficients of the cubics   
        Cubic coefficients[][] = new Cubic[n][2];  line(172);
        for (i = 0; i < n; i++) {
            coefficients[i][0] = new Cubic(((Point) v.elementAt(i)).x, DXs[i],
                3 * (((Point) v.elementAt(i+1)).x - ((Point) v.elementAt(i)).x) -
                2 * DXs[i] - DXs[i+1],
                2 * (((Point) v.elementAt(i)).x - ((Point) v.elementAt(i+1)).x) +
                DXs[i] + DXs[i+1]);  line(173);

            coefficients[i][1] = new Cubic(((Point) v.elementAt(i)).y, DYs[i],
                3 * (((Point) v.elementAt(i+1)).y - ((Point) v.elementAt(i)).y) -
                2 * DYs[i] - DYs[i+1],
                2 * (((Point) v.elementAt(i)).y - ((Point) v.elementAt(i+1)).y) +
                DYs[i] + DYs[i+1]);  line(174);
        }
        return coefficients;
    }
}



Users of Netscape Communicator 4.7 on the PC and Netscape on the Macintosh, please click here