From 94161b4924bf6f2effe5d4fd667c1d1ff4c88d8b Mon Sep 17 00:00:00 2001 From: "V. Khmelevskiy" Date: Fri, 20 Apr 2018 13:31:31 +0700 Subject: [PATCH] basic bezier curves with sample --- examples/bezier/.gitignore | 14 ++ examples/bezier/bezier_sample.png | Bin 0 -> 55474 bytes examples/bezier/dub.json | 32 +++++ examples/bezier/source/app.d | 230 ++++++++++++++++++++++++++++++ src/dlangui/core/math3d.d | 140 ++++++++++++++++++ 5 files changed, 416 insertions(+) create mode 100644 examples/bezier/.gitignore create mode 100644 examples/bezier/bezier_sample.png create mode 100644 examples/bezier/dub.json create mode 100644 examples/bezier/source/app.d diff --git a/examples/bezier/.gitignore b/examples/bezier/.gitignore new file mode 100644 index 00000000..7c617ab4 --- /dev/null +++ b/examples/bezier/.gitignore @@ -0,0 +1,14 @@ +.dub +docs.json +__dummy.html +docs/ +bezier.so +bezier.dylib +bezier.dll +bezier.a +bezier.lib +bezier-test-* +*.exe +*.o +*.obj +*.lst diff --git a/examples/bezier/bezier_sample.png b/examples/bezier/bezier_sample.png new file mode 100644 index 0000000000000000000000000000000000000000..2e4a6c0b4358860c2ace59c77d3d7432bbce8a1c GIT binary patch literal 55474 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 literal 0 HcmV?d00001 diff --git a/examples/bezier/dub.json b/examples/bezier/dub.json new file mode 100644 index 00000000..2488598f --- /dev/null +++ b/examples/bezier/dub.json @@ -0,0 +1,32 @@ +{ + "name": "bezier", + "description": "dlangui bezier curves samples", + "license": "Boost", + + "targetPath": "bin", + "targetType": "executable", + "targetName": "bezier", + + "sourceFiles-windows-x86-dmd": ["$PACKAGE_DIR/../../src/win_app.def"], + + "dependencies": { + "dlangui": {"path": "../../"} + }, + "configurations" : [ + { + "name" : "default" + }, + { + "name" : "sdl", + "subConfigurations" : { + "dlangui" : "sdl" + } + }, + { + "name" : "x11", + "subConfigurations" : { + "dlangui" : "x11" + } + } + ] +} diff --git a/examples/bezier/source/app.d b/examples/bezier/source/app.d new file mode 100644 index 00000000..760716cc --- /dev/null +++ b/examples/bezier/source/app.d @@ -0,0 +1,230 @@ +module app; + +static assert(ENABLE_OPENGL, "All bezier samples in this module using + floating point drawing functions which is not supported in minimal config"); + +import dlangui; + +import std.algorithm.comparison; + +mixin APP_ENTRY_POINT; + +// helper for scaling relative to average 96dpi FullHD, IDK but maybe a bad idea after all +T scaledByDPI(T)(T val) { + return val *= (SCREEN_DPI()/cast(T)96); +} + +/// Entry point for dlangui based application +extern (C) int UIAppMain(string[] args) { + // portrait "mode" window + Window window = Platform.instance.createWindow("Bezier curves", null, WindowFlag.Resizable, 480.scaledByDPI, 600.scaledByDPI); + window.mainWidget = new BezierSamples(); + window.show(); + return Platform.instance.enterMessageLoop(); +} + +class BezierSamples : VerticalLayout { + + this() { + this(null); + } + this(string id) { + super(id); + addChild(new CubicTraceSample); + addChild(new FlattenCubicSample); + addChild(new ColoredCubicTraceSample); + addChild(new FlattenCubicGuidesSample); + addChild(new FlattenQuadraticSample); + } + + override bool animating() { return true; } +} + + +abstract class SampleCanvas : CanvasWidget { + + dstring _sampleName = "Bezier sample"; + static immutable vec2[] _controlPointsDefaultRatios = [vec2(0,0.2), vec2(0.2,0.2), vec2(0.8,0.8), vec2(1,0.8)]; + static immutable vec2[] _controlPointsQuadratic = [vec2(0.2,0.8), vec2(0.7,0.4), vec2(0.3,0.2)]; + + this() { + fillHorizontal(); + auto p = 5.scaledByDPI; + margins(Rect(p, p, p, p)); + p = 15.scaledByDPI; + padding(Rect(p, p, p, p)); + minHeight = 250.scaledByDPI; + } + + dstring sampleName() { return _sampleName; } + + override protected void measuredContent(int parentWidth, int parentHeight, int contentWidth, int contentHeight) { + _measuredWidth = max(minHeight, contentWidth); + _measuredHeight = minHeight; + } + + protected void drawText(DrawBuf buf, Rect rc, dstring text) { + FontRef font = font(); + Point sz = font.textSize(text); + applyAlign(rc, sz, Align.HCenter, Align.Bottom ); + font.drawText(buf, rc.left, rc.top, text, textColor, 4, 0, textFlags); + } + + protected void calcRectSize(const vec2[] controlPoints, vec2[] result) { + assert(result.length >= controlPoints.length); + auto r = _pos; + applyMargins(r); + applyPadding(r); + vec2 pos = vec2(r.left, r.top); + vec2 size = vec2(r.width, r.height); + result[] = controlPoints[]; // copy points + result[0] = result[0].mul(size) + pos; + result[1] = result[1].mul(size) + pos; + result[2] = result[2].mul(size) + pos; + if(controlPoints.length > 3) + result[3] = result[3].mul(size) + pos; + } + + // override to draw + override void doDraw(DrawBuf buf, Rect rc) { + } +} + +class CubicTraceSample : SampleCanvas { + this() { + _sampleName = "Cubic bezier curve drawn with ellipses (slow, high overdraw)"; + } + override void doDraw(DrawBuf buf, Rect rc) { + vec2[4] points; + calcRectSize(_controlPointsDefaultRatios, points); + auto len = (points[0]-points[3]).magnitude; + auto interval = 1f/len; + auto step = interval; + // evaluate normal bezier curve and trace with circles + foreach ( i ; 0..len ) { + auto b = bezierCubic(points, interval); + interval+=step; + buf.drawEllipseF(b.x, b.y, 3, 3, 0, Color.black, Color.black); + } + drawText(buf,rc, sampleName()); + } +} + +class FlattenCubicSample : SampleCanvas { + this() { + _sampleName = "Flattened cubic bezier curve drawn with lines (fast)"; + } + override void doDraw(DrawBuf buf, Rect rc) { + vec2[4] points; + calcRectSize(_controlPointsDefaultRatios, points); + enum segments = 10; + auto lines = flattenBezierCubic(points, segments); + buf.polyLineF(lines, 3f.scaledByDPI, Color.black); + drawText(buf,rc, sampleName()); + } +} + +class ColoredCubicTraceSample : SampleCanvas { + this() { + _sampleName = "Simple colored cubic bezier curve drawn with ellipsises"; + } + override void doDraw(DrawBuf buf, Rect rc) { + vec2[4] points; + calcRectSize(_controlPointsDefaultRatios, points); + auto len = (points[0]-points[3]).magnitude; + auto interval = 1/len; + auto step = interval; + // evaluate normal bezier curve and trace with circles + foreach ( i ; 0..len ) { + auto b = bezierCubic(points, interval); + interval+=step; + buf.drawEllipseF(b.x, b.y, 3, 3, 0, COLOR_TRANSPARENT, lerpColor01(Color.blue, Color.red, interval)); + } + drawText(buf,rc, sampleName()); + } + + // clamp to [0,1] and lerp color + static uint lerpColor01(uint a, uint b, float ratio) { + ratio = clamp(ratio, 0, 1); + return blendARGB(b, a, cast(uint)(ratio * 255)); + } +} + +class FlattenCubicGuidesSample : SampleCanvas { + this() { + _sampleName = "Flattened cubic bezier curve with direction and normal vectors"; + } + override void doDraw(DrawBuf buf, Rect rc) { + vec2[4] points; + calcRectSize(_controlPointsDefaultRatios, points); + enum segmentsCount = 10; + auto lines = flattenBezierCubic(points, segmentsCount); + drawCubicControlsGuides(buf, points); + buf.polyLineF(lines, 3f.scaledByDPI, Color.black); + drawCubicSegmentsNormDir(buf, points, segmentsCount, lines, true, true); + drawText(buf,rc, sampleName()); + } +} + +class FlattenQuadraticSample : SampleCanvas { + this() { + _sampleName = "Flattened quadratic bezier curve"; + } + override void doDraw(DrawBuf buf, Rect rc) { + vec2[3] points; + calcRectSize(_controlPointsQuadratic, points); + + // guide lines + buf.drawLineF(points[0], points[1], 1, Color.dark_gray); + buf.drawLineF(points[1], points[2], 1, Color.dark_gray); + + + enum segmentCount = 10; + auto lines = flattenBezierQuadratic(points, segmentCount); + buf.polyLineF(lines, 3f.scaledByDPI, Color.black); + + // end points + buf.drawEllipseF(points[0].x, points[0].y, 5,5, 1, Color.antique_white, Color.cyan); + buf.drawEllipseF(points[2].x, points[2].y, 5,5, 1, Color.antique_white, Color.cyan); + buf.drawEllipseF(points[1].x, points[1].y, 3,3, 0, Color.antique_white, Color.cyan); + + // draw the direction & normal vectors + auto segStep = 1f/segmentCount; + foreach(i; 0..segmentCount) { + auto dir = bezierQuadraticDirection(points, i*segStep); + auto norm = dir.rotated90ccw; + auto point = lines[i]; + buf.drawLineF(point, point + (dir * 15f), 3.scaledByDPI, Color.yellow ); + buf.drawLineF(point, point + (norm * 15f), 3.scaledByDPI, Color.red ); + } + + drawText(buf,rc, sampleName()); + } +} + + + +void drawCubicControlsGuides(DrawBuf buf, vec2[] controls) { + buf.drawLineF(controls[0], controls[1], 1, Color.black); + buf.drawLineF(controls[2], controls[3], 1, Color.black); + buf.drawEllipseF(controls[0].x, controls[0].y, 5,5, 1, Color.antique_white, Color.cyan); + buf.drawEllipseF(controls[3].x, controls[3].y, 5,5, 1, Color.antique_white, Color.cyan); + buf.drawEllipseF(controls[1].x, controls[1].y, 3,3, 0, Color.antique_white, Color.cyan); + buf.drawEllipseF(controls[2].x, controls[2].y, 3,3, 0, Color.antique_white, Color.cyan); +} + +void drawCubicSegmentsNormDir(DrawBuf buf, vec2[] controls, int segmentCount, vec2[] segments, bool direction, bool normals) { + if ( !direction && !normals && segments.length < 2) + return; + // draw the direction & normal vectors + auto segStep = 1f/segmentCount; + foreach(i; 0..segmentCount) { + auto dir = bezierCubicDirection(controls, i*segStep); + auto norm = dir.rotated90ccw; + auto point = segments[i]; + if ( direction ) + buf.drawLineF(point, point + (dir * 15f), 3.scaledByDPI, Color.yellow ); + if ( normals ) + buf.drawLineF(point, point + (norm * 15f), 3.scaledByDPI, Color.red ); + } +} \ No newline at end of file diff --git a/src/dlangui/core/math3d.d b/src/dlangui/core/math3d.d index 424212f7..19f627af 100644 --- a/src/dlangui/core/math3d.d +++ b/src/dlangui/core/math3d.d @@ -1740,3 +1740,143 @@ vec3 triangleNormal(float[3] p1, float[3] p2, float[3] p3) { /// Alias for 2d float point alias PointF = vec2; + + + + +// this form can be used within shaders +/// cubic bezier curve +PointF bezierCubic(const PointF[] cp, float t) pure @nogc @safe + in { assert(cp.length > 3); } do +{ + // control points + auto p0 = cp[0]; + auto p1 = cp[1]; + auto p2 = cp[2]; + auto p3 = cp[3]; + + float u1 = (1.0 - t); + float u2 = t * t; + // the polynomials + float b3 = u2 * t; + float b2 = 3.0 * u2 * u1; + float b1 = 3.0 * t * u1 * u1; + float b0 = u1 * u1 * u1; + // cubic bezier interpolation + PointF p = p0 * b0 + p1 * b1 + p2 * b2 + p3 * b3; + return p; +} + +/// quadratic bezier curve (not tested) +PointF bezierQuadratic(const PointF[] cp, float t) pure @nogc @safe + in { assert(cp.length > 2); } do +{ + auto p0 = cp[0]; + auto p1 = cp[1]; + auto p2 = cp[2]; + + float u1 = (1.0 - t); + float u2 = u1 * u1; + + float b2 = t * t; + float b1 = 2.0 * u1 * t; + float b0 = u2; + + PointF p = p0 * b0 + p1 * b1 + p2 * b2; + return p; +} + +/// cubic bezier (first) derivative +PointF bezierCubicDerivative(const PointF[] cp, float t) pure @nogc @safe + in { assert(cp.length > 3); } do +{ + auto p0 = cp[0]; + auto p1 = cp[1]; + auto p2 = cp[2]; + auto p3 = cp[3]; + + float u1 = (1.0 - t); + float u2 = t * t; + float u3 = 6*(u1)*t; + float d0 = 3 * u1 * u1; + // -3*P0*(1-t)^2 + P1*(3*(1-t)^2 - 6*(1-t)*t) + P2*(6*(1-t)*t - 3*t^2) + 3*P3*t^2 + PointF d = p0*(-d0) + p1*(d0 - u3) + p2*(u3 - 3*u2) + (p3*3)*u2; + return d; +} + +/// quadratic bezier (first) derivative +PointF bezierQuadraticDerivative(const PointF[] cp, float t) pure @nogc @safe + in { assert(cp.length > 2); } do +{ + auto p0 = cp[0]; + auto p1 = cp[1]; + auto p2 = cp[2]; + + float u1 = (1.0 - t); + // -2*(1-t)*(p1-p0) + 2*t*(p2-p1); + PointF d = (p0-p1)*-2*u1 + (p2-p1)*2*t; + return d; +} + +// can't be pure due to normalize & vec2 ctor +/// evaluates cubic bezier direction(tangent) at point t +PointF bezierCubicDirection(const PointF[] cp, float t) { + auto d = bezierCubicDerivative(cp,t); + d.normalize(); + return PointF(tan(d.x), tan(d.y)); +} + +/// evaluates quadratic bezier direction(tangent) at point t +PointF bezierQuadraticDirection(const PointF[] cp, float t) { + auto d = bezierQuadraticDerivative(cp,t); + d.normalize(); + return PointF(tan(d.x), tan(d.y)); +} + +/// templated version of bezier flatten curve function, allocates temporary buffer +PointF[] flattenBezier(alias BezierFunc)(const PointF[] cp, int segmentCountInclusive) + if (is(typeof(BezierFunc)==function)) +{ + if (segmentCountInclusive < 2) + return PointF[].init; + PointF[] coords = new PointF[segmentCountInclusive+1]; + flattenBezier!BezierFunc(cp, segmentCountInclusive, coords); + return coords; +} + +/// flatten bezier curve function, writes to provided buffer instead of allocation +void flattenBezier(alias BezierFunc)(const PointF[] cp, int segmentCountInclusive, PointF[] outSegments) + if (is(typeof(BezierFunc)==function)) +{ + if (segmentCountInclusive < 2) + return; + float step = 1f/segmentCountInclusive; + outSegments[0] = BezierFunc(cp, 0); + foreach(i; 1..segmentCountInclusive) { + outSegments[i] = BezierFunc(cp, i*step); + } + outSegments[segmentCountInclusive] = BezierFunc(cp, 1f); +} + + +/// flattens cubic bezier curve, returns PointF[segmentCount+1] array or empty array if <1 segments +PointF[] flattenBezierCubic(const PointF[] cp, int segmentCount) { + return flattenBezier!bezierCubic(cp,segmentCount); +} + +/// flattens quadratic bezier curve, returns PointF[segmentCount+1] array or empty array if <1 segments +PointF[] flattenBezierQuadratic(const PointF[] cp, int segmentCount) { + return flattenBezier!bezierQuadratic(cp, segmentCount); +} + +/// calculates normal vector at point t using direction +PointF bezierCubicNormal(const PointF[] cp, float t) { + auto d = bezierCubicDirection(cp, t); + return d.rotated90ccw; +} + +/// calculates normal vector at point t using direction +PointF bezierQuadraticNormal(const PointF[] cp, float t) { + auto d = bezierQuadraticDerivative(cp, t); + return d.rotated90ccw; +}