This commit is contained in:
2011-02-18 16:03:38 +00:00
parent 6b607c8653
commit afc2dedfb6
88 changed files with 18223 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
/*
* ControlCurve.
*
* JavaZOOM : jlgui@javazoom.net
* http://www.javazoom.net
*
*-----------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*----------------------------------------------------------------------
*/
package javazoom.jlgui.player.amp.equalizer.ui;
import java.awt.Polygon;
public abstract class ControlCurve
{
static final int EPSILON = 36; /* square of distance for picking */
protected Polygon pts;
protected int selection = -1;
int maxHeight = -1;
int minHeight = -1;
public ControlCurve()
{
pts = new Polygon();
}
public int boundY(int y)
{
int ny = y;
if ((minHeight >= 0) && (y < minHeight))
{
ny = 0;
}
if ((maxHeight >= 0) && (y >= maxHeight))
{
ny = maxHeight - 1;
}
return ny;
}
public void setMaxHeight(int h)
{
maxHeight = h;
}
public void setMinHeight(int h)
{
minHeight = h;
}
/**
* Return index of control point near to (x,y) or -1 if nothing near.
* @param x
* @param y
* @return
*/
public int selectPoint(int x, int y)
{
int mind = Integer.MAX_VALUE;
selection = -1;
for (int i = 0; i < pts.npoints; i++)
{
int d = sqr(pts.xpoints[i] - x) + sqr(pts.ypoints[i] - y);
if (d < mind && d < EPSILON)
{
mind = d;
selection = i;
}
}
return selection;
}
/**
* Square of an int.
* @param x
* @return
*/
static int sqr(int x)
{
return x * x;
}
/**
* Add a control point, return index of new control point.
* @param x
* @param y
* @return
*/
public int addPoint(int x, int y)
{
pts.addPoint(x, y);
return selection = pts.npoints - 1;
}
/**
* Set selected control point.
* @param x
* @param y
*/
public void setPoint(int x, int y)
{
if (selection >= 0)
{
pts.xpoints[selection] = x;
pts.ypoints[selection] = y;
}
}
/**
* Remove selected control point.
*/
public void removePoint()
{
if (selection >= 0)
{
pts.npoints--;
for (int i = selection; i < pts.npoints; i++)
{
pts.xpoints[i] = pts.xpoints[i + 1];
pts.ypoints[i] = pts.ypoints[i + 1];
}
}
}
public abstract Polygon getPolyline();
}

View File

@@ -0,0 +1,46 @@
/*
* Cubic.
*
* JavaZOOM : jlgui@javazoom.net
* http://www.javazoom.net
*
*-----------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*----------------------------------------------------------------------
*/
package javazoom.jlgui.player.amp.equalizer.ui;
public class Cubic
{
float a, b, c, d; /* a + b*u + c*u^2 +d*u^3 */
public Cubic(float a, float b, float c, float d)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
/**
* Evaluate cubic.
* @param u
* @return
*/
public float eval(float u)
{
return (((d * u) + c) * u + b) * u + a;
}
}

View File

@@ -0,0 +1,441 @@
/*
* EqualizerUI.
*
* JavaZOOM : jlgui@javazoom.net
* http://www.javazoom.net
*
*-----------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*----------------------------------------------------------------------
*/
package javazoom.jlgui.player.amp.equalizer.ui;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javazoom.jlgui.player.amp.PlayerActionEvent;
import javazoom.jlgui.player.amp.PlayerUI;
import javazoom.jlgui.player.amp.skin.AbsoluteLayout;
import javazoom.jlgui.player.amp.skin.DropTargetAdapter;
import javazoom.jlgui.player.amp.skin.ImageBorder;
import javazoom.jlgui.player.amp.skin.Skin;
import javazoom.jlgui.player.amp.util.Config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This class implements an equalizer UI.
* <p/>
* The equalizer consists of 32 band-pass filters.
* Each band of the equalizer can take on a fractional value between
* -1.0 and +1.0.
* At -1.0, the input signal is attenuated by 6dB, at +1.0 the signal is
* amplified by 6dB.
*/
public class EqualizerUI extends JPanel implements ActionListener, ChangeListener
{
private static Log log = LogFactory.getLog(EqualizerUI.class);
private int minGain = 0;
private int maxGain = 100;
private int[] gainValue = { 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50 };
private int[] PRESET_NORMAL = { 50, 50, 50, 50, 50, 50, 50, 50, 50, 50 };
private int[] PRESET_CLASSICAL = { 50, 50, 50, 50, 50, 50, 70, 70, 70, 76 };
private int[] PRESET_CLUB = { 50, 50, 42, 34, 34, 34, 42, 50, 50, 50 };
private int[] PRESET_DANCE = { 26, 34, 46, 50, 50, 66, 70, 70, 50, 50 };
private int[] PRESET_FULLBASS = { 26, 26, 26, 36, 46, 62, 76, 78, 78, 78 };
private int[] PRESET_FULLBASSTREBLE = { 34, 34, 50, 68, 62, 46, 28, 22, 18, 18 };
private int[] PRESET_FULLTREBLE = { 78, 78, 78, 62, 42, 24, 8, 8, 8, 8 };
private int[] PRESET_LAPTOP = { 38, 22, 36, 60, 58, 46, 38, 24, 16, 14 };
private int[] PRESET_LIVE = { 66, 50, 40, 36, 34, 34, 40, 42, 42, 42 };
private int[] PRESET_PARTY = { 32, 32, 50, 50, 50, 50, 50, 50, 32, 32 };
private int[] PRESET_POP = { 56, 38, 32, 30, 38, 54, 56, 56, 54, 54 };
private int[] PRESET_REGGAE = { 48, 48, 50, 66, 48, 34, 34, 48, 48, 48 };
private int[] PRESET_ROCK = { 32, 38, 64, 72, 56, 40, 28, 24, 24, 24 };
private int[] PRESET_TECHNO = { 30, 34, 48, 66, 64, 48, 30, 24, 24, 28 };
private Config config = null;
private PlayerUI player = null;
private Skin ui = null;
private JPopupMenu mainpopup = null;
public static final int LINEARDIST = 1;
public static final int OVERDIST = 2;
private float[] bands = null;
private int[] eqgains = null;
private int eqdist = OVERDIST;
public EqualizerUI()
{
super();
setDoubleBuffered(true);
config = Config.getInstance();
eqgains = new int[10];
setLayout(new AbsoluteLayout());
int[] vals = config.getLastEqualizer();
if (vals != null)
{
for (int h = 0; h < vals.length; h++)
{
gainValue[h] = vals[h];
}
}
// DnD support disabled.
DropTargetAdapter dnd = new DropTargetAdapter()
{
public void processDrop(Object data)
{
return;
}
};
DropTarget dt = new DropTarget(this, DnDConstants.ACTION_COPY, dnd, false);
}
/**
* Return skin.
* @return
*/
public Skin getSkin()
{
return ui;
}
/**
* Set skin.
* @param ui
*/
public void setSkin(Skin ui)
{
this.ui = ui;
}
/**
* Set parent player.
* @param mp
*/
public void setPlayer(PlayerUI mp)
{
player = mp;
}
public void loadUI()
{
log.info("Load EqualizerUI (EDT=" + SwingUtilities.isEventDispatchThread() + ")");
removeAll();
// Background
ImageBorder border = new ImageBorder();
border.setImage(ui.getEqualizerImage());
setBorder(border);
// On/Off
add(ui.getAcEqOnOff(), ui.getAcEqOnOff().getConstraints());
ui.getAcEqOnOff().removeActionListener(this);
ui.getAcEqOnOff().addActionListener(this);
// Auto
add(ui.getAcEqAuto(), ui.getAcEqAuto().getConstraints());
ui.getAcEqAuto().removeActionListener(this);
ui.getAcEqAuto().addActionListener(this);
// Sliders
add(ui.getAcEqPresets(), ui.getAcEqPresets().getConstraints());
for (int i = 0; i < ui.getAcEqSliders().length; i++)
{
add(ui.getAcEqSliders()[i], ui.getAcEqSliders()[i].getConstraints());
ui.getAcEqSliders()[i].setValue(maxGain - gainValue[i]);
ui.getAcEqSliders()[i].removeChangeListener(this);
ui.getAcEqSliders()[i].addChangeListener(this);
}
if (ui.getSpline() != null)
{
ui.getSpline().setValues(gainValue);
add(ui.getSpline(), ui.getSpline().getConstraints());
}
// Popup menu on TitleBar
mainpopup = new JPopupMenu();
String[] presets = { "Normal", "Classical", "Club", "Dance", "Full Bass", "Full Bass & Treble", "Full Treble", "Laptop", "Live", "Party", "Pop", "Reggae", "Rock", "Techno" };
JMenuItem mi;
for (int p = 0; p < presets.length; p++)
{
mi = new JMenuItem(presets[p]);
mi.removeActionListener(this);
mi.addActionListener(this);
mainpopup.add(mi);
}
ui.getAcEqPresets().removeActionListener(this);
ui.getAcEqPresets().addActionListener(this);
validate();
}
/* (non-Javadoc)
* @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
*/
public void stateChanged(ChangeEvent e)
{
for (int i = 0; i < ui.getAcEqSliders().length; i++)
{
gainValue[i] = maxGain - ui.getAcEqSliders()[i].getValue();
}
if (ui.getSpline() != null) ui.getSpline().repaint();
// Apply equalizer values.
synchronizeEqualizer();
}
/**
* Set bands array for equalizer.
*
* @param bands
*/
public void setBands(float[] bands)
{
this.bands = bands;
}
/**
* Apply equalizer function.
*
* @param gains
* @param min
* @param max
*/
public void updateBands(int[] gains, int min, int max)
{
if ((gains != null) && (bands != null))
{
int j = 0;
float gvalj = (gains[j] * 2.0f / (max - min) * 1.0f) - 1.0f;
float gvalj1 = (gains[j + 1] * 2.0f / (max - min) * 1.0f) - 1.0f;
// Linear distribution : 10 values => 32 values.
if (eqdist == LINEARDIST)
{
float a = (gvalj1 - gvalj) * 1.0f;
float b = gvalj * 1.0f - (gvalj1 - gvalj) * j;
// x=s*x'
float s = (gains.length - 1) * 1.0f / (bands.length - 1) * 1.0f;
for (int i = 0; i < bands.length; i++)
{
float ind = s * i;
if (ind > (j + 1))
{
j++;
gvalj = (gains[j] * 2.0f / (max - min) * 1.0f) - 1.0f;
gvalj1 = (gains[j + 1] * 2.0f / (max - min) * 1.0f) - 1.0f;
a = (gvalj1 - gvalj) * 1.0f;
b = gvalj * 1.0f - (gvalj1 - gvalj) * j;
}
// a*x+b
bands[i] = a * i * 1.0f * s + b;
}
}
// Over distribution : 10 values => 10 first value of 32 values.
else if (eqdist == OVERDIST)
{
for (int i = 0; i < gains.length; i++)
{
bands[i] = (gains[i] * 2.0f / (max - min) * 1.0f) - 1.0f;
}
}
}
}
/* (non-Javadoc)
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent e)
{
String cmd = e.getActionCommand();
log.debug("Action=" + cmd + " (EDT=" + SwingUtilities.isEventDispatchThread() + ")");
// On/Off
if (cmd.equals(PlayerActionEvent.ACEQONOFF))
{
if (ui.getAcEqOnOff().isSelected())
{
config.setEqualizerOn(true);
}
else
{
config.setEqualizerOn(false);
}
synchronizeEqualizer();
}
// Auto
else if (cmd.equals(PlayerActionEvent.ACEQAUTO))
{
if (ui.getAcEqAuto().isSelected())
{
config.setEqualizerAuto(true);
}
else
{
config.setEqualizerAuto(false);
}
}
// Presets
else if (cmd.equals(PlayerActionEvent.ACEQPRESETS))
{
if (e.getModifiers() == MouseEvent.BUTTON1_MASK)
{
mainpopup.show(this, ui.getAcEqPresets().getLocation().x, ui.getAcEqPresets().getLocation().y);
}
}
else if (cmd.equals("Normal"))
{
updateSliders(PRESET_NORMAL);
synchronizeEqualizer();
}
else if (cmd.equals("Classical"))
{
updateSliders(PRESET_CLASSICAL);
synchronizeEqualizer();
}
else if (cmd.equals("Club"))
{
updateSliders(PRESET_CLUB);
synchronizeEqualizer();
}
else if (cmd.equals("Dance"))
{
updateSliders(PRESET_DANCE);
synchronizeEqualizer();
}
else if (cmd.equals("Full Bass"))
{
updateSliders(PRESET_FULLBASS);
synchronizeEqualizer();
}
else if (cmd.equals("Full Bass & Treble"))
{
updateSliders(PRESET_FULLBASSTREBLE);
synchronizeEqualizer();
}
else if (cmd.equals("Full Treble"))
{
updateSliders(PRESET_FULLTREBLE);
synchronizeEqualizer();
}
else if (cmd.equals("Laptop"))
{
updateSliders(PRESET_LAPTOP);
synchronizeEqualizer();
}
else if (cmd.equals("Live"))
{
updateSliders(PRESET_LIVE);
synchronizeEqualizer();
}
else if (cmd.equals("Party"))
{
updateSliders(PRESET_PARTY);
synchronizeEqualizer();
}
else if (cmd.equals("Pop"))
{
updateSliders(PRESET_POP);
synchronizeEqualizer();
}
else if (cmd.equals("Reggae"))
{
updateSliders(PRESET_REGGAE);
synchronizeEqualizer();
}
else if (cmd.equals("Rock"))
{
updateSliders(PRESET_ROCK);
synchronizeEqualizer();
}
else if (cmd.equals("Techno"))
{
updateSliders(PRESET_TECHNO);
synchronizeEqualizer();
}
}
/**
* Update sliders from gains array.
*
* @param gains
*/
public void updateSliders(int[] gains)
{
if (gains != null)
{
for (int i = 0; i < gains.length; i++)
{
gainValue[i + 1] = gains[i];
ui.getAcEqSliders()[i + 1].setValue(maxGain - gainValue[i + 1]);
}
}
}
/**
* Apply equalizer values.
*/
public void synchronizeEqualizer()
{
config.setLastEqualizer(gainValue);
if (config.isEqualizerOn())
{
for (int j = 0; j < eqgains.length; j++)
{
eqgains[j] = -gainValue[j + 1] + maxGain;
}
updateBands(eqgains, minGain, maxGain);
}
else
{
for (int j = 0; j < eqgains.length; j++)
{
eqgains[j] = (maxGain - minGain) / 2;
}
updateBands(eqgains, minGain, maxGain);
}
}
/**
* Return equalizer bands distribution.
* @return
*/
public int getEqdist()
{
return eqdist;
}
/**
* Set equalizer bands distribution.
* @param i
*/
public void setEqdist(int i)
{
eqdist = i;
}
/**
* Simulates "On/Off" selection.
*/
public void pressOnOff()
{
ui.getAcEqOnOff().doClick();
}
/**
* Simulates "Auto" selection.
*/
public void pressAuto()
{
ui.getAcEqAuto().doClick();
}
}

View File

@@ -0,0 +1,108 @@
/*
* NaturalSpline.
*
* JavaZOOM : jlgui@javazoom.net
* http://www.javazoom.net
*
*-----------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*----------------------------------------------------------------------
*/
package javazoom.jlgui.player.amp.equalizer.ui;
import java.awt.Polygon;
public class NaturalSpline extends ControlCurve
{
public final int STEPS = 12;
public NaturalSpline()
{
super();
}
/*
* calculates the natural cubic spline that interpolates y[0], y[1], ...
* y[n] The first segment is returned as C[0].a + C[0].b*u + C[0].c*u^2 +
* C[0].d*u^3 0<=u <1 the other segments are in C[1], C[2], ... C[n-1]
*/
Cubic[] calcNaturalCubic(int n, int[] x)
{
float[] gamma = new float[n + 1];
float[] delta = new float[n + 1];
float[] D = new float[n + 1];
int i;
/*
* We solve the equation [2 1 ] [D[0]] [3(x[1] - x[0]) ] |1 4 1 | |D[1]|
* |3(x[2] - x[0]) | | 1 4 1 | | . | = | . | | ..... | | . | | . | | 1 4
* 1| | . | |3(x[n] - x[n-2])| [ 1 2] [D[n]] [3(x[n] - x[n-1])]
*
* by using row operations to convert the matrix to upper triangular and
* then back sustitution. The D[i] are the derivatives at the knots.
*/
gamma[0] = 1.0f / 2.0f;
for (i = 1; i < n; i++)
{
gamma[i] = 1 / (4 - gamma[i - 1]);
}
gamma[n] = 1 / (2 - gamma[n - 1]);
delta[0] = 3 * (x[1] - x[0]) * gamma[0];
for (i = 1; i < n; i++)
{
delta[i] = (3 * (x[i + 1] - x[i - 1]) - delta[i - 1]) * gamma[i];
}
delta[n] = (3 * (x[n] - x[n - 1]) - delta[n - 1]) * gamma[n];
D[n] = delta[n];
for (i = n - 1; i >= 0; i--)
{
D[i] = delta[i] - gamma[i] * D[i + 1];
}
/* now compute the coefficients of the cubics */
Cubic[] C = new Cubic[n];
for (i = 0; i < n; i++)
{
C[i] = new Cubic((float) x[i], D[i], 3 * (x[i + 1] - x[i]) - 2 * D[i] - D[i + 1], 2 * (x[i] - x[i + 1]) + D[i] + D[i + 1]);
}
return C;
}
/**
* Return a cubic spline.
*/
public Polygon getPolyline()
{
Polygon p = new Polygon();
if (pts.npoints >= 2)
{
Cubic[] X = calcNaturalCubic(pts.npoints - 1, pts.xpoints);
Cubic[] Y = calcNaturalCubic(pts.npoints - 1, pts.ypoints);
// very crude technique - just break each segment up into steps lines
int x = (int) Math.round(X[0].eval(0));
int y = (int) Math.round(Y[0].eval(0));
p.addPoint(x, boundY(y));
for (int i = 0; i < X.length; i++)
{
for (int j = 1; j <= STEPS; j++)
{
float u = j / (float) STEPS;
x = Math.round(X[i].eval(u));
y = Math.round(Y[i].eval(u));
p.addPoint(x, boundY(y));
}
}
}
return p;
}
}

