Finding x and y offset of point after rotation - math

Hi so I'm stuck with a problem. My old projectile system would only shoot in 4 directions so when finding a new offset it was easy, I just had to inverse certain numbers
case "down":
xOffset = hitbox.xOffset;
yOffset = hitbox.yOffset;
width = hitbox.width;
height = hitbox.height;
break;
case "up":
xOffset = hitbox.xOffset;
yOffset = -hitbox.yOffset - hitbox.height;
width = hitbox.width;
height = hitbox.height;
break;
case "right":
xOffset = hitbox.yOffset;
yOffset = hitbox.xOffset;
width = hitbox.height;
height = hitbox.width;
break;
case "left":
xOffset = -hitbox.yOffset - hitbox.height;
yOffset = hitbox.xOffset;
width = hitbox.height;
height = hitbox.width;
break;
So this worked perfectly, but now the problem is I am no longer using rects for my hitbox and using circles, and i also now implemented 360 shooting.
I have a function that gets the x and y speed based on where I clicked but I do not have the function to get the new hitbox based on it.
newSpeed.x += speed * Math.cos(angle * Math.PI / 180);
newSpeed.y += speed * Math.sin(angle * Math.PI / 180);
How can I do something similar to get the new offset?

Related

WebGL Walkthrough, Move around the 3D scene

I'm new to WebGL, and I'm trying to create a walk-through for a website, I have taken my Maya model into WebGL with the help of inka3D, but when I apply the following code for the movement, it doesn't work as it explains. Only the left arrow works fine.
function resize()
{
var width = canvas.offsetWidth;
var height = canvas.offsetHeight;
canvas.width = width;
canvas.height = height;
aspect = width / height;
}
var cameraTargetX = 37.2878151;
var cameraTargetY = 12.846137;
var cameraTargetZ = 7.17901707;
var dx = 5;
var dy = 5;
window.addEventListener('keydown',doKeyDown,true);
function doKeyDown(evt){
switch (evt.keyCode) {
case 38: /* Up arrow was pressed */
if (cameraTargetY - dy > 0){
cameraTargetY -= dy;
}
break;
case 40: /* Down arrow was pressed */
if (cameraTargetY + dy < height){
cameraTargetY += dy;
}
break;
case 37: /* Left arrow was pressed Fine*/
if (cameraTargetX - dx > 0){
cameraTargetX -= dx;
}
break;
case 39: /* Right arrow was pressed */
if (cameraTargetX + dx < width){
cameraTargetX += dx;
}
break;
}
}
};
If only the left arrow works this means that difference of (cameraTargetX - dx ) > 0. Thats why you can translate. The reason is cameraTargetX is 37 diff of 5 make it 32 and on key press you can visualize this in 5X7(loop). Key is pressed 7 times until the value become lesser than zero
But when var cameraTargetY = 12.846137; and dy is 5 it take only 5x2(loop) just a fraction and the value become lesser than zero and you can visualize the diff.
Solution is as stated dx and dy are delta values means this should be very small as variable convection so try with
var dx = 0.05;
var dy = 0.05;
You will get answer. If any doubt feel free to ask

D3.geo : responsive frame given a geojson object?

