I’m trying to draw data from a Wacom pen.
If I make each Datapoint a circle (radius proportional to pressure), it looks okay if I write slowly, but if I write at normal speed it starts to look like a bunch of circles rather than a smooth line.
What I’ve done is consider the set of points as the backbone, and between points k and k+1 I first get the midpoint, then I extend a positive and negative perpendicular (the length depending on the pressure). And construct a path from these points.
It is really ugly, but it kind of works:
float x(int i) { return getWidth() * inkling.penData[i].x / 1920.f; };
float y(int i) { return getHeight() * inkling.penData[i].y / 1920.f; };
void drawStroke(Graphics& g, int start, int end) {
if (end-start < 5)
return;
using PointF = Point<float>;
std::vector<PointF> pts;
for(int i=start; i<=end; ++i)
pts.push_back(PointF(x(i),y(i)));
// smooth data
for(int i=1; i < pts.size(); ++i) pts[i] = 0.5f * pts[i-1] + .5f * pts[i];
for(int i=pts.size()-2; i >= 0; --i) pts[i] = 0.5f * pts[i+1] + .5f * pts[i];
std::vector<float> widths;
for(int i=start; i<=end; ++i)
widths.push_back(inkling.penData[i].pressure / 1023.f);
// smooth data
for(int i=1; i < widths.size(); ++i) widths[i] = 0.5f * widths[i-1] + .5f * widths[i];
for(int i=widths.size()-2; i >= 0; --i) widths[i] = 0.5f * widths[i+1] + .5f * widths[i];
std::vector<PointF> A, B;
for(int i=0; i < pts.size()-1; ++i) {
auto seg = pts[i+1] - pts[i];
auto normal = PointF(seg.y, -seg.x);
auto unitNormal = normal / normal.getDistanceFromOrigin();
auto center = pts[i] + .5f * seg;
float thickness = (widths[i] + widths[i+1]) / 2.f;
thickness *= 1.f;
A.push_back( center + thickness * unitNormal );
B.push_back( center - thickness * unitNormal );
}
Path path;
path.startNewSubPath(A[0]);
for(int i=1; i<A.size(); ++i)
path.lineTo(A[i]);
for(int i=B.size()-1; i>=0; --i)
path.lineTo(B[i]);
path.closeSubPath();
g.setColour(Colours::blue);
g.fillPath(path);
}
void paint (Graphics& g) override
{
g.setColour(Colour(255, 245, 200));
g.fillAll (Colour(255, 245, 200));
auto& D = inkling.penData;
int strokeStart = 0;
while(true) {
while(strokeStart < D.size() && D[strokeStart].pressure == 0.f)
++strokeStart;
if(strokeStart == D.size())
break;
int strokeEnd = strokeStart;
while(strokeEnd < D.size() && D[strokeEnd].pressure > 0.f)
++strokeEnd;
--strokeEnd;
drawStroke(g, strokeStart, strokeEnd);
strokeStart = strokeEnd+1;
};
…produces…
Can anyone see a better way of doing it?
My thinking is that it must be really inefficient, I bet it would be much more efficient to generate a triangle strip. But that could end up being pages of GL code.
I like the idea of drawing to an embedded HTML 5 canvas, then I can leverage D3 and friends, and get GL at a fraction of the code complexity. The main problem is: how to communicate between JavaScript and C++. Can anyone see a way through?
π