I'm attempting to draw an arbitrary sinewave on-screen; I wanted to use
beziers, but I'm not smart enough, and I also want the ability to expand
to other mathematical functions, so doing a connect-the-dots
implementation seemed the best way to go.
I'm having some trouble with the drawing, though, and I'm not sure if
it's my algorithm, or I'm doing something stupid, or there's a bug in
the drawing code. I'm generating a geometry, and then putting it in a
path with a stroke. Here's the method that generates the geometry:
protected override ComplexGeometry GenerateGeometry(int Resolution)
{
if (Data == null)
{
return new ComplexGeometry();
}
StreamGeometry real = new StreamGeometry(), complex = new
StreamGeometry();
double xmin = 0, xmax = 100, xwidth = xmax - xmin, ymin = 50, ymax =
100, ywidth = ymax - ymin;
Converter<double, double> canvasXToGraph = delegate(double canvas)
{
return ((canvas - xmin) / xwidth) * VisibleRegion.Width +
VisibleRegion.Left;
},
graphXToCanvas = delegate(double graph)
{
return (graph - VisibleRegion.Left) / VisibleRegion.Width * xwidth
+ xmin;
},
canvasYToGraph = delegate(double canvas)
{
return ((canvas - ymin) / ywidth) * VisibleRegion.Height +
VisibleRegion.Bottom;
},
graphYToCanvas = delegate(double graph)
{
return (graph - VisibleRegion.Bottom) / VisibleRegion.Height *
ywidth + ymin;
};
StreamGeometryContext realContext = real.Open(), complexContext =
complex.Open();
realContext.BeginFigure(new Point(graphXToCanvas(VisibleRegion.Left),
graphYToCanvas(RealAt(VisibleRegion.Left, Time))), false, false);
complexContext.BeginFigure(new
Point(graphXToCanvas(VisibleRegion.Left),
graphYToCanvas(ImaginaryAt(VisibleRegion.Left, Time))), false, false);
for (int i = 0; i < Resolution; i++)
{
double d = VisibleRegion.Left + VisibleRegion.Width * (double)i /
(double)Resolution;
realContext.LineTo(new Point(graphXToCanvas(d),
graphYToCanvas(Data.CalculateReal(d, Time))), true, true);
complexContext.LineTo(new Point(graphXToCanvas(d),
graphYToCanvas(Data.CalculateImaginary(d, Time))), true, true);
}
realContext.Close();
complexContext.Close();
return new ComplexGeometry(real, complex);
}
And the type used by the Data property:
public class SinusoidalWaveData : WaveData
{
private double wavenumber;
public double Wavenumber
{
get { return wavenumber; }
set { wavenumber = value; FirePropertyChanged("Wavenumber"); }
}
private double angularFrequency;
public double AngularFrequency
{
get { return angularFrequency; }
set { angularFrequency = value;
FirePropertyChanged("AngularFrequency"); }
}
private double amplitude;
public double Amplitude
{
get { return amplitude; }
set { amplitude = value; }
}
public override double CalculateImaginary(double position, double time)
{
return Amplitude * Math.Cos(Wavenumber * position - AngularFrequency
* time);
}
public override double CalculateReal(double position, double time)
{
return Amplitude * Math.Sin(Wavenumber * position - AngularFrequency
* time);
}
public override double CalculateRealSlope(double position, double time)
{
return Wavenumber * CalculateImaginary(position, time);
}
}
I hope that held up well...can anybody see any pertinent errors, or is
there a much better way to do this? I wouldn't mind something with a
little better performance, although so far this hasn't been too bad (I
have a feeling it won't scale well).
Nick