I use Mike Bostock's code to Center a map in d3 given a geoJSON object.
The important part of the code is this:
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("/d/4090846/us.json", function(error, us) {
var states = topojson.feature(us, us.objects.states),
state = states.features.filter(function(d) { return d.id === 34; })[0];
/* ******************* AUTOCENTERING ************************* */
// Create a unit projection.
var projection = d3.geo.albers()
.scale(1)
.translate([0, 0]);
// Create a path generator.
var path = d3.geo.path()
.projection(projection);
// Compute the bounds of a feature of interest, then derive scale & translate.
var b = path.bounds(state),
s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
// Update the projection to use computed scale & translate.
projection
.scale(s)
.translate(t);
/* ******************* END *********************************** */
// Landmass
svg.append("path")
.datum(states)
.attr("class", "feature")
.attr("d", path);
// Focus
svg.append("path")
.datum(state)
.attr("class", "outline")
.attr("d", path);
});
For example, bl.ocks.org/4707858 zoom in such:
How to center and zoom on the target topo/geo.json AND adjust the svg frame dimensions so it fit a 5% margin on each size ?
Mike's explained
Basically, Mike's code states the frame dimensions via
var width = 960, height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
Once the frame is hardly set, then you check out the largest limiting ratio so your geojson shape fill your svg frame on its largest dimension relative to the svg frame dimensions widht & height. Aka, if the shape's width VS frame width or shape height VS frame height is the highest. This, in turn, help to recalculate the scale via 1/highest ratio so the shape is as small as required. It's all done via:
var b = path.bounds(state),
s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height);
// b as [[left, bottom], [right, top]]
// (b[1][0] - b[0][0]) = b.left - b.right = shape's width
// (b[1][3] - b[0][4]) = b.top - b.bottom = shape's height
Then, refreshing your scale and transition you get Mike Bostock's zoom:
New framing
To frame up around the geojson shape is actually a simplification of Mike's code. First, set temporary svg dimensions:
var width = 200;
var svg = d3.select("body").append("svg")
.attr("width", width);
Then, get the dimensions of the shapes and compute around it :
var b = path.bounds(state);
// b.s = b[0][1]; b.n = b[1][1]; b.w = b[0][0]; b.e = b[1][0];
b.height = Math.abs(b[1][1] - b[0][1]); b.width = Math.abs(b[1][0] - b[0][0]);
var r = ( b.height / b.width );
var s = 0.9 / (b.width / width); // dimension of reference: `width` (constant)
//var s = 1 / Math.max(b.width / width, b.height / height ); // dimension of reference: largest side.
var t = [(width - s * (b[1][0] + b[0][0])) / 2, (width*r - s * (b[1][1] + b[0][1])) / 2]; //translation
Refresh projection and svg's height:
var proj = projection
.scale(s)
.translate(t);
svg.attr("height", width*r);
It's done and fit the pre-allocated width=150px, find the needed height, and zoom properly. See http://bl.ocks.org/hugolpz/9643738d5f79c7b594d0

Radius of projected sphere in screen space

