Java/2D Graphics GUI/Line

Материал из Java эксперт
Перейти к: навигация, поиск

A line is drawn using two points

   
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LinesDashes1 extends JPanel {
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    float[] dash1 = { 2f, 0f, 2f };
    g2d.drawLine(20, 40, 250, 40);
    BasicStroke bs1 = new BasicStroke(1, 
        BasicStroke.CAP_BUTT, 
        BasicStroke.JOIN_ROUND, 
        1.0f, 
        dash1,
        2f);
    g2d.setStroke(bs1);
    g2d.drawLine(20, 80, 250, 80);
    }
  public static void main(String[] args) {
    LinesDashes1 lines = new LinesDashes1();
    JFrame frame = new JFrame("Lines");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(lines);
    frame.setSize(280, 270);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}



Dash style line

   
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LinesDashes1 extends JPanel {
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    float[] dash1 = { 2f, 0f, 2f };
    g2d.drawLine(20, 40, 250, 40);
    BasicStroke bs1 = new BasicStroke(1, 
        BasicStroke.CAP_BUTT, 
        BasicStroke.JOIN_ROUND, 
        1.0f, 
        dash1,
        2f);
    g2d.setStroke(bs1);
    g2d.drawLine(20, 80, 250, 80);
  }
  public static void main(String[] args) {
    LinesDashes1 lines = new LinesDashes1();
    JFrame frame = new JFrame("Lines");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(lines);
    frame.setSize(280, 270);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}



Draw a point: use a drawLine() method

   
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Points extends JPanel {
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    g2d.setColor(Color.red);
    for (int i = 0; i <= 100000; i++) {
      Dimension size = getSize();
      int w = size.width ;
      int h = size.height;
      Random r = new Random();
      int x = Math.abs(r.nextInt()) % w;
      int y = Math.abs(r.nextInt()) % h;
      g2d.drawLine(x, y, x, y);
    }
  }
  public static void main(String[] args) {
    Points points = new Points();
    JFrame frame = new JFrame("Points");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(points);
    frame.setSize(250, 200);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}



Draw Dashed

   
/*
    GNU LESSER GENERAL PUBLIC LICENSE
    Copyright (C) 2006 The Lobo Project
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    Contact info: lobochief@users.sourceforge.net
*/
import java.awt.*;
public class GUITasks {
  public static Frame getTopFrame() {
    Frame[] frames = Frame.getFrames();
    for(int i = 0; i < frames.length; i++) {
      if(frames[i].getFocusOwner() != null) {
        return frames[i];
      }
    }
    if(frames.length > 0) {
      return frames[0];
    }
    return null;
  }
  
  public static void drawDashed(Graphics g, int x1, int y1, int x2, int y2, int dashSize, int gapSize) {
    if(x2 < x1) {
      int temp = x1;
      x1 = x2;
      x2 = temp;
    }
    if(y2 < y1) {
      int temp = y1;
      y1 = y2;
      y2 = temp;
    }
    int totalDash = dashSize + gapSize;
    if(y1 == y2) {
      int virtualStartX = (x1 / totalDash) * totalDash;
      for(int x = virtualStartX; x < x2; x += totalDash) {
        int topX = x + dashSize;
        if(topX > x2) {
          topX = x2;
        }
        int firstX = x;
        if(firstX < x1) {
          firstX = x1;
        }
        if(firstX < topX) {
          g.drawLine(firstX, y1, topX, y1);
        }
      }
    }
    else if(x1 == x2) {
      int virtualStartY = (y1 / totalDash) * totalDash;
      for(int y = virtualStartY; y < y2; y += totalDash) {
        int topY = y + dashSize;
        if(topY > y2) {
          topY = y2;
        }
        int firstY = y;
        if(firstY < y1) {
          firstY = y1;
        }
        if(firstY < topY) {
          g.drawLine(x1, firstY, x1, topY);
        }
      }     
    }
    else {
      // Not supported
      g.drawLine(x1, y1, x2, y2);
    }
  }
}



