diff options
author | JAROWMR <jarorutjes07@gmail.com> | 2024-12-03 19:32:15 +0100 |
---|---|---|
committer | JAROWMR <jarorutjes07@gmail.com> | 2024-12-03 19:32:15 +0100 |
commit | ff618da3f97237796042fa3664da59ed147bc1da (patch) | |
tree | cfc283425f145b16a2b2e3b1a0a5760e8015a2b1 /src/crepe/system | |
parent | fb018cd8511e9cae408f8ea7d2b73e66a452625b (diff) |
improved comments
Diffstat (limited to 'src/crepe/system')
-rw-r--r-- | src/crepe/system/CollisionSystem.cpp | 167 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.h | 32 | ||||
-rw-r--r-- | src/crepe/system/PhysicsSystem.cpp | 8 |
3 files changed, 89 insertions, 118 deletions
diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index 380cc3d..2d5ce9d 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -10,6 +10,7 @@ #include "api/EventManager.h" #include "api/BoxCollider.h" #include "api/CircleCollider.h" +#include "api/Metadata.h" #include "api/Vector2.h" #include "api/Rigidbody.h" #include "api/Transform.h" @@ -23,18 +24,16 @@ using namespace crepe; void CollisionSystem::update() { - std::vector<CollisionInternal> all_colliders; - ComponentManager & mgr = this->component_manager; game_object_id_t id = 0; - RefVector<Rigidbody> rigidbodies = mgr.get_components_by_type<Rigidbody>(); + RefVector<Rigidbody> rigidbodies = this->component_manager.get_components_by_type<Rigidbody>(); // Collisions can only happen on object with a rigidbody for(Rigidbody& rigidbody : rigidbodies) { if (!rigidbody.active) continue; id = rigidbody.game_object_id; Transform& transform = this->component_manager.get_components_by_id<Transform>(id).front().get(); // Check if the boxcollider is active and has the same id as the rigidbody. - RefVector<BoxCollider> boxcolliders = mgr.get_components_by_type<BoxCollider>(); + RefVector<BoxCollider> boxcolliders = this->component_manager.get_components_by_type<BoxCollider>(); for (BoxCollider& boxcollider : boxcolliders) { if(boxcollider.game_object_id != id) continue; if(!boxcollider.active) continue; @@ -48,7 +47,7 @@ void CollisionSystem::update() { ); } // Check if the circlecollider is active and has the same id as the rigidbody. - RefVector<CircleCollider> circlecolliders = mgr.get_components_by_type<CircleCollider>(); + RefVector<CircleCollider> circlecolliders = this->component_manager.get_components_by_type<CircleCollider>(); for (CircleCollider& circlecollider : circlecolliders) { if(circlecollider.game_object_id != id) continue; if(!circlecollider.active) continue; @@ -64,59 +63,63 @@ void CollisionSystem::update() { } // Check between all colliders if there is a collision - std::vector<std::pair<CollisionInternal,CollisionInternal>> collided = gather_collisions(all_colliders); + std::vector<std::pair<CollisionInternal,CollisionInternal>> collided = this->gather_collisions(all_colliders); // For both objects call the collision handler for (auto& collision_pair : collided) { - collision_handler_request(collision_pair.first,collision_pair.second); - collision_handler_request(collision_pair.second,collision_pair.first); + this->collision_handler_request(collision_pair.first,collision_pair.second); + this->collision_handler_request(collision_pair.second,collision_pair.first); } } -void CollisionSystem::collision_handler_request(CollisionInternal& data1,CollisionInternal& data2){ +void CollisionSystem::collision_handler_request(CollisionInternal& this_data,CollisionInternal& other_data){ - CollisionInternalType type = get_collider_type(data1.collider,data2.collider); - std::pair<vec2,CollisionSystem::Direction> resolution_data = collision_handler(data1,data2,type); + CollisionInternalType type = this->get_collider_type(this_data.collider,other_data.collider); + std::pair<vec2,CollisionSystem::Direction> resolution_data = this->collision_handler(this_data,other_data,type); - OptionalRef<Collider> collider1; - OptionalRef<Collider> collider2; + OptionalRef<Metadata> this_metadata = this->component_manager.get_components_by_id<Metadata>(this_data.id).front().get(); + OptionalRef<Metadata> other_metadata = this->component_manager.get_components_by_id<Metadata>(other_data.id).front().get(); + OptionalRef<Collider> this_collider; + OptionalRef<Collider> other_collider; switch (type) { case CollisionInternalType::BOX_BOX:{ - collider1 = std::get<std::reference_wrapper<BoxCollider>>(data1.collider); - collider2 = std::get<std::reference_wrapper<BoxCollider>>(data2.collider); + this_collider = std::get<std::reference_wrapper<BoxCollider>>(this_data.collider); + other_collider = std::get<std::reference_wrapper<BoxCollider>>(other_data.collider); break; } case CollisionInternalType::BOX_CIRCLE:{ - collider1 = std::get<std::reference_wrapper<BoxCollider>>(data1.collider); - collider2 = std::get<std::reference_wrapper<CircleCollider>>(data2.collider); + this_collider = std::get<std::reference_wrapper<BoxCollider>>(this_data.collider); + other_collider = std::get<std::reference_wrapper<CircleCollider>>(other_data.collider); break; } case CollisionInternalType::CIRCLE_BOX:{ - collider1 = std::get<std::reference_wrapper<CircleCollider>>(data1.collider); - collider2 = std::get<std::reference_wrapper<BoxCollider>>(data2.collider); + this_collider = std::get<std::reference_wrapper<CircleCollider>>(this_data.collider); + other_collider = std::get<std::reference_wrapper<BoxCollider>>(other_data.collider); break; } case CollisionInternalType::CIRCLE_CIRCLE:{ - collider1 = std::get<std::reference_wrapper<CircleCollider>>(data1.collider); - collider2 = std::get<std::reference_wrapper<CircleCollider>>(data2.collider); + this_collider = std::get<std::reference_wrapper<CircleCollider>>(this_data.collider); + other_collider = std::get<std::reference_wrapper<CircleCollider>>(other_data.collider); break; } } - + // collision info crepe::CollisionSystem::CollisionInfo collision_info{ - .first_collider = collider1, - .first_transform = data1.transform, - .first_rigidbody = data1.rigidbody, - .second_collider = collider2, - .second_transform = data2.transform, - .second_rigidbody = data2.rigidbody, + .this_collider = this_collider, + .this_transform = this_data.transform, + .this_rigidbody = this_data.rigidbody, + .this_metadata = this_metadata, + .other_collider = other_collider, + .other_transform = other_data.transform, + .other_rigidbody = other_data.rigidbody, + .other_metadata = other_metadata, .resolution = resolution_data.first, .resolution_direction = resolution_data.second, }; // Determine if static needs to be called - determine_collision_handler(collision_info); + this->determine_collision_handler(collision_info); } @@ -126,33 +129,33 @@ std::pair<vec2,CollisionSystem::Direction> CollisionSystem::collision_handler(Co case CollisionInternalType::BOX_BOX: { const BoxCollider & collider1 = std::get<std::reference_wrapper<BoxCollider>>(data1.collider); const BoxCollider & collider2 = std::get<std::reference_wrapper<BoxCollider>>(data2.collider); - vec2 collider_pos1 = get_current_position(collider1.offset, data1.transform, data1.rigidbody); - vec2 collider_pos2 = get_current_position(collider2.offset, data2.transform, data2.rigidbody); - resolution = get_box_box_resolution(collider1,collider2,collider_pos1,collider_pos2); + vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, data1.rigidbody); + vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, data2.rigidbody); + resolution = this->get_box_box_resolution(collider1,collider2,collider_pos1,collider_pos2); break; } case CollisionInternalType::BOX_CIRCLE: { const BoxCollider & collider1 = std::get<std::reference_wrapper<BoxCollider>>(data1.collider); const CircleCollider & collider2 = std::get<std::reference_wrapper<CircleCollider>>(data2.collider); - vec2 collider_pos1 = get_current_position(collider1.offset, data1.transform, data1.rigidbody); - vec2 collider_pos2 = get_current_position(collider2.offset, data2.transform, data2.rigidbody); - resolution = get_circle_box_resolution(collider2,collider1,collider_pos2,collider_pos1); + vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, data1.rigidbody); + vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, data2.rigidbody); + resolution = this->get_circle_box_resolution(collider2,collider1,collider_pos2,collider_pos1); break; } case CollisionInternalType::CIRCLE_CIRCLE: { const CircleCollider & collider1 = std::get<std::reference_wrapper<CircleCollider>>(data1.collider); const CircleCollider & collider2 = std::get<std::reference_wrapper<CircleCollider>>(data2.collider); - vec2 collider_pos1 = get_current_position(collider1.offset, data1.transform, data1.rigidbody); - vec2 collider_pos2 = get_current_position(collider2.offset, data2.transform, data2.rigidbody); - resolution = get_circle_circle_resolution(collider1,collider2,collider_pos1,collider_pos2); + vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, data1.rigidbody); + vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, data2.rigidbody); + resolution = this->get_circle_circle_resolution(collider1,collider2,collider_pos1,collider_pos2); break; } case CollisionInternalType::CIRCLE_BOX: { const CircleCollider & collider1 = std::get<std::reference_wrapper<CircleCollider>>(data1.collider); const BoxCollider & collider2 = std::get<std::reference_wrapper<BoxCollider>>(data2.collider); - vec2 collider_pos1 = get_current_position(collider1.offset, data1.transform, data1.rigidbody); - vec2 collider_pos2 = get_current_position(collider2.offset, data2.transform, data2.rigidbody); - resolution = get_circle_box_resolution(collider1,collider2,collider_pos1,collider_pos2); + vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, data1.rigidbody); + vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, data2.rigidbody); + resolution = this->get_circle_box_resolution(collider1,collider2,collider_pos1,collider_pos2); break; } } @@ -179,10 +182,10 @@ vec2 CollisionSystem::get_box_box_resolution(const BoxCollider& box_collider1,co vec2 delta = final_position2 - final_position1; // Compute half-dimensions of the boxes - float half_width1 = box_collider1.width / 2.0; - float half_height1 = box_collider1.height / 2.0; - float half_width2 = box_collider2.width / 2.0; - float half_height2 = box_collider2.height / 2.0; + float half_width1 = box_collider1.dimensions.x / 2.0; + float half_height1 = box_collider1.dimensions.y / 2.0; + float half_width2 = box_collider2.dimensions.x / 2.0; + float half_height2 = box_collider2.dimensions.y / 2.0; // Calculate overlaps along X and Y axes float overlap_x = (half_width1 + half_width2) - std::abs(delta.x); @@ -234,8 +237,8 @@ vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider& circle_col vec2 delta = circle_position - box_position; // Compute half-dimensions of the box - float half_width = box_collider.width / 2.0f; - float half_height = box_collider.height / 2.0f; + float half_width = box_collider.dimensions.x / 2.0f; + float half_height = box_collider.dimensions.y / 2.0f; // Clamp circle center to the nearest point on the box vec2 closest_point; @@ -261,37 +264,37 @@ vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider& circle_col void CollisionSystem::determine_collision_handler(CollisionInfo& info){ // Check rigidbody type for static - if(info.first_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) return; + if(info.this_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) return; // If second body is static perform the static collision handler in this system - if(info.second_rigidbody.data.body_type == Rigidbody::BodyType::STATIC){ + if(info.other_rigidbody.data.body_type == Rigidbody::BodyType::STATIC){ static_collision_handler(info); }; // Call collision event for user CollisionEvent data(info); - EventManager::get_instance().trigger_event<CollisionEvent>(data, info.first_collider.game_object_id); + EventManager::get_instance().trigger_event<CollisionEvent>(data, info.this_collider.game_object_id); } void CollisionSystem::static_collision_handler(CollisionInfo& info){ // Move object back using calculate move back value - info.first_transform.position += info.resolution; + info.this_transform.position += info.resolution; // If bounce is enabled mirror velocity - if(info.first_rigidbody.data.elastisity > 0) { + if(info.this_rigidbody.data.elastisity_coefficient > 0) { if(info.resolution_direction == Direction::BOTH) { - info.first_rigidbody.data.linear_velocity.y = -info.first_rigidbody.data.linear_velocity.y * info.first_rigidbody.data.elastisity; - info.first_rigidbody.data.linear_velocity.x = -info.first_rigidbody.data.linear_velocity.x * info.first_rigidbody.data.elastisity; + info.this_rigidbody.data.linear_velocity.y = -info.this_rigidbody.data.linear_velocity.y * info.this_rigidbody.data.elastisity_coefficient; + info.this_rigidbody.data.linear_velocity.x = -info.this_rigidbody.data.linear_velocity.x * info.this_rigidbody.data.elastisity_coefficient; } else if(info.resolution_direction == Direction::Y_DIRECTION) { - info.first_rigidbody.data.linear_velocity.y = -info.first_rigidbody.data.linear_velocity.y * info.first_rigidbody.data.elastisity; + info.this_rigidbody.data.linear_velocity.y = -info.this_rigidbody.data.linear_velocity.y * info.this_rigidbody.data.elastisity_coefficient; } else if(info.resolution_direction == Direction::X_DIRECTION){ - info.first_rigidbody.data.linear_velocity.x = -info.first_rigidbody.data.linear_velocity.x * info.first_rigidbody.data.elastisity; + info.this_rigidbody.data.linear_velocity.x = -info.this_rigidbody.data.linear_velocity.x * info.this_rigidbody.data.elastisity_coefficient; } } // Stop movement if bounce is disabled else { - info.first_rigidbody.data.linear_velocity = {0,0}; + info.this_rigidbody.data.linear_velocity = {0,0}; } } @@ -332,24 +335,6 @@ std::vector<std::pair<CollisionSystem::CollisionInternal,CollisionSystem::Collis return collisions_ret; } -std::optional<std::pair<std::reference_wrapper<Transform>, std::reference_wrapper<Rigidbody>>> -CollisionSystem::get_active_transform_and_rigidbody(game_object_id_t game_object_id) const{ - RefVector<Transform> transforms = this->component_manager.get_components_by_id<Transform>(game_object_id); - if (transforms.empty()) return std::nullopt; - - Transform& transform = transforms.front().get(); - if (!transform.active) return std::nullopt; - - RefVector<Rigidbody> rigidbodies = this->component_manager.get_components_by_id<Rigidbody>(game_object_id); - if (rigidbodies.empty()) return std::nullopt; - - Rigidbody& rigidbody = rigidbodies.front().get(); - if (!rigidbody.active) return std::nullopt; - - // Return the active components - return std::make_pair(std::ref(transform), std::ref(rigidbody)); -} - CollisionSystem::CollisionInternalType CollisionSystem::get_collider_type(const collider_variant& collider1,const collider_variant& collider2) const{ if(std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider1)){ if(std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) @@ -376,22 +361,22 @@ bool CollisionSystem::get_collision(const CollisionInternal& first_info,const Co case CollisionInternalType::BOX_BOX: { const BoxCollider & box_collider1 = std::get<std::reference_wrapper<BoxCollider>>(first_info.collider); const BoxCollider & box_collider2 = std::get<std::reference_wrapper<BoxCollider>>(second_info.collider); - return get_box_box_collision(box_collider1,box_collider2,first_info.transform,second_info.transform,second_info.rigidbody,second_info.rigidbody); + return this->get_box_box_collision(box_collider1,box_collider2,first_info.transform,second_info.transform,second_info.rigidbody,second_info.rigidbody); } case CollisionInternalType::BOX_CIRCLE: { const BoxCollider & box_collider = std::get<std::reference_wrapper<BoxCollider>>(first_info.collider); const CircleCollider & circle_collider = std::get<std::reference_wrapper<CircleCollider>>(second_info.collider); - return get_box_circle_collision(box_collider,circle_collider,first_info.transform,second_info.transform,second_info.rigidbody,second_info.rigidbody); + return this->get_box_circle_collision(box_collider,circle_collider,first_info.transform,second_info.transform,second_info.rigidbody,second_info.rigidbody); } case CollisionInternalType::CIRCLE_CIRCLE: { const CircleCollider & circle_collider1 = std::get<std::reference_wrapper<CircleCollider>>(first_info.collider); const CircleCollider & circle_collider2 = std::get<std::reference_wrapper<CircleCollider>>(second_info.collider); - return get_circle_circle_collision(circle_collider1,circle_collider2,first_info.transform,second_info.transform,second_info.rigidbody,second_info.rigidbody); + return this->get_circle_circle_collision(circle_collider1,circle_collider2,first_info.transform,second_info.transform,second_info.rigidbody,second_info.rigidbody); } case CollisionInternalType::CIRCLE_BOX: { const CircleCollider & circle_collider = std::get<std::reference_wrapper<CircleCollider>>(first_info.collider); const BoxCollider & box_collider = std::get<std::reference_wrapper<BoxCollider>>(second_info.collider); - return get_box_circle_collision(box_collider,circle_collider,first_info.transform,second_info.transform,second_info.rigidbody,second_info.rigidbody); + return this->get_box_circle_collision(box_collider,circle_collider,first_info.transform,second_info.transform,second_info.rigidbody,second_info.rigidbody); } } return false; @@ -401,14 +386,14 @@ bool CollisionSystem::get_collision(const CollisionInternal& first_info,const Co bool CollisionSystem::get_box_box_collision(const BoxCollider& box1, const BoxCollider& box2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) const { // Get current positions of colliders - vec2 final_position1 = get_current_position(box1.offset,transform1,rigidbody1); - vec2 final_position2 = get_current_position(box2.offset,transform2,rigidbody2); + vec2 final_position1 = this->get_current_position(box1.offset,transform1,rigidbody1); + vec2 final_position2 = this->get_current_position(box2.offset,transform2,rigidbody2); // Calculate half-extents (half width and half height) - float half_width1 = box1.width / 2.0; - float half_height1 = box1.height / 2.0; - float half_width2 = box2.width / 2.0; - float half_height2 = box2.height / 2.0; + float half_width1 = box1.dimensions.x / 2.0; + float half_height1 = box1.dimensions.y / 2.0; + float half_width2 = box2.dimensions.x / 2.0; + float half_height2 = box2.dimensions.y / 2.0; // Check if the boxes overlap along the X and Y axes return (final_position1.x + half_width1 > final_position2.x - half_width2 && @@ -419,12 +404,12 @@ bool CollisionSystem::get_box_box_collision(const BoxCollider& box1, const BoxCo bool CollisionSystem::get_box_circle_collision(const BoxCollider& box1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) const { // Get current positions of colliders - vec2 final_position1 = get_current_position(box1.offset, transform1, rigidbody1); - vec2 final_position2 = get_current_position(circle2.offset, transform2, rigidbody2); + vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1); + vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2); // Calculate box half-extents - float half_width = box1.width / 2.0; - float half_height = box1.height / 2.0; + float half_width = box1.dimensions.x / 2.0; + float half_height = box1.dimensions.y / 2.0; // Find the closest point on the box to the circle's center float closest_x = std::max(final_position1.x - half_width, std::min(final_position2.x, final_position1.x + half_width)); @@ -441,8 +426,8 @@ bool CollisionSystem::get_box_circle_collision(const BoxCollider& box1, const Ci bool CollisionSystem::get_circle_circle_collision(const CircleCollider& circle1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) const { // Get current positions of colliders - vec2 final_position1 = get_current_position(circle1.offset,transform1,rigidbody1); - vec2 final_position2 = get_current_position(circle2.offset,transform2,rigidbody2); + vec2 final_position1 = this->get_current_position(circle1.offset,transform1,rigidbody1); + vec2 final_position2 = this->get_current_position(circle2.offset,transform2,rigidbody2); float distance_x = final_position1.x - final_position2.x; float distance_y = final_position1.y - final_position2.y; diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index 7fb8b45..85ae5ad 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -8,6 +8,7 @@ #include "api/Transform.h" #include "api/BoxCollider.h" #include "api/CircleCollider.h" +#include "api/Metadata.h" #include "api/Vector2.h" #include "Collider.h" @@ -68,12 +69,14 @@ public: * Includes information about the colliding objects and the resolution data for handling the collision. */ struct CollisionInfo{ - Collider& first_collider; - Transform& first_transform; - Rigidbody& first_rigidbody; - Collider& second_collider; - Transform& second_transform; - Rigidbody& second_rigidbody; + Collider& this_collider; + Transform& this_transform; + Rigidbody& this_rigidbody; + Metadata& this_metadata; + Collider& other_collider; + Transform& other_transform; + Rigidbody& other_rigidbody; + Metadata& other_metadata; //! The resolution vector for the collision. vec2 resolution; //! The direction of movement for resolving the collision. @@ -214,23 +217,6 @@ private: bool get_collision(const CollisionInternal& first_info,const CollisionInternal& second_info, CollisionInternalType type) const; /** - * \brief Retrieves the active Transform and Rigidbody components for a given game object. - * - * This function looks up the Transform and Rigidbody components associated with the specified - * game object ID. It checks if both components are present and active. If both are found - * to be active, they are returned wrapped in reference wrappers; otherwise, an empty - * optional is returned. - * - * \param game_object_id The ID of the game object for which to retrieve the components. - * - * \return A std::optional containing a pair of reference wrappers to the active Transform - * and Rigidbody components, or std::nullopt if either component is not found - * or not active. - */ - std::optional<std::pair<std::reference_wrapper<Transform>, std::reference_wrapper<Rigidbody>>> get_active_transform_and_rigidbody(game_object_id_t game_object_id) const; - - - /** * \brief Detects collisions between two BoxColliders. * * \param box1 The first BoxCollider. diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp index 8f21727..f3833cb 100644 --- a/src/crepe/system/PhysicsSystem.cpp +++ b/src/crepe/system/PhysicsSystem.cpp @@ -31,11 +31,11 @@ void PhysicsSystem::update() { * gravity); } // Add damping - if (rigidbody.data.angular_velocity_factor != 1 && rigidbody.data.angular_velocity_factor > 0) { - rigidbody.data.angular_velocity *= rigidbody.data.angular_velocity_factor; + if (rigidbody.data.angular_velocity_coefficient != 1 && rigidbody.data.angular_velocity_coefficient > 0) { + rigidbody.data.angular_velocity *= rigidbody.data.angular_velocity_coefficient; } - if (rigidbody.data.linear_velocity_factor != vec2{1, 1} && rigidbody.data.linear_velocity_factor.x > 0 && rigidbody.data.linear_velocity_factor.y > 0) { - rigidbody.data.linear_velocity *= rigidbody.data.linear_velocity_factor; + if (rigidbody.data.linear_velocity_coefficient != vec2{1, 1} && rigidbody.data.linear_velocity_coefficient.x > 0 && rigidbody.data.linear_velocity_coefficient.y > 0) { + rigidbody.data.linear_velocity *= rigidbody.data.linear_velocity_coefficient; } // Max velocity check |