I'm trying to find the visible size of a sphere in pixels, after projection to screen space. The sphere is centered at the origin with the camera looking right at it. Thus the projected sphere should be a perfect circle in two dimensions. I am aware of this 1 existing question. However, the formula given there doesn't seem to produce the result I want. It is too small by a few percent. I assume this is because it is not correctly taking perspective into account. After projecting to screen space you do not see half the sphere but significantly less, due to perspective foreshortening (you see just a cap of the sphere instead of the full hemisphere 2).
How can I derive an exact 2D bounding circle?
Indeed, with a perspective projection you need to compute the height of the sphere "horizon" from the eye / center of the camera (this "horizon" is determined by rays from the eye tangent to the sphere).
Notations:
d: distance between the eye and the center of the sphere
r: radius of the sphere
l: distance between the eye and a point on the sphere "horizon", l = sqrt(d^2 - r^2)
h: height / radius of the sphere "horizon"
theta: (half-)angle of the "horizon" cone from the eye
phi: complementary angle of theta
h / l = cos(phi)
but:
r / d = cos(phi)
so, in the end:
h = l * r / d = sqrt(d^2 - r^2) * r / d
Then once you have h, simply apply the standard formula (the one from the question you linked) to get the projected radius pr in the normalized viewport:
pr = cot(fovy / 2) * h / z
with z the distance from the eye to the plane of the sphere "horizon":
z = l * cos(theta) = sqrt(d^2 - r^2) * h / r
so:
pr = cot(fovy / 2) * r / sqrt(d^2 - r^2)
And finally, multiply pr by height / 2 to get the actual screen radius in pixels.
What follows is a small demo done with three.js. The sphere distance, radius and the vertical field of view of the camera can be changed by using respectively the n / f, m / p and s / w pairs of keys. A yellow line segment rendered in screen-space shows the result of the computation of the radius of the sphere in screen-space. This computation is done in the function computeProjectedRadius().
projected-sphere.js:
"use strict";
function computeProjectedRadius(fovy, d, r) {
var fov;
fov = fovy / 2 * Math.PI / 180.0;
//return 1.0 / Math.tan(fov) * r / d; // Wrong
return 1.0 / Math.tan(fov) * r / Math.sqrt(d * d - r * r); // Right
}
function Demo() {
this.width = 0;
this.height = 0;
this.scene = null;
this.mesh = null;
this.camera = null;
this.screenLine = null;
this.screenScene = null;
this.screenCamera = null;
this.renderer = null;
this.fovy = 60.0;
this.d = 10.0;
this.r = 1.0;
this.pr = computeProjectedRadius(this.fovy, this.d, this.r);
}
Demo.prototype.init = function() {
var aspect;
var light;
var container;
this.width = window.innerWidth;
this.height = window.innerHeight;
// World scene
aspect = this.width / this.height;
this.camera = new THREE.PerspectiveCamera(this.fovy, aspect, 0.1, 100.0);
this.scene = new THREE.Scene();
this.scene.add(THREE.AmbientLight(0x1F1F1F));
light = new THREE.DirectionalLight(0xFFFFFF);
light.position.set(1.0, 1.0, 1.0).normalize();
this.scene.add(light);
// Screen scene
this.screenCamera = new THREE.OrthographicCamera(-aspect, aspect,
-1.0, 1.0,
0.1, 100.0);
this.screenScene = new THREE.Scene();
this.updateScenes();
this.renderer = new THREE.WebGLRenderer({
antialias: true
});
this.renderer.setSize(this.width, this.height);
this.renderer.domElement.style.position = "relative";
this.renderer.autoClear = false;
container = document.createElement('div');
container.appendChild(this.renderer.domElement);
document.body.appendChild(container);
}
Demo.prototype.render = function() {
this.renderer.clear();
this.renderer.setViewport(0, 0, this.width, this.height);
this.renderer.render(this.scene, this.camera);
this.renderer.render(this.screenScene, this.screenCamera);
}
Demo.prototype.updateScenes = function() {
var geometry;
this.camera.fov = this.fovy;
this.camera.updateProjectionMatrix();
if (this.mesh) {
this.scene.remove(this.mesh);
}
this.mesh = new THREE.Mesh(
new THREE.SphereGeometry(this.r, 16, 16),
new THREE.MeshLambertMaterial({
color: 0xFF0000
})
);
this.mesh.position.z = -this.d;
this.scene.add(this.mesh);
this.pr = computeProjectedRadius(this.fovy, this.d, this.r);
if (this.screenLine) {
this.screenScene.remove(this.screenLine);
}
geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0.0, 0.0, -1.0));
geometry.vertices.push(new THREE.Vector3(0.0, -this.pr, -1.0));
this.screenLine = new THREE.Line(
geometry,
new THREE.LineBasicMaterial({
color: 0xFFFF00
})
);
this.screenScene = new THREE.Scene();
this.screenScene.add(this.screenLine);
}
Demo.prototype.onKeyDown = function(event) {
console.log(event.keyCode)
switch (event.keyCode) {
case 78: // 'n'
this.d /= 1.1;
this.updateScenes();
break;
case 70: // 'f'
this.d *= 1.1;
this.updateScenes();
break;
case 77: // 'm'
this.r /= 1.1;
this.updateScenes();
break;
case 80: // 'p'
this.r *= 1.1;
this.updateScenes();
break;
case 83: // 's'
this.fovy /= 1.1;
this.updateScenes();
break;
case 87: // 'w'
this.fovy *= 1.1;
this.updateScenes();
break;
}
}
Demo.prototype.onResize = function(event) {
var aspect;
this.width = window.innerWidth;
this.height = window.innerHeight;
this.renderer.setSize(this.width, this.height);
aspect = this.width / this.height;
this.camera.aspect = aspect;
this.camera.updateProjectionMatrix();
this.screenCamera.left = -aspect;
this.screenCamera.right = aspect;
this.screenCamera.updateProjectionMatrix();
}
function onLoad() {
var demo;
demo = new Demo();
demo.init();
function animationLoop() {
demo.render();
window.requestAnimationFrame(animationLoop);
}
function onResizeHandler(event) {
demo.onResize(event);
}
function onKeyDownHandler(event) {
demo.onKeyDown(event);
}
window.addEventListener('resize', onResizeHandler, false);
window.addEventListener('keydown', onKeyDownHandler, false);
window.requestAnimationFrame(animationLoop);
}
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Projected sphere</title>
<style>
body {
background-color: #000000;
}
</style>
<script src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r61/three.min.js"></script>
<script src="projected-sphere.js"></script>
</head>
<body onLoad="onLoad()">
<div id="container"></div>
</body>
</html>
Let the sphere have radius r and be seen at a distance d from the observer. The projection plane is at distance f from the observer.
The sphere is seen under the half angle asin(r/d), so the apparent radius is f.tan(asin(r/d)), which can be written as f . r / sqrt(d^2 - r^2). [The wrong formula being f . r / d.]
The illustrated accepted answer above is excellent, but I needed a solution without knowing the field of view, just a matrix to transform between world and screen space, so I had to adapt the solution.
Reusing some variable names from the other answer, calculate the start point of the spherical cap (the point where line h meets line d):
capOffset = cos(asin(l / d)) * r
capCenter = sphereCenter + ( sphereNormal * capOffset )
where capCenter and sphereCenter are points in world space, and sphereNormal is a normalized vector pointing along d, from the sphere center towards the camera.
Transform the point to screen space:
capCenter2 = matrix.transform(capCenter)
Add 1 (or any amount) to the x pixel coordinate:
capCenter2.x += 1
Transform it back to world space:
capCenter2 = matrix.inverse().transform(capCenter2)
Measure the distance between the original and new points in world space, and divide into the amount you added to get a scale factor:
scaleFactor = 1 / capCenter.distance(capCenter2)
Multiply that scale factor by the cap radius h to get the visible screen radius in pixels:
screenRadius = h * scaleFactor

