Creating a Cairo-Inspired Graphics Library for MetaTrader 5 (Part 6): Curves and Adaptive Flattening

Why this article was written
After Part 5 the renderer fills arbitrary outlines, anti-aliases their edges and punches real holes through them. What it still could not draw was a curve.
Every demo up to this point faked them. A "circle" was a polygon the demo computed itself, with a segment count picked by hand. That works until the shape changes size — too few segments and the curve shows facets, too many and you pay for vertices nobody can see.
Part 6 adds CurveTo, and with it the question that really matters: how
many straight segments, and where?
What is inside
The answer is not a fixed number. It is De Casteljau subdivision — the method Paul de Casteljau worked out at Citroën in 1959 — which splits a curve in half using nothing but midpoint averages. Six additions and a halving give you both halves exactly. No trigonometry, no polynomial evaluation.
Knowing when to stop. A new Path.mqh helper, DistanceToChord, measures
how far the two control points sit from the straight line between the endpoints.
If that total is within tolerance, the curve is already flat enough and the
chord is emitted. Otherwise it splits and asks each half the same question.
What adaptive actually buys you. Over one test path — a 280 px sweep, a 110 px bend and a 40 px corner — adaptive flattening at 0.30 px produced 46, 27 and 15 vertices. A fixed loop of 32 segments produced 33 for each: too many for the gentle sweep, too few for the hairpin. The article is careful to say this is not a universal theorem, and shows where the comparison is fair.
Flatten now, or store the curve? Both options are laid out honestly. The
library flattens immediately into the same vertex array MoveTo and LineTo
already use, so BuildEdges and the rasterizer need no curve awareness at
all. The cost is stated plainly: a path is fixed at the resolution it was built
for, which rules out transforms, dashed strokes, hit testing and morphing on the
original analytic curve.
One dial, and the dial it is not. Config.mqh gains
CairoFlattenTolerance = 0.30, a runtime global rather than a #define. A
comparison table separates it from CairoAaSamples: one controls geometric
fidelity, the other paint smoothness. Turning up anti-aliasing will never
fix a faceted curve, and the article explains why.
Two verbs, one of them free. CurveTo takes a cubic. QuadTo takes a
quadratic — the form TrueType outlines use — and converts it to a cubic by
degree elevation, which is exact, not an approximation.
An honest note on scope
Circles and arcs are not in this part. A circle is not a Bézier curve, and approximating one with the usual 0.5523 constant is a compromise this series declines to make. Part 7 derives the arc properly.
State of the library after this part:
Color.mqh Part 1 the ARGB color type
Part 4 + CairoBlendOver
Config.mqh Part 4 CairoAaSamples - the AA quality dial
Part 6 + CairoFlattenTolerance <- today
Surface.mqh Part 1 a pixel buffer bound to one chart object
Path.mqh Part 2 points, contours, the pen
Part 3 + BuildEdges
Part 5 + AddThickLine
Part 6 + CurveTo, QuadTo, the flattener <- today
Raster.mqh Part 3/4 the scanline fill, with coverage
Part 5 + the fill ruleRaster.mqh is untouched. Curves never reach it — by the time it runs, there
are only line segments.
More in this series
Building a real 2D renderer for MetaTrader 5 in pure MQL5 — anti-aliased shapes, transparency and gradients, with no DLLs and no external tools. About this series
- Part 1Why the terminal needs its own 2D-rendererSeptember 3, 2026
- Part 2Points, Contours and the PathSeptember 10, 2026
- Part 3Edges and the First Filled ShapeSeptember 28, 2026
- Part 4Anti-Aliasing, Coverage and CompositingSeptember 28, 2026
- Part 5Fill Rules, Holes and BordersOctober 1, 2026
- Part 6Curves and Adaptive Flattening — you are hereOctober 23, 2026
- Part 7Arcs, Circles and Rounded RectanglesOctober 27, 2026
Continue reading on MQL5.com
The full article with complete source code is published on MQL5.com.


