| #include "rive/animation/animation.hpp" | |
| #include "rive/animation/animation_state.hpp" | |
| #include "rive/animation/any_state.hpp" | |
| #include "rive/animation/entry_state.hpp" | |
| #include "rive/animation/exit_state.hpp" | |
| #include "rive/animation/linear_animation.hpp" | |
| #include "rive/animation/linear_animation_instance.hpp" | |
| #include "rive/animation/state_machine_bool.hpp" | |
| #include "rive/animation/state_machine_input_instance.hpp" | |
| #include "rive/animation/state_machine_instance.hpp" | |
| #include "rive/animation/state_machine_number.hpp" | |
| #include "rive/animation/state_machine_trigger.hpp" | |
| #include "rive/artboard.hpp" | |
| #include "rive/bones/bone.hpp" | |
| #include "rive/bones/root_bone.hpp" | |
| #include "rive/component.hpp" | |
| #include "rive/constraints/constraint.hpp" | |
| #include "rive/core.hpp" | |
| #include "rive/core/binary_reader.hpp" | |
| #include "rive/file.hpp" | |
| #include "rive/layout.hpp" | |
| #include "rive/math/mat2d.hpp" | |
| #include "rive/node.hpp" | |
| #include "rive/renderer.hpp" | |
| #include "rive/shapes/cubic_vertex.hpp" | |
| #include "rive/shapes/path.hpp" | |
| #include "rive/transform_component.hpp" | |
| #include "js_alignment.hpp" | |
| #include <emscripten.h> | |
| #include <emscripten/bind.h> | |
| #include <emscripten/val.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <string> | |
| #include <vector> | |
| // #include <sanitizer/lsan_interface.h> | |
| using namespace emscripten; | |
| // We had to do this because binding the core class const defined types directly | |
| // caused wasm-ld linker issues. See | |
| // https://www.mail-archive.com/emscripten-discuss@googlegroups.com/msg09132.html | |
| // for other people suffering our same pains. | |
| const uint16_t stateMachineBoolTypeKey = rive::StateMachineBoolBase::typeKey; | |
| const uint16_t stateMachineNumberTypeKey = rive::StateMachineNumberBase::typeKey; | |
| const uint16_t stateMachineTriggerTypeKey = rive::StateMachineTriggerBase::typeKey; | |
| rive::Alignment convertAlignment(JsAlignment alignment) | |
| { | |
| switch (alignment) | |
| { | |
| case JsAlignment::topLeft: | |
| return rive::Alignment::topLeft; | |
| case JsAlignment::topCenter: | |
| return rive::Alignment::topCenter; | |
| case JsAlignment::topRight: | |
| return rive::Alignment::topRight; | |
| case JsAlignment::centerLeft: | |
| return rive::Alignment::centerLeft; | |
| case JsAlignment::center: | |
| return rive::Alignment::center; | |
| case JsAlignment::centerRight: | |
| return rive::Alignment::centerRight; | |
| case JsAlignment::bottomLeft: | |
| return rive::Alignment::bottomLeft; | |
| case JsAlignment::bottomCenter: | |
| return rive::Alignment::bottomCenter; | |
| case JsAlignment::bottomRight: | |
| return rive::Alignment::bottomRight; | |
| } | |
| return rive::Alignment::center; | |
| } | |
| rive::Mat2D computeAlignment(rive::Fit fit, JsAlignment alignment, rive::AABB orig, rive::AABB dest) | |
| { | |
| return rive::computeAlignment(fit, convertAlignment(alignment), orig, dest); | |
| } | |
| rive::Vec2D mapXY(rive::Mat2D invertedMatrix, rive::Vec2D canvasVector) | |
| { | |
| return invertedMatrix * canvasVector; | |
| } | |
| EMSCRIPTEN_KEEPALIVE | |
| EMSCRIPTEN_BINDINGS(RiveWASM) | |
| { | |
| function("computeAlignment", &computeAlignment); | |
| function("mapXY", &mapXY); | |
| #ifdef ENABLE_QUERY_FLAT_VERTICES | |
| class_<rive::FlattenedPath>("FlattenedPath") | |
| .function("length", optional_override([](rive::FlattenedPath& self) -> size_t { | |
| return self.vertices().size(); | |
| })) | |
| .function("isCubic", optional_override([](rive::FlattenedPath& self, size_t index) -> bool { | |
| if (index >= self.vertices().size()) | |
| { | |
| return false; | |
| } | |
| return self.vertices()[index]->is<rive::CubicVertex>(); | |
| })) | |
| .function("x", optional_override([](rive::FlattenedPath& self, size_t index) -> float { | |
| return self.vertices()[index]->x(); | |
| })) | |
| .function("y", optional_override([](rive::FlattenedPath& self, size_t index) -> float { | |
| return self.vertices()[index]->y(); | |
| })) | |
| .function("inX", optional_override([](rive::FlattenedPath& self, size_t index) -> float { | |
| return self.vertices()[index]->as<rive::CubicVertex>()->renderIn()[0]; | |
| })) | |
| .function("inY", optional_override([](rive::FlattenedPath& self, size_t index) -> float { | |
| return self.vertices()[index]->as<rive::CubicVertex>()->renderIn()[1]; | |
| })) | |
| .function("outX", optional_override([](rive::FlattenedPath& self, size_t index) -> float { | |
| return self.vertices()[index]->as<rive::CubicVertex>()->renderOut()[0]; | |
| })) | |
| .function("outY", optional_override([](rive::FlattenedPath& self, size_t index) -> float { | |
| return self.vertices()[index]->as<rive::CubicVertex>()->renderOut()[1]; | |
| })); | |
| #endif | |
| class_<rive::Vec2D>("Vec2D") | |
| .constructor<float, float>() | |
| // TODO: For next major verison, make these properties instead of methods to match | |
| // patterns on other math-based Rive classes, such as Mat2D | |
| .function("x", optional_override([](rive::Vec2D self) -> float { return self.x; })) | |
| .function("y", optional_override([](rive::Vec2D self) -> float { return self.y; })); | |
| class_<rive::Mat2D>("Mat2D") | |
| .constructor<>() | |
| .property("xx", | |
| select_overload<float() const>(&rive::Mat2D::xx), | |
| select_overload<void(float)>(&rive::Mat2D::xx)) | |
| .property("xy", | |
| select_overload<float() const>(&rive::Mat2D::xy), | |
| select_overload<void(float)>(&rive::Mat2D::xy)) | |
| .property("yx", | |
| select_overload<float() const>(&rive::Mat2D::yx), | |
| select_overload<void(float)>(&rive::Mat2D::yx)) | |
| .property("yy", | |
| select_overload<float() const>(&rive::Mat2D::yy), | |
| select_overload<void(float)>(&rive::Mat2D::yy)) | |
| .property("tx", | |
| select_overload<float() const>(&rive::Mat2D::tx), | |
| select_overload<void(float)>(&rive::Mat2D::tx)) | |
| .property("ty", | |
| select_overload<float() const>(&rive::Mat2D::ty), | |
| select_overload<void(float)>(&rive::Mat2D::ty)) | |
| .function("invert", optional_override([](rive::Mat2D& self, rive::Mat2D& result) -> bool { | |
| return self.invert(&result); | |
| })) | |
| .function("multiply", | |
| optional_override([](rive::Mat2D& self, rive::Mat2D& result, rive::Mat2D& other) | |
| -> void { result = rive::Mat2D::multiply(self, other); })); | |
| class_<rive::File>("File") | |
| .function("defaultArtboard", | |
| optional_override([](rive::File& self) -> rive::ArtboardInstance* { | |
| return self.artboardAt(0).release(); | |
| }), | |
| allow_raw_pointers()) | |
| .function("artboardByName", | |
| optional_override([](const rive::File& self, | |
| const std::string& name) -> rive::ArtboardInstance* { | |
| return self.artboardNamed(name).release(); | |
| }), | |
| allow_raw_pointers()) | |
| .function( | |
| "artboardByIndex", | |
| optional_override([](const rive::File& self, size_t index) -> rive::ArtboardInstance* { | |
| return self.artboardAt(index).release(); | |
| }), | |
| allow_raw_pointers()) | |
| .function("artboardCount", &rive::File::artboardCount); | |
| class_<rive::Artboard>("ArtboardBase"); | |
| class_<rive::ArtboardInstance, base<rive::Artboard>>("Artboard") | |
| #ifdef ENABLE_QUERY_FLAT_VERTICES | |
| .function("flattenPath", | |
| optional_override([](rive::Artboard& self, | |
| size_t index, | |
| bool transformToParent) -> rive::FlattenedPath* { | |
| auto artboardObjects = self.objects(); | |
| if (index >= artboardObjects.size()) | |
| { | |
| return nullptr; | |
| } | |
| auto object = artboardObjects[index]; | |
| if (!object->is<rive::Path>()) | |
| { | |
| return nullptr; | |
| } | |
| auto path = object->as<rive::Path>(); | |
| return path->makeFlat(transformToParent); | |
| }), | |
| allow_raw_pointers()) | |
| #endif | |
| .property("name", select_overload<const std::string&() const>(&rive::Artboard::name)) | |
| .function("advance", | |
| optional_override([](rive::ArtboardInstance& self, double seconds) -> bool { | |
| return self.advance(seconds); | |
| }), | |
| allow_raw_pointers()) | |
| .function("draw", | |
| optional_override([](rive::ArtboardInstance& self, rive::Renderer* renderer) { | |
| return self.draw(renderer, rive::Artboard::DrawOption::kNormal); | |
| }), | |
| allow_raw_pointers()) | |
| .function("transformComponent", | |
| optional_override([](rive::ArtboardInstance& self, const std::string& name) { | |
| return self.find<rive::TransformComponent>(name); | |
| }), | |
| allow_raw_pointers()) | |
| .function("node", | |
| optional_override([](rive::ArtboardInstance& self, const std::string& name) { | |
| return self.find<rive::Node>(name); | |
| }), | |
| allow_raw_pointers()) | |
| .function("bone", | |
| optional_override([](rive::ArtboardInstance& self, const std::string& name) { | |
| return self.find<rive::Bone>(name); | |
| }), | |
| allow_raw_pointers()) | |
| .function("rootBone", | |
| optional_override([](rive::ArtboardInstance& self, const std::string& name) { | |
| return self.find<rive::RootBone>(name); | |
| }), | |
| allow_raw_pointers()) | |
| // Animations | |
| .function("animationByIndex", | |
| optional_override( | |
| [](rive::ArtboardInstance& self, size_t index) -> rive::LinearAnimation* { | |
| return self.animation(index); | |
| }), | |
| allow_raw_pointers()) | |
| .function("animationByName", | |
| optional_override([](rive::ArtboardInstance& self, const std::string& name) | |
| -> rive::LinearAnimation* { return self.animation(name); }), | |
| allow_raw_pointers()) | |
| .function("animationCount", optional_override([](rive::ArtboardInstance& self) -> size_t { | |
| return self.animationCount(); | |
| })) | |
| // State machines | |
| .function("stateMachineByIndex", | |
| optional_override( | |
| [](rive::ArtboardInstance& self, size_t index) -> rive::StateMachine* { | |
| return self.stateMachine(index); | |
| }), | |
| allow_raw_pointers()) | |
| .function("stateMachineByName", | |
| optional_override([](rive::ArtboardInstance& self, const std::string& name) | |
| -> rive::StateMachine* { return self.stateMachine(name); }), | |
| allow_raw_pointers()) | |
| .function("stateMachineCount", | |
| optional_override([](rive::ArtboardInstance& self) -> size_t { | |
| return self.stateMachineCount(); | |
| })) | |
| .property("bounds", optional_override([](const rive::ArtboardInstance& self) -> rive::AABB { | |
| return self.bounds(); | |
| })) | |
| .property("frameOrigin", | |
| select_overload<bool() const>(&rive::Artboard::frameOrigin), | |
| select_overload<void(bool)>(&rive::Artboard::frameOrigin)); | |
| class_<rive::TransformComponent>("TransformComponent") | |
| .property("scaleX", | |
| select_overload<float() const>(&rive::TransformComponent::scaleX), | |
| select_overload<void(float)>(&rive::TransformComponent::scaleX)) | |
| .property("scaleY", | |
| select_overload<float() const>(&rive::TransformComponent::scaleY), | |
| select_overload<void(float)>(&rive::TransformComponent::scaleY)) | |
| .property("rotation", | |
| select_overload<float() const>(&rive::TransformComponent::rotation), | |
| select_overload<void(float)>(&rive::TransformComponent::rotation)) | |
| .function("worldTransform", | |
| optional_override([](rive::TransformComponent& self) -> rive::Mat2D& { | |
| return self.mutableWorldTransform(); | |
| }), | |
| allow_raw_pointers()) | |
| .function( | |
| "parentWorldTransform", | |
| optional_override([](rive::TransformComponent& self, rive::Mat2D& result) -> void { | |
| result = rive::Mat2D(getParentWorld(self)); | |
| }), | |
| allow_raw_pointers()); | |
| class_<rive::Node, base<rive::TransformComponent>>("Node") | |
| .property("x", | |
| select_overload<float() const>(&rive::TransformComponent::x), | |
| select_overload<void(float)>(&rive::Node::x)) | |
| .property("y", | |
| select_overload<float() const>(&rive::TransformComponent::y), | |
| select_overload<void(float)>(&rive::Node::y)); | |
| class_<rive::Bone, base<rive::TransformComponent>>("Bone").property( | |
| "length", | |
| select_overload<float() const>(&rive::Bone::length), | |
| select_overload<void(float)>(&rive::Bone::length)); | |
| class_<rive::RootBone, base<rive::Bone>>("RootBone") | |
| .property("x", | |
| select_overload<float() const>(&rive::TransformComponent::x), | |
| select_overload<void(float)>(&rive::RootBone::x)) | |
| .property("y", | |
| select_overload<float() const>(&rive::TransformComponent::y), | |
| select_overload<void(float)>(&rive::RootBone::y)); | |
| class_<rive::Animation>("Animation") | |
| .property("name", select_overload<const std::string&() const>(&rive::AnimationBase::name)); | |
| class_<rive::LinearAnimation, base<rive::Animation>>("LinearAnimation") | |
| .property("name", select_overload<const std::string&() const>(&rive::AnimationBase::name)) | |
| .property("duration", | |
| select_overload<uint32_t() const>(&rive::LinearAnimationBase::duration)) | |
| .property("fps", select_overload<uint32_t() const>(&rive::LinearAnimationBase::fps)) | |
| .property("workStart", | |
| select_overload<uint32_t() const>(&rive::LinearAnimationBase::workStart)) | |
| .property("workEnd", select_overload<uint32_t() const>(&rive::LinearAnimationBase::workEnd)) | |
| .property("enableWorkArea", | |
| select_overload<bool() const>(&rive::LinearAnimationBase::enableWorkArea)) | |
| .property("loopValue", | |
| select_overload<uint32_t() const>(&rive::LinearAnimationBase::loopValue)) | |
| .property("speed", select_overload<float() const>(&rive::LinearAnimationBase::speed)) | |
| .function("apply", &rive::LinearAnimation::apply, allow_raw_pointers()); | |
| class_<rive::LinearAnimationInstance>("LinearAnimationInstance") | |
| .constructor<rive::LinearAnimation*, rive::ArtboardInstance*>() | |
| .property("time", | |
| select_overload<float() const>(&rive::LinearAnimationInstance::time), | |
| select_overload<void(float)>(&rive::LinearAnimationInstance::time)) | |
| .property("didLoop", &rive::LinearAnimationInstance::didLoop) | |
| .function("advance", &rive::LinearAnimationInstance::advance) | |
| .function("apply", &rive::LinearAnimationInstance::apply, allow_raw_pointers()); | |
| class_<rive::StateMachine, base<rive::Animation>>("StateMachine"); | |
| class_<rive::StateMachineInstance>("StateMachineInstance") | |
| .constructor<rive::StateMachine*, rive::ArtboardInstance*>() | |
| .function("advance", &rive::StateMachineInstance::advance, allow_raw_pointers()) | |
| .function("inputCount", &rive::StateMachineInstance::inputCount) | |
| .function("input", &rive::StateMachineInstance::input, allow_raw_pointers()) | |
| .function("pointerDown", | |
| optional_override([](rive::StateMachineInstance& self, double x, double y) { | |
| self.pointerDown(rive::Vec2D((float)x, (float)y)); | |
| })) | |
| .function("pointerMove", | |
| optional_override([](rive::StateMachineInstance& self, double x, double y) { | |
| self.pointerMove(rive::Vec2D((float)x, (float)y)); | |
| })) | |
| .function("pointerUp", | |
| optional_override([](rive::StateMachineInstance& self, double x, double y) { | |
| self.pointerUp(rive::Vec2D((float)x, (float)y)); | |
| })) | |
| .function("stateChangedCount", &rive::StateMachineInstance::stateChangedCount) | |
| .function( | |
| "stateChangedNameByIndex", | |
| optional_override([](rive::StateMachineInstance& self, size_t index) -> std::string { | |
| const rive::LayerState* state = self.stateChangedByIndex(index); | |
| if (state != nullptr) | |
| switch (state->coreType()) | |
| { | |
| case rive::AnimationState::typeKey: | |
| return state->as<rive::AnimationState>()->animation()->name(); | |
| case rive::EntryState::typeKey: | |
| return "entry"; | |
| case rive::ExitState::typeKey: | |
| return "exit"; | |
| case rive::AnyState::typeKey: | |
| return "any"; | |
| } | |
| return "unknown"; | |
| }), | |
| allow_raw_pointers()); | |
| class_<rive::SMIInput>("SMIInput") | |
| .property("type", &rive::SMIInput::inputCoreType) | |
| .property("name", &rive::SMIInput::name) | |
| .class_property("bool", &stateMachineBoolTypeKey) | |
| .class_property("number", &stateMachineNumberTypeKey) | |
| .class_property("trigger", &stateMachineTriggerTypeKey) | |
| .function("asBool", | |
| optional_override([](rive::SMIInput& self) -> rive::SMIBool* { | |
| if (self.inputCoreType() != stateMachineBoolTypeKey) | |
| { | |
| return nullptr; | |
| } | |
| return static_cast<rive::SMIBool*>(&self); | |
| }), | |
| allow_raw_pointers()) | |
| .function("asNumber", | |
| optional_override([](rive::SMIInput& self) -> rive::SMINumber* { | |
| if (self.inputCoreType() != stateMachineNumberTypeKey) | |
| { | |
| return nullptr; | |
| } | |
| return static_cast<rive::SMINumber*>(&self); | |
| }), | |
| allow_raw_pointers()) | |
| .function("asTrigger", | |
| optional_override([](rive::SMIInput& self) -> rive::SMITrigger* { | |
| if (self.inputCoreType() != stateMachineTriggerTypeKey) | |
| { | |
| return nullptr; | |
| } | |
| return static_cast<rive::SMITrigger*>(&self); | |
| }), | |
| allow_raw_pointers()); | |
| class_<rive::SMIBool, base<rive::SMIInput>>("SMIBool").property( | |
| "value", | |
| select_overload<bool() const>(&rive::SMIBool::value), | |
| select_overload<void(bool)>(&rive::SMIBool::value)); | |
| class_<rive::SMINumber, base<rive::SMIInput>>("SMINumber") | |
| .property("value", | |
| select_overload<float() const>(&rive::SMINumber::value), | |
| select_overload<void(float)>(&rive::SMINumber::value)); | |
| class_<rive::SMITrigger, base<rive::SMIInput>>("SMITrigger") | |
| .function("fire", &rive::SMITrigger::fire); | |
| enum_<rive::Fit>("Fit") | |
| .value("fill", rive::Fit::fill) | |
| .value("contain", rive::Fit::contain) | |
| .value("cover", rive::Fit::cover) | |
| .value("fitWidth", rive::Fit::fitWidth) | |
| .value("fitHeight", rive::Fit::fitHeight) | |
| .value("none", rive::Fit::none) | |
| .value("scaleDown", rive::Fit::scaleDown); | |
| enum_<JsAlignment>("Alignment") | |
| .value("topLeft", JsAlignment::topLeft) | |
| .value("topCenter", JsAlignment::topCenter) | |
| .value("topRight", JsAlignment::topRight) | |
| .value("centerLeft", JsAlignment::centerLeft) | |
| .value("center", JsAlignment::center) | |
| .value("centerRight", JsAlignment::centerRight) | |
| .value("bottomLeft", JsAlignment::bottomLeft) | |
| .value("bottomCenter", JsAlignment::bottomCenter) | |
| .value("bottomRight", JsAlignment::bottomRight); | |
| value_object<rive::AABB>("AABB") | |
| .field("minX", &rive::AABB::minX) | |
| .field("minY", &rive::AABB::minY) | |
| .field("maxX", &rive::AABB::maxX) | |
| .field("maxY", &rive::AABB::maxY); | |
| // #ifdef DEBUG | |
| // function("doLeakCheck", &__lsan_do_recoverable_leak_check); | |
| // #endif | |
| } |