High-quality polyline rendering for openFrameworks with variable width, color, and smooth joints. Based on the VASE renderer algorithm by TSANG, Hao Fung for high-quality polyline tessellation.
- Variable width - Each vertex can have a different stroke width
- Variable color - Per-vertex colors with smooth interpolation
- Joint styles - Round, Bevel, and Miter (with configurable limit)
- Cap styles - Round, Square, and Butt
- Catmull-Rom smoothing - Optional spline interpolation for silky curves
- Cross-platform - Uses openFrameworks' native mesh rendering
ofxVase tessellates polylines into triangle meshes with proper geometry for:
- Variable-width strokes
- Rounded joints at turns (prevents gaps and overlaps)
- Semicircular end caps
- Per-vertex color interpolation
All rendering uses standard ofMesh, so it works everywhere openFrameworks does.
#include "ofxVase.h"
void ofApp::draw() {
// Draw an ofPolyline with uniform width
ofPolyline line;
line.addVertex(100, 100);
line.addVertex(200, 150);
line.addVertex(300, 100);
ofSetColor(255, 100, 100);
ofxVase::draw(line, 10.0f); // 10px width
}void ofApp::setup() {
ofxVase::setJointStyle(ofxVase::JointStyle::Round);
ofxVase::setCapStyle(ofxVase::CapStyle::Round);
}void ofApp::draw() {
std::vector<glm::vec2> points = {
{100, 100}, {200, 200}, {300, 150}, {400, 200}
};
std::vector<ofFloatColor> colors = {
ofFloatColor::red,
ofFloatColor::yellow,
ofFloatColor::green,
ofFloatColor::blue
};
std::vector<float> widths = {5, 20, 10, 30};
ofxVase::Options opts;
opts.joint = ofxVase::JointStyle::Round;
opts.cap = ofxVase::CapStyle::Round;
ofxVase::Polyline poly(points, colors, widths, opts);
ofEnableAlphaBlending();
ofDisableDepthTest(); // Important for proper alpha blending
poly.holder.toMesh().draw();
ofEnableDepthTest();
}class ofApp : public ofBaseApp {
ofxVase::Renderer renderer;
void setup() {
renderer.setup();
}
void draw() {
ofxVase::Polyline poly(points, colors, widths, opts);
renderer.begin(); // Enables alpha blending and depth test settings
renderer.draw(poly);
renderer.end();
}
};Enable opts.smoothing = N to subdivide each segment N times using Catmull-Rom spline interpolation. This creates smooth curves from fewer control points:
opts.smoothing = 4; // 4 subdivisions per segment = silky smooth curves0= no smoothing (default, straight lines between points)1-2= subtle smoothing3-5= smooth curves6+= very smooth (but more vertices)
All three joint styles are fully implemented:
- Round - Smooth circular arc at turns (default)
- Bevel - Simple flat cut at turns
- Miter - Sharp pointed corners with configurable limit (
opts.miterLimit, defaults to 4.0)
opts.joint = ofxVase::JointStyle::Round; // or Bevel, or Miter
opts.miterLimit = 4.0f; // Only affects Miter joints- Round - Semicircular end caps (default)
- Square - Extended rectangular caps
- Butt - Flat caps with no extension
opts.cap = ofxVase::CapStyle::Round; // or Square, or Butt- Shader-based anti-aliasing - The original VASE renderer uses fragment shaders for feathered edges. We have the shaders in
src/shaders/but currently use mesh rendering for simplicity. Future: implement VBO path with shader-based AA. - Feathering - Soft anti-aliased edges without MSAA (requires shader implementation)
- Performance optimization - Batch rendering, VBO caching for static polylines
- Dashed/dotted lines - Pattern support
- Gradient strokes - Texture coordinate generation for shader-based gradients
BSD 3-Clause License - See LICENSE.txt
