common shape: added arc implementation 89/239989/4
authorMichal Szczecinski <m.szczecinsk@partner.samsung.com>
Fri, 31 Jul 2020 09:07:53 +0000 (11:07 +0200)
committerMichal Szczecinski <m.szczecinsk@partner.samsung.com>
Mon, 3 Aug 2020 06:31:54 +0000 (08:31 +0200)
Change-Id: Ib483e24d8e358b2860ca8d46e8b88d58d12bdb62

.gitignore
inc/thorvg.h
inc/thorvg_capi.h
src/bindings/capi/tvgCapi.cpp
src/lib/tvgShape.cpp
test/makefile
test/testArc.cpp [new file with mode: 0644]

index b796b5e..7f4d09b 100644 (file)
@@ -21,3 +21,4 @@ testGradientTransform
 testSvg
 testAsync
 testCapi
+testArc
index c738a0e..50b03fd 100644 (file)
@@ -237,6 +237,7 @@ public:
     //Shape
     Result appendRect(float x, float y, float w, float h, float rx, float ry) noexcept;
     Result appendCircle(float cx, float cy, float rx, float ry) noexcept;
+    Result appendArc(float x, float y, float w, float h, float startAngle, float sweep) noexcept;
     Result appendPath(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept;
 
     //Stroke
index 9fa4d59..695b994 100644 (file)
@@ -133,6 +133,7 @@ TVG_EXPORT Tvg_Result tvg_shape_cubic_to(Tvg_Paint* paint, float cx1, float cy1,
 TVG_EXPORT Tvg_Result tvg_shape_close(Tvg_Paint* paint);
 TVG_EXPORT Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry);
 TVG_EXPORT Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry);
+TVG_EXPORT Tvg_Result tvg_shape_append_arc(Tvg_Paint* paint, float x, float y, float w, float h, float startAngle, float sweep);
 TVG_EXPORT Tvg_Result tvg_shape_append_path(Tvg_Paint* paint, const Tvg_Path_Command* cmds, uint32_t cmdCnt, const Tvg_Point* pts, uint32_t ptsCnt);
 TVG_EXPORT Tvg_Result tvg_shape_set_stroke_width(Tvg_Paint* paint, float width);
 TVG_EXPORT Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
index 9ceb113..8ddbc07 100644 (file)
@@ -178,6 +178,10 @@ TVG_EXPORT Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y,
     return (Tvg_Result) reinterpret_cast<Shape*>(paint)->appendRect(x, y, w, h, rx, ry);
 }
 
+TVG_EXPORT Tvg_Result tvg_shape_append_arc(Tvg_Paint* paint, float x, float y, float w, float h, float startAngle, float sweep)
+{
+    return (Tvg_Result) reinterpret_cast<Shape*>(paint)->appendArc(x, y, w ,h, startAngle, sweep);
+}
 
 TVG_EXPORT Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry)
 {
@@ -248,4 +252,4 @@ TVG_EXPORT Tvg_Result tvg_shape_transform(Tvg_Paint* paint, const Tvg_Matrix* m)
 
 #ifdef __cplusplus
 }
-#endif
\ No newline at end of file
+#endif
index 59718d8..57c097a 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef _TVG_SHAPE_CPP_
 #define _TVG_SHAPE_CPP_
 
+#include <limits>
+
 #include "tvgShapeImpl.h"
 
 /************************************************************************/
@@ -148,6 +150,76 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
     return Result::Success;
 }
 