Create 3rd person camera position calculation with quaternions

I want to create a 3rd person camera similiar to example. The camera should stick behind the object and rotate if the rotation difference between camera and object is too high (maybe above ten percent).
This is my actual camera code:
var targetPosition = this.getTargetPosition();
var targetRotation = this.getTargetRotation();
var tmpQuaternion = new THREE.Quaternion();
tmpQuaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), 180 * (Math['PI'] / 180));
this.camera.quaternion = targetRotation;
this.camera.position = targetPosition;
this.camera.quaternion.multiplySelf(tmpQuaternion);
this.camera.quaternion.normalize();
this.camera.updateMatrix();
this.camera.translateZ(200);
this.camera.translateY(50);
But there are several problems right now. The camera quaternion should not set directly to the target rotation. But I dont know how to calculate the difference between camera quaternion and target quaternion and use maybe this if the distance is too high:
var qm = new THREE.Quaternion();
THREE.Quaternion.slerp(targetRotation, this.camera.quaternion, qm, time);
this.camera.quaternion = qm;
The second problem is the position itself. Currently I set camera position to the object position and translate it back to view behind, but the translation should be already in target position and the camera position should be translated to the target position.
Update 1: I made an example html: http://ssachtleben.github.com/CameraProblem/
Update 2: I made some progress now. Seems like I get quaternion difference with this function:
getAxisAngle = function(quaternion1, quaternion2) {
var tmpQuaternion = new THREE.Quaternion();
tmpQuaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), 180 * (Math['PI'] / 180));
var tmpRotation1 = quaternion1.clone();
tmpRotation1.multiplySelf(tmpQuaternion);
tmpRotation1.normalize();
var tmpRotation2 = quaternion2.clone();
if (tmpRotation2.w > 1) {
tmpRotation2.normalize();
}
var angle1 = 2 * Math['acos'](tmpRotation1.w);
var angle2 = 2 * Math['acos'](tmpRotation2.w);
var diff = angle1 > angle2 ? angle1 - angle2 : angle2 - angle1;
return diff;
};
But know I need to freeze the axis if the angle difference is too high. How can I do this?
Any help would be appreciated.
Ok finally the camera is fixed and works as excepted:
var targetPosition = this.getTargetPosition();
var targetRotation = this.getTargetRotation();
var tmpQuaternion = new THREE.Quaternion();
tmpQuaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), 180 * (Math['PI'] / 180));
targetRotation.multiplySelf(tmpQuaternion);
targetRotation.quaternion.normalize();
var qm = new THREE.Quaternion();
THREE.Quaternion.slerp(this.camera.quaternion, targetRotation, qm, 0.07);
this.camera.quaternion = qm;
this.camera.quaternion.normalize();

Rotate point from a given center in Flex

I'm trying to rotate a point in my Canvas from a given point (center). In my MouseDown handler, I save the point where user click (oldPos), and in my MouseMove handler, I'm doing this:
private function onMouseMove(event:MouseEvent):void
{
// Where the user pointer right now
var endPoint:Point = new Point(event.localX,event.localY);
// Calculate angle in radians from the user pointer
var angle:Number = getLineAngleFromHorizontal(oldPos,endPoint);
var rad:Number = Math.PI * (angle / 180);
// Point which I want to rotate
pTop = new Point(oldPos.x,oldPos.y - 30);
var distance:Number = Point.distance(oldPos,pTop);
// Calculate the translation point from previously distance and angle
var translatePoint:Point = Point.polar(distance, rad);
// New point coordinates (in theory)
pTop.x += translatePoint.x;
pTop.y += translatePoint.y;
// Then, draw the line...
}
Where getLineAngleFromHorizontal is a function that returns the angle formed by a center and a give point:
private function getLineAngleFromHorizontal(p1:Point,p2:Point):Number
{
var RotVecOrigen:Point = new Point((p2.x-p1.x),(p2.y-p1.y));
var ModRot:Number = Math.sqrt((RotVecOrigen.x*RotVecOrigen.x)+(RotVecOrigen.y*RotVecOrigen.y));
var ret:Number;
if(((RotVecOrigen.x < 0) && (RotVecOrigen.y <= 0))||((RotVecOrigen.x >= 0) && (RotVecOrigen.y < 0)))
{
ret = Math.round((180.0*(Math.acos(RotVecOrigen.x/ModRot))/Math.PI));
}else{
ret = Math.round((180.0*(-Math.acos(RotVecOrigen.x/ModRot))/Math.PI));
}
return ret;
}
To see an example, watch the image below:
But I don't know why isn't work. I mean, pTop point isn't move where I want, and I think that my calcs are correct.
Can anybody help me? (maybe someone with Math knowledge)
I'm not entirely sure what you want to accomplish. Do you want your new point to be at an 330 degree offset from your center point?
If you want to move your point 330 degrees, use this:
function directionalDistance($start:Point, $direction:Number, $distance:Number, $zeroDegreesUp:Boolean = false):Point{
if($zeroDegreesUp) $direction = ( $direction + 270)%360;
var x:Number = Math.cos($direction * Math.PI / 180) * $distance;
var y:Number = Math.sin($direction * Math.PI / 180) * $distance;
return new Point($start.x +x, $start.y + y);
}
//
var newPoint:Point = directionalDistance(new Point(event.localX,event.localY), 330, 50, true);

Resources