Animate the camera depending on the event - aframe

With a simple code it is possible to move the position of the camera when clicking on an object. But is it possible to animated the camera to move to those positions?
This is my code:
<a-scene>
<a-entity >
<a-box id="box01" color="#6173F4" position="1 0 0"></a-box>
<a-box id="box02" color="#6173F4" position="-1 0 0"></a-box>
<a-camera id="cam">
<a-cursor></a-cursor>
</a-camera>
<script>
var cam = document.querySelector('#cam');
var caja1 = document.querySelector('#box01');
var caja2 = document.querySelector('#box02');
caja1.addEventListener('click', function () {
cam.setAttribute('position' , {x: 1, y: 0, z: 4});
cam.setAttribute('rotation' , {x: 0, y: 0, z: 0});
});
caja2.addEventListener('click', function () {
cam.setAttribute('position' , {x: -3, y: 3, z: 4});
cam.setAttribute('rotation' , {x: -30, y: -30, z: 0});
});
</script>
</a-entity>
<a-text value="Hello, A-Frame!" color="#FFF"
position="0 0 1" scale="1.5 1.5 1.5"></a-text>
<a-sky color="#4069b4"></a-sky>
</a-scene>

Don McCurdy has got you covered.
Check his demo out, which might be exactly what you want:
https://sandbox.donmccurdy.com/checkpoints/
(fixed broken link)

Related

QML: How to scale rotated Item around mouse point

I need scale and rotate a Rectangle around mouse point. When the Rectangle is not rotated my solution works fine, but if I apply Rotation transform I face the problem - my Rectangle move to an unexpected point. In my solution I use a MouseArea for drag the Rectangle, Scale and Rotation transforms for scale and rotate.
import QtQuick 2.0
Rectangle {
id: root
color: "gray"
Rectangle {
color: "black"
width: 360
height: 200
opacity: 0.5
x: 500
y: 350
}
Rectangle {
id: sample
color: "green"
width: 360
height: 200
opacity: 0.5
x: 500
y: 350
property real currX: 0
property real currY: 0
property real currZoom: 1
property real maxZoom: 5
property real minZoom: 0.5
transform: [
Scale {
id: scaler
origin.x: sample.currX
origin.y: sample.currY
xScale: sample.currZoom
yScale: sample.currZoom
},
Rotation{
id: rotation
origin.x: 180
origin.y: 100
angle: 30
}
]
MouseArea{
id: mouseArea
anchors.fill: parent
drag.target: sample
onClicked: mouse => {
zoom(true, mouse.x, mouse.y)
}
onWheel: (wheel) => {
var isIn = wheel.angleDelta.y > 0
zoom(isIn, wheel.x, wheel.y)
}
function zoom(isIn, x, y) {
var prevZoom = sample.currZoom
var prevX = sample.currX
var prevY = sample.currY
sample.currX = x
sample.currY = y
sample.currZoom = calculateZoom(isIn, prevZoom)
sample.x = sample.x + (prevX - sample.currX) * (1 - prevZoom)
sample.y = sample.y + (prevY - sample.currY) * (1 - prevZoom)
printSamplePostion()
}
function calculateZoom(isIn, prevZoom) {
var newZoom = isIn ? prevZoom + 0.1 : prevZoom - 0.1
if (newZoom > mouseArea.maxZoom)
newZoom = mouseArea.maxZoom
if (newZoom < mouseArea.minZoom)
newZoom = mouseArea.minZoom
return newZoom
}
function printSamplePostion() {
console.log("== x: 500 y: 350 ======")
console.log("-- x: " + sample.x)
console.log("-- y: " + sample.y)
console.log("=======================")
}
}
}
}
Thanks in advance

Confused with PathView?

I am trying to know how PathView works. I follow the official doc to write some sample code, the code work well but the final result is not what I wanted?
The Code
import QtQuick
Rectangle {
id: root
border.color: "blue"
Component {
id: theDele
Rectangle {
id: rect
color: Qt.rgba(Math.random(), Math.random(),Math.random(), 1)
visible: PathView.onPath
width: 50; height: 50
scale: PathView.itemScale
rotation: PathView.itemAngle
property var rotX: PathView.itemAngle
transform: [
Translate {x: 25; y: 25}
]
Text {
anchors.centerIn: parent
text: index + "ABC"
}
}
}
PathView {
anchors.fill: parent
model: 100
pathItemCount: 10
delegate: theDele
path: Path {
startX: 0; startY: 100
PathAttribute {name: "itemAngle"; value: -90}
PathAttribute {name: "itemScale"; value: 0.5}
PathLine {x: 300; y: 100}
PathPercent {value: 0.2}
PathAttribute {name: "itemAngle"; value: 0}
PathAttribute {name: "itemScale"; value: 1}
PathLine {x: 300; y: 500}
PathPercent {value: 0.8}
PathAttribute {name: "itemAngle"; value: 90}
PathAttribute {name: "itemScale"; value: 0.5}
PathLine {x: 600; y: 500}
PathPercent { value: 1 }
}
}
}
I want work like this
In this demo, I try to draw three PathLine; 1st Line proportion is
0.2; 2nd Line proportion is 0.8;3rd Line proportion is 0.2.
So 1st Line should have 2 Items; 2nd Line should have 6 Items, and 3rd Line should have 2 Items. But This demo 2nd Line has 7 Items?
And 1st Line of all Items should scale 0.5, and rotate -90, And 2nd Line of all Items should scale 1, and rotate 0, And 3rd Line of all Items should scale 0.5, and rotate 90, But the demo items seem to scale and rotate linearly ?

