From e11850a3fe05385920d7ec42eb46b415c38c7ef6 Mon Sep 17 00:00:00 2001 From: scheffle Date: Sat, 11 May 2024 17:33:02 +0200 Subject: [PATCH] add TransformMatrix to script object --- .../examples/standalone/resource/test.uidesc | 2 +- .../detail/drawcontextobject.cpp | 186 +++++++++++++++++- .../detail/drawcontextobject.h | 2 + .../uidescription-scripting/uiscripting.cpp | 4 + 4 files changed, 187 insertions(+), 7 deletions(-) diff --git a/vstgui/standalone/examples/standalone/resource/test.uidesc b/vstgui/standalone/examples/standalone/resource/test.uidesc index 9612da537..42395851d 100644 --- a/vstgui/standalone/examples/standalone/resource/test.uidesc +++ b/vstgui/standalone/examples/standalone/resource/test.uidesc @@ -1047,7 +1047,7 @@ "mouse-enabled": "true", "opacity": "1", "origin": "10, 350", - "script": "view.draw = function (context, rect) {\n\tvar viewBounds = view.getBounds ();\n\tcontext.saveGlobalState ();\n\tcontext.setLineStyle (\"dotted\");\n\tcontext.setFrameColor (\"navy\");\n\tcontext.setLineWidth (2);\n\tcontext.drawLine ({x: viewBounds.left, y:viewBounds.top}, {x: viewBounds.right, y:viewBounds.bottom});\n\tcontext.restoreGlobalState ();\n\n\tvar polygon = [];\n\tpolygon[0] = {x: 10, y: 0};\n\tpolygon[1] = {x: 0, y: 10};\n\tpolygon[2] = {x: 10, y: 20};\n\tpolygon[3] = {x: 20, y: 10};\n\tpolygon[4] = {x: 10, y: 0};\n\tcontext.setFrameColor (\"red\");\n\tcontext.setFillColor (\"green\");\n\tcontext.drawPolygon (polygon, \"filledAndStroked\");\n\n\tcontext.setFont (\"JSTest\");\n\tcontext.setFontColor (\"black\");\n\tcontext.drawString (\"Hello JavaScript\", viewBounds, \"center\");\n\n\tvar clipRect = {left: 5, top: 5, right: 15, bottom: 15};\n\tcontext.setClipRect (clipRect);\n\tcontext.setFillColor (\"cyan\");\n\tcontext.drawRect (view.getBounds (), \"filled\");\n};\n", + "script": "view.angle = 0;\n\nview.draw = function (context, rect) {\n\tvar viewBounds = view.getBounds ();\n\tcontext.saveGlobalState ();\n\tcontext.setLineStyle (\"dotted\");\n\tcontext.setFrameColor (\"navy\");\n\tcontext.setLineWidth (2);\n\tcontext.drawLine ({x: viewBounds.left, y:viewBounds.top}, {x: viewBounds.right, y:viewBounds.bottom});\n\tcontext.restoreGlobalState ();\n\n\tvar polygon = [];\n\tpolygon[0] = {x: 10, y: 0};\n\tpolygon[1] = {x: 0, y: 10};\n\tpolygon[2] = {x: 10, y: 20};\n\tpolygon[3] = {x: 20, y: 10};\n\tpolygon[4] = {x: 10, y: 0};\n\tcontext.setFrameColor (\"red\");\n\tcontext.setFillColor (\"green\");\n\tcontext.drawPolygon (polygon, \"filledAndStroked\");\n\n\tvar tm = makeTransformMatrix ();\n\ttm.rotate (view.angle, {x: 10, y: 10});\n\t\n\tvar path = context.createGraphicsPath();\n\tpath.beginSubpath ({x: 10, y: 0});\n\tpath.addLine ({x: 0, y: 10});\n\tpath.addLine ({x: 10, y: 20});\n\tpath.addLine ({x: 20, y: 10});\n\tpath.addLine ({x: 10, y: 0});\n\tcontext.setFrameColor (\"pink\");\n\tcontext.setLineWidth (1);\n\tcontext.drawGraphicsPath (path, \"stroked\", tm);\n\n\tcontext.setFont (\"JSTest\");\n\tcontext.setFontColor (\"black\");\n\tcontext.drawString (\"Hello JavaScript\", viewBounds, \"center\");\n\n\tvar clipRect = {left: 5, top: 5, right: 15, bottom: 15};\n\tcontext.setClipRect (clipRect);\n\tcontext.setFillColor (\"cyan\");\n\tcontext.drawRect (view.getBounds (), \"filled\");\n};\n\nview.timer = createTimer(view, 16, function(view) {\n\tview.angle += 0.5;\n\tview.invalid ();\n});\n\nview.timer.start ();\n\n", "size": "110, 25", "transparent": "false", "wants-focus": "false" diff --git a/vstgui/uidescription-scripting/detail/drawcontextobject.cpp b/vstgui/uidescription-scripting/detail/drawcontextobject.cpp index a59989e06..e2053ead4 100644 --- a/vstgui/uidescription-scripting/detail/drawcontextobject.cpp +++ b/vstgui/uidescription-scripting/detail/drawcontextobject.cpp @@ -36,6 +36,44 @@ static SharedPointer getGraphicsPath (CScriptVar* var, std::strin return {}; } +//------------------------------------------------------------------------ +static std::shared_ptr extractTransformMatrix (CScriptVar* var, + std::string_view signature) +{ + auto customData = var->getCustomData (); + if (!customData.has_value ()) + throw CScriptException ("Variable is not a transform matrix"); + try + { + auto path = std::any_cast> (customData); + return path; + } + catch (const std::bad_any_cast& e) + { + throw CScriptException ("Variable is not a transform matrix"); + } + return {}; +} + +//------------------------------------------------------------------------ +static std::shared_ptr getTransformMatrix (CScriptVar* var, + std::string_view varName, + std::string_view signature) +{ + auto tmVar = getArgument (var, varName, signature); + return extractTransformMatrix (tmVar, signature); +} + +//------------------------------------------------------------------------ +static std::shared_ptr getOptionalTransformMatrix (CScriptVar* var, + std::string_view varName, + std::string_view signature) +{ + if (auto tmVar = getOptionalArgument (var, varName)) + return extractTransformMatrix (tmVar, signature); + return {}; +} + //------------------------------------------------------------------------ static CRect getRect (CScriptVar* var, std::string_view varName, std::string_view signature) { @@ -80,6 +118,131 @@ static int64_t getInt (CScriptVar* var, std::string_view varName, std::string_vi return intVar->getInt (); } +//------------------------------------------------------------------------ +struct TransformMatrixScriptObject : ScriptObject +{ + using TransformMatrixPtr = std::shared_ptr; + + TransformMatrixScriptObject (TransformMatrixPtr tm = std::make_shared ()) + { + scriptVar = new CScriptVar ("", SCRIPTVAR_OBJECT); + scriptVar->addRef (); + scriptVar->setCustomData (tm); + addFunc ("concat"sv, [tm] (auto var) { concat (tm, var); }, {"transformMatrix"sv}); + addFunc ("inverse"sv, [tm] (auto var) { inverse (tm, var); }); + addFunc ("rotate"sv, [tm] (auto var) { rotate (tm, var); }, {"angle"sv, "center?"sv}); + addFunc ("scale"sv, [tm] (auto var) { scale (tm, var); }, {"x"sv, "y"sv}); + addFunc ("skewX"sv, [tm] (auto var) { skewX (tm, var); }, {"skewX"sv}); + addFunc ("skewY"sv, [tm] (auto var) { skewY (tm, var); }, {"skewY"sv}); + addFunc ("translate"sv, [tm] (auto var) { translate (tm, var); }, {"x"sv, "y"sv}); + addFunc ("transform"sv, [tm] (auto var) { transform (tm, var); }, {"pointOrRect"sv}); + } + + static void inverse (const TransformMatrixPtr& tm, CScriptVar* var) + { + static constexpr auto signature = "matrix.inverse();"sv; + auto iTm = tm->inverse (); + TransformMatrixScriptObject obj (std::make_shared (iTm)); + var->setReturnVar (obj.take ()); + } + + static void concat (const TransformMatrixPtr& tm, CScriptVar* var) + { + static constexpr auto signature = "matrix.concat(transformMatrix);"sv; + auto other = getTransformMatrix (var, "transformMatrix"sv, signature); + auto result = *(tm.get ()) * *(other.get ()); + *(tm.get ()) = result; + } + + static void translate (const TransformMatrixPtr& tm, CScriptVar* var) + { + static constexpr auto signature = "matrix.translate(x, y);"sv; + auto x = getDouble (var, "x"sv, signature); + auto y = getDouble (var, "y"sv, signature); + tm->translate (x, y); + } + + static void scale (const TransformMatrixPtr& tm, CScriptVar* var) + { + static constexpr auto signature = "matrix.scale(x, y);"sv; + auto x = getDouble (var, "x"sv, signature); + auto y = getDouble (var, "y"sv, signature); + tm->scale (x, y); + } + + static void rotate (const TransformMatrixPtr& tm, CScriptVar* var) + { + static constexpr auto signature = "matrix.rotate(angle, center?);"sv; + auto angle = getDouble (var, "angle"sv, signature); + if (auto centerVar = getOptionalArgument (var, "center?"sv)) + { + auto center = fromScriptPoint (*centerVar); + tm->rotate (angle, center); + } + else + { + tm->rotate (angle); + } + } + + static void skewX (const TransformMatrixPtr& tm, CScriptVar* var) + { + static constexpr auto signature = "matrix.skewX(angle);"sv; + auto angle = getDouble (var, "angle"sv, signature); + tm->skewX (angle); + } + + static void skewY (const TransformMatrixPtr& tm, CScriptVar* var) + { + static constexpr auto signature = "matrix.skewY(angle);"sv; + auto angle = getDouble (var, "angle"sv, signature); + tm->skewY (angle); + } + + static void transform (const TransformMatrixPtr& tm, CScriptVar* var) + { + static constexpr auto signature = "matrix.transform(pointOrRect);"sv; + auto pointOrRect = getArgument (var, "pointOrRect"sv, signature); + auto xVar = getOptionalArgument (pointOrRect, "x"sv); + auto yVar = getOptionalArgument (pointOrRect, "y"sv); + if (xVar && yVar) + { + auto x = xVar->getDouble (); + auto y = yVar->getDouble (); + tm->transform (x, y); + xVar->setDouble (x); + yVar->setDouble (y); + return; + } + auto leftVar = getOptionalArgument (pointOrRect, "left"sv); + auto topVar = getOptionalArgument (pointOrRect, "top"sv); + auto rightVar = getOptionalArgument (pointOrRect, "right"sv); + auto bottomVar = getOptionalArgument (pointOrRect, "bottom"sv); + if (leftVar && topVar && rightVar && bottomVar) + { + CRect r (leftVar->getDouble (), topVar->getDouble (), rightVar->getDouble (), + bottomVar->getDouble ()); + tm->transform (r); + leftVar->setDouble (r.left); + topVar->setDouble (r.top); + rightVar->setDouble (r.right); + bottomVar->setDouble (r.bottom); + return; + } + + string s ("Argument is not a point or a rect in "); + s.append (signature); + throw CScriptException (std::move (s)); + } +}; + +//------------------------------------------------------------------------ +TJS::CScriptVar* makeTransformMatrixObject () +{ + TransformMatrixScriptObject obj; + return obj.take (); +} + //------------------------------------------------------------------------ struct GraphicsPathScriptObject : ScriptObject { @@ -89,17 +252,27 @@ struct GraphicsPathScriptObject : ScriptObject scriptVar->addRef (); scriptVar->setCustomData (p); + addFunc ("addEllipse"sv, [p] (auto var) { addEllipse (p, var); }, {"rect"sv}); addFunc ("addArc"sv, [p] (auto var) { addArc (p, var); }, {"rect"sv, "startAngle"sv, "endAngle"sv, "clockwise"sv}); - addFunc ("addEllipse"sv, [p] (auto var) { addEllipse (p, var); }, {"rect"sv}); - addFunc ("addRect"sv, [p] (auto var) { addRect (p, var); }, {"rect"sv}); - addFunc ("addLine"sv, [p] (auto var) { addLine (p, var); }, {"to"sv}); addFunc ("addBezierCurve"sv, [p] (auto var) { addBezierCurve (p, var); }, {"control1"sv, "control2"sv, "end"sv}); - addFunc ("beginSubpath"sv, [p] (auto var) { beginSubpath (p, var); }, {"start"sv}); - addFunc ("closeSubpath"sv, [p] (auto var) { closeSubpath (p); }); + addFunc ("addLine"sv, [p] (auto var) { addLine (p, var); }, {"to"sv}); + addFunc ("addPath"sv, [p] (auto var) { addPath (p, var); }, + {"path"sv, "transformMatrix?"sv}); + addFunc ("addRect"sv, [p] (auto var) { addRect (p, var); }, {"rect"sv}); addFunc ("addRoundRect"sv, [p] (auto var) { addRoundRect (p, var); }, {"rect"sv, "radius"sv}); + addFunc ("closeSubpath"sv, [p] (auto var) { closeSubpath (p); }); + addFunc ("beginSubpath"sv, [p] (auto var) { beginSubpath (p, var); }, {"start"sv}); + } + + static void addPath (const SharedPointer& path, CScriptVar* var) + { + static constexpr auto signature = "path.addPath(path, transformMatrix?);"sv; + auto otherPath = getGraphicsPath (var, "path"sv, signature); + auto tm = getOptionalTransformMatrix (var, "transformMatrix?"sv, signature); + path->addPath (*otherPath.get (), tm ? tm.get () : nullptr); } static void addArc (const SharedPointer& path, CScriptVar* var) @@ -254,7 +427,8 @@ struct DrawContextObject::Impl auto path = getGraphicsPath (var, "path"sv, signature); auto modeVar = getOptionalArgument (var, "mode?"); auto mode = modeVar ? getPathDrawMode (modeVar) : CDrawContext::PathDrawMode::kPathFilled; - context->drawGraphicsPath (path, mode); + auto tm = getOptionalTransformMatrix (var, "transform?"sv, signature); + context->drawGraphicsPath (path, mode, tm ? tm.get () : nullptr); } void drawLine (CScriptVar* var) const diff --git a/vstgui/uidescription-scripting/detail/drawcontextobject.h b/vstgui/uidescription-scripting/detail/drawcontextobject.h index f02efce93..a9196b5e5 100644 --- a/vstgui/uidescription-scripting/detail/drawcontextobject.h +++ b/vstgui/uidescription-scripting/detail/drawcontextobject.h @@ -12,6 +12,8 @@ namespace VSTGUI { namespace ScriptingInternal { +TJS::CScriptVar* makeTransformMatrixObject (); + //------------------------------------------------------------------------ struct DrawContextObject : ScriptObject, TJS::IScriptVarLifeTimeObserver diff --git a/vstgui/uidescription-scripting/uiscripting.cpp b/vstgui/uidescription-scripting/uiscripting.cpp index ff3d0192c..6199b448b 100644 --- a/vstgui/uidescription-scripting/uiscripting.cpp +++ b/vstgui/uidescription-scripting/uiscripting.cpp @@ -200,6 +200,10 @@ struct ScriptContext::Impl : ViewListenerAdapter, }); jsContext->addNative ("function log(obj)", [this] (CScriptVar* var) { log (var->getParameter ("obj"sv)); }); + + jsContext->addNative ("function makeTransformMatrix()", [] (CScriptVar* var) { + var->setReturnVar (makeTransformMatrixObject ()); + }); } void reset (bool terminate = false)