EulerAngles arrrgh

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

@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.material.map = readImage(asset.builtin.Surfaces.Desert_Cliff_Roughness)
    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.material.map = readImage(asset.builtin.Surfaces.Desert_Cliff_Roughness)
    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

@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.

@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.material.map = readImage(asset.builtin.Surfaces.Desert_Cliff_Roughness)
    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.material.map = readImage(asset.builtin.Surfaces.Desert_Cliff_Roughness)
    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

@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!

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

check the quat docs in the reference

@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.

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

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 …

No you don’t

@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.

@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.

@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.

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.

@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.

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.

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.

@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?

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.material.map = readImage(asset.builtin.Surfaces.Desert_Cliff_Roughness)
    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.material.map = readImage(asset.builtin.Surfaces.Desert_Cliff_Roughness)
    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

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.