Howdy, Stranger!

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

EulerAngles arrrgh

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?

Comments

  • dave1707dave1707 Mod
    Posts: 8,948

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

    @dave1707 fantastic. Thanks heaps and heaps!

  • Posts: 728

    @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) ground.material.map = readImage(asset.builtin.Surfaces.Desert_Cliff_Roughness) 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
  • dave1707dave1707 Mod
    edited April 6 Posts: 8,948

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

    you can edit in the squiggles after pasting.

  • dave1707dave1707 Mod
    Posts: 8,948

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

  • edited April 7 Posts: 728
    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.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
    -- 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: 728
    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: 84

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

  • Posts: 728
    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: 728

    @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: 84

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

  • edited April 7 Posts: 728

    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?

  • dave1707dave1707 Mod
    Posts: 8,948

    @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: 728
    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.
  • dave1707dave1707 Mod
    Posts: 8,948

    @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: 728
    Yeah, that sounds right, though I’m not as concerned with restoring rotation as I am with updating the sliders.
  • dave1707dave1707 Mod
    Posts: 8,948

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

  • dave1707dave1707 Mod
    Posts: 8,948

    @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
    
  • Posts: 728

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

  • dave1707dave1707 Mod
    edited April 7 Posts: 8,948

    @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
    
  • Posts: 914

    @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: 914

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

    check the quat docs in the reference

  • Posts: 728

    @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: 914

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

  • edited April 8 Posts: 914

    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: 728

    No you don’t

  • edited April 8 Posts: 728

    @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: 728

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

  • dave1707dave1707 Mod
    Posts: 8,948

    @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: 914

    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: 728

    @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: 914

    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: 914

    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: 728

    @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: 914

    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
    
  • edited April 9 Posts: 914

    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: 728

    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: 914

    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: 914

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

  • edited April 9 Posts: 914

    switchState is always false. what am i blindly missing?

  • Posts: 914

    arrgh about line 20 says ground, should be ground2?

  • edited April 9 Posts: 914

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

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

Sign In or Register to comment.