# How to simulate the force to bend a tube?

I have a 3D tube, and there are two rope inside the tube. For example,

The two red color indicates the rope. Then, I pull rope `b`, and the tube would be bend.

Intuitively, I think the tube would bend into a arc. I want to know how to simulate this situation?

I have tried to use multiple cylinder to simulate this situation, and the `pull` actoin is simulated as a force on the top of tube. My simulation code is:

``````import direct.directbase.DirectStart
from panda3d.core import Vec3, TransformState, Point3, BitMask32, LPoint3, LVector3, Material
from panda3d.bullet import BulletWorld, BulletDebugNode, BulletCylinderShape
from panda3d.bullet import BulletPlaneShape, BulletSphericalConstraint, BulletConeTwistConstraint
from panda3d.bullet import BulletRigidBodyNode, BulletTriangleMesh, BulletTriangleMeshShape
from panda3d.bullet import BulletBoxShape
from direct.showbase.InputStateGlobal import inputState
import numpy as np

base.cam.setPos(0, -10, 0)
base.cam.lookAt(0, 0, 0)

debugNode = BulletDebugNode('Debug')
debugNode.showWireframe(True)
debugNode.showConstraints(True)
debugNode.showBoundingBoxes(True)
debugNode.showNormals(False)
debugNP = render.attachNewNode(debugNode)
debugNP.show()

# World
world = BulletWorld()
world.setGravity(Vec3(0, 0, 0))
world.setDebugNode(debugNP.node())

# Plane
shape = BulletPlaneShape(Vec3(0, 0, 1), 0)
node = BulletRigidBodyNode('Ground')
nodePath = render.attachNewNode(node)
nodePath.setPos(0, 0, 0)
world.attachRigidBody(node)

inputState.watchWithModifiers('x_minu', 'a')

num_segment = 30 # 5段圆柱
segment_length = 1

previous_node = None
node_paths = []
for i in range(num_segment):
node = BulletRigidBodyNode(f'segment_{i}')
node.setMass(1.0)

if i == 0:
# node.set_static(True)
node_path = render.attachNewNode(node)
node_path.setPos(0, 0, segment_length/2)

constraint = BulletSphericalConstraint(node, Point3(0, 0, -segment_length / 2))
world.attach_constraint(constraint)
else:
# # 将前一个节点连接到当前节点
constraint = BulletConeTwistConstraint(previous_node, node, TransformState.make_pos(Vec3(0, 0, segment_length/2)),
TransformState.make_pos(Vec3(0, 0, -segment_length/2)))
constraint.setLimit(5, 5, 0, 100, relaxation=10)
# constraint.set_damping(100)

world.attach_constraint(constraint)

node_path = render.attachNewNode(node)
node_path.setPos(previous_node_path.get_pos() + Vec3(0, 0, segment_length))

previous_node = node
previous_node_path = node_path
world.attachRigidBody(node)
node_paths.append(node_path)

def process_input():
force = LVector3(0, 0, 0)

if inputState.isSet('x_minu'): force.x = -1.0

force*=30

last = node_paths[-1]
last.node().set_active(True)
last.node().apply_central_force(force)

dt = globalClock.getDt()
process_input()
world.doPhysics(dt)

base.run()

``````

This code has three problems:

1. The tube would be bend when I press `a`, but the `BulletConeTwistConstraint` would try to reset it. And I don’t know how to fix it.

2. The tube would no bend into a arc. For example:

3. The `BulletConeTwistConstraint` would make this tube acting behave like the wind: the `BulletConeTwistConstraint` would alternately make the tube bend to right and left. I hope it bend and keep the status.

Is there any solution can simulate the tube and the inside rope? Any suggestion is appreciated~~~

I have made changes that are to achieve the effect of the stick.

1. You did not take into account the collision of segments between each other, which led to unexpected results, I added group collision masks to eliminate this.

3. If you want to keep the stick shape, maybe you need to write your own algorithm for this, I have no idea what you can do with it, as an option not to update the physical world or something else.

``````from panda3d.core import loadPrcFileData

import direct.directbase.DirectStart
from panda3d.core import Vec3, TransformState, Point3, BitMask32, LPoint3, LVector3, Material
from panda3d.bullet import BulletWorld, BulletDebugNode, BulletCylinderShape
from panda3d.bullet import BulletPlaneShape, BulletSphericalConstraint, BulletConeTwistConstraint
from panda3d.bullet import BulletRigidBodyNode, BulletTriangleMesh, BulletTriangleMeshShape
from panda3d.bullet import BulletBoxShape
from direct.showbase.InputStateGlobal import inputState

base.cam.setPos(0, -10, 0)
base.cam.lookAt(0, 0, 0)

debugNode = BulletDebugNode('Debug')
debugNode.showWireframe(True)
debugNode.showConstraints(True)
debugNode.showBoundingBoxes(True)
debugNode.showNormals(False)
debugNP = render.attachNewNode(debugNode)
debugNP.show()

# World
world = BulletWorld()
world.setGravity(Vec3(0, 0, 0))
world.setDebugNode(debugNP.node())
world.setGroupCollisionFlag(0, 0, False)

# Plane
shape = BulletPlaneShape(Vec3(0, 0, 1), 0)
node = BulletRigidBodyNode('Ground')
nodePath = render.attachNewNode(node)
nodePath.setPos(0, 0, 0)
world.attachRigidBody(node)

inputState.watchWithModifiers('x_minu', 'a')

num_segment = 5 # 5段圆柱
segment_length = 5

previous_node = None
node_paths = []
for i in range(num_segment):
node = BulletRigidBodyNode(f'segment_{i}')
node.setMass(1)

