| #include "rive/shapes/list_path.hpp" |
| #include "rive/math/math_types.hpp" |
| #include "rive/artboard.hpp" |
| #include "rive/shapes/cubic_detached_vertex.hpp" |
| #include "rive/shapes/straight_vertex.hpp" |
| #include "rive/shapes/vertex.hpp" |
| #include "rive/shapes/path_vertex.hpp" |
| #include "rive/viewmodel/viewmodel_instance.hpp" |
| #include "rive/viewmodel/symbol_type.hpp" |
| #include "rive/viewmodel/viewmodel_instance_number.hpp" |
| #include "rive/generated/core_registry.hpp" |
| |
| using namespace rive; |
| |
| VertexListener::~VertexListener() |
| { |
| delete m_vertex; |
| deleteProperties(); |
| } |
| |
| void VertexListener::deleteProperties() |
| { |
| |
| for (auto& property : m_properties) |
| { |
| delete property; |
| } |
| m_properties.clear(); |
| } |
| |
| VertexListener::VertexListener(Vertex* vertex, |
| rcp<ViewModelInstance> instance, |
| Path* path) : |
| m_vertex(vertex), m_instance(instance), m_path(path) |
| { |
| createProperties(); |
| } |
| |
| void VertexListener::createProperties() |
| { |
| |
| createPropertyListener(SymbolType::vertexX); |
| createPropertyListener(SymbolType::vertexY); |
| createPropertyListener(SymbolType::rotation); |
| createPropertyListener(SymbolType::inRotation); |
| createPropertyListener(SymbolType::outRotation); |
| createPropertyListener(SymbolType::distance); |
| createPropertyListener(SymbolType::inDistance); |
| createPropertyListener(SymbolType::outDistance); |
| createInPointPropertyListener(); |
| createOutPointPropertyListener(); |
| } |
| |
| void VertexListener::remap(rcp<ViewModelInstance> instance) |
| { |
| if (instance != m_instance) |
| { |
| m_instance = instance; |
| deleteProperties(); |
| createProperties(); |
| } |
| } |
| |
| VertexPropertyListenerBase* VertexListener::createSinglePropertyListener( |
| SymbolType symbolType) |
| { |
| uint16_t propertyKey = 0; |
| float multiplier = 1.0f; |
| switch (symbolType) |
| { |
| case SymbolType::vertexX: |
| propertyKey = VertexBase::xPropertyKey; |
| break; |
| case SymbolType::vertexY: |
| propertyKey = VertexBase::yPropertyKey; |
| break; |
| case SymbolType::inRotation: |
| propertyKey = CubicDetachedVertexBase::inRotationPropertyKey; |
| multiplier = math::PI / 180.0f; |
| break; |
| case SymbolType::outRotation: |
| propertyKey = CubicDetachedVertexBase::outRotationPropertyKey; |
| multiplier = math::PI / 180.0f; |
| break; |
| case SymbolType::inDistance: |
| propertyKey = CubicDetachedVertexBase::inDistancePropertyKey; |
| break; |
| case SymbolType::outDistance: |
| propertyKey = CubicDetachedVertexBase::outDistancePropertyKey; |
| break; |
| default: |
| break; |
| } |
| |
| auto prop = m_instance->propertyValue(symbolType); |
| if (prop != nullptr && prop->is<ViewModelInstanceNumber>()) |
| { |
| auto vpl = new VertexPropertyListenerSingle(m_vertex, |
| this, |
| prop, |
| multiplier, |
| propertyKey); |
| return vpl; |
| } |
| return nullptr; |
| } |
| |
| VertexPropertyListenerBase* VertexListener::createDistancePropertyListener() |
| { |
| |
| auto prop = m_instance->propertyValue(SymbolType::distance); |
| if (prop != nullptr && prop->is<ViewModelInstanceNumber>()) |
| { |
| std::vector<uint16_t> keys = {}; |
| auto inPropKey = CubicDetachedVertexBase::inDistancePropertyKey; |
| keys.push_back(inPropKey); |
| auto outPropKey = CubicDetachedVertexBase::outDistancePropertyKey; |
| keys.push_back(outPropKey); |
| auto vpl = |
| new VertexPropertyListenerMulti(m_vertex, this, prop, 1.0f, keys); |
| return vpl; |
| } |
| return nullptr; |
| } |
| |
| VertexPropertyListenerBase* VertexListener::createRotationPropertyListener() |
| { |
| |
| auto prop = m_instance->propertyValue(SymbolType::rotation); |
| if (prop != nullptr && prop->is<ViewModelInstanceNumber>()) |
| { |
| std::vector<uint16_t> keys = {}; |
| auto inPropKey = CubicDetachedVertexBase::inRotationPropertyKey; |
| keys.push_back(inPropKey); |
| auto outPropKey = CubicDetachedVertexBase::outRotationPropertyKey; |
| keys.push_back(outPropKey); |
| auto vpl = new VertexPropertyListenerMulti(m_vertex, |
| this, |
| prop, |
| math::PI / 180.0f, |
| keys); |
| return vpl; |
| } |
| return nullptr; |
| } |
| |
| void VertexListener::createInPointPropertyListener() |
| { |
| auto xProp = m_instance->propertyValue(SymbolType::cubicVertexInPointX); |
| auto yProp = m_instance->propertyValue(SymbolType::cubicVertexInPointY); |
| if ((xProp != nullptr && xProp->is<ViewModelInstanceNumber>()) || |
| (yProp != nullptr && yProp->is<ViewModelInstanceNumber>())) |
| { |
| auto vpl = new VertexPropertyListenerPoint( |
| m_vertex, |
| this, |
| xProp, |
| yProp, |
| CubicDetachedVertexBase::inDistancePropertyKey, |
| CubicDetachedVertexBase::inRotationPropertyKey); |
| |
| vpl->writeValue(); |
| m_properties.push_back(vpl); |
| } |
| } |
| |
| void VertexListener::createOutPointPropertyListener() |
| { |
| auto xProp = m_instance->propertyValue(SymbolType::cubicVertexOutPointX); |
| auto yProp = m_instance->propertyValue(SymbolType::cubicVertexOutPointY); |
| if ((xProp != nullptr && xProp->is<ViewModelInstanceNumber>()) || |
| (yProp != nullptr && yProp->is<ViewModelInstanceNumber>())) |
| { |
| auto vpl = new VertexPropertyListenerPoint( |
| m_vertex, |
| this, |
| xProp, |
| yProp, |
| CubicDetachedVertexBase::outDistancePropertyKey, |
| CubicDetachedVertexBase::outRotationPropertyKey); |
| |
| vpl->writeValue(); |
| m_properties.push_back(vpl); |
| } |
| } |
| |
| void VertexListener::createPropertyListener(SymbolType symbolType) |
| { |
| VertexPropertyListenerBase* listener = nullptr; |
| switch (symbolType) |
| { |
| case SymbolType::vertexX: |
| case SymbolType::vertexY: |
| case SymbolType::inRotation: |
| case SymbolType::outRotation: |
| case SymbolType::inDistance: |
| case SymbolType::outDistance: |
| listener = createSinglePropertyListener(symbolType); |
| break; |
| case SymbolType::distance: |
| listener = createDistancePropertyListener(); |
| break; |
| case SymbolType::rotation: |
| listener = createRotationPropertyListener(); |
| break; |
| default: |
| break; |
| } |
| if (listener != nullptr) |
| { |
| listener->writeValue(); |
| m_properties.push_back(listener); |
| } |
| } |
| |
| VertexPropertyListenerBase::VertexPropertyListenerBase( |
| Vertex* vertex, |
| VertexListener* vertexListener) : |
| m_vertex(vertex), m_vertexListener(vertexListener) |
| {} |
| |
| void VertexPropertyListenerBase::addDirt(ComponentDirt value, bool recurse) |
| { |
| writeValue(); |
| m_vertexListener->markDirty(); |
| } |
| |
| VertexPropertyListener::VertexPropertyListener( |
| Vertex* vertex, |
| VertexListener* vertexListener, |
| ViewModelInstanceValue* instanceValue, |
| float multiplier) : |
| VertexPropertyListenerBase(vertex, vertexListener), |
| m_instanceValue(instanceValue), |
| m_multiplier(multiplier) |
| { |
| |
| if (m_instanceValue != nullptr) |
| { |
| m_instanceValue->addDependent(this); |
| } |
| } |
| |
| VertexPropertyListener::~VertexPropertyListener() |
| { |
| |
| if (m_instanceValue != nullptr) |
| { |
| m_instanceValue->removeDependent(this); |
| } |
| } |
| |
| VertexPropertyListenerSingle::VertexPropertyListenerSingle( |
| Vertex* vertex, |
| VertexListener* vertexListener, |
| ViewModelInstanceValue* instanceValue, |
| float multiplier, |
| uint16_t propertyKey) : |
| VertexPropertyListener(vertex, vertexListener, instanceValue, multiplier), |
| m_propertyKey(propertyKey) |
| {} |
| |
| VertexPropertyListenerSingle::~VertexPropertyListenerSingle() {} |
| |
| void VertexPropertyListenerSingle::writeValue() |
| { |
| CoreRegistry::setDouble( |
| m_vertex, |
| m_propertyKey, |
| m_instanceValue->as<ViewModelInstanceNumber>()->propertyValue() * |
| m_multiplier); |
| } |
| |
| VertexPropertyListenerMulti::VertexPropertyListenerMulti( |
| Vertex* vertex, |
| VertexListener* vertexListener, |
| ViewModelInstanceValue* instanceValue, |
| float multiplier, |
| std::vector<uint16_t> propertyKeys) : |
| VertexPropertyListener(vertex, vertexListener, instanceValue, multiplier), |
| m_propertyKeys(propertyKeys) |
| {} |
| |
| void VertexPropertyListenerMulti::writeValue() |
| { |
| for (auto& key : m_propertyKeys) |
| { |
| |
| CoreRegistry::setDouble( |
| m_vertex, |
| key, |
| m_instanceValue->as<ViewModelInstanceNumber>()->propertyValue() * |
| m_multiplier); |
| } |
| } |
| |
| VertexPropertyListenerPoint::VertexPropertyListenerPoint( |
| Vertex* vertex, |
| VertexListener* vertexListener, |
| ViewModelInstanceValue* xValue, |
| ViewModelInstanceValue* yValue, |
| uint16_t distKey, |
| uint16_t rotKey) : |
| VertexPropertyListenerBase(vertex, vertexListener), |
| m_xValue(xValue), |
| m_yValue(yValue), |
| m_distKey(distKey), |
| m_rotKey(rotKey) |
| { |
| if (m_xValue) |
| { |
| m_xValue->addDependent(this); |
| } |
| if (m_yValue) |
| { |
| m_yValue->addDependent(this); |
| } |
| } |
| |
| VertexPropertyListenerPoint::~VertexPropertyListenerPoint() |
| { |
| |
| if (m_xValue) |
| { |
| m_xValue->removeDependent(this); |
| } |
| if (m_yValue) |
| { |
| m_yValue->removeDependent(this); |
| } |
| } |
| |
| void VertexPropertyListenerPoint::writeValue() |
| { |
| auto x = |
| m_xValue ? m_xValue->as<ViewModelInstanceNumber>()->propertyValue() : 0; |
| auto y = |
| m_yValue ? m_yValue->as<ViewModelInstanceNumber>()->propertyValue() : 0; |
| auto vec = Vec2D(x, y); |
| auto length = vec.length(); |
| auto rotation = std::atan2(vec.y, vec.x); |
| CoreRegistry::setDouble(m_vertex, m_distKey, length); |
| CoreRegistry::setDouble(m_vertex, m_rotKey, rotation); |
| } |
| |
| ListPath::~ListPath() |
| { |
| for (auto& vertex : m_vertexListeners) |
| { |
| delete vertex; |
| } |
| } |
| |
| void ListPath::updateList(std::vector<rcp<ViewModelInstanceListItem>>* list) |
| { |
| auto currentVertexSize = m_vertexListeners.size(); |
| size_t index = 0; |
| for (auto& listItem : *list) |
| { |
| auto instance = listItem->viewModelInstance(); |
| if (instance != nullptr) |
| { |
| if (index >= currentVertexSize) |
| { |
| // We treat all vertices as CubicDetached vertices because all |
| // others can be expressed by this one. There doesn't seem to be |
| // significant performance implications of doing so. But in the |
| // future it might be valuable to be smarter and build the |
| // corresponding one. |
| auto vertex = new CubicDetachedVertex(); |
| auto vertexListener = |
| new VertexListener(vertex, instance, this); |
| m_vertexListeners.push_back(vertexListener); |
| addVertex(vertex); |
| } |
| else |
| { |
| auto vertexListener = m_vertexListeners[index]; |
| vertexListener->remap(instance); |
| } |
| index++; |
| } |
| } |
| while (m_vertexListeners.size() > index) |
| { |
| auto vertex = m_vertexListeners.back(); |
| m_vertexListeners.pop_back(); |
| popVertex(); |
| delete vertex; |
| } |
| markPathDirty(); |
| } |