View File

@@ -0,0 +1,133 @@
/*
* SplinePanel.
*
* JavaZOOM : jlgui@javazoom.net
* http://www.javazoom.net
*
*-----------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*----------------------------------------------------------------------
*/
package javazoom.jlgui.player.amp.equalizer.ui;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Polygon;
import javax.swing.JPanel;
import javazoom.jlgui.player.amp.skin.AbsoluteConstraints;
public class SplinePanel extends JPanel
{
private AbsoluteConstraints constraints = null;
private Image backgroundImage = null;
private Image barImage = null;
private int[] values = null;
private Color[] gradient = null;
public SplinePanel()
{
super();
setDoubleBuffered(true);
setLayout(null);
}
public Color[] getGradient()
{
return gradient;
}
public void setGradient(Color[] gradient)
{
this.gradient = gradient;
}
public void setConstraints(AbsoluteConstraints cnts)
{
constraints = cnts;
}
public AbsoluteConstraints getConstraints()
{
return constraints;
}
public Image getBarImage()
{
return barImage;
}
public void setBarImage(Image barImage)
{
this.barImage = barImage;
}
public Image getBackgroundImage()
{
return backgroundImage;
}
public void setBackgroundImage(Image backgroundImage)
{
this.backgroundImage = backgroundImage;
}
public int[] getValues()
{
return values;
}
public void setValues(int[] values)
{
this.values = values;
}
/* (non-Javadoc)
* @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
public void paintComponent(Graphics g)
{
if (backgroundImage != null) g.drawImage(backgroundImage, 0, 0, null);
if (barImage != null) g.drawImage(barImage, 0, getHeight()/2, null);
if ((values != null) && (values.length > 0))
{
NaturalSpline curve = new NaturalSpline();
float dx = 1.0f * getWidth() / (values.length - 2);
int h = getHeight();
curve.setMaxHeight(h);
curve.setMinHeight(0);
for (int i = 1; i < values.length; i++)
{
int x1 = (int) Math.round(dx * (i - 1));
int y1 = ((int) Math.round((h * values[i] / 100)));
y1 = curve.boundY(y1);
curve.addPoint(x1, y1);
}
Polygon spline = curve.getPolyline();
if (gradient != null)
{
for (int i=0;i<(spline.npoints-1);i++)
{
g.setColor(gradient[spline.ypoints[i]]);
g.drawLine(spline.xpoints[i], spline.ypoints[i],spline.xpoints[i+1], spline.ypoints[i+1]);
}
}
else
{
g.drawPolyline(spline.xpoints, spline.ypoints, spline.npoints);
}
}
}
}