#### Howdy, Stranger!

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

# EulerAngles arrrgh

Posts: 1,186

I thought I could save a craft entity’s EulerAngles as a vec3, change the rotation around elsewhere in the code, and then use the saved vec3 if I needed to restore that previous rotation, but nope.

It seems like EulerAngles change every time you assign them.

How can I save/restore an entity’s rotation?

• Posts: 9,286

@UberGoober Is this what you want. Start the program and tap the screen to save the image at the home positions. Use the sliders to rotate the image then tap the screen to set it back to the home position.

``````viewer.mode=STANDARD

function setup()
parameter.integer("gx",-180,180,0)
parameter.integer("gy",-180,180,0)
parameter.integer("gz",-180,180,0)
save=false
str1="tap screen to save rotation angles"
str2="tap screen to restore rotation angles"

scene = craft.scene()
ground = scene:entity()
ground.model = craft.model.cube(vec3(1,.2,1))
ground.material = craft.material(asset.builtin.Materials.Specular)
scene.camera.position=vec3(0,1,-4)
fill(255)
end

function update(dt)
ground.rotation=quat.eulerAngles(gx,gy,gz)
scene:update(dt)
end

function draw()
update(DeltaTime)
scene:draw()
if not save then
text(str1,WIDTH/2,HEIGHT/2)
else
text(str2,WIDTH/2,HEIGHT/2)
end
end

function touched(t)
if t.state==BEGAN then
if save==false then
hold=vec3(gx,gy,gz)
save=true
else
gx,gy,gz=hold.x,hold.y,hold.z
end
end
end
``````
• Posts: 1,186

@dave1707 fantastic. Thanks heaps and heaps!

• Posts: 1,186

@dave1707

I tried to nudge your code towards my actual use case and I got the strange results I’m seeing in my actual use case.

If you want to see it, try setting all the sliders to negative numbers and then tapping the screen a bunch of times.

``````viewer.mode=STANDARD

function setup()
parameter.integer("gx",-180,180,0)
parameter.integer("gy",-180,180,0)
parameter.integer("gz",-180,180,0)
toggle=false
str1="tap screen to save rotation angles"
str2="tap screen to swap rotation angles"

scene = craft.scene()
ground = scene:entity()
ground.model = craft.model.cube(vec3(1,.2,1))
ground.material = craft.material(asset.builtin.Materials.Specular)
scene.camera.position=vec3(0,1,-4)
fill(255)
end

function update(dt)
ground.rotation=quat.eulerAngles(gx,gy,gz)
scene:update(dt)
end

function draw()
update(DeltaTime)
scene:draw()
if not toggle then
text(str1,WIDTH/2,HEIGHT/2)
else
text(str2,WIDTH/2,HEIGHT/2)
end
end

function touched(t)
if t.state==BEGAN then
if toggle==false then
hold1=quat.eulerAngles(gx,gy,gz)
if hold2 then
local recoveredEulers = hold2:angles()
gx,gy,gz=recoveredEulers.x,recoveredEulers.y,recoveredEulers.z
else
gx,gy,gz = 0,0,0
end
toggle=true
else
hold2=quat.eulerAngles(gx,gy,gz)
local recoveredEulers = hold1:angles()
gx,gy,gz=recoveredEulers.x,recoveredEulers.y,recoveredEulers.z
toggle=false
end
end
end

``````
• edited April 6 Posts: 9,286

@UberGoober Tap above the middle of the screen to save the angles, Tap below the middle to restore the angles.

``````viewer.mode=STANDARD

function setup()
parameter.integer("gx",-180,180,0)
parameter.integer("gy",-180,180,0)
parameter.integer("gz",-180,180,0)
toggle=false

scene = craft.scene()
ground = scene:entity()
ground.model = craft.model.cube(vec3(1,.2,1))
ground.material = craft.material(asset.builtin.Materials.Specular)
scene.camera.position=vec3(0,1,-4)
fill(255)
end

function update(dt)
ground.rotation=quat.eulerAngles(gx,gy,gz)
scene:update(dt)
end

function draw()
update(DeltaTime)
scene:draw()
text("save angles",WIDTH/2,HEIGHT/2+50)
text("restore angles",WIDTH/2,HEIGHT/2-50)
end

function touched(t)
if t.state==BEGAN then
if t.y >HEIGHT/2 then
hold=vec3(gx,gy,gz)
print("saved")
else
gx,gy,gz=hold.x,hold.y,hold.z
ground.rotation=quat.eulerAngles(gx,gy,gz)
print("restored")
end
end
end
``````
• edited April 7 Posts: 1,186
I think I get it. It seems like the crucial thing is that you’re setting the visible x, y, z of the parameters separate from using those values in the quarternion.

I modified it again to be a step closer to what I’m going to use it for. It seems to work. Am I doing it right?

~~~

-- dave1707 Quats 2 viz ubergoober

viewer.mode=STANDARD

function setup()
swapVec = vec3()
scene = craft.scene()
ground = scene:entity()
ground.model = craft.model.cube(vec3(1,.2,1))
ground.material = craft.material(asset.builtin.Materials.Specular)
scene.camera.position=vec3(0,1,-4)
fill(159,100,229)
parameter.boolean("switchAngleSets", false, function(switchState)
print(switchState)
local newVec = swapVec
swapVec = vec3(gx,gy,gz)
gx,gy,gz = newVec.x, newVec.y, newVec.z
ground.rotation = quat.eulerAngles(gx,gy,gz)
end)
parameter.integer("gx",-180,180,0)
parameter.integer("gy",-180,180,0)
parameter.integer("gz",-180,180,0)
end

function update(dt)
ground.rotation=quat.eulerAngles(gx,gy,gz)
scene:update(dt)
end

function draw()
update(DeltaTime)
scene:draw()
if switchAngleSets == true then
text("angle set A",WIDTH/2,HEIGHT/2+50)
else
text("angle set B",WIDTH/2,HEIGHT/2-50)
end
end

~~~
• Posts: 1,186
Yeah, it’s posting from the iPhone that screws up the formatting. Apparently Vanilla Forums don’t work the same in their phone-size mode as they do in their full-size mode.
• Posts: 1,057

you can edit in the squiggles after pasting.

• Posts: 9,286

It seems to work OK. It’s switches between the 2 settings.

• edited April 7 Posts: 1,186
Empty line

```

-- dave1707 Quats 2 viz ubergoober 2

viewer.mode=STANDARD

function setup()

scene = craft.scene()
scene.camera.position=vec3(0,1,-4)

ground = scene:entity()
ground.model = craft.model.cube(vec3(1,.2,1))
ground.material = craft.material(asset.builtin.Materials.Specular)
ground.position = vec3(1,1,1)
ground.eulersForParameterSliders = vec3(0,0,0)

ground2 = scene:entity()
ground2.model = craft.model.cube(vec3(1,.2,1))
ground2.material = craft.material(asset.builtin.Materials.Specular)
ground2.position = vec3(-1,1,1)
ground.eulersForParameterSliders = vec3(0,0,0)

controlledBlock = ground

fill(255)

parameter.integer("gx",-180,180,0)
parameter.integer("gy",-180,180,0)
parameter.integer("gz",-180,180,0)
parameter.boolean("switchControlledBlock", false, function(switchState)
if switchState == true then
controlledBlock = ground2
ground.eulersForParameterSliders = vec3(gx,gy,gz)
else
controlledBlock = ground
ground2.eulersForParameterSliders = vec3(gx,gy,gz)
end
if controlledBlock.eulersForParameterSliders then
-- does not work: gx,gy,gz = controlledBlock.rotation:angles().x, controlledBlock.rotation:angles().y, controlledBlock.rotation:angles().z
gx,gy,gz = controlledBlock.eulersForParameterSliders.x, controlledBlock.eulersForParameterSliders.y, controlledBlock.eulersForParameterSliders.z
end
end)
end

function update(dt)
controlledBlock.rotation=quat.eulerAngles(gx,gy,gz)
scene:update(dt)
end

function draw()
update(DeltaTime)
scene:draw()
if switchControlledBlock == true then
text("eulers control block on right",WIDTH/2,HEIGHT/2+150)
else
text("eulers control block on left",WIDTH/2,HEIGHT/2-150)
end
end

```

Empty line
• edited April 7 Posts: 1,186
Well as you can see I tried four times to post my latest version and the formatting didn’t work on any of them.

The real kick in the crotch is that the formatting *did* work correctly in the preview.
• Posts: 140

Just use the ` character three times instead of the ~ character

• Posts: 1,186
So that version is the closest yet to my actual use case.

As you can see I had to do it by storing the Eulers to show on the sliders as a separate variable on the entities themselves.

In practice I may have to do this between 30 or 40 entities or more, so if I have to store the Eulers separately I’ll either have to do what I did here or make a separate table to store them in, which doesn’t seem a lot different.

Unless there’s some way to get the properties that should go on the sliders from the existing attributes of an entity, but I haven’t been able to do that—can you?
• edited April 7 Posts: 1,186

@skar

I’ll try that, but you should know that I’ve used the ~~~ marks successfully in tons of other posts—in fact every single other post where I’ve posted code.

Edit: as you can see above I tried it and it still didn’t work.

• Posts: 140

Maybe you need a line space between the top of the post and the first three `

• edited April 7 Posts: 1,186

I’m concerned that everybody’s going to get all caught up in how the posts are formatted and my question will be lost.

@dave1707, I haven’t found a way to derive the right values to show on the parameter sliders from the entity’s own attributes. Can it be done?

• Posts: 9,286

@UberGoober I guess I’m not sure what you’re trying to accomplish. Do you have a lot of items where you want to save their original rotation values and be able at some point return them to that original rotation.

• Posts: 1,186
That’s basically right.

I have a bunch of on-screen entities that I can cycle through and use x, y, z sliders to rotate.

When I move from one entity to the next, I need to set the sliders to the correct values for the current object.

But I can’t get those values from the entity’s own Eulers or from using `rotation:angles()`, it seems.

So I may have to take the klutzy path of directly storing them on the entity.
• Posts: 9,286

@UberGoober When you tap on an entity, you want the rotation values for that entity to update the x,y,z sliders and you want to rotate the entity with the sliders or reset that entity to it original rotation.

• Posts: 1,186
Yeah, that sounds right, though I’m not as concerned with restoring rotation as I am with updating the sliders.
• Posts: 9,286

@UberGoober I’ll try playing with that later. I have other things I need to do for the next few hours.

• Posts: 9,286

@UberGoober Is this closer. Tap the screen above or below the middle to select that entity. Then use the sliders to rotate that entity. I only did 2 entities, but it can be expanded.

``````viewer.mode=STANDARD

function setup()
rectMode(CENTER)
parameter.integer("gx",-180,180,0)
parameter.integer("gy",-180,180,0)
parameter.integer("gz",-180,180,0)

scene = craft.scene()
scene.camera.position=vec3(0,0,-5)

ground1 = scene:entity()
ground1.model = craft.model.cube(vec3(1,.2,1))
ground1.material = craft.material(asset.builtin.Materials.Specular)
ground1.position = vec3(0,1,1)
ground1.eulersForParameterSliders = vec3(0,0,0)

ground2 = scene:entity()
ground2.model = craft.model.cube(vec3(1,.2,1))
ground2.material = craft.material(asset.builtin.Materials.Specular)
ground2.position = vec3(0,-1,1)
g1=vec3(0,0,0)
g2=vec3(0,0,0)
s=0
fill(255)
end

function update(dt)
scene:update(dt)
draw()
end

function draw()
background(0)
if s==1 then
g1=vec3(gx,gy,gz)
elseif s==2 then
g2=vec3(gx,gy,gz)
end
ground1.rotation=quat.eulerAngles(g1.x,g1.y,g1.z)
ground2.rotation=quat.eulerAngles(g2.x,g2.y,g2.z)
scene:draw()
hold1=g1
hold2=g2
fill(255)
if s==1 then
text("selected",50,HEIGHT*.75)
elseif s==2 then
text("selected",50,HEIGHT*.25)
end
end

function touched(t)
if t.state==BEGAN then
if t.y >HEIGHT/2 then
s=1
gx=hold1.x
gy=hold1.y
gz=hold1.z
ground1.rotation=quat.eulerAngles(g1.x,g1.y,g1.z)
else
s=2
gx=hold2.x
gy=hold2.y
gz=hold2.z
ground1.rotation=quat.eulerAngles(g2.x,g2.y,g2.z)
end
end
end
``````
• Posts: 1,186

@dave1707 storing the display Eulers in hold1 and hold2 is basically the same solution as storing the display Eulers on the entities, right?

In both cases it’s creating a new variable to hold the display values, and I think either implementation would be fine.

What I’m wondering is if it can be done without creating any new variables, just purely by reverse-engineering the display values from the entity’s quats or Eulers or some combination.

It’s not a huge deal to create a new variable, of course, I just wondered if someone knew a solution to the reverse-engineering problem.

I’m lucky your demo turned out to be so close to the thing I’m actually doing, it made it a lot easier to discuss.

• edited April 7 Posts: 9,286

@UberGoober Is this any better. Removed the hold variable.

``````viewer.mode=STANDARD