Drawing a Line using Java 2D Graphics API

   
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
public class Main extends javax.swing.JFrame {
  public Main() {
    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setSize(600, 600);
  }
  public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    Line2D lin = new Line2D.Float(100, 100, 250, 260);
    g2.draw(lin);
  }
  public static void main(String args[]) {
    new Main().setVisible(true);
  }
}



Draw Optimized Line

   
/*
 * Copyright (C) 2004 NNL Technology AB
 * Visit www.infonode.net for information about InfoNode(R) 
 * products and how to contact NNL Technology AB.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
 * MA 02111-1307, USA.
 */

// $Id: GraphicsUtil.java,v 1.4 2005/12/04 13:46:04 jesper Exp $

import javax.swing.*;
import java.awt.*;
/**
 * @author johan
 */
public class GraphicsUtil {
  public static void drawOptimizedLine(Graphics g, int x1, int y1, int x2, int y2) {
    if (g.getColor().getAlpha() < 255 && (x1 == x2 || y1 == y2))
      g.fillRect(x1 < x2 ? x1 : x2, y1 < y2 ? y1 : y2, Math.abs(x2 - x1) + 1, Math.abs(y2 - y1) + 1);
    else
      g.drawLine(x1, y1, x2, y2);
  }
  public static Rectangle calculateIntersectionClip(int x, int y, int width, int height, Shape originalClip) {
    Rectangle bounds = originalClip.getBounds();
    SwingUtilities.ruputeIntersection(x, y, width, height, bounds);
    return bounds;
  }
}



Line dashes style 2

   
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LinesDashes2 extends JPanel {
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    g2d.drawLine(20, 40, 250, 40);
    float[] dash2 = { 1f, 1f, 1f };
    BasicStroke bs2 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash2,2f);
    g2d.setStroke(bs2);
    g2d.drawLine(20, 20, 250, 20);
  }
  public static void main(String[] args) {
    LinesDashes2 lines = new LinesDashes2();
    JFrame frame = new JFrame("Lines");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(lines);
    frame.setSize(280, 270);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}



Line Dash Style 4

   
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LinesDashes4 extends JPanel {
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    
    float[] dash4 = { 4f, 4f, 1f };
    BasicStroke bs4 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash4,
        2f);
        
    g2d.setStroke(bs4);
    g2d.drawLine(20, 20, 250, 20);
  }
  public static void main(String[] args) {
    LinesDashes4 lines = new LinesDashes4();
    JFrame frame = new JFrame("Lines");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(lines);
    frame.setSize(280, 270);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}



Line-graph drawable

  
/**
 * 
 * LibSparkline : a free Java sparkline chart library
 * 
 *
 * Project Info:  http://reporting.pentaho.org/libsparkline/
 *
 * (C) Copyright 2008, by Larry Ogrodnek, Pentaho Corporation and Contributors.
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the Apache License 2.0.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * You should have received a copy of the Apache License 2.0 along with this library;
 * if not, a online version is available at http://www.apache.org/licenses/
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
 * in the United States and other countries.]
 *
 * ------------
 * LineGraphDrawable.java
 * ------------
 */
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
/**
 * A very fast and very simple line-graph drawable. This code is based on the
 * LineGraph class writen by Larry Ogrodnek but instead of producing a
 * low-resolution image, this class writes the content into a Graphics2D
 * context.
 * 
 * @author Thomas Morgner
 */
