Sure
Class:
class Plotter : protected virtual GraphicsTypes
{
public:
struct Style
{
Style() : mFill(false), mThickness(1.f) {}
Style(const IColor color, bool fill, float thickness = 1.f)
: mColor(color), mFill(fill), mThickness(thickness) {}
IColor mColor;
bool mFill = false;
float mThickness = 1.f;
};
Plotter(const IRECT& bounds) : mBounds(bounds) {}
void SetBounds(const IRECT& bounds) { mBounds = bounds; }
void Plot(IGraphics& g, const float* normY, unsigned long N, const Style& style);
void Plot(IGraphics& g, const float* normX, const float* normY, unsigned long N, const Style& style);
protected:
float XCalc(float xNorm) { return mBounds.L + (mBounds.W() * xNorm); }
float YCalc(float yNorm) { return mBounds.B - (mBounds.H() * yNorm); }
void Complete(IGraphics& g, const Style& s);
IRECT mBounds;
}
Implementation::
void Plotter::Plot(IGraphics& g, const float* normY, unsigned long N, const Style& style)
{
g.PathClear();
g.PathClipRegion(mBounds);
g.PathMoveTo(mBounds.L, YCalc(normY[0]));
for (unsigned long i = 1; i < N; i++)
g.PathLineTo(XCalc((1.f / (N - 1)) * i), YCalc(normY[i]));
Complete(g, style);
}
void Plotter::Plot(IGraphics& g, const float* normX, const float* normY, unsigned long N, const Style& style)
{
// The x values must be in order, so we can trim the ends
for (; N > 1 && normX[0] < 0.f && normX[1] < 0.f; N--)
{
normX++;
normY++;
}
for (; N > 1 && normX[N - 2] > 1.f && normX[N - 1] > 1.f; N--);
if (!N)
return;
g.PathClear();
g.PathClipRegion(mBounds);
// Draw
if (normX[0] >= 0.f)
{
g.PathMoveTo(mBounds.L, YCalc(normY[0]));
}
else
{
g.PathMoveTo(normX[0], YCalc(normY[0]));
normX++;
normY++;
N--;
}
for (unsigned long i = 0 ; i < N; i++)
g.PathLineTo(XCalc(normX[i]), YCalc(normY[i]));
if (N && normX[N - 1] < 1.f)
g.PathLineTo(mBounds.R, YCalc(normY[N - 1]));
Complete(g, style);
}
void Plotter::Complete(IGraphics& g, const Style& s)
{
if (s.mFill)
{
g.PathLineTo(mBounds.R, mBounds.B);
g.PathLineTo(mBounds.L, mBounds.B);
g.PathClose();
g.PathFill(s.mColor);
}
else
{
IStrokeOptions options;
options.mJoinOption = ELineJoin::Round;
g.PathStroke(s.mColor, s.mThickness, options);
}
g.PathClipRegion();
}