diff --git a/examples/sample-scenes/assets/Organisms/man.weird b/examples/sample-scenes/assets/Organisms/man.weird index e7c653b8..d1d205f7 100644 --- a/examples/sample-scenes/assets/Organisms/man.weird +++ b/examples/sample-scenes/assets/Organisms/man.weird @@ -1,9 +1,9 @@ { "camera": { "position": [ - -3.5375001430511475, - 1.024999976158142, - 15.000022888183594 + 0.049999963492155075, + -1.7374986410140991, + 5.00000524520874 ], "rotation": [ 0.0, @@ -22,7 +22,7 @@ "isStatic": false, "materialId": 15 }, - "id": 33, + "id": 35, "rigidBody2D": { "physicsPosition": [ 0.0, @@ -53,7 +53,7 @@ "isStatic": false, "materialId": 15 }, - "id": 34, + "id": 36, "rigidBody2D": { "physicsPosition": [ -1.0, @@ -84,7 +84,7 @@ "isStatic": false, "materialId": 15 }, - "id": 35, + "id": 37, "rigidBody2D": { "physicsPosition": [ -1.0, @@ -115,7 +115,7 @@ "isStatic": false, "materialId": 15 }, - "id": 36, + "id": 38, "rigidBody2D": { "physicsPosition": [ -1.0, @@ -146,7 +146,7 @@ "isStatic": false, "materialId": 15 }, - "id": 37, + "id": 39, "rigidBody2D": { "physicsPosition": [ 1.0, @@ -177,7 +177,7 @@ "isStatic": false, "materialId": 15 }, - "id": 38, + "id": 40, "rigidBody2D": { "physicsPosition": [ 1.0, @@ -208,7 +208,7 @@ "isStatic": false, "materialId": 15 }, - "id": 39, + "id": 41, "rigidBody2D": { "physicsPosition": [ 1.0, @@ -239,7 +239,7 @@ "isStatic": false, "materialId": 11 }, - "id": 40, + "id": 42, "rigidBody2D": { "physicsPosition": [ 0.0, @@ -270,7 +270,7 @@ "isStatic": false, "materialId": 8 }, - "id": 41, + "id": 43, "rigidBody2D": { "physicsPosition": [ -1.0, @@ -301,7 +301,7 @@ "isStatic": false, "materialId": 8 }, - "id": 42, + "id": 44, "rigidBody2D": { "physicsPosition": [ 1.0, @@ -332,7 +332,7 @@ "isStatic": false, "materialId": 8 }, - "id": 43, + "id": 45, "rigidBody2D": { "physicsPosition": [ 0.0, @@ -363,7 +363,7 @@ "isStatic": false, "materialId": 8 }, - "id": 44, + "id": 46, "rigidBody2D": { "physicsPosition": [ 2.0, @@ -394,7 +394,7 @@ "isStatic": false, "materialId": 8 }, - "id": 45, + "id": 47, "rigidBody2D": { "physicsPosition": [ 3.0, @@ -425,7 +425,7 @@ "isStatic": false, "materialId": 11 }, - "id": 46, + "id": 48, "rigidBody2D": { "physicsPosition": [ 4.0, @@ -456,7 +456,7 @@ "isStatic": false, "materialId": 8 }, - "id": 47, + "id": 49, "rigidBody2D": { "physicsPosition": [ -2.0, @@ -487,7 +487,7 @@ "isStatic": false, "materialId": 8 }, - "id": 48, + "id": 50, "rigidBody2D": { "physicsPosition": [ -3.0, @@ -518,7 +518,7 @@ "isStatic": false, "materialId": 11 }, - "id": 49, + "id": 51, "rigidBody2D": { "physicsPosition": [ -4.0, @@ -548,87 +548,75 @@ "physics": { "distanceConstraints": [ { - "A": 1, - "B": 0, + "A": 3, + "B": 2, "distance": 1.0, "k": 1.0 }, { - "A": 0, - "B": 4, + "A": 2, + "B": 1, "distance": 1.0, "k": 1.0 }, { - "A": 4, - "B": 9, + "A": 1, + "B": 0, "distance": 1.0, "k": 1.0 }, { - "A": 9, - "B": 10, + "A": 0, + "B": 4, "distance": 1.0, "k": 1.0 }, { - "A": 10, - "B": 7, + "A": 4, + "B": 5, "distance": 1.0, "k": 1.0 }, { - "A": 10, - "B": 8, + "A": 5, + "B": 6, "distance": 1.0, "k": 1.0 }, { - "A": 8, - "B": 1, + "A": 4, + "B": 9, "distance": 1.0, "k": 1.0 }, { - "A": 10, - "B": 0, + "A": 0, + "B": 10, "distance": 1.0, "k": 1.0 }, { "A": 1, - "B": 10, - "distance": 1.4142135381698608, + "B": 8, + "distance": 1.0, "k": 1.0 }, { "A": 8, - "B": 0, - "distance": 1.4142135381698608, + "B": 10, + "distance": 1.0, "k": 1.0 }, { - "A": 0, + "A": 10, "B": 9, - "distance": 1.4142135381698608, + "distance": 1.0, "k": 1.0 }, { "A": 10, - "B": 4, - "distance": 1.4142135381698608, - "k": 1.0 - }, - { - "A": 7, - "B": 8, - "distance": 1.4142135381698608, - "k": 1.0 - }, - { - "A": 7, - "B": 9, - "distance": 1.4142135381698608, + "B": 7, + "distance": 1.0, "k": 1.0 }, { @@ -669,26 +657,26 @@ }, { "A": 1, - "B": 2, - "distance": 1.0, + "B": 10, + "distance": 1.4142135381698608, "k": 1.0 }, { - "A": 2, - "B": 3, - "distance": 1.0, + "A": 0, + "B": 8, + "distance": 1.4142135381698608, "k": 1.0 }, { - "A": 4, - "B": 5, - "distance": 1.0, + "A": 0, + "B": 9, + "distance": 1.4142135381698608, "k": 1.0 }, { - "A": 5, - "B": 6, - "distance": 1.0, + "A": 10, + "B": 4, + "distance": 1.4142135381698608, "k": 1.0 }, { @@ -705,8 +693,8 @@ }, { "A": 3, - "B": 14, - "distance": 3.1622776985168457, + "B": 1, + "distance": 2.0, "k": 0.0024806864093989134 }, { @@ -716,15 +704,69 @@ "k": 0.0024806864093989134 }, { - "A": 0, - "B": 6, + "A": 6, + "B": 0, "distance": 2.2360680103302, "k": 0.0024806864093989134 }, { "A": 6, - "B": 13, - "distance": 4.242640495300293, + "B": 4, + "distance": 2.0, + "k": 0.0024806864093989134 + }, + { + "A": 2, + "B": 0, + "distance": 1.4142135381698608, + "k": 0.0024806864093989134 + }, + { + "A": 0, + "B": 5, + "distance": 1.4142135381698608, + "k": 0.0024806864093989134 + }, + { + "A": 1, + "B": 14, + "distance": 1.4142135381698608, + "k": 0.0024806864093989134 + }, + { + "A": 4, + "B": 11, + "distance": 1.4142135381698608, + "k": 0.0024806864093989134 + }, + { + "A": 8, + "B": 7, + "distance": 1.4142135381698608, + "k": 0.0024806864093989134 + }, + { + "A": 7, + "B": 9, + "distance": 1.4142135381698608, + "k": 0.0024806864093989134 + }, + { + "A": 14, + "B": 2, + "distance": 2.2360680103302, + "k": 0.0024806864093989134 + }, + { + "A": 14, + "B": 3, + "distance": 3.1622776985168457, + "k": 0.0024806864093989134 + }, + { + "A": 5, + "B": 11, + "distance": 2.2360680103302, "k": 0.0024806864093989134 }, { @@ -733,10 +775,52 @@ "distance": 3.1622776985168457, "k": 0.0024806864093989134 }, + { + "A": 5, + "B": 9, + "distance": 2.0, + "k": 0.0024806864093989134 + }, + { + "A": 6, + "B": 9, + "distance": 3.0, + "k": 0.0024806864093989134 + }, + { + "A": 2, + "B": 8, + "distance": 2.0, + "k": 0.0024806864093989134 + }, { "A": 3, - "B": 16, - "distance": 4.242640495300293, + "B": 8, + "distance": 3.0, + "k": 0.0024806864093989134 + }, + { + "A": 16, + "B": 1, + "distance": 3.1622776985168457, + "k": 0.0024806864093989134 + }, + { + "A": 4, + "B": 13, + "distance": 3.1622776985168457, + "k": 0.0024806864093989134 + }, + { + "A": 1, + "B": 6, + "distance": 2.8284270763397217, + "k": 0.0024806864093989134 + }, + { + "A": 3, + "B": 4, + "distance": 2.8284270763397217, "k": 0.0024806864093989134 } ], @@ -745,8 +829,16 @@ }, "tags": [ { - "entityId": 40, + "entityId": 42, "name": "head" + }, + { + "entityId": 38, + "name": "foot_left" + }, + { + "entityId": 41, + "name": "foot_right" } ], "version": 1 diff --git a/examples/sample-scenes/include/DestroyScene.h b/examples/sample-scenes/include/DestroyScene.h index a669833d..3673db1a 100644 --- a/examples/sample-scenes/include/DestroyScene.h +++ b/examples/sample-scenes/include/DestroyScene.h @@ -87,9 +87,9 @@ class DestroyScene : public Scene m_testShape = addShape(DefaultShapes::BOX, variables, 2, CombinationType::Addition); } - void onShapeCollision(WeirdEngine::ShapeCollisionEvent& event) override + void onEntityShapeCollision(WeirdEngine::EntityShapeCollisionEvent& event) override { - event.friction *= 100.0f; + event.raw.friction *= 100.0f; m_collisionDetected = true; } diff --git a/examples/sample-scenes/include/MoleculeEditor.h b/examples/sample-scenes/include/MoleculeEditor.h index a81b64ec..7ac3ad95 100644 --- a/examples/sample-scenes/include/MoleculeEditor.h +++ b/examples/sample-scenes/include/MoleculeEditor.h @@ -38,6 +38,7 @@ class MoleculeEditor : public Scene Drag, Spring, Distance, + Remove, TagEditor, Material }; @@ -86,7 +87,7 @@ class MoleculeEditor : public Scene int m_selectedMaterial = 1; ToolMode m_toolMode = ToolMode::Drag; - std::array m_toolToggles{}; + std::array m_toolToggles{}; Entity m_gravityToggleEntity = static_cast(-1); Entity m_gridToggleEntity = static_cast(-1); @@ -353,8 +354,8 @@ class MoleculeEditor : public Scene void buildToolbar() { - const char* labels[] = {"drag", "spring", "distance", "tag", "material"}; - for (int i = 0; i < 5; i++) + const char* labels[] = {"drag", "spring", "distance", "remove", "tag", "material"}; + for (int i = 0; i < 6; i++) { float y = (Display::height - TOOL_Y_START) - (i * TOOL_SPACING); float p[8]{TOOL_X, y, TOOL_BTN_HALF, TOOL_BTN_HALF}; @@ -401,7 +402,7 @@ class MoleculeEditor : public Scene void syncToolbar() { int activated = -1; - for (int i = 0; i < 5; i++) + for (int i = 0; i < 6; i++) { auto& t = m_ecs.getComponent(m_toolToggles[i]); if (t.active && t.state == ButtonState::Down) @@ -413,7 +414,7 @@ class MoleculeEditor : public Scene if (activated >= 0) { m_toolMode = static_cast(activated); - for (int i = 0; i < 5; i++) + for (int i = 0; i < 6; i++) { if (i != activated) m_ecs.getComponent(m_toolToggles[i]).active = false; @@ -535,7 +536,7 @@ class MoleculeEditor : public Scene if (rightDown && !m_rightWasDown) { - if (m_toolMode == ToolMode::Spring || m_toolMode == ToolMode::Distance) + if (m_toolMode == ToolMode::Spring || m_toolMode == ToolMode::Distance || m_toolMode == ToolMode::Remove) onConstraintStart(); else if (m_toolMode == ToolMode::TagEditor) onTagEditorRightClick(); @@ -653,8 +654,15 @@ class MoleculeEditor : public Scene Entity hit = pickBallAtMouse(); if (hit != static_cast(-1) && hit != m_constraintStartBall) { - LinkType type = (m_toolMode == ToolMode::Distance) ? LinkType::Distance : LinkType::Spring; - addConstraintLink(m_constraintStartBall, hit, type); + if (m_toolMode == ToolMode::Remove) + { + removeConstraintLink(m_constraintStartBall, hit); + } + else + { + LinkType type = (m_toolMode == ToolMode::Distance) ? LinkType::Distance : LinkType::Spring; + addConstraintLink(m_constraintStartBall, hit, type); + } } m_constraintStartBall = static_cast(-1); @@ -738,6 +746,34 @@ class MoleculeEditor : public Scene m_links.push_back({a, b, idA, idB, restDistance, line, type}); } + void removeConstraintLink(Entity a, Entity b) + { + for (size_t i = 0; i < m_links.size();) + { + DistanceLink& link = m_links[i]; + bool sameDir = (link.a == a && link.b == b); + bool reverseDir = (link.a == b && link.b == a); + if (!sameDir && !reverseDir) + { + ++i; + continue; + } + + if (m_draggedLink == &link) + m_draggedLink = nullptr; + + m_ecs.destroyEntity(link.lineEntity); + m_links.erase(m_links.begin() + i); + } + + int idA = getSimulationId(a); + int idB = getSimulationId(b); + if (idA < 0 || idB < 0) + return; + + m_simulation2D.removeDistanceConstraint(static_cast(idA), static_cast(idB)); + } + void handleConstraintLineClicks() { // Detect click-down via ShapeButton state. @@ -1064,8 +1100,8 @@ class MoleculeEditor : public Scene << (allConstraints.size() - prevConstraintCount) << " links from " << path << "\n"; } - void onShapeCollision(WeirdEngine::ShapeCollisionEvent& event) override + void onEntityShapeCollision(WeirdEngine::EntityShapeCollisionEvent& event) override { - event.friction *= 100.0f; + event.raw.friction *= 100.0f; } }; diff --git a/examples/sample-scenes/include/WalkScene.h b/examples/sample-scenes/include/WalkScene.h new file mode 100644 index 00000000..29589495 --- /dev/null +++ b/examples/sample-scenes/include/WalkScene.h @@ -0,0 +1,185 @@ +#pragma once + +#include + +#include + +#include "globals.h" + +using namespace WeirdEngine; + +struct Foot : public Component +{ + Foot() {}; + + vec2 direction = vec2(1.0f, 0.0f); + vec2 initialPos = vec2(0.0f, 0.0f); + float forceMagnitude = 1.0f; + bool directionChanged = false; + float t = 0.0f; + bool onFloor = false; + bool stepStarted = false; +}; + +class WalkScene : public Scene +{ +public: + WalkScene(const PhysicsSettings& settings) + : Scene(settings) {}; + +private: + + Entity m_head; + + // Inherited via Scene + void onStart() override + { + m_debugInput = true; + m_debugFly = true; + + auto tags = loadWeirdFile(ASSETS_PATH "Organisms/man.weird"); + + Entity firstCreated = static_cast(m_ecs.getEntityCount()); + + Entity lastCreated = static_cast(m_ecs.getEntityCount()); + + for (Entity e = 0; e < (lastCreated - firstCreated); e++) + { + auto& t = m_ecs.getComponent(firstCreated + e); + t.position += vec3(-10.0f, 0.0f, 0.0f); + } + + Entity leftFootEntity = tags["foot_left"]; + m_ecs.addComponent(leftFootEntity); + + Entity rightFootEntity = tags["foot_right"]; + m_ecs.addComponent(rightFootEntity); + + m_head = tags["head"]; + + + float boundsVars2[8]{0.0f, -24.0f, 200.0f, 20.0f}; + Entity inside = addShape(DefaultShapes::BOX, boundsVars2, DisplaySettings::LightGray, CombinationType::Addition); + + m_ecs.getComponent(m_mainCamera).position = g_cameraPositon; + + m_simulation2D.setGravity(-10.0f); + } + + void onUpdate(float delta) override + { + g_cameraPositon = m_ecs.getComponent(m_mainCamera).position; + + if (Input::GetKeyDown(Input::Q)) + { + setSceneComplete(); + } + } + + int m_currentFoot = 0; + bool m_feetTouching = false; + void onPhysicsStep() override + { + auto componentArray = m_ecs.getComponentArray(); + auto rigidBodies = m_ecs.getComponentArray(); + + for (size_t i = 0; i < componentArray->getSize(); i++) + { + auto& foot = componentArray->getDataAtIdx(i); + auto& rb = rigidBodies->getDataFromEntity(foot.Owner); + + if(i != m_currentFoot) + { + if(foot.onFloor) + m_simulation2D.fix(rb.simulationId); + continue; + } + + auto& headRB = m_ecs.getComponent(m_head); + m_simulation2D.addForce(headRB.simulationId, vec2(0.0f, 1.0f)); + + m_simulation2D.unFix(rb.simulationId); + + // Start step + if (!foot.stepStarted) + { + if (foot.onFloor) + { + foot.initialPos = m_simulation2D.getPosition(rb.simulationId); + foot.stepStarted = true; + foot.t = 0.0f; + // m_simulation2D.setPosition(rb.simulationId, foot.initialPos + vec2(0.0f, 0.1f)); + m_simulation2D.unFix(rb.simulationId); + } + } + else + { + // End step + if (foot.onFloor && foot.t > 0.1f) + { + foot.stepStarted = false; + m_currentFoot = (m_currentFoot + 1) % componentArray->getSize(); + // std::cout << "Switching foot: " << m_currentFoot << std::endl; + } + else + { + vec2 f; + + if (foot.t < 0.1f) + { + f = (foot.direction + vec2(0.0f, 1.0f)) * foot.forceMagnitude; + } + else if (foot.t < 0.25f) + { + f = (foot.direction + vec2(0.0f, 0.0f)) * foot.forceMagnitude; + } + else + { + f = (foot.direction + vec2(0.0f, -10.0f * foot.t)) * foot.forceMagnitude * foot.t; + } + + if(m_feetTouching) + f.x = 0.0f; + + m_simulation2D.addForce(rb.simulationId, f); + + // vec2 offset = vec2(-std::sin(foot.t), 1.0f - std::abs(std::cos(2.0f * foot.t))); + // offset.y *= 0.5f; + // m_simulation2D.setPosition(rb.simulationId, foot.initialPos + offset); + foot.t += 0.5f * m_simulation2D.getDeltaTime(); + } + } + } + + m_feetTouching = false; + } + + void onEntityCollision(WeirdEngine::EntityCollisionEvent& event) override + { + Entity entityA = event.entityA; + Entity entityB = event.entityB; + + if (m_ecs.hasComponent(entityA) && m_ecs.hasComponent(entityB)) + { + m_feetTouching = true; + } + } + + void onEntityShapeCollision(WeirdEngine::EntityShapeCollisionEvent& event) override + { + Entity entity = event.entity; + if (m_ecs.hasComponent(entity)) + { + auto& foot = m_ecs.getComponent(entity); + if (event.raw.state == CollisionState::START) + { + foot.onFloor = true; + } + else if (event.raw.state == CollisionState::END) + { + + foot.onFloor = false; + } + } + } +}; diff --git a/examples/sample-scenes/src/main.cpp b/examples/sample-scenes/src/main.cpp index d7fd8780..3938559e 100644 --- a/examples/sample-scenes/src/main.cpp +++ b/examples/sample-scenes/src/main.cpp @@ -10,6 +10,7 @@ #include "MouseCollisionScene.h" #include "RopeScene.h" #include "ShapesCombinations.h" +#include "WalkScene.h" #include "globals.h" #include "MoleculeEditor.h" @@ -21,6 +22,7 @@ WeirdEngine::vec3 g_cameraPositon = vec3(15.0f, 7.5f, 35.0f); int main(int argc, char* argv[]) { SceneManager& sceneManager = SceneManager::getInstance(); + sceneManager.registerScene("shapes"); sceneManager.registerScene("rope"); sceneManager.registerScene("cursor-collision"); @@ -30,6 +32,7 @@ int main(int argc, char* argv[]) sceneManager.registerScene("scene-editor", ASSETS_PATH "example.weird"); sceneManager.registerScene("molecule-editor"); sceneManager.registerScene("life"); + sceneManager.registerScene("walk"); DisplaySettings displaySettings{}; displaySettings.width = 800; diff --git a/include/weird-engine/Scene.h b/include/weird-engine/Scene.h index 9625927d..ac6e4983 100644 --- a/include/weird-engine/Scene.h +++ b/include/weird-engine/Scene.h @@ -21,6 +21,19 @@ namespace WeirdEngine { using namespace ECS; + struct EntityCollisionEvent + { + CollisionEvent& raw; + Entity entityA; + Entity entityB; + }; + + struct EntityShapeCollisionEvent + { + ShapeCollisionEvent& raw; + Entity entity; + }; + constexpr int SOUND_QUEUE_SIZE = 16; // Forward declaration – full definition in SceneSerializer.h @@ -99,7 +112,8 @@ namespace WeirdEngine virtual void onRender(WeirdRenderer::RenderTarget& renderTarget) {}; virtual void onPhysicsStep() {}; virtual void onCollision(WeirdEngine::CollisionEvent& event) {}; - virtual void onShapeCollision(WeirdEngine::ShapeCollisionEvent& event) {}; + virtual void onEntityCollision(WeirdEngine::EntityCollisionEvent& event) {}; + virtual void onEntityShapeCollision(WeirdEngine::EntityShapeCollisionEvent& event) {}; virtual void onDestroy() {}; void setSceneComplete(std::string nextScene = "") @@ -143,6 +157,9 @@ namespace WeirdEngine // Return the entity that owns a tag, or MAX_ENTITIES if none. Entity getEntityByTag(const std::string& name) const; + // Resolve a physics SimulationID to the owning entity. + Entity getEntityForSimulationId(SimulationID simulationId); + SDFRenderSystem2DContext m_2DWorldRenderContext; SDFRenderSystem2DContext m_UIRenderContext; diff --git a/include/weird-physics/Simulation2D.h b/include/weird-physics/Simulation2D.h index ed950c2e..9f23565e 100644 --- a/include/weird-physics/Simulation2D.h +++ b/include/weird-physics/Simulation2D.h @@ -96,6 +96,7 @@ namespace WeirdEngine void addPositionConstraint(SimulationID a, SimulationID b, float distance = 1.0f); void addGravitationalConstraint(SimulationID a, SimulationID b, float gravity); bool setDistanceConstraintDistance(SimulationID a, SimulationID b, float distance); + bool removeDistanceConstraint(SimulationID a, SimulationID b); void fix(SimulationID id); void unFix(SimulationID id); diff --git a/src/weird-engine/Scene.cpp b/src/weird-engine/Scene.cpp index 5a86f215..22846412 100644 --- a/src/weird-engine/Scene.cpp +++ b/src/weird-engine/Scene.cpp @@ -158,15 +158,20 @@ namespace WeirdEngine void Scene::handleCollision(CollisionEvent& event, void* userData) { - // Unsafe cast! Prone to error. Scene* self = static_cast(userData); self->onCollision(event); + + EntityCollisionEvent entityEvent{event, + self->getEntityForSimulationId(event.bodyA), + self->getEntityForSimulationId(event.bodyB)}; + self->onEntityCollision(entityEvent); } void Scene::handleShapeCollision(ShapeCollisionEvent& event, void* userData) { Scene* self = static_cast(userData); - self->onShapeCollision(event); + EntityShapeCollisionEvent entityEvent{event, self->getEntityForSimulationId(event.body)}; + self->onEntityShapeCollision(entityEvent); const float m_soundFalloff = 0.1f; bool spatialAudio = false; @@ -336,6 +341,15 @@ namespace WeirdEngine return it->second; } + Entity Scene::getEntityForSimulationId(SimulationID simulationId) + { + auto rigidBodies = m_ecs.getComponentArray(); + if (simulationId >= static_cast(rigidBodies->getSize())) + return INVALID_ENTITY; + + return rigidBodies->getEntityAtIdx(static_cast(simulationId)); + } + void Scene::saveScene(const std::string& filename) { SceneSerializer::save(*this, filename); diff --git a/src/weird-physics/Simulation2D.cpp b/src/weird-physics/Simulation2D.cpp index 8bf484eb..bbac7654 100644 --- a/src/weird-physics/Simulation2D.cpp +++ b/src/weird-physics/Simulation2D.cpp @@ -963,6 +963,29 @@ namespace WeirdEngine return false; } + bool Simulation2D::removeDistanceConstraint(SimulationID a, SimulationID b) + { + if (a == b) + return false; + + std::lock_guard lock(m_objectMutex); + + size_t previousSize = m_distanceConstraints.size(); + m_distanceConstraints.erase( + std::remove_if(m_distanceConstraints.begin(), m_distanceConstraints.end(), + [a, b](const DistanceConstraint& constraint) + { + bool sameDirection = + (constraint.A == static_cast(a) && constraint.B == static_cast(b)); + bool reverseDirection = + (constraint.A == static_cast(b) && constraint.B == static_cast(a)); + return sameDirection || reverseDirection; + }), + m_distanceConstraints.end()); + + return previousSize != m_distanceConstraints.size(); + } + void Simulation2D::fix(SimulationID id) { std::lock_guard lock(m_fixMutex);