#### Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

# Locomotive using joints

Mod
edited April 1 Posts: 9,415

Here’s something I was playing with the last few days. It’s a locomotive demo using Revolute, Distance and Prismatic joints. The piston uses Prismatic, the wheels use Revolute, and the joints connecting the wheels and piston uses Distance. The only piece the gets any value to move is the piston. When the piston moves, it causes wheel number 3 to start rotating by the distance joint connecting them. Wheel 3 then rotates wheel 2 and wheel 4 with their connecting joints. When 2 rotates wheel 1 with their connecting joint. By increasing the pressure slider, the piston moves faster causing wheel 3 and the other wheels to move faster. You can slide the parameter “piston joint” to break the joint between the piston and wheel 3 causing them to slow down. Parameter “Joints 1_2, 2_3, and 3_4” breaks the joints between those wheels and they will slow down. Wheels 1, 2, and 3 have angularDamping, so they will slow down faster. Wheel 4 has no damping, so it will rotate for awhile.

PS. Run in landscape orientation.

``````viewer.mode=STANDARD

-- locomotive

function setup()
rectMode(CENTER)
parameter.integer("pressure",1,100,5)
parameter.boolean("piston joint",false)
parameter.boolean("joint1_2",false)
parameter.boolean("joint2_3",false)
parameter.boolean("joint3_4",false)

-- create wheels
w1=wheel(200,200,200,200,200,260)
w2=wheel(380,200,380,200,380,260)
w3=wheel(560,200,560,200,560,260)
w4=wheel(740,200,740,200,740,260)

createPiston()
createJoints()
end