Aframe/Three fit to screen - calculate zoom

I want to zoom the camera in Three/Aframe so an image fits to screen.
This is the code I'm using:
this._camera = document.getElementById('camera').getAttribute('camera')
this._ratio = this._assetWidth/this._assetHeight
this._vFOV = window.THREE.Math.degToRad( this._camera?.fov || 80 )
this._height = 2 * Math.tan( this._vFOV / 2 ) * this.data.distance
this._width = this._height * this._ratio
this._zoom = this._ratio > 1 ? this._width/window.innerWidth : this._height/window.innerHeight
console.log(this._zoom, this._ratio, this._width, window.innerWidth)
I've got to the part where I need to calculate Zoom so that the object fits to the screen, i.e. if it's landscape fits to width, if it's portrait fit to height.
I thought this was the answer but it's not. that's to calculate the camera position rather than zoom value.
I'm stuck on how you work out the zoom value.
Any clues?
Fitting an object to the screen by:
changing the camera FoV
zooming the camera
repositioning the camera / object
is quite similar once you understand where the formulas came from.
We'll use this neat image (from this SO thread) as it covers all three topics:
0. What do we want to achieve
We want the object (the longer side of either its width or height) to cover the filmHeight - so it fits the screen.
1. Recalculating the FoV
In this case we do know the focalLength (camera distance from the object) and filmHeight (object width or height). We can calculate fov / 2 thanks to our friend trigonometry:
Tan (fov / 2) = (filmHeight / 2) / focalLength
=>
fov = 2 * ATan ((filmHeight / 2)) / focalLength * 180 / PI
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("fit", {
init: function() {
const plane = document.querySelector("a-plane")
const distance = this.el.object3D.position.distanceTo(plane.object3D.position)
var height = plane.getAttribute("geometry").height
var newFov = 2 * Math.atan((height / 2) / distance) * (180 / Math.PI); // in degrees
this.el.sceneEl.camera.fov = newFov
}
})
</script>
<a-scene>
<a-plane position="0 1.6 -2" material="src: https://i.imgur.com/wjobVTN.jpg"></a-plane>
<a-camera position="0 1.6 0" fit></a-camera>
</a-scene>
2. Repositioning the object / camera
Same triangle different variables. Now we want to know the focalLength:
Tan (fov / 2) = (filmHeight / 2) / focalLength
=>
focalLength = (filmHeight / 2) / Tan (fov / 2)
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("fit", {
init: function() {
const plane = document.querySelector("a-plane")
const height = plane.getAttribute("geometry").height
const fov = this.el.sceneEl.camera.fov * (Math.PI / 180);
const newDistance = Math.abs((height / 2) / Math.tan(fov / 2))
plane.object3D.position.z = -1 * newDistance;
}
})
</script>
<a-scene>
<a-plane position="0 1.6 -2" material="src: https://i.imgur.com/wjobVTN.jpg"></a-plane>
<a-camera position="0 1.6 0" fit></a-camera>
</a-scene>
3. Zooming the camera
If we know what distance should the camera be from the object for it to fill the screen - we know what is the relation between the current distance, and the new one:
zoom = currentDistance / necessaryDistance
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("fit", {
init: function() {
const plane = document.querySelector("a-plane");
const distance = this.el.object3D.position.distanceTo(plane.object3D.position);
const height = plane.getAttribute("geometry").height;
const fov = this.el.sceneEl.camera.fov * (Math.PI / 180);
const newDistance = Math.abs((height / 2) / Math.tan(fov / 2));
this.el.sceneEl.camera.zoom = distance / newDistance;
}
})
</script>
<a-scene>
<a-plane position="0 1.6 -2" material="src: https://i.imgur.com/wjobVTN.jpg"></a-plane>
<a-camera position="0 1.6 0" fit></a-camera>
</a-scene>

