From fdbf42f478137cf4c4caf4b4345cc7b2592d525c Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 17 May 2020 16:50:19 +0900 Subject: [PATCH] common: support Scene Transform this contains testSceneTransform example Change-Id: I460b05dc8bc4a842e26e950c800c5c35f8d3da7f --- .gitignore | 1 + src/lib/gl_engine/tvgGlRenderer.cpp | 2 +- src/lib/gl_engine/tvgGlRenderer.h | 2 +- src/lib/sw_engine/tvgSwCommon.h | 2 +- src/lib/sw_engine/tvgSwRenderer.cpp | 2 +- src/lib/sw_engine/tvgSwRenderer.h | 2 +- src/lib/sw_engine/tvgSwShape.cpp | 2 +- src/lib/tvgCanvasImpl.h | 4 +- src/lib/tvgRenderCommon.h | 139 +++++++++++++---------- src/lib/tvgScene.cpp | 17 ++- src/lib/tvgSceneImpl.h | 81 +++++++++++++- src/lib/tvgShapeImpl.h | 13 ++- test/makefile | 1 + test/testSceneTransform.cpp | 168 ++++++++++++++++++++++++++++ 14 files changed, 358 insertions(+), 78 deletions(-) create mode 100644 test/testSceneTransform.cpp diff --git a/.gitignore b/.gitignore index 29175734..6210eb83 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ testUpdate testDirectUpdate testScene testTransform +testSceneTransform diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 792e5241..48a0ee56 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -65,7 +65,7 @@ bool GlRenderer::dispose(const Shape& shape, void *data) } -void* GlRenderer::prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) +void* GlRenderer::prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) { //prepare shape data GlShape* sdata = static_cast(data); diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index 4d6ba793..557f9e70 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -23,7 +23,7 @@ namespace tvg class GlRenderer : public RenderMethod { public: - void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override; + void* prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool render(const Shape& shape, void *data) override; bool clear() override; diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 62a6b297..3068b9bb 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -96,7 +96,7 @@ void shapeReset(SwShape& sdata); bool shapeGenOutline(const Shape& shape, SwShape& sdata); void shapeDelOutline(SwShape& sdata); bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip); -void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform); +void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderMatrix& transform); SwRleData* rleRender(const SwShape& sdata, const SwSize& clip); bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a); diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 36e367b2..2d974450 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -81,7 +81,7 @@ bool SwRenderer::dispose(const Shape& shape, void *data) return true; } -void* SwRenderer::prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) +void* SwRenderer::prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) { //prepare shape data SwShape* sdata = static_cast(data); diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 3dfb076c..4c0a7baa 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -22,7 +22,7 @@ class SwRenderer : public RenderMethod public: Surface surface; - void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override; + void* prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool render(const Shape& shape, void *data) override; bool target(uint32_t* buffer, size_t stride, size_t w, size_t h); diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index d7229dce..efad9328 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -215,7 +215,7 @@ void _deleteRle(SwShape& sdata) /* External Class Implementation */ /************************************************************************/ -void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform) +void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderMatrix& transform) { auto outline = sdata.outline; assert(outline); diff --git a/src/lib/tvgCanvasImpl.h b/src/lib/tvgCanvasImpl.h index 20a5efd0..afa2a0b1 100644 --- a/src/lib/tvgCanvasImpl.h +++ b/src/lib/tvgCanvasImpl.h @@ -71,9 +71,9 @@ struct Canvas::Impl for(auto paint: paints) { if (auto scene = dynamic_cast(paint)) { - if (!SCENE_IMPL->update(*renderer)) return -1; + if (!SCENE_IMPL->update(*renderer, nullptr)) return -1; } else if (auto shape = dynamic_cast(paint)) { - if (!SHAPE_IMPL->update(*shape, *renderer)) return -1; + if (!SHAPE_IMPL->update(*shape, *renderer, nullptr)) return -1; } } return 0; diff --git a/src/lib/tvgRenderCommon.h b/src/lib/tvgRenderCommon.h index a3bfcb6d..0ad1d558 100644 --- a/src/lib/tvgRenderCommon.h +++ b/src/lib/tvgRenderCommon.h @@ -30,13 +30,85 @@ struct Surface enum RenderUpdateFlag {None = 0, Path = 1, Fill = 2, Transform = 4, All = 8}; -struct RenderTransform +struct RenderMatrix { //3x3 Matrix Elements float e11, e12, e13; float e21, e22, e23; float e31, e32, e33; + static void rotate(RenderMatrix* out, float degree) + { + constexpr auto PI = 3.141592f; + + if (fabsf(degree) < FLT_EPSILON) return; + + auto radian = degree / 180.0f * PI; + auto cosVal = cosf(radian); + auto sinVal = sinf(radian); + + auto t11 = out->e11 * cosVal + out->e12 * sinVal; + auto t12 = out->e11 * -sinVal + out->e12 * cosVal; + auto t21 = out->e21 * cosVal + out->e22 * sinVal; + auto t22 = out->e21 * -sinVal + out->e22 * cosVal; + auto t31 = out->e31 * cosVal + out->e32 * sinVal; + auto t32 = out->e31 * -sinVal + out->e32 * cosVal; + + out->e11 = t11; + out->e12 = t12; + out->e21 = t21; + out->e22 = t22; + out->e31 = t31; + out->e32 = t32; + } + + static void scale(RenderMatrix* out, float factor) + { + out->e11 *= factor; + out->e22 *= factor; + out->e33 *= factor; + } + + static void identity(RenderMatrix* out) + { + out->e11 = 1.0f; + out->e12 = 0.0f; + out->e13 = 0.0f; + out->e21 = 0.0f; + out->e22 = 1.0f; + out->e23 = 0.0f; + out->e31 = 0.0f; + out->e32 = 0.0f; + out->e33 = 1.0f; + } + + static void translate(RenderMatrix* out, float x, float y) + { + out->e31 += x; + out->e32 += y; + } + + static void multiply(const RenderMatrix* lhs, const RenderMatrix* rhs, RenderMatrix* out) + { + assert(lhs && rhs && out); + + out->e11 = lhs->e11 * rhs->e11 + lhs->e12 * rhs->e21 + lhs->e13 * rhs->e31; + out->e12 = lhs->e11 * rhs->e12 + lhs->e12 * rhs->e22 + lhs->e13 * rhs->e32; + out->e13 = lhs->e11 * rhs->e13 + lhs->e12 * rhs->e23 + lhs->e13 * rhs->e33; + + out->e21 = lhs->e21 * rhs->e11 + lhs->e22 * rhs->e21 + lhs->e23 * rhs->e31; + out->e22 = lhs->e21 * rhs->e12 + lhs->e22 * rhs->e22 + lhs->e23 * rhs->e32; + out->e23 = lhs->e21 * rhs->e13 + lhs->e22 * rhs->e23 + lhs->e23 * rhs->e33; + + out->e31 = lhs->e31 * rhs->e11 + lhs->e32 * rhs->e21 + lhs->e33 * rhs->e31; + out->e32 = lhs->e31 * rhs->e12 + lhs->e32 * rhs->e22 + lhs->e33 * rhs->e32; + out->e33 = lhs->e31 * rhs->e13 + lhs->e32 * rhs->e23 + lhs->e33 * rhs->e33; + } +}; + +struct RenderTransform +{ + RenderMatrix m; float x = 0.0f; float y = 0.0f; float degree = 0.0f; //rotation degree @@ -44,74 +116,19 @@ struct RenderTransform bool update() { - constexpr auto PI = 3.141592f; - //Init Status if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON && fabsf(degree) <= FLT_EPSILON && fabsf(factor - 1) <= FLT_EPSILON) { return false; } - //identity - e11 = 1.0f; - e12 = 0.0f; - e13 = 0.0f; - e21 = 0.0f; - e22 = 1.0f; - e23 = 0.0f; - e31 = 0.0f; - e32 = 0.0f; - e33 = 1.0f; - - //rotation - if (fabsf(degree) > FLT_EPSILON) { - auto radian = degree / 180.0f * PI; - auto cosVal = cosf(radian); - auto sinVal = sinf(radian); - - auto t11 = e11 * cosVal + e12 * sinVal; - auto t12 = e11 * -sinVal + e12 * cosVal; - auto t21 = e21 * cosVal + e22 * sinVal; - auto t22 = e21 * -sinVal + e22 * cosVal; - auto t31 = e31 * cosVal + e32 * sinVal; - auto t32 = e31 * -sinVal + e32 * cosVal; - - e11 = t11; - e12 = t12; - e21 = t21; - e22 = t22; - e31 = t31; - e32 = t32; - } - - //scale - e11 *= factor; - e22 *= factor; - e33 *= factor; - - //translate - e31 += x; - e32 += y; + RenderMatrix::identity(&m); + RenderMatrix::scale(&m, factor); + RenderMatrix::rotate(&m, degree); + RenderMatrix::translate(&m, x, y); return true; } - - RenderTransform& operator*=(const RenderTransform rhs) - { - e11 = e11 * rhs.e11 + e12 * rhs.e21 + e13 * rhs.e31; - e12 = e11 * rhs.e12 + e12 * rhs.e22 + e13 * rhs.e32; - e13 = e11 * rhs.e13 + e12 * rhs.e23 + e13 * rhs.e33; - - e21 = e21 * rhs.e11 + e22 * rhs.e21 + e23 * rhs.e31; - e22 = e21 * rhs.e12 + e22 * rhs.e22 + e23 * rhs.e32; - e23 = e21 * rhs.e13 + e22 * rhs.e23 + e23 * rhs.e33; - - e31 = e31 * rhs.e11 + e32 * rhs.e21 + e33 * rhs.e31; - e32 = e31 * rhs.e12 + e32 * rhs.e22 + e33 * rhs.e32; - e33 = e31 * rhs.e13 + e32 * rhs.e23 + e33 * rhs.e33; - - return *this; - } }; @@ -119,7 +136,7 @@ class RenderMethod { public: virtual ~RenderMethod() {} - virtual void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) = 0; + virtual void* prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) = 0; virtual bool dispose(const Shape& shape, void *data) = 0; virtual bool render(const Shape& shape, void *data) = 0; virtual bool clear() = 0; diff --git a/src/lib/tvgScene.cpp b/src/lib/tvgScene.cpp index 4b2a6131..dba9637a 100644 --- a/src/lib/tvgScene.cpp +++ b/src/lib/tvgScene.cpp @@ -64,21 +64,30 @@ int Scene::reserve(size_t size) noexcept } -int Scene::scale(float scaleFacator) noexcept +int Scene::scale(float factor) noexcept { - return 0; + auto impl = pImpl.get(); + assert(impl); + + return impl->scale(factor); } int Scene::rotate(float degree) noexcept { - return 0; + auto impl = pImpl.get(); + assert(impl); + + return impl->rotate(degree); } int Scene::translate(float x, float y) noexcept { - return 0; + auto impl = pImpl.get(); + assert(impl); + + return impl->translate(x, y); } diff --git a/src/lib/tvgSceneImpl.h b/src/lib/tvgSceneImpl.h index a2ce90d4..4def2231 100644 --- a/src/lib/tvgSceneImpl.h +++ b/src/lib/tvgSceneImpl.h @@ -26,11 +26,14 @@ struct Scene::Impl { vector paints; + RenderTransform *transform = nullptr; + size_t flag = RenderUpdateFlag::None; ~Impl() { //Are you sure clear() prior to this? assert(paints.empty()); + if (transform) delete(transform); } bool clear(RenderMethod& renderer) @@ -48,18 +51,44 @@ struct Scene::Impl return true; } - bool update(RenderMethod &renderer) + bool updateInternal(RenderMethod &renderer, const RenderMatrix* transform, size_t flag) { for(auto paint: paints) { if (auto scene = dynamic_cast(paint)) { - if (!SCENE_IMPL->update(renderer)) return false; + if (!SCENE_IMPL->update(renderer, transform, flag)) return false; } else if (auto shape = dynamic_cast(paint)) { - if (!SHAPE_IMPL->update(*shape, renderer)) return false; + if (!SHAPE_IMPL->update(*shape, renderer, transform, flag)) return false; } } return true; } + bool update(RenderMethod &renderer, const RenderMatrix* pTransform = nullptr, size_t pFlag = 0) + { + if (flag & RenderUpdateFlag::Transform) { + assert(transform); + if (!transform->update()) { + delete(transform); + transform = nullptr; + } + } + + auto ret = true; + + if (transform && pTransform) { + RenderMatrix outTransform; + RenderMatrix::multiply(pTransform, &transform->m, &outTransform); + ret = updateInternal(renderer, &outTransform, pFlag | flag); + } else { + auto outTransform = pTransform ? pTransform : &transform->m; + ret = updateInternal(renderer, outTransform, pFlag | flag); + } + + flag = RenderUpdateFlag::None; + + return ret; + } + bool render(RenderMethod &renderer) { for(auto paint: paints) { @@ -94,6 +123,52 @@ struct Scene::Impl } return true; } + + bool scale(float factor) + { + if (transform) { + if (fabsf(factor - transform->factor) <= FLT_EPSILON) return -1; + } else { + if (fabsf(factor) <= FLT_EPSILON) return -1; + transform = new RenderTransform(); + assert(transform); + } + transform->factor = factor; + flag |= RenderUpdateFlag::Transform; + + return 0; + } + + bool rotate(float degree) + { + if (transform) { + if (fabsf(degree - transform->degree) <= FLT_EPSILON) return -1; + } else { + if (fabsf(degree) <= FLT_EPSILON) return -1; + transform = new RenderTransform(); + assert(transform); + } + transform->degree = degree; + flag |= RenderUpdateFlag::Transform; + + return 0; + } + + bool translate(float x, float y) + { + if (transform) { + if (fabsf(x - transform->x) <= FLT_EPSILON && fabsf(y - transform->y) <= FLT_EPSILON) return -1; + } else { + if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON) return -1; + transform = new RenderTransform(); + assert(transform); + } + transform->x = x; + transform->y = y; + flag |= RenderUpdateFlag::Transform; + + return 0; + } }; #endif //_TVG_SCENE_IMPL_H_ \ No newline at end of file diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index e552a3ba..30874262 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -66,7 +66,7 @@ struct Shape::Impl return renderer.render(shape, edata); } - bool update(Shape& shape, RenderMethod& renderer) + bool update(Shape& shape, RenderMethod& renderer, const RenderMatrix* pTransform = nullptr, size_t pFlag = 0) { if (flag & RenderUpdateFlag::Transform) { assert(transform); @@ -75,7 +75,16 @@ struct Shape::Impl transform = nullptr; } } - edata = renderer.prepare(shape, edata, transform, static_cast(flag)); + + if (transform && pTransform) { + RenderMatrix outTransform; + RenderMatrix::multiply(pTransform, &transform->m, &outTransform); + edata = renderer.prepare(shape, edata, &outTransform, static_cast(pFlag | flag)); + } else { + auto outTransform = pTransform ? pTransform : &transform->m; + edata = renderer.prepare(shape, edata, outTransform, static_cast(pFlag | flag)); + } + flag = RenderUpdateFlag::None; if (edata) return true; diff --git a/test/makefile b/test/makefile index e437084c..50710e08 100644 --- a/test/makefile +++ b/test/makefile @@ -9,3 +9,4 @@ all: gcc -o testDirectUpdate testDirectUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testScene testScene.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testTransform testTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` diff --git a/test/testSceneTransform.cpp b/test/testSceneTransform.cpp new file mode 100644 index 00000000..622dafc4 --- /dev/null +++ b/test/testSceneTransform.cpp @@ -0,0 +1,168 @@ +#include +#include + +using namespace std; + +#define WIDTH 800 +#define HEIGHT 800 + +static uint32_t buffer[WIDTH * HEIGHT]; +unique_ptr canvas = nullptr; +tvg::Scene* pScene1 = nullptr; +tvg::Scene* pScene2 = nullptr; + +void tvgtest() +{ + //Initialize TizenVG Engine + tvg::Engine::init(); + + //Create a Canvas + canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); + + //Create a Scene + auto scene = tvg::Scene::gen(); + pScene1 = scene.get(); + scene->reserve(3); //reserve 3 shape nodes (optional) + + //Prepare Round Rectangle + auto shape1 = tvg::Shape::gen(); + shape1->appendRect(-235, -250, 400, 400, 50); //x, y, w, h, cornerRadius + shape1->fill(0, 255, 0, 255); //r, g, b, a + scene->push(move(shape1)); + + //Prepare Circle + auto shape2 = tvg::Shape::gen(); + shape2->appendCircle(-165, -150, 200, 200); //cx, cy, radiusW, radiusH + shape2->fill(255, 255, 0, 255); //r, g, b, a + scene->push(move(shape2)); + + //Prepare Ellipse + auto shape3 = tvg::Shape::gen(); + shape3->appendCircle(265, 250, 150, 100); //cx, cy, radiusW, radiusH + shape3->fill(0, 255, 255, 255); //r, g, b, a + scene->push(move(shape3)); + + scene->translate(350, 350); + scene->scale(0.7); + + //Create another Scene + auto scene2 = tvg::Scene::gen(); + pScene2 = scene2.get(); + scene2->reserve(2); //reserve 2 shape nodes (optional) + +#if 0 + //Star + auto shape4 = tvg::Shape::gen(); + + //Appends Paths + shape4->moveTo(0, -114.5); + shape4->lineTo(54, -5.5); + shape4->lineTo(175, 11.5); + shape4->lineTo(88, 95.5); + shape4->lineTo(108, 216.5); + shape4->lineTo(0, 160.5); + shape4->lineTo(-102, 216.5); + shape4->lineTo(-87, 96.5); + shape4->lineTo(-173, 12.5); + shape4->lineTo(-53, -5.5); + shape4->close(); + shape4->fill(0, 0, 127, 127); + + float x, y, w, h; + shape4->bounds(x, y, w, h); + scene2->push(move(shape4)); + + //Circle + auto shape5 = tvg::Shape::gen(); + + auto cx = -150.0f; + auto cy = -150.0f; + auto radius = 125.0f; + auto halfRadius = radius * 0.552284f; + + //Append Paths + shape5->moveTo(cx, cy - radius); + shape5->cubicTo(cx + halfRadius, cy - radius, cx + radius, cy - halfRadius, cx + radius, cy); + shape5->cubicTo(cx + radius, cy + halfRadius, cx + halfRadius, cy + radius, cx, cy+ radius); + shape5->cubicTo(cx - halfRadius, cy + radius, cx - radius, cy + halfRadius, cx - radius, cy); + shape5->cubicTo(cx - radius, cy - halfRadius, cx - halfRadius, cy - radius, cx, cy - radius); + shape5->fill(127, 0, 0, 127); + scene2->push(move(shape5)); + + scene2->translate(300, 300); + + //Push scene2 onto the scene + scene->push(move(scene2)); +#endif + //Draw the Scene onto the Canvas + canvas->push(move(scene)); + + canvas->draw(); + canvas->sync(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +} + +void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progress) +{ + /* Update scene directly. + You can update only necessary properties of this scene, + while retaining other properties. */ + + pScene1->rotate(360 * progress); + + //Update shape for drawing (this may work asynchronously) + canvas->update(pScene1); + + //Draw Next frames + canvas->draw(); + canvas->sync(); + + //Update Efl Canvas + Eo* img = (Eo*) effect; + evas_object_image_data_update_add(img, 0, 0, WIDTH, HEIGHT); +} + +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + +int main(int argc, char **argv) +{ + //Initialize TizenVG Engine + tvg::Engine::init(); + + tvgtest(); + + //Show the result using EFL... + elm_init(argc, argv); + + Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); + + Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_size_set(img, WIDTH, HEIGHT); + evas_object_image_data_set(img, buffer); + evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(img); + + elm_win_resize_object_add(win, img); + evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); + evas_object_show(win); + + Elm_Transit *transit = elm_transit_add(); + elm_transit_effect_add(transit, transit_cb, img, nullptr); + elm_transit_duration_set(transit, 2); + elm_transit_repeat_times_set(transit, -1); + elm_transit_go(transit); + + elm_run(); + elm_shutdown(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +}