blob: c980c7cc158480cde63d18a9504f56a184685b0d [file] [log] [blame]
#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
}