Shift A-Frame Origin

When I place a block at the origin it is very far back from my sensors. I wish to move the aframe origin forward 1 meter (1 in the -z direction). Additionally I am using a component which tracks the cameras position and so I cannot just wrap everything in an <a-entity> and move that forward. How can I change the position of the origin?
Component:
AFRAME.registerComponent('info-panel', {
tick: function() {
var el = this.el;
var camera = document.querySelector('a-camera');
var cpos = camera.getAttribute('position');
var x = cpos.x;
var z = cpos.z;
var angle;
if (z === 0) {
if (x === 0) {
angle = 0;
} else if (x > 0) {
angle = 90;
} else {
angle = -90;
}
} else {
angle = (z > 0 ? 0 : 180);
angle += 180 / Math.PI * Math.atan(x / z);
}
el.setAttribute('rotation', {x: 0, y: angle, z: 0});
}
});
Scene:
<a-scene>
    <a-camera></a-camera>
    <a-panel info-panel></a-panel>
</a-scene>
Or you can position the camera with wrapper entity.
How do a place the camera first position
<a-entity position="0 0 5">
<a-camera></a-camera>
</a-entity>
I'm not sure if there is such functionality,
I would wrap everything except the camera in <a-entity>
So You can position 'everything' except the camera.

QML: using Matrix4x4 transform

How do you rotate, using Matrix4x4 transform, a QML item around another axis than z, with the center of the item as origin of the transformation?
To rotate around the y axis with (0,0) as origin, I tried naively:
Image {
source: "..."
width: 100
height: 100
transform: Matrix4x4 {
property real a: Math.PI / 4
matrix: Qt.matrix4x4(
Math.cos(a), 0, -Math.sin(a), 0,
0, 1, 0, 0,
Math.sin(a), 0, Math.cos(a), 0,
0, 0, 0, 1)
}
}
As a result, I get a cut width item whereas I am looking for a perspective effect.
Can anyone explain how the transformation matrix of QML items works?
Here's comment from Unity 8:
// Rotating 3 times at top/bottom because that increases the perspective.
// This is a hack, but as QML does not support real 3D coordinates
// getting a higher perspective can only be done by a hack. This is the most
// readable/understandable one I could come up with.
Link to source code: https://github.com/ubports/unity8/blob/xenial/qml/Launcher/LauncherDelegate.qml#L287
The thing to be careful about is there appears to be clipping around z >= 0, where the object is technically in front of your monitor. To ensure the object stays on screen, you need to translate it so that it remains behind the monitor. In the following example, because I know the object is 300x300 and that it is centered, I know that I only need to push it into the screen by 150 pixels.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
property real xrotation: 0
property real yrotation: 0
property real zrotation: 0
Image {
width: 300
height: 300
anchors.centerIn: parent
source: "image-32.svg"
sourceSize: Qt.size(width, height)
transform: Matrix4x4 {
matrix: (() => {
let m = Qt.matrix4x4();
m.translate(Qt.vector3d(150, 150, -150));
m.rotate(zrotation, Qt.vector3d(0, 0, 1));
m.rotate(yrotation, Qt.vector3d(0, 1, 0));
m.rotate(xrotation, Qt.vector3d(1, 0, 0));
m.translate(Qt.vector3d(-150, -150, 0));
return m;
})()
}
}
Timer {
running: xbutton.pressed
repeat: true
interval: 100
onTriggered: xrotation += 5
}
Timer {
running: ybutton.pressed
repeat: true
interval: 100
onTriggered: yrotation += 5
}
Timer {
running: zbutton.pressed
repeat: true
interval: 100
onTriggered: zrotation += 5
}
footer: Frame {
RowLayout {
width: parent.width
Button {
id: xbutton
text: "X"
}
Button {
id: ybutton
text: "Y"
}
Button {
id: zbutton
text: "Z"
}
Button {
text: "Reset"
onClicked: {
xrotation = yrotation = zrotation = 0;
}
}
}
}
}
// image-32.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M2 5v22h28V5zm27 21H3v-5.474l4.401-3.5 1.198.567L14 13.106l5 4.531 3.506-3.123L29 20.39zm-5.997-12.422a.652.652 0 0 0-.926-.033L19 16.293l-4.554-4.131a.652.652 0 0 0-.857-.013L8.45 16.417l-.826-.391a.642.642 0 0 0-.72.117L3 19.248V6h26v13.082zM19 8a2 2 0 1 0 2 2 2.002 2.002 0 0 0-2-2zm0 3a1 1 0 1 1 1-1 1.001 1.001 0 0 1-1 1z"/><path fill="none" d="M0 0h32v32H0z"/></svg>
You can Try it Online!

Resources