+Result Shape::appendArc(float x, float y, float w, float h, float startAngle, float sweep) noexcept
+{
+    const float M_PI_HALF = M_PI / 2.0;
+    const float radius = w / 2;
+
+    Point center = {x + radius, y + radius};
+
+    auto impl = pImpl.get();
+
+    startAngle = (startAngle * M_PI) / 180;
+    sweep = sweep * M_PI / 180;
+
+    auto  nCurves = ceil(sweep / M_PI_HALF);
+    auto fract = fmod(sweep, M_PI_HALF);
+    fract = (fract < std::numeric_limits<float>::epsilon()) ? M_PI_HALF : fract;
+
+    for (int i = 0; i < nCurves; ++i) {
+        //bezier parameters
+        Point start = {0, 0};
+        Point end  = {0, 0};
+        Point bControl1 = {0, 0};
+        Point bControl2 = {0, 0};
+
+        //variables needed to calculate bezier control points
+        auto ax = 0.0f, ay = 0.0f, bx = 0.0f, by = 0.0f, q1 = 0.0f, q2 = 0.0f, k2 = 0.0f;
+        auto endAngle = startAngle + ((i != nCurves - 1) ? M_PI_HALF : fract);
+
+        //get bezier start and end point
+        start.x = radius * cos(startAngle);
+        start.y = radius * sin(startAngle);
+        end.x = radius * cos(endAngle);
+        end.y = radius * sin(endAngle);
+
+        //get bezier control points using article:
+        //(http://itc.ktu.lt/index.php/ITC/article/view/11812/6479)
+        ax = start.x;
+        ay = start.y;
+        bx = end.x;
+        by = end.y;
+
+        q1 = ax * ax + ay * ay;
+        q2 = ax * bx + ay * by + q1;
+
+        k2 = static_cast<float> (4.0/3.0) * ((sqrt(2 * q1 * q2) - q2) / (ax * by - ay * bx));
+
+        bControl1.x = ax - k2 * ay;
+        bControl1.y = ay + k2 * ax;
+        bControl2.x = bx + k2 * by;
+        bControl2.y = by - k2 * bx;
+
+        //move points to proper arc center
+        start.x += center.x;
+        start.y += center.y;
+        end.x += center.x;
+        end.y += center.y;
+        bControl1.x += center.x;
+        bControl1.y += center.y;
+        bControl2.x += center.x;
+        bControl2.y += center.y;
+
+        impl->path->moveTo(start.x, start.y);
+        impl->path->cubicTo(bControl1.x, bControl1.y, bControl2.x, bControl2.y, end.x, end.y);
+
+        startAngle = endAngle;
+    }
+
+    IMPL->flag |= RenderUpdateFlag::Path;
+    return Result::Success;
+}
+
 
 Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept
 {
index e1a8afb..29e6e77 100644 (file)
@@ -18,4 +18,5 @@ all:
        gcc -o testGradientTransform testGradientTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
        gcc -o testSvg testSvg.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
        gcc -o testAsync testAsync.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
+       gcc -o testArc testArc.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
        gcc -o testCapi testCapi.c -g `pkg-config --cflags --libs elementary thorvg`
diff --git a/test/testArc.cpp b/test/testArc.cpp
new file mode 100644 (file)
index 0000000..3878aff
--- /dev/null
@@ -0,0 +1,142 @@
+#include "testCommon.h"
+
+/************************************************************************/
+/* Drawing Commands                                                     */
+/************************************************************************/
+
+void tvgDrawCmds(tvg::Canvas* canvas)
+{
+    if (!canvas) return;
+
+    //draw reference rectangles
+    auto shape2 = tvg::Shape::gen();
+    shape2->appendRect(0, 0, 200, 200, 0, 0);
+    shape2->stroke(255, 0, 0, 255);
+    shape2->stroke(2);
+
+    auto shape3 = tvg::Shape::gen();
+    shape3->moveTo(0, 100);
+    shape3->lineTo(200, 100);
+    shape3->stroke(255, 0, 0, 255);
+    shape3->stroke(2);
+
+    auto shape4 = tvg::Shape::gen();
+    shape4->moveTo(100, 0);
+    shape4->lineTo(100, 200);
+    shape4->stroke(255, 0, 0, 255);
+    shape4->stroke(2);
+
+    //test arc
+    auto shape1 = tvg::Shape::gen();
+    shape1->appendArc(0, 0, 200, 200, 10, 270);
+    shape1->stroke(255, 255, 255, 255);
+    shape1->stroke(3);
+
+    //Appends Paths
+    if (canvas->push(move(shape2)) != tvg::Result::Success) return;
+    if (canvas->push(move(shape3)) != tvg::Result::Success) return;
+    if (canvas->push(move(shape4)) != tvg::Result::Success) return;
+    if (canvas->push(move(shape1)) != tvg::Result::Success) return;
+}
+
+/************************************************************************/
+/* Sw Engine Test Code                                                  */
+/************************************************************************/
+
+static unique_ptr<tvg::SwCanvas> swCanvas;
+
+void tvgSwTest(uint32_t* buffer)
+{
+    //Create a Canvas
+    swCanvas = tvg::SwCanvas::gen();
+    swCanvas->target(buffer, WIDTH, WIDTH, HEIGHT);
+
+    /* Push the shape into the Canvas drawing list
+       When this shape is into the canvas list, the shape could update & prepare
+       internal data asynchronously for coming rendering.
+       Canvas keeps this shape node unless user call canvas->clear() */
+    tvgDrawCmds(swCanvas.get());
+}
+
+void drawSwView(void* data, Eo* obj)
+{
+    if (swCanvas->draw() == tvg::Result::Success) {
+        swCanvas->sync();
+    }
+}
+
+
+/************************************************************************/
+/* GL Engine Test Code                                                  */
+/************************************************************************/
+
+static unique_ptr<tvg::GlCanvas> glCanvas;
+
+void initGLview(Evas_Object *obj)
+{
+    static constexpr auto BPP = 4;
+
+    //Create a Canvas
+    glCanvas = tvg::GlCanvas::gen();
+    glCanvas->target(nullptr, WIDTH * BPP, WIDTH, HEIGHT);
+
+    /* Push the shape into the Canvas drawing list
+       When this shape is into the canvas list, the shape could update & prepare
+       internal data asynchronously for coming rendering.
+       Canvas keeps this shape node unless user call canvas->clear() */
+    tvgDrawCmds(glCanvas.get());
+}
+
+void drawGLview(Evas_Object *obj)
+{
+    auto gl = elm_glview_gl_api_get(obj);
+    gl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+    gl->glClear(GL_COLOR_BUFFER_BIT);
+
+    if (glCanvas->draw() == tvg::Result::Success) {
+        glCanvas->sync();
+    }
+}
+
+
+/************************************************************************/
+/* Main Code                                                            */
+/************************************************************************/
+
+int main(int argc, char **argv)
+{
+    tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
+
+    if (argc > 1) {
+        if (!strcmp(argv[1], "gl")) tvgEngine = tvg::CanvasEngine::Gl;
+    }
+
+    //Initialize ThorVG Engine
+    if (tvgEngine == tvg::CanvasEngine::Sw) {
+        cout << "tvg engine: software" << endl;
+    } else {
+        cout << "tvg engine: opengl" << endl;
+    }
+
+    //Initialize ThorVG Engine
+    if (tvg::Initializer::init(tvgEngine) == tvg::Result::Success) {
+
+        elm_init(argc, argv);
+
+        if (tvgEngine == tvg::CanvasEngine::Sw) {
+            createSwView();
+        } else {
+            createGlView();
+        }
+
+        elm_run();
+        elm_shutdown();
+
+        //Terminate ThorVG Engine
+        tvg::Initializer::term(tvgEngine);
+
+    } else {
+        cout << "engine is not supported" << endl;
+    }
+    return 0;
+}