function setup()
rectMode(CENTER)
parameter.integer("gx",-180,180,0)
parameter.integer("gy",-180,180,0)
parameter.integer("gz",-180,180,0)
toggle=false

scene = craft.scene()
scene.camera.position=vec3(0,0,-5)

ground1 = scene:entity()
ground1.model = craft.model.cube(vec3(1,.2,1))
ground1.material = craft.material(asset.builtin.Materials.Specular)
ground1.position = vec3(0,1,1)
ground1.eulersForParameterSliders = vec3(0,0,0)

ground2 = scene:entity()
ground2.model = craft.model.cube(vec3(1,.2,1))
ground2.material = craft.material(asset.builtin.Materials.Specular)
ground2.position = vec3(0,-1,1)
gx1,gy1,gz1=0,0,0
gx2,gy2,gz2=0,0,0
s=0
fill(255)
end

function update(dt)
scene:update(dt)
draw()

end

function draw()
if s==1 then
gx1,gy1,gz1=gx,gy,gz
elseif s==2 then
gx2,gy2,gz2=gx,gy,gz
end
ground1.rotation=quat.eulerAngles(gx1,gy1,gz1)
ground2.rotation=quat.eulerAngles(gx2,gy2,gz2)
scene:draw()
if s==1 then
text("selected",50,HEIGHT*.75)
elseif s==2 then
text("selected",50,HEIGHT*.25)
end

end

function touched(t)
if t.state==BEGAN then
if t.y >HEIGHT/2 then
s=1
gx=gx1
gy=gy1
gz=gz1
ground1.rotation=quat.eulerAngles(gx1,gy1,gz1)
else
s=2
gx=gx2
gy=gy2
gz=gz2
ground1.rotation=quat.eulerAngles(gx2,gy2,gz2)
end
end
end
``````
• Posts: 1,057

@UberGoober i'm not sure i understand what you're trying to do. are you trying to get the euler angles out of a rotated thing, rather than save a set for each thing in your own code? what can we get from the thing? can we get a quat? are we looking for a way to convert quat to euler? or ... would it help to back up a step and look at what you want to accomplish in-screen? or ... something else?

thanks!

• edited April 8 Posts: 1,057

if you just want the angles from a thing, i think it's thing.rotation:angles()

check the quat docs in the reference

• Posts: 1,186

@RonJeffries

are you trying to get the euler angles out of a rotated thing, rather than save a set for each thing in your own code?

Yes, I want to use the rotated thing’s own baked-in quats, or its baked-in Eulers, to calculate the values that should be shown on x,y,z sliders.

what can we get from the thing? can we get a quat? are we looking for a way to convert quat to euler?

We can get quats and Eulers. But:

• the Eulers we get out, as you may know, often don’t match the Eulers we put in, so they’re unreliable, and often not useful on x,y,z sliders, which is the whole source of the trouble I’m having.
• the quats we get out can be converted to Eulers using the angles() function, but again, the Eulers we use to calculate a quat are not guaranteed to be the Eulers we get out of it.

In the end it’s not a huge deal to store a set of values for each individual entity, so if there’s no way to calculate this, I’ll just do that.

• edited April 8 Posts: 1,057

does thing.rotation:angles() not do it? got a test?

• edited April 8 Posts: 1,057

if we put them back and the thing isn't differently rotated, it's all good, innit? there's more than one way to get to a given rotation, and some paths create gimbal lock, so working with quats is often better. for one-shot user input euler is ok.

grr, guess i have to code something ...

• Posts: 1,186

No you don’t

• edited April 8 Posts: 1,186

@RonJeffries Look in my post above that the formatting is all screwy on. There’s a commented-out line that says “doesn’t work”.

Remove the comment marks and comment out the line below it. You’ll see the results.

• edited April 8 Posts: 1,186

@RonJeffries I forgot to mention it’s easiest to see what’s going wrong if you set the sliders to negative values during the test.

• Posts: 9,286

@UberGoober Thats why I was using variables to keep track of everything. I noticed that if I tried to just use the quat commands, I wasn’t getting the angles out that I was putting in.

• Posts: 1,057

Yes. After quite some experimentation, I think what is going on is this:

Codea draws rotated things, like most systems, using quaternions. There are technical reasons for this including avoiding gimbal lock and the ease of "adding" and "subtracting" rotations to adjust angles.

When you put in a set of eulerAngles, quat jumps through its own orifice to compute the direct rotation to the desired angle. This is almost never the same as moving by the angles you put in: it goes straight there.