function draw()
background(142, 95, 31)
fill(255)
text("Angular velocity (1)   "..w1.wheel.angularVelocity//-1,WIDTH/2,HEIGHT-25)
text("Angular velocity (2)   "..w2.wheel.angularVelocity//-1,WIDTH/2,HEIGHT-50)
text("Angular velocity (3)   "..w3.wheel.angularVelocity//-1,WIDTH/2,HEIGHT-75)
text("Angular velocity (4)   "..w4.wheel.angularVelocity//-1,WIDTH/2,HEIGHT-100)

w1:draw()
w2:draw()
w3:draw()
w4:draw()
drawPiston()
drawJoints()

if r1.linearVelocity.x>0 then
r1.linearVelocity=vec2(pressure*10,0)
else
r1.linearVelocity=vec2(pressure*-10,0)
end

if piston_joint and j5~=nil then
j5:destroy()
j5=nil
end
if joint1_2 and j61~=nil then
j61:destroy()
j61=nil
w1.wheel.angularDamping=1
end
if joint2_3 and j62~=nil then
j62:destroy()
j62=nil
w2.wheel.angularDamping=1
end
if joint3_4 and j63~=nil then
j63:destroy()
j63=nil
w3.wheel.angularDamping=1
end

end

function createPiston()
p1=physics.body(CIRCLE,0)   -- anchor piston
p1.x=100
p1.y=350
p1.type=STATIC

r1=physics.body(CIRCLE,0)  -- piston rect
r1.x=100
r1.y=350
end

function drawPiston()
if j5==nil then
return
end
pushStyle()
strokeWidth(8)
fill(255)
stroke(0)
rect(110,350,200,50) -- piston cylinder
fill(0)
rect(r1.x,r1.y,20,50) -- piston
strokeWidth(0)
rectMode(CORNER)
if pressure>0 then
if r1.linearVelocity.x>0 then
fill(255,0,0,pressure*2+55)
rect(p1.x-84,r1.y-20,r1.x-24,40)   -- red pressure
elseif r1.linearVelocity.x<0 then
fill(255,0,0,pressure*2+55)
rect(r1.x+8,r1.y-20,p1.x-r1.x+96,40)   -- red pressure
end
end
popStyle()
end

function drawJoints()
pushStyle()
stroke(172)
strokeWidth(4)
if j61~=nil then
line(j61.bodyA.x,j61.bodyA.y,j61.bodyB.x,j61.bodyB.y)
end
if j62~=nil then
line(j62.bodyA.x,j62.bodyA.y,j62.bodyB.x,j62.bodyB.y)
end
if j63~=nil then
line(j63.bodyA.x,j63.bodyA.y,j63.bodyB.x,j63.bodyB.y)
end
if j5~=nil then
line(j5.bodyA.x,j5.bodyA.y,j5.bodyB.x,j5.bodyB.y)
end
popStyle()
end

function createJoints()
j31=physics.joint(REVOLUTE,w1.anchor1,w1.wheel,vec2(w1.anchor1.x,w1.anchor1.y))
j32=physics.joint(REVOLUTE,w2.anchor1,w2.wheel,vec2(w2.anchor1.x,w2.anchor1.y))
j33=physics.joint(REVOLUTE,w3.anchor1,w3.wheel,vec2(w3.anchor1.x,w3.anchor1.y))
j34=physics.joint(REVOLUTE,w4.anchor1,w4.wheel,vec2(w4.anchor1.x,w4.anchor1.y))

j41=physics.joint(REVOLUTE,w1.wheel,w1.anchor2,vec2(w1.wheel.x,w1.wheel.y+60))
j42=physics.joint(REVOLUTE,w2.wheel,w2.anchor2,vec2(w2.wheel.x,w2.wheel.y+60))
j43=physics.joint(REVOLUTE,w3.wheel,w3.anchor2,vec2(w3.wheel.x,w3.wheel.y+60))
j44=physics.joint(REVOLUTE,w4.wheel,w4.anchor2,vec2(w4.wheel.x,w4.wheel.y+60))

j61=physics.joint(DISTANCE,w1.anchor2,w2.anchor2,
vec2(w1.anchor2.x,w1.anchor2.y),vec2(w2.anchor2.x,w2.anchor2.y))
j62=physics.joint(DISTANCE,w2.anchor2,w3.anchor2,
vec2(w2.anchor2.x,w2.anchor2.y),vec2(w3.anchor2.x,w3.anchor2.y))
j63=physics.joint(DISTANCE,w3.anchor2,w4.anchor2,
vec2(w3.anchor2.x,w3.anchor2.y),vec2(w4.anchor2.x,w4.anchor2.y))

j1=physics.joint(PRISMATIC,p1,r1,p1.position,vec2(1,0))
j5=physics.joint(DISTANCE,r1,w3.anchor2,r1.position,w3.anchor2.position)
end

wheel=class()

function wheel:init(a1x,a1y,wx,wy,a2x,a2y)
self.anchor1=physics.body(CIRCLE,0)
self.anchor1.x=a1x
self.anchor1.y=a1y
self.anchor1.type=STATIC

self.wheel=physics.body(CIRCLE,80)
self.wheel.x=wx
self.wheel.y=wy

self.anchor2=physics.body(CIRCLE,0)
self.anchor2.x=a2x
self.anchor2.y=a2y
end

function wheel:draw()
pushMatrix()
pushStyle()
stroke(255)
strokeWidth(6)
fill(46, 30, 29)
translate(self.anchor1.x,self.anchor1.y)
rotate(self.wheel.angle)
fill(255)
strokeWidth(3)
stroke(103, 66, 64)
for z=0,360,30 do
line(0,0,x,y)
end
ellipse(0,0,30)                 -- center circle
ellipse(0,60,20)                -- outer circle
fill(0)
ellipse(0,0,10)                -- small center circle
popStyle()
popMatrix()
end
``````

• Mod
Posts: 1,089

neat. now i'll have to figure out what you did

• Mod
Posts: 9,415

@RonJeffries Thanks, if you have questions about anything, just ask. It took a lot of trial and error writing this because I wasn’t sure how the joints actually worked. I spent a lot of time on small programs playing with a single joint until I understood what was happening.

• Mod
Posts: 1,089

yes, that's what i'd do too. that's a neat example, should be added to the examples that ship with codea

• edited August 29 Posts: 1,277

@dave1707 this is a great demo project!

Attached is a project that puts all the code into a stand-alone class so it can be used as a dependency if anyone wants to.

The class abstracts the code enough that it can make the exact same train as in the original example, but it can also make multiple pistons and wheels and connect them in any way you like—it won’t always work out, but you can try any configuration you want.

• edited August 30 Posts: 1,277

Did a little more refinement and renamed the project “dave1707 Locomotion API”.

@dave1707’s code can now be used, with simple commands, to make copies of his train or to make something new out of the parts he made:

To make one of his trains at any size, use `makeTrain(x, y, width)`, like this:

``````locoNator = LocoMotionInator()
locoNator:makeTrain(130, 400, 1200)

``````

If called without parameters, it will make a train that has roughly the size and placement of Dave’s original train.

This “API” also lets you use his code piece-by-piece with four functions:

• `makeWheel(x, y, radius)` makes a wheel with a connection point
• `makePiston(x, y, width, height)` makes a piston with an adjustable `pressure` setting
• `attachPistonToWheel(piston, wheel)` connects a piston to a wheel’s connection point
• `attachWheelToWheel(wheel1, wheel2)` connects two wheels by their connection points

For example, this is the code that makes the “weirdDangThingy” contraption in the video:

``````    locoNator = LocoMotionInator()

local p = locoNator:makePiston(WIDTH*0.75, HEIGHT*0.55, 200, 40)
p.pressure = 25

local w1 = locoNator:makeWheel(WIDTH*0.85, HEIGHT*0.95, 35)
local w2 = locoNator:makeWheel(WIDTH*0.92, HEIGHT*0.25, 25)
local w3 = locoNator:makeWheel(WIDTH*0.72, HEIGHT*0.8, 25)
local w4 = locoNator:makeWheel(WIDTH*0.96, HEIGHT*0.96, 25)
local w5 = locoNator:makeWheel(WIDTH*0.46, HEIGHT*0.82, 25)

locoNator:attachPistonToWheel(p, w2)
locoNator:attachWheelToWheel(w1, w2)
locoNator:attachWheelToWheel(w2, w3)
locoNator:attachWheelToWheel(w2, w5)
locoNator:attachWheelToWheel(w3, w4)

``````

...and also, of course, you’re free to go in the code and change the colors however you like.

I hope someone has fun with the cool stuff dave1707 wrote!

• Mod
Posts: 9,415

@UberGoober Nice job, but you’re putting too much work into this. I did this just for kicks and to have an example for anyone who wanted to see how joints worked.

• Posts: 1,277

@dave1707 you have your kicks and I have mine.

• Mod
Posts: 9,415

@UberGoober True.

• Posts: 1,277
Also, like I mentioned in another post, this is how I have to approach code if I want to understand it.

My brain grapes just can’t comprehend or retain the purpose of code with single-letter variables, and it’s similarly hard for me to understand or retain how the various parts of the code work together without separating them into discrete functions.

And I wanted to understand this code as well as I could, because you suggested it might be helpful with a different problem I was having— and once I had made it verbose and modular enough for me to understand and retain, it was pretty close to being an API anyway, so I just went all the way with it.
• Mod
Posts: 9,415

@UberGoober I guess I’m too used to using 1 letter variable names and not separating code into different functions. I guess that’s what happens when I’m half coding and half watching TV.

• Posts: 2,348

@dave1707 - guess I'm in your gang, I tend to abreviate my variables to give a hint of their purpose but I don't like long winded descriptive names especially on an iPad. They tend to make lines wrap which I prefer to avoid. Also I prefer to edit in portrait and not landscape, so I see more lines of code (generally).

I do think it's better with short variable names but commenting on their purpose in the text is a good practice. Shame I'm not good at that in practice !!

I can understand professional programmers, with reems of code in their projects, using long winded names.

• edited August 31 Posts: 1,277
I don’t have anything against short variable names, and I honestly admire people who can understand someone else’s code immediately without any of the variables being English words—I’m stunned at how much you guys can accomplish, and I assume your brains can just keep all that stuff in your head as you write it.

I simply do not have that skill, and I just literally can’t understand what code is doing—even my own!—unless I make the names something I can read.

It’s not a criticism, it’s that I personally can’t learn from anyone else’s code unless I make the variables describe what they are.
• Posts: 171

this is crazy awesome, if you wanted to recreate a combustion engine you could because Codea uses box2D and box2D is wicked good

• Posts: 1,277
@Bri_G I used to believe that the best code is obsessively commented code, but then I came across someone who suggested that the best code is code that is obvious without comments.

And I think the code you guys write **is** obvious to you without comments, just not to me.
• Posts: 2,348
@uberguber - no probs, whatever your style we all just want to learn and create.
• Mod
Posts: 9,415

@UberGoober That's not always true. A lot of times I look at old code I wrote and I haven’t the slightest idea what I was doing. I might know what’s happening while I write the code and I’ll try to condense it right after I write it, but later on it’s a mystery to me.