if i == 0:
# node.set_static(True)
node_path = render.attachNewNode(node)
node_path.setPos(0, 0, segment_length/2)
node_path.node().set_angular_factor(False)

constraint = BulletSphericalConstraint(node, Point3(0, 0, -segment_length / 2))
world.attach_constraint(constraint)

else:
# # 将前一个节点连接到当前节点
constraint = BulletConeTwistConstraint(previous_node, node, TransformState.make_pos(Vec3(0, 0, segment_length/2)),
TransformState.make_pos(Vec3(0, 0, -segment_length/2)))
constraint.setLimit(3, 3, 0, 0, relaxation=2)
#constraint.set_damping(100)

world.attach_constraint(constraint)

node_path = render.attachNewNode(node)
node_path.setPos(previous_node_path.get_pos() + Vec3(0, 0, segment_length))

previous_node = node
previous_node_path = node_path
world.attachRigidBody(node)
node_paths.append(node_path)

def process_input():
force = LVector3(0, 0, 0)

if inputState.isSet('x_minu'): force.x = -1.0

force*=30

last = node_paths[-1]
last.node().set_active(True)
last.node().apply_central_force(force)

dt = globalClock.getDt()
process_input()
world.doPhysics(dt)

base.run()
``````

The `rest` means all cylinder is vertical. The cylinder tube turn to be bend by a force, then the force disappear and the `BulletConeTwistConstraint` would try to make the cylinder tube to be `rest`.

My question is: is it possible to add a continuous force to achieve a balance between added force and the `BulletConeTwistConstraint` force? In this way, the stick may keep the shape.

Then, I modify my code as:

``````...
num_segment = 50
segment_length = 1
...

def process_input():
force = LVector3(1, 0, 0)

# if inputState.isSet('x_minu'): force.x = -1.0
# if inputState.isSet('x_add'): force.x = 1.0

force *= 1

last = node_paths[-1]
last.node().set_active(True)
last.node().apply_central_force(force)
``````

In this modified `process_input`, there is alway a force on the last cylinder. I expect this continous force may achieve a balance with the `BulletConeTwistConstraint` force. But, the stick is still hard to keep static.

Why the continuous force do not ahieve the balance?

Because you are using a simulation of physical laws, in reality you will also not be able to hold an object in a certain state. As I said earlier, you can develop an algorithm for constructing a composite object in the form you need using segment transformation data. When removing the force, you can return the object to its previous state, by the reverse action.

However, this requires effort in terms of implementation, there is no simple solution.

A short example that needs improvement.

``````from panda3d.core import loadPrcFileData

import direct.directbase.DirectStart
from panda3d.core import Vec3, TransformState, Point3, BitMask32, LPoint3, LVector3, Material
from panda3d.bullet import BulletWorld, BulletDebugNode, BulletCylinderShape
from panda3d.bullet import BulletPlaneShape, BulletSphericalConstraint, BulletConeTwistConstraint
from panda3d.bullet import BulletRigidBodyNode, BulletTriangleMesh, BulletTriangleMeshShape
from panda3d.bullet import BulletBoxShape
from direct.showbase.InputStateGlobal import inputState

base.cam.setPos(0, -10, 0)
base.cam.lookAt(0, 0, 0)

debugNode = BulletDebugNode('Debug')
debugNode.showWireframe(True)
#debugNode.showConstraints(True)
#debugNode.showBoundingBoxes(True)
#debugNode.showNormals(False)
debugNP = render.attachNewNode(debugNode)
debugNP.show()

# World
world = BulletWorld()
world.setGravity(Vec3(0, 0, 0))
world.setDebugNode(debugNP.node())
world.setGroupCollisionFlag(0, 0, False)

# Plane
shape = BulletPlaneShape(Vec3(0, 0, 1), 0)
node = BulletRigidBodyNode('Ground')
nodePath = render.attachNewNode(node)
nodePath.setPos(0, 0, 0)
world.attachRigidBody(node)

inputState.watchWithModifiers('x_minu', 'a')
inputState.watchWithModifiers('hold', 'space')

num_segment = 5 # 5段圆柱
segment_length = 5

previous_node = None
node_paths = []
for i in range(num_segment):
node = BulletRigidBodyNode(f'segment_{i}')
node.setMass(1)

if i == 0:
# node.set_static(True)
node_path = render.attachNewNode(node)
node_path.setPos(0, 0, segment_length/2)
node_path.node().set_angular_factor(False)

constraint = BulletSphericalConstraint(node, Point3(0, 0, -segment_length / 2))
world.attach_constraint(constraint)

else:
# # 将前一个节点连接到当前节点
constraint = BulletConeTwistConstraint(previous_node, node, TransformState.make_pos(Vec3(0, 0, segment_length/2)),
TransformState.make_pos(Vec3(0, 0, -segment_length/2)))
constraint.setLimit(5, 5, 0, 0.2, relaxation = 10)
#constraint.set_damping(100)

world.attach_constraint(constraint)

node_path = render.attachNewNode(node)
node_path.setPos(previous_node_path.get_pos() + Vec3(0, 0, segment_length))

previous_node = node
previous_node_path = node_path
world.attachRigidBody(node)
node_paths.append(node_path)

state = 1

def process_input():
force = LVector3(0, 0, 0)

if inputState.isSet('x_minu'): force.x = -1.0

force*=50

last = node_paths[-1]
last.node().set_active(True)
last.node().apply_central_force(force)

process_input()

if inputState.isSet('hold'):
state = 0
else:
state = 1

if state == 1:
dt = globalClock.getDt()
world.doPhysics(dt)