public class LineGraphDrawable {
  private static final int DEFAULT_SPACING = 2;
  private int spacing;
  private Color color;
  private Color background;
  private Number[] data;
  /**
   * Creates a default bargraph drawable with some sensible default colors and
   * spacings.
   */
  public LineGraphDrawable() {
    this.color = Color.black;
    this.spacing = DEFAULT_SPACING;
  }
  /**
   * Returns the numeric data for the drawable or null, if the drawable has no
   * data.
   * 
   * @return the data.
   */
  public Number[] getData() {
    return data;
  }
  /**
   * Defines the numeric data for the drawable or null, if the drawable has no
   * data.
   * 
   * @param data
   *          the data (can be null).
   */
  public void setData(final Number[] data) {
    this.data = data;
  }
  /**
   * Returns the main color for the bars.
   * 
   * @return the main color for the bars, never null.
   */
  public Color getColor() {
    return color;
  }
  /**
   * Defines the main color for the bars.
   * 
   * @param color
   *          the main color for the bars, never null.
   */
  public void setColor(final Color color) {
    if (color == null) {
      throw new NullPointerException();
    }
    this.color = color;
  }
  /**
   * Returns the color for the background of the graph. This property can be
   * null, in which case the bar will have a transparent background.
   * 
   * @return color for the background or null, if the graph has a transparent
   *         background color.
   */
  public Color getBackground() {
    return background;
  }
  /**
   * Defines the color for the background of the graph. This property can be
   * null, in which case the bar will have a transparent background.
   * 
   * @param background
   *          the background or null, if the graph has a transparent background
   *          color.
   */
  public void setBackground(final Color background) {
    this.background = background;
  }
  /**
   * Returns the spacing between the bars.
   * 
   * @return the spacing between the bars.
   */
  public int getSpacing() {
    return spacing;
  }
  /**
   * Defines the spacing between the bars.
   * 
   * @param spacing
   *          the spacing between the bars.
   */
  public void setSpacing(final int spacing) {
    this.spacing = spacing;
  }
  /**
   * Draws the bar-graph into the given Graphics2D context in the given area.
   * This method will not draw a graph if the data given is null or empty.
   * 
   * @param graphics
   *          the graphics context on which the bargraph should be rendered.
   * @param drawArea
   *          the area on which the bargraph should be drawn.
   */
  public void draw(final Graphics2D graphics, final Rectangle2D drawArea) {
    if (graphics == null) {
      throw new NullPointerException();
    }
    if (drawArea == null) {
      throw new NullPointerException();
    }
    final int height = (int) drawArea.getHeight();
    if (height <= 0) {
      return;
    }
    final Graphics2D g2 = (Graphics2D) graphics.create();
    if (background != null) {
      g2.setPaint(background);
      g2.draw(drawArea);
    }
    if (data == null || data.length == 0) {
      g2.dispose();
      return;
    }
    g2.translate(drawArea.getX(), drawArea.getY());
    float d = getDivisor(data, height);
    final int spacing = getSpacing();
    final int w = (((int) drawArea.getWidth()) - (spacing * (data.length - 1))) / (data.length - 1);
    float min = Float.MAX_VALUE;
    for (int index = 0; index < data.length; index++) {
      Number i = data[index];
      if (i == null) {
        continue;
      }
      final float value = i.floatValue();
      if (value < min) {
        min = value;
      }
    }
    int x = 0;
    int y = -1;
    if (d == 0.0) {
      // special case -- a horizontal straight line
      d = 1.0f;
      y = -height / 2;
    }
    final Line2D.Double line = new Line2D.Double();
    for (int i = 0; i < data.length - 1; i++) {
      final int px1 = x;
      x += (w + spacing);
      final int px2 = x;
      g2.setPaint(color);
      final Number number = data[i];
      final Number nextNumber = data[i + 1];
      if (number == null && nextNumber == null) {
        final float delta = height - ((0 - min) / d);
        line.setLine(px1, y + delta, px2, y + delta);
      } else if (number == null) {
        line.setLine(px1, y + (height - ((0 - min) / d)), px2, y
            + (height - ((nextNumber.floatValue() - min) / d)));
      } else if (nextNumber == null) {
        line.setLine(px1, y + (height - ((number.floatValue() - min) / d)), px2, y
            + (height - ((0 - min) / d)));
      } else {
        line.setLine(px1, y + (height - ((number.floatValue() - min) / d)), px2, y
            + (height - ((nextNumber.floatValue() - min) / d)));
      }
      g2.draw(line);
    }
    g2.dispose();
  }
  /**
   * Computes the scale factor to scale the given numeric data into the target
   * height.
   * 
   * @param data
   *          the numeric data.
   * @param height
   *          the target height of the graph.
   * @return the scale factor.
   */
  public static float getDivisor(final Number[] data, final int height) {
    if (data == null) {
      throw new NullPointerException("Data array must not be null.");
    }
    if (height < 1) {
      throw new IndexOutOfBoundsException("Height must be greater or equal to 1");
    }
    float max = Float.MIN_VALUE;
    float min = Float.MAX_VALUE;
    for (int index = 0; index < data.length; index++) {
      Number i = data[index];
      if (i == null) {
        continue;
      }
      final float numValue = i.floatValue();
      if (numValue < min) {
        min = numValue;
      }
      if (numValue > max) {
        max = numValue;
      }
    }
    if (max <= min) {
      return 1.0f;
    }
    if (height == 1) {
      return 0;
    }
    return (max - min) / (height - 1);
  }
}



