home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / pc / java / ens65a3u / tubes.java < prev   
Encoding:
Java Source  |  1996-08-14  |  18.5 KB  |  618 lines

  1. // -*- c++ -*-
  2. //
  3. // Copyright (c) 1996 Dmitri Bassarab
  4. //
  5. // Permission to use, copy, hack, and distribute this software and its
  6. // documentation for NON-COMMERCIAL purposes and without fee is hereby
  7. // granted provided that this copyright notice appears in all copies.
  8. //
  9. import java.awt.*;
  10. import java.awt.image.*;
  11. import java.applet.Applet;
  12. import java.util.Vector;
  13. import java.util.Random;
  14.  
  15. //
  16. //  Non-flicker applet.
  17. //
  18. public class Tubes extends Applet
  19. {
  20.   final static Color colors[] = {
  21.     Color.black,
  22.     Color.red,
  23.     Color.yellow,
  24.     Color.green,
  25.     Color.cyan,
  26.     Color.blue,
  27.     Color.magenta,
  28.     Color.white };
  29.   
  30.   private Image     _image = null;
  31.   private Graphics  _graphics;
  32.   private Dimension _size;
  33.   private Help      _help;
  34.   private Puzzle    _puzzle;
  35.   private int       _level = 4;
  36.   
  37.   public void init()
  38.     {
  39.       setLayout(new BorderLayout());
  40.       Panel panel = new Panel();
  41.       panel.setLayout(new GridLayout(1, 0));
  42.       panel.add(new Button("Goal"));
  43.       panel.add(new Button("Random"));
  44.       panel.add(new Button("Help"));
  45.       add("North", panel);
  46.       panel = new Panel();
  47.       panel.setLayout(new GridLayout(1, 0));
  48.       panel.add(new Button("Left"));
  49.       panel.add(new Button("Flip"));
  50.       panel.add(new Button("Right"));
  51.       add("South", panel);
  52.       _help = new Help(this, _level);
  53.       _puzzle = new Puzzle(this, _level + 2);
  54.     }
  55.   public void stop() { if(_help != null) _help.hide(); }
  56.   public boolean action(Event e, Object o)
  57.     {
  58.       if(o.equals("Help")){
  59.     _help.show();
  60.     return true;
  61.       } else if(o.equals("Goal"))
  62.     _puzzle.goal();
  63.       else if(o.equals("Random"))
  64.     _puzzle.randomize();
  65.       else if(o.equals("Left"))
  66.     _puzzle.left();
  67.       else if(o.equals("Flip"))
  68.     _puzzle.flip();
  69.       else if(o.equals("Right"))
  70.     _puzzle.right();
  71.       repaint();
  72.       return true;
  73.     }
  74.   public boolean mouseDown(Event e, int x, int y)
  75.     {
  76.       if(x < _size.width / 3)
  77.     _puzzle.left();
  78.       else if(x < _size.width * 2 / 3)
  79.     _puzzle.flip();
  80.       else
  81.     _puzzle.right();
  82.       repaint();
  83.       return true;
  84.     }
  85.   public void update(Graphics g) { paint(g); }
  86.   public void paint(Graphics g)
  87.     {
  88.       showStatus(_puzzle.goal_p()? " G O A L !": "");
  89.       if(_image == null ||
  90.      _size.width != size().width || _size.height != size().height){
  91.     _size = new Dimension(size());
  92.     _image = createImage(_size.width, _size.height);
  93.     _graphics = _image.getGraphics();
  94.       }
  95.       _graphics.setColor(Color.black);
  96.       _graphics.fillRect(0, 0, _size.width, _size.height);
  97.       _graphics.translate(_size.width / 2, _size.height / 2);
  98.       _puzzle.draw(_graphics);
  99.       _graphics.translate(-_size.width / 2, -_size.height / 2);
  100.       g.drawImage(_image, 0, 0, null);
  101.     }
  102.   Image makeImage(int w, int h)
  103.     {
  104.       Image i = createImage(w, h);
  105.       Graphics g = i.getGraphics();
  106.       g.setColor(Hollow.color);
  107.       g.fillRect(0, 0, w, h);
  108.       return i;
  109.     }
  110.   Image hollowImage(Image i)
  111.     {
  112.       return createImage(new FilteredImageSource(i.getSource(), new Hollow()));
  113.     }
  114.   static void drawImageCentered(Graphics g, Image i, int x, int y)
  115.     { g.drawImage(i, x - i.getWidth(null)/2, y - i.getHeight(null)/2, null); }
  116.   void setLevel(int level)
  117.     {
  118.       if(_level == level)
  119.     return;
  120.       _puzzle.setSize((_level = level) + 2);
  121.       repaint();
  122.     }
  123. }
  124.  
  125. //
  126. //  Read it! It may ...
  127. //
  128. class Help extends Frame
  129. {
  130.   private static String _contents[] = {
  131.     "Desiderata:",
  132.     "   Once upon a time we got a 6-Tubes Puzzle for",
  133.     "   an exercise in Artificial Intelligence.  Then",
  134.     "   I implemented a graphical simulator of the",
  135.     "   puzzle (originally with Open GL), which helped",
  136.     "   me a lot in figuring out an appropriate",
  137.     "   heuristic for solution from an arbitrary",
  138.     "   initial state. Subsequently, I rewrote the",
  139.     "   puzzle as an applet with Java. It's now open",
  140.     "   to the general public, so you can play it in",
  141.     "   your spare time. Enjoy!",
  142.     "",
  143.     "Topology:",
  144.     "   The original 3D game consists of two sets of",
  145.     "   tubes, the upper and the lower, each having 6",
  146.     "   tubes arranged in a circle according to their",
  147.     "   sizes. The two sets are combined together in",
  148.     "   such way that one set can be rotated",
  149.     "   relatively to the other.  The proposed",
  150.     "   implementation simplifies the topology by",
  151.     "   stretching the circle into the line and",
  152.     "   allowing circular shift of the upper set of",
  153.     "   tubes.",
  154.     "",
  155.     "Goal:",
  156.     "   The tubes contain 21 colored beads. There is",
  157.     "   one red bead, two yellow beads, ..., and",
  158.     "   finally, 6 magenta beads. The number of beads",
  159.     "   of each color corresponds the capacity of the",
  160.     "   tube of same color. The goal of the game is to",
  161.     "   put all the beads into corresponding tubes.",
  162.     "   As you play, monitor the status line of your",
  163.     "   browser/appletviewer; it'll inform you",
  164.     "   whenever the current puzzle state is the goal",
  165.     "   state.",
  166.     "",
  167.     "Controls:",
  168.     "   In order to reach the goal state from an",
  169.     "   arbitrary initial bead distribution the",
  170.     "   following three operations are allowed:",
  171.     "",
  172.     "   o FLIP:  The two sets are flipped so that the",
  173.     "            lower set becomes the upper one and",
  174.     "            vice versa. As a result of this",
  175.     "            operation the beads fall from the",
  176.     "            tubes of the new upper set into the",
  177.     "            corresponding tubes of the new lower",
  178.     "            set;",
  179.     "",
  180.     "   o RIGHT: The upper set is circularly rotated",
  181.     "            once to the right. As a result, beads",
  182.     "            from the upper set of tubes might",
  183.     "            fall into the tubes of lower set;",
  184.     "",
  185.     "   o LEFT:  Same as RIGHT but in the other",
  186.     "            direction;",
  187.     "",
  188.     "   Buttons with these names are located at the",
  189.     "   bottom of the applet. Moreover, the buttons",
  190.     "   divide the applet into three equally sized",
  191.     "   areas, so clicking in an appropriate area",
  192.     "   performs the same action as the button",
  193.     "   below. The buttons located over the applet are",
  194.     "   self-explanatory. The HELP frame you are",
  195.     "   currently reading, contains the LEVEL widget",
  196.     "   which allows to change the skill level of the",
  197.     "   puzzle (the number of tubes).",
  198.     null };
  199.   
  200.   private static String _levelNames[] = {
  201.     "I'm too young to die",
  202.     "Hey, not too rough",
  203.     "Hurt me plenty",
  204.     "Ultra-Violence",
  205.     "Nightmare!",
  206.     null };
  207.   private Tubes _applet;
  208.   
  209.   Help(Tubes applet, int level)
  210.     {
  211.       super("Puzzle Help");
  212.       _applet = applet;
  213.       add("West", new Panel());
  214.       add("East", new Panel());
  215.       Panel panel = new Panel();
  216.       add("South", panel);
  217.       panel.add(new Button("Hide"));
  218.       panel = new Panel();
  219.       add("North", panel);
  220.       panel.add(new Label("Level:"));
  221.       Choice choice = new Choice();
  222.       panel.add(choice);
  223.       for(int i = 0; _levelNames[i] != null; i++)
  224.     choice.addItem(_levelNames[i]);
  225.       choice.select(level);
  226.       TextArea text = new TextArea(20, 40);
  227.       text.setEditable(false);
  228.       for(int i = 0; _contents[i] != null; i++)
  229.     text.appendText(" " + _contents[i] + "\n");
  230.       add("Center", text);
  231.       pack();
  232.     }
  233.   public boolean action(Event e, Object o) 
  234.     {
  235.       if("Hide".equals(o)){
  236.     hide();
  237.     return true;
  238.       }
  239.       for(int i = 0; _levelNames[i] != null; i++)
  240.     if(_levelNames[i].equals(o)){
  241.       _applet.setLevel(i);
  242.       return true;
  243.     }
  244.       return super.action(e, o);
  245.     }
  246. }
  247.  
  248. //
  249. //  Puzzle itself. Contains two set of slots: "_upper" & "_lower".
  250. //
  251. class Puzzle
  252. {
  253.   private Tubes _applet;
  254.   private int   _diameter = 48;
  255.   private int   _height   =  6; 
  256.   private int   _margin   = 12;
  257.   private int   _size     =  0;
  258.   private Color _color    = Tubes.colors[7];
  259.   private Image _supportImage; // slot holder: 2 margins & _size segments
  260.   private Image _marginImage;
  261.   private Image _segmentImage;
  262.   private Queue _upper;
  263.   private Queue _lower;
  264.  
  265.   Puzzle(Tubes applet, int size)
  266.     {
  267.       _applet = applet;
  268.       _marginImage = makeBox
  269.     (_margin, _diameter + 2 * _margin, _height, false, 0);
  270.       _segmentImage = makeBox
  271.     (_diameter, _diameter + 2 * _margin, _height, true, _diameter / 2);
  272.       new Bead(_applet, _diameter / 2);
  273.       setSize(size);
  274.     }
  275.   private Image makeSupport()
  276.     {
  277.       Rectangle b = (new IsoPoint(0, 0)).getBox
  278.     (IsoPoint.XY,_size * _diameter + 2 * _margin, _diameter + 2 * _margin);
  279.       Image i = _applet.makeImage(b.width, b.height += 2 * _height);
  280.       IsoPoint p = new IsoPoint(b.width / 2, b.height / 2);
  281.       p.isoMove(_diameter * _size / 2 + _margin / 2, 0, 0);
  282.       Graphics g = i.getGraphics();
  283.       Tubes.drawImageCentered(g, _marginImage, p.x, p.y);
  284.       p.isoMove((-_diameter - _margin) / 2, 0, 0);
  285.       for(int j = 0; j < _size; j++, p.isoMove(-_diameter, 0, 0))
  286.     Tubes.drawImageCentered(g, _segmentImage, p.x, p.y);
  287.       p.isoMove((_diameter - _margin) / 2, 0, 0);
  288.       Tubes.drawImageCentered(g, _marginImage, p.x, p.y);
  289.       return _applet.hollowImage(i);
  290.     }
  291.   private Image makeBox(int w, int l, int h, boolean withHole, int r)
  292.     {
  293.       Rectangle b = (new IsoPoint(0, 0)).getBox(IsoPoint.XY, w, l);
  294.       Image i = _applet.makeImage(b.width, b.height += 2 * h);
  295.       Graphics g = i.getGraphics();
  296.       IsoPoint p = new IsoPoint(b.width / 2, b.height / 2);
  297.       p.isoMove(0, 0, -h / 2);
  298.       Tubes.drawImageCentered
  299.     (g, makeCover(_color.darker(), w, l, withHole, r), p.x, p.y);
  300.       p.isoMove(-w / 2, -l / 2, 0);
  301.       Polygon perimeter = p.createIsoRectangle(p.XY, w, l);
  302.       g.setColor(_color.darker());
  303.       p.move(perimeter.xpoints[2], perimeter.ypoints[2]);
  304.       g.fillPolygon(p.createIsoRectangle(p.XZ, -w, h));
  305.       g.fillPolygon(p.createIsoRectangle(p.YZ, -l, h));
  306.       p.move(perimeter.xpoints[0], perimeter.ypoints[0]);
  307.       g.fillPolygon(p.createIsoRectangle(p.XZ, w, h));
  308.       g.setColor(_color.darker().darker());
  309.       g.fillPolygon(p.createIsoRectangle(p.YZ, l, h));
  310.       p.move(b.width / 2, b.height / 2);
  311.       p.isoMove(0, 0, h / 2);
  312.       Tubes.drawImageCentered
  313.     (g, makeCover(_color, w, l, withHole, r), p.x, p.y);
  314.       return _applet.hollowImage(i);
  315.     }
  316.   private Image makeCover(Color c, int w, int l, boolean withHole, int r)
  317.     {
  318.       Rectangle b = (new IsoPoint(0, 0)).getBox(IsoPoint.XY, w, l);
  319.       Image i = _applet.makeImage(b.width, b.height);
  320.       Graphics g = i.getGraphics();
  321.       g.setColor(c);
  322.       IsoPoint p = new IsoPoint(b.width / 2, b.height / 2);
  323.       p.isoMove(-w / 2, -l / 2, 0);
  324.       g.fillPolygon(p.createIsoRectangle(p.XY, w, l));
  325.       if(withHole){
  326.      p.isoMove(w / 2, l / 2, 0);
  327.      g.setColor(Hollow.color);
  328.      p.fillIsoCircle(g, r);
  329.       }
  330.       return _applet.hollowImage(i);
  331.     }
  332.   void draw(Graphics g)
  333.     {
  334.       IsoPoint o = new IsoPoint(0, 0);
  335.       o.isoMove(_diameter * (_size - 1) / 2, 0, 0);
  336.       IsoPoint p = new IsoPoint(o.x, o.y);
  337.       for(int i = _size - 1; 0 <= i; i--, p.isoMove(-_diameter, 0, 0))
  338.     ((Slot) _lower.at(i)).draw(g, p, -_height / 2, _diameter / 2);
  339.       Tubes.drawImageCentered(g, _supportImage, 0, 0);
  340.       p.move(o.x, o.y);
  341.       for(int i = _size - 1; 0 <= i; i--, p.isoMove(-_diameter, 0, 0))
  342.     ((Slot) _upper.at(i)).draw(g, p,  _height / 2, _diameter / 2);
  343.     }
  344.   void setSize(int size)
  345.     {
  346.       if(_size == size)
  347.     return;
  348.       _size = size;
  349.       _supportImage = makeSupport();
  350.       goal();
  351.     }
  352.   void goal()
  353.     {
  354.       _upper = new Queue(_size + 1);
  355.       _lower = new Queue(_size + 1);
  356.       for (int i = 0; i < _size; i++) {
  357.     _upper.append(new Slot(i + 1));
  358.     _lower.append(new Slot(i + 1));
  359.     for(int j = 0; j <=i; j++)
  360.       ((Slot) _lower.at(i)).prepend(new Bead(i + 1));
  361.       }
  362.     }
  363.   void left()
  364.     {
  365.       _upper.append(_upper.getFirst());
  366.       gravity();
  367.     }    
  368.   void right()
  369.     {
  370.       _upper.prepend(_upper.getLast());
  371.       gravity();
  372.     }    
  373.   void flip()
  374.     {
  375.       Queue t = _upper; _upper = _lower; _lower = t;
  376.       gravity();
  377.     }
  378.   void gravity() // 9.81 m/c^2
  379.     { 
  380.       for (int i = 0; i < _size; i++) {
  381.     Slot u = (Slot)_upper.at(i);
  382.     Slot l = (Slot)_lower.at(i);
  383.     while (!u.isEmpty() && !l.isFull())
  384.       l.prepend(u.getFirst());
  385.       }
  386.     }
  387.   void randomize()
  388.     {
  389.       goal();
  390.       Random r = new Random();
  391.       do
  392.     for(int i = 0; i < _size * _size; i++){
  393.       int times = r.nextInt() % _size;
  394.       for(int j = 0; j < times; j++)
  395.         right();
  396.       times = r.nextInt() % 2;
  397.       for(int j = 0; j < times; j++)
  398.         flip();
  399.       times = r.nextInt() % _size;
  400.       for(int j = 0; j < times; j++)
  401.         right();
  402.     }
  403.       while(goal_p());
  404.     }
  405.   boolean goal_p() // predicate
  406.     {
  407.       for(int i = 0; i < _size; i++){
  408.     Slot l = (Slot)_lower.at(i);
  409.     if(l.size() != l.getColor())
  410.       return false;
  411.     for(int j = 0; j < l.size(); j++)
  412.       if(l.getColor() != ((Bead) l.at(j)).getColor())
  413.         return false;
  414.       }
  415.       return true;
  416.     }
  417. }
  418.  
  419. //
  420. //  Aka tube. Maximal capacity: "_color" beads.
  421. //
  422. class Slot extends Queue
  423. {
  424.   private int _color;
  425.   
  426.   Slot(int color)
  427.     {
  428.       super(color);
  429.       _color = color;
  430.     }
  431.   boolean isFull() { return size() == _color; }
  432.   int getColor() { return _color; }
  433.   void drawCage(Graphics g, IsoPoint o, int h, int r, boolean back)
  434.     {
  435.       g.setColor(back? Tubes.colors[_color].darker(): Tubes.colors[_color]);
  436.       IsoPoint p = new IsoPoint(o.x, o.y);
  437.       p.isoMove(0, 0, h);
  438.       p.drawHalfIsoCircle(g, r, back);
  439.       int s = (h < 0)? -1: 1;
  440.       p.drawHalfIsoCylinder(g, s * (2 * r * _color - Math.abs(h)), r, back);
  441.       p.move(o.x, o.y);
  442.       p.isoMove(0, 0, s * 2 * r);
  443.       for(int i = 0; i < _color; i++, p.isoMove(0, 0, s * 2 * r))
  444.      p.drawHalfIsoCircle(g, r, back);
  445.     }
  446.   void draw(Graphics g, IsoPoint o, int h, int r)
  447.     {
  448.       drawCage(g, o, h, r, true);
  449.       IsoPoint p = new IsoPoint(o.x, o.y);
  450.       p.isoMove(0, 0, (h < 0)? -r - 2 * r * (_color - size()): r);
  451.       for(int i = 0; i < size(); i++, p.isoMove(0, 0, (h < 0)? -2 * r: 2 * r))
  452.     ((Bead) at(i)).draw(g, p.x, p.y);
  453.       drawCage(g, o, h, r, false);
  454.     }
  455. }
  456.  
  457. //
  458. //  Ambient/diffuse/specular light model. Viewer: (0, 0, 1).
  459. //  White light: (1, 1, 1). Material shininess: 16.
  460. //
  461. class Bead 
  462. {
  463.   private static Image[] _image;
  464.   private        int     _color;
  465.  
  466.   Bead(Tubes applet, int radius) // once invoked constructor
  467.     {
  468.       int r = radius - 1;
  469.       double cosLN[] = new double[(2 * r + 1) * (2 * r + 1)];
  470.       double cosRV[] = new double[(2 * r + 1) * (2 * r + 1)];
  471.       double sqrt3 = Math.sqrt(3);
  472.       int index = 0;
  473.       for(int y = 0; y <= 2 * r; y++){
  474.     for(int x = 0; x <= 2 * r; x++, index++){
  475.       cosLN[index] = cosRV[index] = 2;
  476.       long d = Math.round(Math.sqrt((x-r) * (x-r) + (y-r) * (y-r)));
  477.       if(d <= r){
  478.         double z = Math.sqrt(r * r - d * d);
  479.         double R = Math.sqrt(z * z + (x-r) * (x-r) + (y-r) * (y-r));
  480.         cosLN[index] = ((x-r) - (y-r) + z) / R / sqrt3;
  481.         if(cosLN[index] > 0)
  482.           cosRV[index]
  483.         = 1 - Math.pow(2 * z * cosLN[index] / R - 1/sqrt3, 16);
  484.         cosLN[index] = (cosLN[index] + 1) / 2;
  485.       }
  486.     }
  487.       }
  488.       _image  = new Image[7];
  489.       for(int c = 0; c < 7; c++){
  490.     int pixels[] = new int[(2 * r + 1) * (2 * r + 1)];
  491.     index = 0;            
  492.     for(int y = 0; y <= 2 * r; y++){
  493.       for(int x = 0; x <= 2 * r; x++, index++){
  494.         pixels[index] = 0;
  495.         if(cosLN[index] < 2) {
  496.           Color color = blend(cosLN[index], Tubes.colors[c], Color.black);
  497.           if(cosRV[index] < 2)
  498.         color = blend(cosRV[index], color, Color.white);
  499.           pixels[index] = color.getRGB();
  500.         }
  501.       }
  502.     }
  503.     _image[c] = applet.createImage
  504.       (new MemoryImageSource(2 * r + 1, 2 * r + 1, pixels, 0, 2 * r + 1));
  505.       }
  506.     }
  507.   Bead(int color) { _color = color; }
  508.   int getColor() { return _color; }
  509.   void draw(Graphics g, int x, int y) 
  510.     { Tubes.drawImageCentered(g, _image[_color], x, y); }
  511.   private Color blend(double factor, Color color, Color light) 
  512.     {
  513.       return new Color
  514.     ((int)(factor * color.getRed()   + (1 - factor) * light.getRed()),
  515.      (int)(factor * color.getGreen() + (1 - factor) * light.getGreen()),
  516.      (int)(factor * color.getBlue()  + (1 - factor) * light.getBlue()));
  517.     }
  518. }
  519.  
  520. //
  521. //  Wraper for java.util.Vector;
  522. //
  523. class Queue extends Vector 
  524. {
  525.   Queue(int size) { super(size); }
  526.   void append(Object object) { addElement(object); }
  527.   void prepend(Object object) { insertElementAt(object, 0); }
  528.   Object at(int i) { return elementAt(i); }
  529.   Object getFirst()
  530.     {
  531.       Object object = firstElement();
  532.       removeElementAt(0);
  533.       return object;
  534.     }
  535.   Object getLast()
  536.     {
  537.       Object object = lastElement();
  538.       removeElementAt(size() - 1);
  539.       return object;
  540.     }
  541. }
  542.  
  543. //
  544. //  Encapsulates all pseudo-iso drawings.
  545. //
  546. class IsoPoint extends Point
  547. {
  548.   final static int XY = 1, XZ = 2, YZ = 3;
  549.   
  550.   IsoPoint(int x, int y) { super(x, y); }
  551.   void isoMove(int dX, int dY, int dZ)
  552.     {
  553.       translate( dX * 3 / 4, -dX / 6);
  554.       translate(-dY * 3 / 4, -dY / 6);
  555.       translate(0, -dZ);
  556.     }
  557.   Polygon createIsoRectangle(int plane, int w, int h)
  558.     {
  559.       Polygon it = new Polygon();
  560.       it.addPoint(x, y);
  561.       IsoPoint p = new IsoPoint(x, y);
  562.       if(plane == IsoPoint.XZ)
  563.     p.isoMove(0, 0, h);
  564.       else
  565.     p.isoMove(0, (plane == IsoPoint.XY)? h: w, 0);
  566.       it.addPoint(p.x, p.y);
  567.       if(plane == IsoPoint.YZ)
  568.     p.isoMove(0, 0, h);
  569.       else
  570.     p.isoMove(w, 0, 0);
  571.       it.addPoint(p.x, p.y);
  572.       if(plane == IsoPoint.XZ)
  573.     p.isoMove(0, 0, -h);
  574.       else
  575.     p.isoMove(0, (plane == IsoPoint.XY)? -h: -w, 0);
  576.       it.addPoint(p.x, p.y);
  577.       return it;
  578.     }
  579.   Rectangle getBox(int plane, int w, int h)
  580.     { return createIsoRectangle(plane, w, h).getBoundingBox(); }
  581.   void fillIsoCircle(Graphics g, int r)
  582.     { g.fillOval(x - r, y - r / 4, 2 * r, 2 * r / 4); }
  583.   void drawHalfIsoCircle(Graphics g, int r, boolean back)
  584.     { g.drawArc(x - r, y - r / 4, 2 * r, 2 * r / 4, 0, (back)? 180: -180); }
  585.   void drawHalfIsoCylinder(Graphics g, int h, int r, boolean back)
  586.     {
  587.       IsoPoint p = new IsoPoint(x, y);
  588.       int s = back? -1: 1;
  589.       p.isoMove(-s * r, 0, 0);
  590.       g.drawLine(p.x, p.y, p.x, p.y - h);
  591.          p.move(x, y);
  592.          p.translate(0, s * r / 4);
  593.          g.drawLine(p.x, p.y, p.x, p.y - h);
  594.       p.move(x, y);
  595.       p.isoMove(0, -s * r, 0);
  596.       g.drawLine(p.x, p.y, p.x, p.y - h);
  597.          p.move(x, y);
  598.          p.translate(s * r, 0);
  599.          g.drawLine(p.x, p.y, p.x, p.y - h);
  600.     }
  601. }
  602.  
  603. //
  604. //  Replaces Hollow.color'ed pixel with transparent one.
  605. //  There is a bug/feature in JDK for WinNT: "argb" parameter
  606. //  contains wrong color for off-screen drawable images.
  607. //  Should I submit the bug?
  608. //
  609. class Hollow extends RGBImageFilter
  610. {
  611.   private static int _color = 0xff000000;
  612.   static Color color = new Color(_color);
  613.  
  614.   Hollow() { canFilterIndexColorModel = true; }
  615.   public int filterRGB(int x, int y, int argb)
  616.     { return (argb == _color)? 0x00000000: argb; }
  617. }
  618.