When you convert a rotation back to euler angles, you therefore almost never get back what you put in. However, what you get back should, when stuffed back in to the eulerAngles function, return an equivalent rotation (not even necessarily the same one, but one that will produce the safe effect).

In short, there is no way to get back the angles you put in. This is not a bug, it is the nature of the compromise to make euler angles work at all.

I don't know what the basic problem is that you're trying to solve, but if you truly need the angles and can't live with saving the rotations, I think you're stuck with wrapping the eulers into a class or a bunch of variables or a table or something of that kind.

• Posts: 1,186

@RonJeffries thanks for the unwrapping you did there.

If you try the code I suggested, with the change to the comments I suggested, you’ll see that the angles returned by the quarternions do not, in fact, work identically to the Eulers put in.

I don’t know why this should be. The logic that you used to make that conclusion is the same logic that led me to try it, but having tried it, unless I did something wrong, it just doesn’t work.

• edited April 8 Posts: 1,057

i'm not sure why, but i think the angles are coming back ok - though possibly changed as described above - but they're not going correctly into the parameter fields.

i added a print after the setting of gc etc. as you can see in the attached pic, the numbers are non-zero but the parameters aren't updated.

• edited April 8 Posts: 1,057

i was seeing the same thing with dave's program, modded to fetch the values from the rotation and set gx gy gz. sometimes the parameters don't set on screen, but the numbers print as expected.

• Posts: 1,186

@RonJeffries Wait, so you're not seeing the blocks change their positions when you switch back and forth between them? Did you set the sliders to negative numbers?

• Posts: 1,057

no, i do not see the blocks switch. here's my prog:

``````viewer.mode=STANDARD

function setup()

scene = craft.scene()
scene.camera.position=vec3(0,1,-4)

ground = scene:entity()
ground.model = craft.model.cube(vec3(1,.2,1))
ground.material = craft.material(asset.builtin.Materials.Specular)
ground.position = vec3(1,1,1)
ground.eulersForParameterSliders = vec3(0,0,0)

ground2 = scene:entity()
ground2.model = craft.model.cube(vec3(1,.2,1))
ground2.material = craft.material(asset.builtin.Materials.Specular)
ground2.position = vec3(-1,1,1)
ground.eulersForParameterSliders = vec3(0,0,0)

controlledBlock = ground

fill(255)

parameter.integer("gx",-180,180,0)
parameter.integer("gy",-180,180,0)
parameter.integer("gz",-180,180,0)
parameter.boolean("switchControlledBlock", false, function(switchState)
if switchState == true then
controlledBlock = ground2
ground.eulersForParameterSliders = vec3(gx,gy,gz)
else
controlledBlock = ground
ground2.eulersForParameterSliders = vec3(gx,gy,gz)
end
if controlledBlock.eulersForParameterSliders then
gx,gy,gz = controlledBlock.rotation:angles().x, controlledBlock.rotation:angles().y, controlledBlock.rotation:angles().z
--gx,gy,gz = controlledBlock.eulersForParameterSliders.x, controlledBlock.eulersForParameterSliders.y, controlledBlock.eulersForParameterSliders.z
print(switchState, gx,gy,gz)
end
end)
end

function update(dt)
controlledBlock.rotation=quat.eulerAngles(gx,gy,gz)
scene:update(dt)
end

function draw()
update(DeltaTime)
scene:draw()
if switchControlledBlock == true then
text("eulers control block on right",WIDTH/2,HEIGHT/2+150)
else
text("eulers control block on left",WIDTH/2,HEIGHT/2-150)
end
end
``````
• edited April 9 Posts: 1,057

part of the problem here is parameter.integer. use parameter.number. you can't set a parameter.integer to a non-integer value. it's ignored, i think, if you do.

• edited April 9 Posts: 1,186

Parameter.integer is a good catch.

I’ve run the code you posted. Did you actually hand-test negative numbers as I suggested? When I do that I still see the shifting.

• Posts: 1,057

darn, you're right. that's weird. gotta be a real bug. let's try to get a smaller program with no manual input.

• Posts: 1,057

wait. i think it may be swapping the coords. gotta look at this when i'm not tired.

• edited April 9 Posts: 1,057

switchState is always false. what am i blindly missing?

• Posts: 1,057

arrgh about line 20 says ground, should be ground2?

• edited April 9 Posts: 1,057

filed bug report, i'm convinced angles is buggy.

@UberGoober i suspect you can save and restore thing.rotation with impunity.