Lines Dashes style 3

   
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LinesDashes3 extends JPanel {
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    
    float[] dash3 = { 4f, 0f, 2f };
    BasicStroke bs3 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash3,
        2f);
    
    g2d.setStroke(bs3);
    g2d.drawLine(20, 60, 250, 60);
  }
  public static void main(String[] args) {
    LinesDashes3 lines = new LinesDashes3();
    JFrame frame = new JFrame("Lines");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(lines);
    frame.setSize(280, 270);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}



Line Styles

   
/*
 * Copyright (c) 2000 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 2nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book (recommended),
 * visit http://www.davidflanagan.ru/javaexamples2.
 */
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.GeneralPath;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** A demonstration of Java2D line styles */
public class LineStyles extends JPanel{
  public String getName() {
    return "LineStyles";
  }
  public int getWidth() {
    return 450;
  }
  public int getHeight() {
    return 180;
  }
  int[] xpoints = new int[] { 0, 50, 100 }; // X coordinates of our shape
  int[] ypoints = new int[] { 75, 0, 75 }; // Y coordinates of our shape
  // Here are three different line styles we will demonstrate
  // They are thick lines with different cap and join styles
  Stroke[] linestyles = new Stroke[] {
      new BasicStroke(25.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL),
      new BasicStroke(25.0f, BasicStroke.CAP_SQUARE,
          BasicStroke.JOIN_MITER),
      new BasicStroke(25.0f, BasicStroke.CAP_ROUND,
          BasicStroke.JOIN_ROUND), };
  // Another line style: a 2 pixel-wide dot-dashed line
  Stroke thindashed = new BasicStroke(2.0f, // line width
      /* cap style */BasicStroke.CAP_BUTT,
      /* join style, miter limit */BasicStroke.JOIN_BEVEL, 1.0f,
      /* the dash pattern */new float[] { 8.0f, 3.0f, 2.0f, 3.0f },
      /* the dash phase */0.0f); /* on 8, off 3, on 2, off 3 */
  // Labels to appear in the diagram, and the font to use to display them.
  Font font = new Font("Helvetica", Font.BOLD, 12);
  String[] capNames = new String[] { "CAP_BUTT", "CAP_SQUARE", "CAP_ROUND" };
  String[] joinNames = new String[] { "JOIN_BEVEL", "JOIN_MITER",
      "JOIN_ROUND" };
  /** This method draws the example figure */
  public void paint(Graphics g1) {
    Graphics2D g = (Graphics2D) g1;
    // Use anti-aliasing to avoid "jaggies" in the lines
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    // Define the shape to draw
    GeneralPath shape = new GeneralPath();
    shape.moveTo(xpoints[0], ypoints[0]); // start at point 0
    shape.lineTo(xpoints[1], ypoints[1]); // draw a line to point 1
    shape.lineTo(xpoints[2], ypoints[2]); // and then on to point 2
    // Move the origin to the right and down, creating a margin
    g.translate(20, 40);
    // Now loop, drawing our shape with the three different line styles
    for (int i = 0; i < linestyles.length; i++) {
      g.setColor(Color.gray); // Draw a gray line
      g.setStroke(linestyles[i]); // Select the line style to use
      g.draw(shape); // Draw the shape
      g.setColor(Color.black); // Now use black
      g.setStroke(thindashed); // And the thin dashed line
      g.draw(shape); // And draw the shape again.
      // Highlight the location of the vertexes of the shape
      // This accentuates the cap and join styles we"re demonstrating
      for (int j = 0; j < xpoints.length; j++)
        g.fillRect(xpoints[j] - 2, ypoints[j] - 2, 5, 5);
      g.drawString(capNames[i], 5, 105); // Label the cap style
      g.drawString(joinNames[i], 5, 120); // Label the join style
      g.translate(150, 0); // Move over to the right before looping again
    }
  }
  public static void main(String[] a){
      JFrame f = new JFrame();
      f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
      f.setContentPane(new LineStyles());
      f.setSize(450,200);
      f.setVisible(true);
  }
}



