=============================== Connecting wires, agxWire::Link =============================== In general, an agxWire::Wire has no knowledge of neighboring connected wires. So, for example if one would like to connect a wire to a chain using a swivel, or wires of different radii using a shackle, or three wires connected through a tow plate – certain functionalities of the wire would not work as expected since all features are handled locally in the agxWire::Wire object. agxWire::Link enables such functionality to work over connected wire segments. A link contains a user defined rigid body (with arbitrary geometry), a number of connecting wires and algorithms to transfer functionality between the wires. -------- Features -------- An agxWire::Link can have an arbitrarily number of connections. Typically, one or two connections is recommended for all functionality to be enabled. .. _fig-connecting-wires--agxwire--link-1: .. figure:: images/connecting_wires__agxwire__link_1.png A link with a number of different wire segments connected to it. The mass of a link is normally relatively small in relation to the tension the connected wires could have, so an agxWire::Link includes additional stabilization constraints when the link is exposed to large forces. ------------- Configuration ------------- For simplicity, assume this configuration: .. _fig-connecting-wires--agxwire--link-2: .. figure:: images/connecting_wires__agxwire__link_2.png The most common configuration where two wire segments are connected with a link in between. Two wires connected to a link. Wire 1 has a free begin at (-1, 0, 0), the link positioned in the origin and Wire 2 ends in a free end at (1, 0, 0). Create the wires and the link: .. code-block:: c++ agxWire::WireRef wire1 = new agxWire::Wire( wire1Radius, wire1Resolution ); agxWire::WireRef wire2 = new agxWire::Wire( wire2Radius, wire2Resolution ); // Link rigid body with box geometry. agx::RigidBodyRef linkRb = new agx::RigidBody( new agxCollide::Geometry( new agxCollide::Box( linkHalfExtent ) ) ); linkRb->setPosition( 0, 0, 0 ); agxWire::LinkRef link = new agxWire::Link( linkRb ); The link *must* be positioned any time before the wires are initialized. When the wires are initialized the current position of the link defines the lengths of wires. The link has to know where it’s connected to the wires. A link is either connected to a wire’s begin or end position of a wire and there are several ways of defining this. Connection type from a links point of view: .. code-block:: c++ /** Connection type of the wires connected to this link, i.e., if this link should be attached to begin or end of the wire. */ enum ConnectionType { WIRE_BEGIN = 0, /**< Link attached at begin of wire. */ WIRE_END = 1, /**< Link attached at end of wire. */ INVALID_CONNECTION = 0xFF /**< Invalid connection, e.g., if this link isn't connected to a give wire. */ }; Defining the connections to a link is only a mapping of how the wire composition relates. No nodes are added to the wires! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Routing with explicit connection type ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Explicitly assigning the connection type: .. code-block:: c++ // on the link is given in the link's coordinate system, and // could in this case be: (-linkHalfExtent.x(), 0, 0) link->connect( wire1, wire1ConnectionPosition, agxWire::Link::WIRE_END ); // The link is connected to Wire 2 begin. The connection position // on the link is given in the link's coordinate system, and // could in this case be: (linkHalfExtent.x(), 0, 0) link->connect( wire2, wire2ConnectionPosition, agxWire::Link::WIRE_BEGIN ); The link now has two connections and now we can route the above system: .. code-block:: c++ // Wire 1 first free node. wire1->add( new agxWire::FreeNode( -1, 0, 0 ) ); // Add the link. The wire will find out how it relates to the // link and add a node to the wire route. 'false' means that // the wire shouldn't update the connection type and instead // fail (return false) if the connection type is a mismatch. wire1->add( link, false ); // Wire 2 starts at the link... wire2->add( link, false ); // ...and ends at the free end (1, 0, 0). wire2->add( new agxWire::FreeNode( 1, 0, 0 ) ); The above route is a valid route and the second argument to the add method also assures the connections are valid given the wanted route. If the connection type isn’t that important, pass true as second argument (default is true) and the wire will match and update the connection type to the link. Example: .. code-block:: c++ link->connect( wire1, wire1ConnectionPosition, agxWire::Link::WIRE_END ); print( link->getConnectionType( link->getConnectingNode( wire1 ) ) ); // "WIRE_END" // Routing the other way around. Link is attached at wire begin. wire1->add( link ); wire1->add( new agxWire::FreeNode( -1, 0, 0 ) ); print( link->getConnectionType( link->getConnectingNode( wire1 ) ) ); // "WIRE_BEGIN" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Routing with implicit connection pair ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ agxWire::Link has a static utility method to configure the most common configuration (:numref:`fig-connecting-wires--agxwire--link-2`), so configuring such setup can be done with one call: .. code-block:: c++ // Wire 1 will get WIRE_END connection type and Wire 2 WIRE_BEGIN. agxWire::Link::connect( wire1, wire1ConnectionPosition, link, wire2, wire2ConnectionPosition ); And the actual routing: .. code-block:: c++ wire1->add( new agxWire::FreeNode( -1, 0, 0 ) ); wire1->add( link ); wire2->add( link ); wire2->add( new agxWire::FreeNode( 1, 0, 0 ) ); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Routing and connecting – all in one call ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When routing, a wire has the possibility to know, given nodes already added or not, which connection type a wire should have relative the link. Routing and connecting the system in (:numref:`fig-connecting-wires--agxwire--link-2`): .. code-block:: c++ wire1->add( new agxWire::FreeNode( -1, 0, 0 ) ); // Will connect to the link and give Wire 1 connection type WIRE_END. wire1->add( link, wire1ConnectionPosition ); // Will connect to the link and give Wire 2 connection type WIRE_BEGIN. wire2->add( link, wire2ConnectionPosition ); wire2->add( new agxWire::FreeNode( 1, 0, 0 ) ); .. _wire_winch: --------------------- agxWire::Winch --------------------- The :cpp:`agxWire::Winch` class is derived from the class :ref:`agxWire::WireWinchController`. It adds a few additional features: 1. Can pull in links. 2. Can pull out links. 3. Can store completely inactive wires and their rest lengths. .. _fig-connecting-wires--agxwire--link-3: .. figure:: images/connecting_wires__agxwire__link_3.png Schematic figure of a wire composition with two wire segments. One completely pulled in segment (orange) connected to an (also inactive) pulled in link. The active wire (blue) is connected to the inactive wire through another active link. An agxWire::Winch simulates a linear winch all units of forces are in Newton (N). ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Length of a wire that hasn’t been routed? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The **agxWire::Wire** object doesn’t have a pre-defined length. The length of a wire is only defined by the pulled in length in a winches plus the active distance outside. So for a wire to have a length it must have at least two nodes. A completely pulled in wire doesn’t have active nodes. It only has a connection to a link – which also is inactive (i.e., no position). So for a winch to be able to properly configure a wire that never has been active, one has to ‘report’ the length of completely pulled in wire segments to the winch. *How to create an **agxWire::Winch**, please have a look at the agxWire::WinchController section, the constructor and API are identical.* .. code-block:: c++ // The completely pulled in wire segment. agxWire::WireRef inactiveWire = new agxWire::Wire( inactiveWireRadius, inactiveWireResolution ); // The active wire segment that's connected to the winch. agxWire::WireRef activeWire = new agxWire::Wire( activeWireRadius, activeWireResolution ); // Create the winch (API identical to agxWire::WireWinchController). agxWire::WinchRef winch = new agxWire::Winch( winchRb, winchConnectionPoint, winchDirection ); // Report the inactive wire segment to the winch. winch->add( inactiveWire, inactiveWireLength ); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Routing with completely pulled in wire segments ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The most important thing to remember when routing with inactive segments is not to forget to define the link connections. The following code snippet routes a system similar to :numref:`fig-connecting-wires--agxwire--link-3` but with two inactive, completely pulled in, segments: .. code-block:: c++ // The completely pulled in wire segments. agxWire::WireRef inactiveWire1 = new agxWire::Wire( inactiveWireRadius, inactiveWireResolution ); agxWire::WireRef inactiveWire2 = new agxWire::Wire( inactiveWireRadius, inactiveWireResolution ); // The active wire segment that's connected to the winch. agxWire::WireRef activeWire1 = new agxWire::Wire( activeWireRadius, activeWireResolution ); // The other active wire segment. agxWire::WireRef activeWire2 = new agxWire::Wire( activeWireRadius, activeWireResolution ); // DEFINE THE CONNECTIONS: // Inactive wire 1 is connected to inactive wire 2. agxWire::Link::connect( inactiveWire1, wire1ConnectionPosition, link1, inactiveWire2, wire2ConnectionPosition ); // Inactive wire 2 is connected to active wire 1. agxWire::Link::connect( inactiveWire2, wire1ConnectionPosition, link2, activeWire1, wire2ConnectionPosition ); // Active wire 1 is connected to active wire 2. agxWire::Link::connect( activeWire1, wire1ConnectionPosition, link3, activeWire2, wire2ConnectionPosition ); // Create the winch (API identical to agxWire::WireWinchController). agxWire::WinchRef winch = new agxWire::Winch( winchRb, winchConnectionPoint, winchDirection ); // Report the inactive wire segments to the winch. winch->add( inactiveWire1, inactiveWire1Length ); winch->add( inactiveWire2, inactiveWire2Length ); // Begin route from winch to link2. Note that the initial pulled in // length is how much of activeWire1 that's pulled in. activeWire1->add( winch, initialPulledInLength ); activeWire1->add( link3 ); activeWire2->add( link3 ); activeWire2->add( new agxWire::FreeNode( wire2EndPosition ) ); // Only the active wires have to be added to the simulation. simulation->add( activeWire1 ); simulation->add( activeWire2 ); Only active wires have to be added to the simulation! ^^^^^^^^^^^^^^^^ Pulled in length ^^^^^^^^^^^^^^^^ The agxWire::Winch and agxWire::WireWinchController return the same value for pulled in length – i.e., the amount pulled in of the current segment. An agxWire::Winch has a few additional methods that support the inactive, totally pulled in, wire segments. Example: .. code-block:: c++ agx::Real inactiveWireLength = 10.0; agx::Real activeWirePulledInLength = 5.0; // Add inactive wire, 10 m long. winch->add( inactiveWire, inactiveWireLength ); // Add the winch to the active wire with 5 m pulled in length initially. activeWire->add( winch, activeWirePulledInLength ); print( winch->getPulledInLength() ); // "5.0" print( winch->getTotalPulledInLength() ); // "15.0" print( winch->getNumPulledInWires() ); // "1" // Remove the inactive wire. winch->remove( inactiveWire ); print( winch->getPulledInLength() ); // "5.0" print( winch->getTotalPulledInLength() ); // "5.0" print( winch->getNumPulledInWires() ); // "0" ----------------------- agxWire::Link:Algorithm ----------------------- The link functionality is coupled to separate implementations of agxWire::Link::Algorithm’s. A link algorithm receives callbacks with the link at different stages of a time step and when the link changes state from enabled to disabled, and vice versa. The (current) default algorithms: 1. Detect approaching (sliding) nodes and spawn new algorithms given node type. E.g., from the link’s point of view, when a winch is approaching. 2. Not fully pulled in or out link algorithm. The state before a segment is completely pulled in, the link is on a cylindrical joint, and this algorithm handles when the state changes to fully pulled in or out wire segments. 3. The first inactive link in a winch receives callbacks to detect when it’s time to go active. 4. Enables high resolution ranges to spread over wire segments. It’s possible to implement your own or add extra (i.e., not default) link algorithms. If you are implementing your own link algorithm it’s important to not store/keep a configuration dependent state. Configuration dependent states could be a wire, a start and an end node (which gives a direction). The reason for this constraint is because the link will never know if a wire has been reversed or cut in two, when a node has been removed, been merged with another wire, removed from the simulation etc.. .. _fig-connecting-wires--agxwire--link-4: .. figure:: images/connecting_wires__agxwire__link_4.png High resolution range over link with five connections. ^^^^^^^^^^^^^^^^^^^^^^^^ Optional link algorithms ^^^^^^^^^^^^^^^^^^^^^^^^ For usability or performance reasons, some link algorithms are optional. Contact stabilizing algorithm @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ In some cases, where the tension is high in two or more connections, the link could become unstable when in contact with other objects. This optional link algorithm monitors the tension in the connections and creates additional, stabilizing constraints when the link is in contact with another object. .. _fig-connecting-wires--agxwire--link-5: .. figure:: images/connecting_wires__agxwire__link_5.png Case where it could be suitable with a contact stabilizing algorithm. The contact stabilizing algorithm uses its own version of a contact execute filter. It’s easy to implement a filter, e.g., this filter that matches a rigid body with name ‘sternRoller’: .. code-block:: c++ #include class SternRollerMatcher : public agxWire::LinkObjectStabilizingAlgorithm::LinkExecuteFilter { public: SternRollerMatcher( const agxWire::Link* link ) : agxWire::LinkObjectStabilizingAlgorithm::LinkExecuteFilter( link ) {} // otherGeometry is overlapping the link - check if match. virtual bool matchOther( const agxCollide::Geometry* otherGeometry ) const { return otherGeometry->getRigidBody() != 0L && otherGeometry->getRigidBody()->getName() == "sternRoller"; } }; So when **matchOther** returns true AND the tension is high enough, extra constraints will be added to stabilize the link. There are some already implemented filters. For example filters that match a property (in **agx::PropertyContainer**) in either a geometry or a rigid body. Example with geometry: .. code-block:: c++ // Filter that checks a geometry's property container. agxWire::LinkObjectStabilizingAlgorithm::GeometryPropertyBoolFilterRef geometryPropertyFilter = new agxWire::LinkObjectStabilizingAlgorithm::GeometryPropertyBoolFilter( "stabilize", link ); link->add( new agxWire::LinkObjectStabilizingAlgorithm( geometryPropertyFilter ) ); // Enables algorithm when the link is in contact with this geometry. someGeometry->getPropertyContainer()->addPropertyBool( "stabilize", true ); Example with rigid body: .. code-block:: c++ // Filter that checks a rigid body's property container. agxWire::LinkObjectStabilizingAlgorithm::GeometryPropertyBoolFilterRef rbPropertyFilter = new agxWire::LinkObjectStabilizingAlgorithm::GeometryPropertyBoolFilter( "stabilize", link ); link->add( new agxWire::LinkObjectStabilizingAlgorithm( geometryPropertyFilter ) ); // Enables algorithm when the link is in contact with this rigid body. someRigidBody->getPropertyContainer()->addPropertyBool( "stabilize", true ); ----------------- Known limitations ----------------- ^^^^^^^^^^^^^^^^^^ Links and EyeNodes ^^^^^^^^^^^^^^^^^^ A Link can not slide through an EyeNode. This is a feature that might be added in a future release of AGX Dynamics. ^^^^^^^^^^^^^^^^^^^^^^^^^^ Loops in the configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^ A valid link-wire composition cannot have loops. There are recursive sections in the code, assuming a tree structure of the connection assembly. A link can only detect a loop if the user tries to connect the same wire twice to a link, and other than that, no runtime checks nor parsing of the current configuration to detect loops are made. .. _fig-connecting-wires--agxwire--link-6: .. figure:: images/connecting_wires__agxwire__link_6.png Loops in the link connections is undefined. This loop limitation prevents the user from creating a lasso using links and the connect-interface. But the reason for links is functionality such as pulling them into/out from winches, and pulling a lasso into a winch doesn’t often make that much sense. The more reasonable scenario when pulling a lasso into a winch is that the winch stops, due to jamming (or something similar). To achieve such behavior: 1. Connect the main wire to the link (i.e., not the lasso wire). The link should only have one mapped connection, making the link a so called ‘end link’. I.e., the link defines the end of a link-wire composition. 2. Create and route the lasso wire only using explicit connections. Explicit connections could be separate bodies locked to the link body or body fixed/connecting nodes with the link body as the rigid body. .. _fig-connecting-wires--agxwire--link-7: .. figure:: images/connecting_wires__agxwire__link_7.png Lasso configuration so that a winch will automatically stop when the lasso is about to be pulled in. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Length of connections and winches ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The maximum distance from a connection point, on the link surface, to the link center of mass, must be less than one meter for the winches to work with the link. In principle, this means that a somewhat symmetric link can’t be longer than two meters AND be pulled into or out from a winch. If winches aren’t involved, there’s no limitation in the connection lengths. .. _fig-connecting-wires--agxwire--link-8: .. figure:: images/connecting_wires__agxwire__link_8.png Definition of the connection distance going from the connection point to the center of mass. .. _controlling-link-behaviour: -------------------------- Controlling Link behaviour -------------------------- ^^^^^^^^^^^^^ Link and bend ^^^^^^^^^^^^^ Connecting wires to small link objects are often problematic when simulating offshore operations due to the wire tension to link mass ratio difference. Early, AGX have had support for such connections, supporting stable simulation with wires and links, even when the system involves extreme potential energies. The solution to the initial problem has been to have a variable stiffness of the bend constraint over the link, i.e., controlling more degrees of freedoms to filter out high frequencies. This control of the bend stiffness has been, and will always be, handled internally. When the tension is low the bend was completely ignored making the connection look like a ball joint type of connection. I.e., the link could rotate freely about the connecting point. This type of connection behavior isn’t always desirable and the user should be able to control the bend stiffness when the link isn’t under extreme stress. ^^^^^^^^^^^^^^ Link and twist ^^^^^^^^^^^^^^ If the wire going out from a link connection defines an axis, the twist is defined to be the rotation about that axis. Until now, this degree of freedom has been completely ignored by the wire. Mainly because there’re absolutely no information about it because the wire uses 3 DOF (particle) bodies internally. Bending is still defined when the geometry of this exist given three control points along a wire. Since twist is a completely new concept for the links, consider it experimental, and more work has to be done to figure out the API to control all desirable "effects" of twisting a link about a wire. -------------------------------------------------------------- Introducing interface to a Link Node and Connection Properties -------------------------------------------------------------- ^^^^^^^^^^^^^^^^^^ agxWire::ILinkNode ^^^^^^^^^^^^^^^^^^ This new node is only an interface to the implementation of the special node used when connecting an agxWire::Wire to an agxWire::Link. At this moment, the interface is minimalistic but can be extended easily for future features. .. code-block:: c++ /** Interface class for nodes connected to links. */ class AGXPHYSICS_EXPORT ILinkNode : public agxWire::ConnectingNode { public: /** \return the connection properties for the connection between the wire and the link */ virtual agxWire::ILinkNode::ConnectionProperties* getConnectionProperties() const = 0; }; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ agxWire::ILinkNode::ConnectionProperties ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ New object holding parameters and properties of a connection between a wire and a link. Currently it’s small and it’ll be extended/changed when we fully understand these connections. .. code-block:: c++ /** Object holding parameters, such as bend and twist stiffness, related to wire to link connections. */ class AGXPHYSICS_EXPORT ConnectionProperties { public: /** Default constructor, with default bend and twist stiffness disabled. */ ConnectionProperties(); /** Construct given bend- and twist stiffness. */ ConnectionProperties( agx::Real bendStiffness, agx::Real twistStiffness ); /** Assign bend stiffness of the link to wire connection. The bend stiffness is interpreted as Young's modulus of the wire. I.e., the final stiffness is dependent on the wire radius and segment lengths. Default: 0. \param bendStiffless - new bend stiffness for this connection */ void setBendStiffness( agx::Real bendStiffness ); /** \return the currently used bend stiffness of this connection (default: 0) */ agx::Real getBendStiffness() const; /** Assign twist stiffness of the link to wire connection. The twist stiffness is interpreted as Young's modulus of the wire. I.e., the final stiffness is dependent on the wire radius. Currently, the segment length is assumed to be 1 meter and default value of the stiffness is 0. \param twistStiffness - new twist stiffness for this connection */ void setTwistStiffness( agx::Real twistStiffness ); /** \return the currently used twist stiffness of this connection (default: 0) */ agx::Real getTwistStiffness() const; }; Bend- and twist stiffness may be any real value >= 0. By default the values are all 0 to maintain the old behavior. ^^^^^^^^^^^^^^^^^^^ Default connections ^^^^^^^^^^^^^^^^^^^ Links carries a default connection property. I.e., all new connections inherits the values of this default property. .. code-block:: c++ /** Object that defines the relation between different types of wires (agxWire::Wire). I.e., this link object enables functionality to (for example) move nodes from one wire to another. */ class AGXPHYSICS_EXPORT Link : public agx::Referenced, public agxStream::Serializable { public: ... /** Access default wire connection properties for each new wire that's connected to this link. \note If any value is changed the current connections will not be updated with this value. Only new connection will receive this new default value. \return default connection properties */ agxWire::ILinkNode::ConnectionProperties* getDefaultWireConnectionProperties(); /** \return default connection properties */ const agxWire::ILinkNode::ConnectionProperties* getDefaultWireConnectionProperties() const; ... } As stated in the API documentation, the changes are not propagated to all current connections. It’ll only affect new connections, making it possible to have a default look, but custom if e.g., a chain, instead of a wire, is connected. ----------- Twist again ----------- The implementation of the link to wire twist constraint is basically a hinge axis. I.e., it’s possible to control it within a range, motorize it or lock at certain angle. You’ll have to test and see what the desirable behavior should be. Currently there’s no interface to change these behaviors. Default is a motor at zero speed, i.e., the twist stiffness controls the inertia to rotate the link about the twist axis. ------------- Small Example ------------- Example to change bend stiffness of individual nodes where the wires have identical radius. .. code-block:: c++ void createScene( agxSDK::Simulation* simulation ) { const agx::Real radius = 0.015; agxWire::WireRef wire1 = new agxWire::Wire( radius, 2.0 ); agxWire::LinkRef link1 = new agxWire::Link( createLink( simulation ) ); agxWire::WireRef wire2 = new agxWire::Wire( radius, 2.0 ); agxWire::LinkRef link2 = new agxWire::Link( createLink( simulation ) ); agxWire::WireRef wire3 = new agxWire::Wire( radius, 2.0 ); agxWire::LinkRef link3 = new agxWire::Link( createLink( simulation ) ); link1->getRigidBody()->setPosition( 0, -1, 0 ); link2->getRigidBody()->setPosition( 0, 0, 0 ); link3->getRigidBody()->setPosition( 0, 1, 0 ); simulation->add( new agx::LockJoint( link1->getRigidBody() ) ); simulation->add( new agx::LockJoint( link2->getRigidBody() ) ); simulation->add( new agx::LockJoint( link3->getRigidBody() ) ); wire1->add( link1, agx::Vec3( 0.15, 0, 0 ) ); wire1->add( new agxWire::FreeNode( 3, -1, 0 ) ); wire2->add( link2, agx::Vec3( 0.15, 0, 0 ) ); wire2->add( new agxWire::FreeNode( 3, 0, 0 ) ); wire3->add( link3, agx::Vec3( 0.15, 0, 0 ) ); wire3->add( new agxWire::FreeNode( 3, 1, 0 ) ); simulation->add( wire1 ); simulation->add( wire2 ); simulation->add( wire3 ); agxWire::ILinkNode* link1Node = link1->getConnectingNode( wire1 ); agxWire::ILinkNode* link2Node = link2->getConnectingNode( wire2 ); agxWire::ILinkNode* link3Node = link3->getConnectingNode( wire3 ); // Closest wire in the picture. link1Node->getConnectionProperties()->setBendStiffness( 1.0E12 ); // Middle wire. link2Node->getConnectionProperties()->setBendStiffness( 2.0E10 ); // Farthermost wire. link3Node->getConnectionProperties()->setBendStiffness( 1.0E8 ); } Result after some time. Wire 3 is swinging back and forth but the others are not moving. .. _fig-connecting-wires--agxwire--link-9: .. figure:: images/connecting_wires__agxwire__link_9.png Three links and wires where the wires has identical radius but the connection bend stiffness differs. Another example where all wires has different radius and the links has identical default connection properties: .. code-block:: c++ void createScene( agxSDK::Simulation* simulation ) { const agx::Real radius1 = 0.050; const agx::Real radius2 = 0.025; const agx::Real radius3 = 0.010; agxWire::WireRef wire1 = new agxWire::Wire( radius1, 2.0 ); agxWire::LinkRef link1 = new agxWire::Link( createLink( simulation ) ); agxWire::WireRef wire2 = new agxWire::Wire( radius2, 2.0 ); agxWire::LinkRef link2 = new agxWire::Link( createLink( simulation ) ); agxWire::WireRef wire3 = new agxWire::Wire( radius3, 2.0 ); agxWire::LinkRef link3 = new agxWire::Link( createLink( simulation ) ); // Before making any connections, set default bend stiffness. // Closest wire in the picture. link1->getDefaultConnectionProperties()->setBendStiffness( 1.0E10 ); // Middle wire. link2->getDefaultConnectionProperties()->setBendStiffness( 1.0E10 ); // Farthest wire. link3->getDefaultConnectionProperties()->setBendStiffness( 1.0E10 ); link1->getRigidBody()->setPosition( 0, -1, 0 ); link2->getRigidBody()->setPosition( 0, 0, 0 ); link3->getRigidBody()->setPosition( 0, 1, 0 ); simulation->add( new agx::LockJoint( link1->getRigidBody() ) ); simulation->add( new agx::LockJoint( link2->getRigidBody() ) ); simulation->add( new agx::LockJoint( link3->getRigidBody() ) ); wire1->add( link1, agx::Vec3( 0.15, 0, 0 ) ); wire1->add( new agxWire::FreeNode( 3, -1, 0 ) ); wire2->add( link2, agx::Vec3( 0.15, 0, 0 ) ); wire2->add( new agxWire::FreeNode( 3, 0, 0 ) ); wire3->add( link3, agx::Vec3( 0.15, 0, 0 ) ); wire3->add( new agxWire::FreeNode( 3, 1, 0 ) ); simulation->add( wire1 ); simulation->add( wire2 ); simulation->add( wire3 ); } Resulting in this steady state: .. _fig-connecting-wires--agxwire--link-10: .. figure:: images/connecting_wires__agxwire__link_10.png Three links and wires where the connection bend stiffness is identical but the wires has different radii. A trained eye can see that the relation between the radius and stiffness is proportional to r\ :sup:`4`.