Xsplinefun displays colorful moving splines in a window

   
/**
 * Xsplinefun displays colorful moving splines in a window.
 * 
 * Taken from xsplinefun, Distribution of 02may92, by Jef Poskanzer,
 * jef@netcom.ru, jef@well.sf.ca.us
 * 
 * @copyright (C) 1992 by Jef Poskanzer
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation. This software is provided "as is" without express or implied
 * warranty.
 * 
 * First step in converting to Java was to junk all the X Windows stuff; in the
 * process we lost all the customization (need to re-add with getopt?).
 */
public class XSplineFun {
  public static final int POINTS = 20;
  public static final int DEFAULT_MAX_COLORS = 20;
  /* Spline-fun smarts. */
  static int x[] = new int [POINTS]; 
    static int y[]= new int [POINTS];
         static int  dx[] = new int [POINTS];
                static int dy[]= new int [POINTS];
  static int nred, ngreen, nblue, dred, dgreen, dblue;
  static int color;
  static Color xcolors[] = new Color[DEFAULT_MAX_COLORS];
  public static void main(String[] av) {
    Frame f = new Frame("Spline Fun");
    XSplineFun xf = new XSplineFun();
    xf.init_splines();
    f.add(xf);
    while(true)
      try {
        xf.move_splines();
        Thread.sleep(150);    // msec
      } catch (Exception e) {
        System.out.println(e);
      }
  }
  static void
  init_splines()
  {
    int i;
    /* Initialize points. */
    for ( i = 0; i < POINTS; ++i )
    {
    x[i] = random() % width;
    y[i] = random() % height;
    dx[i] = random() % ( MAX_DELTA * 2 ) - MAX_DELTA;
    if ( dx[i] <= 0 ) --dx[i];
    dy[i] = random() % ( MAX_DELTA * 2 ) - MAX_DELTA;
    if ( dy[i] <= 0 ) --dy[i];
    }
    /* Initalize colors. */
    for ( color = 0; color < ncolors; ++color )
    {
    xcolors[color].red = xcolors[color].green = xcolors[color].blue = 0;
    xcolors[color].pixel = pixels[color];
    xcolors[color].flags = DoRed|DoGreen|DoBlue;
    }
    color = 0;
    nred = ngreen = nblue = 0;
    dred = random() % ( MAX_COLOR_DELTA * 2 ) - MAX_COLOR_DELTA;
    if ( dred <= 0 ) --dred;
    dgreen = random() % ( MAX_COLOR_DELTA * 2 ) - MAX_COLOR_DELTA;
    if ( dgreen <= 0 ) --dgreen;
    dblue = random() % ( MAX_COLOR_DELTA * 2 ) - MAX_COLOR_DELTA;
    if ( dblue <= 0 ) --dblue;
  }
  static void
  rotate_colormap()
  {
    int t, i;
    if ( forwards )
    {
    t = xcolors[0].pixel;
    for ( i = 0; i < ncolors - 1; ++i )
      xcolors[i].pixel = xcolors[i + 1].pixel;
    xcolors[ncolors - 1].pixel = t;
    XStoreColors(display, cmap, xcolors, ncolors );
    }
    else if ( backwards )
    {
    t = xcolors[ncolors - 1].pixel;
    for ( i = ncolors - 1; i > 0; --i )
      xcolors[i].pixel = xcolors[i - 1].pixel;
    xcolors[0].pixel = t;
    XStoreColors(display, cmap, xcolors, ncolors );
    }
  }
  static void
  new_color()
  {
    int t;
    for ( ; ; )
    {
    t = (int) nred + dred;
    if ( t >= 0 && t < 65536 ) break;
    dred = random() % ( MAX_COLOR_DELTA * 2 ) - MAX_COLOR_DELTA;
    if ( dred <= 0 ) --dred;
    }
    xcolors[color].red = nred = t;
    for ( ; ; )
    {
    t = (int) ngreen + dgreen;
    if ( t >= 0 && t < 65536 ) break;
    dgreen = random() % ( MAX_COLOR_DELTA * 2 ) - MAX_COLOR_DELTA;
    if ( dgreen <= 0 ) --dgreen;
    }
    xcolors[color].green = ngreen = t;
    for ( ; ; )
    {
    t = (int) nblue + dblue;
    if ( t >= 0 && t < 65536 ) break;
    dblue = random() % ( MAX_COLOR_DELTA * 2 ) - MAX_COLOR_DELTA;
    if ( dblue <= 0 ) --dblue;
    }
    xcolors[color].blue = nblue = t;
    XStoreColor(display, cmap, &(xcolors[color]) );
    XSetForeground( display, gc, xcolors[color].pixel );
    if ( ++color >= ncolors ) color -= ncolors;
  }
  static void
  move_splines()
  {
    int i, t, px, py, zx, zy, nx, ny;
    /* Rotate colormap if necessary. */
    rotate_colormap();
    /* Choose new color. */
    new_color();
    /* Backwards rotation requires two new colors each loop. */
    if ( backwards )
    new_color();
    /* Move the points. */
    for ( i = 0; i < POINTS; i++ )
    {
    for ( ; ; )
      {
      t = x[i] + dx[i];
      if ( t >= 0 && t < width ) break;
      dx[i] = random() % ( MAX_DELTA * 2 ) - MAX_DELTA;
      if ( dx[i] <= 0 ) --dx[i];
      }
    x[i] = t;
    for ( ; ; )
      {
      t = y[i] + dy[i];
      if ( t >= 0 && t < height ) break;
      dy[i] = random() % ( MAX_DELTA * 2 ) - MAX_DELTA;
      if ( dy[i] <= 0 ) --dy[i];
      }
    y[i] = t;
    }
    /* Draw the figure. */
    px = zx = ( x[0] + x[POINTS-1] ) / 2;
    py = zy = ( y[0] + y[POINTS-1] ) / 2;
    for ( i = 0; i < POINTS-1; ++i )
    {
    nx = ( x[i+1] + x[i] ) / 2;
    ny = ( y[i+1] + y[i] ) / 2;
    XDrawSpline(g, px, py, x[i], y[i], nx, ny );
    px = nx;
    py = ny;
    }
    XDrawSpline(g, px, py, x[POINTS-1], y[POINTS-1], zx, zy );
  }

  /* X spline routine. */
  int abs(x) {
    return x < 0 ? -x : x;
  }
  static void
  XDrawSpline(Graphics g, int x0, y0, x1, y1, x2, y2) {
    register int xa, ya, xb, yb, xc, yc, xp, yp;
    xa = ( x0 + x1 ) / 2;
    ya = ( y0 + y1 ) / 2;
    xc = ( x1 + x2 ) / 2;
    yc = ( y1 + y2 ) / 2;
    xb = ( xa + xc ) / 2;
    yb = ( ya + yc ) / 2;
    xp = ( x0 + xb ) / 2;
    yp = ( y0 + yb ) / 2;
    if ( abs( xa - xp ) + abs( ya - yp ) > SPLINE_THRESH )
    XDrawSpline( display, d, gc, x0, y0, xa, ya, xb, yb );
    else
    XDrawLine( display, d, gc, x0, y0, xb, yb );
    xp = ( x2 + xb ) / 2;
    yp = ( y2 + yb ) / 2;
    if ( abs( xc - xp ) + abs( yc - yp ) > SPLINE_THRESH )
    XDrawSpline( display, d, gc, xb, yb, xc, yc, x2, y2 );
    else
    XDrawLine( display, d, gc, xb, yb, x2, y2 );
  }
}