rotate a globe with the mouse - javascript

I can determine where on a globe the user has clicked.
When the user first clicks the mouse button down, I can store where they have clicked; lets call it the pin.
Then, as they drag the mouse around, I want to put the pin under the mouse cursor.
Here is some code that seems to roughly keep the pin under the mouse pointer, but doesn't correctly maintain the globe; it flickers and spins randomly. Sometimes it flips the axis entirely so the globe is momentarily back-to-front.
function evtPos(evt) {
if(!scene.ortho) return null;
var x = lerp(scene.ortho[0],scene.ortho[1],evt.clientX/canvas.width),
y = lerp(scene.ortho[3],scene.ortho[2],evt.clientY/canvas.height), // flipped
sqrd = x*x+y*y;
return (sqrd > 1)?
null:
mat4_vec3_multiply(mat4_inverse(scene.mvMatrix),[x,y,Math.sqrt(1-sqrd)]);
}
function onMouseDown(evt) {
pin = evtPos(evt);
}
function onMouseMove(evt,keys,isMouseDown) {
if(!isMouseDown) return;
var pt = evtPos(evt);
if(pin == null) pin = pt;
if(pt == null) return;
var d = vec3_sub(pt,pin),
rotx = Math.atan2(d[1],d[2]),
roty = (d[2] >= 0)?
-Math.atan2(d[0] * Math.cos(rotx),d[2]):
Math.atan2(d[0] * Math.cos(rotx),-d[2]),
rotz = Math.atan2(Math.cos(rotx),Math.sin(rotx)*Math.sin(roty));
scene.mvMatrix = mat4_multiply(scene.mvMatrix,mat4_rotation(rotx,[1,0,0]));
scene.mvMatrix = mat4_multiply(scene.mvMatrix,mat4_rotation(roty,[0,1,0]));
scene.mvMatrix = mat4_multiply(scene.mvMatrix,mat4_rotation(rotz,[0,0,1]));
}
function onMouseUp(evt) {
pin = null;
}
Also, over time, an error seems to build up and the pin drifts further and further from the mouse pointer. I presume I should somehow compute the mvMatrix completely rather than by lots of samll increments each event?
I want the user to be able to drag the globe around to navigate naturally. All code to spin globes that I've found uses fixed speeds e.g. arrow keys, rather than 'pinning' the globe under a mouse pointer. Unity has a function Quaternion.FromToRotation(fromPos,toPos) which seems very promising but the source is not available.

One of the approaches for doing this is the arcBall algorithm. There are even JavaScript implementations available so you don't need to roll your own.

Related

How to use .onclick event for multiple times?

For example, I have a rectangle on my html page, and want to rotate it for 90deg by clicking on it:
rectangle.onclick = () => {
rectangle.style.transform = "rotate(90deg)";
When I click once - it rotates,but when i wanna rotate it for the second time - it does not work. Is there any way to to use .onclick event for two or more times?
CSS is not stateful; that is, when you set "rotate(90)" it doesn't rotate the item 90 degrees further, it rotates it to exactly 90 degrees from 0. So, if you want to rotate it, you need to set the correct angle: 90, 180, 270, &c. You can use a simple state variable to keep track of your angle, like this:
let rotation = 0 // rotation angle variable
rectangle.onclick = () => {
if (rotation >= 360) {
rotation = 0
} else {
rotation += 90;
}
rectangle.style.transform = `rotate(${rotation}deg)`;
}
When you set pentangle.style.transform, you're setting a style. That transform is from the baseline, unrotated shape. Your onclick event is likely getting fired multiple times, but each time, it does the same thing: cause the pentangle to be rotated from its default orientation.
You'll need something more like this, which advances the rotation amount based on the current rotation.
pentangle.onclick = () => {
if(pentangle.style.transform == "rotate(90deg)")
pentangle.style.transform = "rotate(180deg)";
else if(pentangle.style.transform == "rotate(180deg)")
pentangle.style.transform = "rotate(270deg)";
else if(pentangle.style.transform == "rotate(270deg)")
pentangle.style.transform = "";
else
pentangle.style.transform = "rotate(90deg)";

3d motion/hitboxing with threejs

I am attempting to do some simple motion with threejs. My goal is to have a box you can move around that cannot walk through walls. As of right now my motion code looks like this
function handleMotion(mesh) {
var xClone = mesh.clone();
xClone.position.x += mesh.velocity.x;
if (checkColl(xClone, collisionMeshList, [mesh.uuid])) {
mesh.velocity.x = 0;
}
var yClone = mesh.clone();
yClone.position.y += mesh.velocity.y;
if (checkColl(yClone, collisionMeshList, [mesh.uuid])) {
mesh.velocity.y = 0;
}
var zClone = mesh.clone();
zClone.position.z += mesh.velocity.z;
if (checkColl(zClone, collisionMeshList, [mesh.uuid])) {
mesh.velocity.z = 0;
}
mesh.position.add(mesh.velocity);
}
It checks each axis of motion to ensure that it is not going to move into a wall on the next frame. The problem is it does not prevent the cube from moving into the wall, however, once it moves into the wall it prevents the cube move moving out.
I also tried adding a "sim step" just to check if it could be increased by increasing the number of calculations it did however led to no change. The code is basically acting like the clones are at the same position as the mesh however I have confirmed they are indeed offset.

window.onmousedown() takes place before window.oncontextmenu()

I am developing a game in JavaScript, which consists in being able to move on the screen and create walls, by holding the left click button, while moving the cursor around on the canvas. On the other hand, by pressing right click, one bullet is created. The problem appears when one bullet is created, by right clicking, which, also, determines the window.onmousedown() function to take place. I do not want to create walls by right clicking on the screen, because the right click is only supposed to create a bullet, not a wall, as the wall should only be created by the left click.
I've tried to create a variable, called wallCannotBeCreated, which is set to false, by default. In my mind, the window.oncontextmenu() function should take place, when right click event takes place, thus wallCannotBeCreated becomes true, in order that window.onmousedown() function is becoming "unable" to create any new wall, until window.onmouseup() function takes place, where wallCannotBeCreated becomes, again, false. Unfortunatelly, it doesn't seem to work, as, always, window.onmousedown() function occurs before window.oncontextmenu() function.
var wallCannotBeCreated = false;
window.oncontextmenu = function (e)
{
//alert("right click was pressed");
// a new bullet must be created
bullets[++counterBullets] = new Bullet(player.x + player.r / 2, player.y, e.x, e.y);
wallCannotBeCreated = true;
console.log(wallCannotBeCreated);
}
window.onmousedown = function()
{
console.log(wallCannotBeCreated);
if(wallCannotBeCreated == false)
{
//console.log("mouse is being pressed");
Keys.click = true;
// one wall must be created
walls[++wallNumber] = new Wall();
walls[wallNumber].timerStart();
}
}
window.onmouseup = function()
{
//console.log("mouse was released");
Keys.click = false;
wallCannotBeCreated = false;
}

CreateJs - Controlling timeline playhead with click & drag

First time posting here, hope I'm doing this right :) I am trying to create a 360 spin in Adobe Animate with createJS, using a sequence of images embedded in a MovieClip. I have managed to get control of the timeline playhead using the code below:-
var start_x;
var startFrame;
var changeDistance;
var travelDistance;
this.threeSixty.addEventListener("mousedown", onMouseDown.bind(this));
this.threeSixty.addEventListener("pressup", onMouseUp.bind(this));
function onMouseDown(e) {
start_x = e.stageX;
startFrame = this.threeSixty.timeline.position;
this.threeSixty.addEventListener("pressmove", onMouseMove.bind(this));
}
function onMouseUp(e) {
this.threeSixty.removeEventListener("pressmove", onMouseMove.bind(this));
}
function onMouseMove(e) {
var changeDistance = e.stageX-start_x;
var travelDistance = startFrame+changeDistance;
if (travelDistance > this.threeSixty.timeline.duration){
this.threeSixty.gotoAndStop(travelDistance % this.threeSixty.timeline.duration);
}else if (travelDistance < 0){
this.threeSixty.gotoAndStop (this.threeSixty.timeline.duration + (travelDistance % threeSixty.timeline.duration));
} else {
this.threeSixty.gotoAndStop(travelDistance);
}
}
The problem is, when you click and drag the image along the X axis, the image sequence only moves forward/backwards by a single frame, as opposed to continuing the image sequence until the either mouse has stopped moving, or the mouse click is released. Any ideas where I might be going wrong here?
Thanks

HTML5 photoshop like polygonal lasso selection

Im looking to build a tool to cut out a portion of a photo by letting the user create a closed shape. The user should be able to start drawing lines. From point a to point b, to c, e, d, e, f .... to eventually point a again to close the shape.
I want to use the HTML5 canvas for this. I think this could be a good fit and I'm thinking about using something like flashcanvas as fallback for IE/older browsers?
Is there any tutorial/open source application that I could use to build this sort of thing?
This is the first time I'm going to build an application using HTML5 canvas so are there any pitfalls I should worry about?
I think this is advanced usage of canvas. You have to know the basics, how to draw, how to use layers, how to manipulate pixels. Just ask google for tutorials.
Assuming you know about the previous, I'll give it a try. I've never done that before but I have an idea :
You need 3 canvas :
the one containing your picture (size of your picture)
a layer where the user draw the selection shape (size of your picture, on top of the first canvas)
a result canvas, will contain your cropped picture (same size, this one doesn't need to be displayed)
When the user click on your picture : actually, he clicks on the layer, the layer is cleared and a new line begins.
When he clicks on it another time, the previous started line is drawn and another one begins, etc... You keep doing this until you click on a non-blank pixel (which means you close the shape).
If you want the user to preview the lines, you need another canvas ( explained here http://dev.opera.com/articles/view/html5-canvas-painting/#line )
When the shape is closed, the user has to click inside or outside the shape to determine which part he wants to select. You fill that part with a semi-transparent gray for example ( flood fill explained here http://www.williammalone.com/articles/html5-canvas-javascript-paint-bucket-tool/ )
Now the layer canvas contains a colored shape corresponding to the user selection.
Get the pixel data from your layer and read through the array, every time you find a non-blank pixel at index i, you copy this pixel from your main canvas to the result canvas :
/* First, get pixel data from your 3 canvas into
* layerPixData, resultPixData, picturePixData
*/
// read the entire pixel array
for (var i = 0 ; i < layerPixData.length ; i+=4 ) {
//if the pixel is not blank, ie. it is part of the selected shape
if ( layerPixData[i] != 255 || layerPixData[i+1] != 255 || layerPixData[i+2] != 255 ) {
// copy the data of the picture to the result
resultPixData[i] = picturePixData[i]; //red
resultPixData[i+1] = picturePixData[i+1]; //green
resultPixData[i+2] = picturePixData[i+2]; //blue
resultPixData[i+3] = picturePixData[i+3]; //alpha
// here you can put the pixels of your picture to white if you want
}
}
If you don't know how pixel manipulation works, read this https://developer.mozilla.org/En/HTML/Canvas/Pixel_manipulation_with_canvas
Then, use putImageData to draw the pixels to your result canvas. Job done !
If you want to move lines of your selection, way to go : http://simonsarris.com/blog/225-canvas-selecting-resizing-shape
Here is how you should do that:
The code at the following adds a canvas on top of your page and then by clicking and dragging on that the selection areas would be highlighted. What you need to do after that is to make a screenshot from the underlying page and also a mask layer out of the created image in your canvas and apply that to the screenshot, just like how it is shown in one other answers.
/* sample css code for the canvas
#overlay-canvas {
position: absolute;
top: 0;
left: 0;
background-color: transparent;
opacity: 0.4;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
}
*/
function getHighIndex(selector) {
if (!selector) { selector = "*" };
var elements = document.querySelectorAll(selector) ||
oXmlDom.documentElement.selectNodes(selector);
var ret = 0;
for (var i = 0; i < elements.length; ++i) {
if (deepCss(elements[i],"position") === "static")
continue;
var temp = deepCss(elements[i], "z-index");
if (temp != "auto")
temp = parseInt(temp, 10) || 0;
else
continue;
if (temp > ret)
ret = temp;
}
return ret;
}
maxZIndex = getHighIndex();
$.fn.extend({
lasso: function () {
return this
.mousedown(function (e) {
// left mouse down switches on "capturing mode"
if (e.which === 1 && !$(this).is(".lassoRunning")) {
var point = [e.offsetX, e.offsetY];
$(this).addClass("lassoRunning");
$(this).data("lassoPoints", [point]);
$(this).trigger("lassoStart", [point]);
}
})
.mouseup(function (e) {
// left mouse up ends "capturing mode" + triggers "Done" event
if (e.which === 1 && $(this).is(".lassoRunning")) {
$(this).removeClass("lassoRunning");
$(this).trigger("lassoDone", [$(this).data("lassoPoints")]);
}
})
.mousemove(function (e) {
// mouse move captures co-ordinates + triggers "Point" event
if ($(this).is(".lassoRunning")) {
var point = [e.offsetX, e.offsetY];
$(this).data("lassoPoints").push(point);
$(this).trigger("lassoPoint", [point]);
}
});
}
});
function onLassoSelect() {
// creating canvas for lasso selection
var _canvas = document.createElement('canvas');
_canvas.setAttribute("id", "overlay-canvas");
_canvas.style.zIndex = ++maxZIndex;
_canvas.width = document.width
_canvas.height = document.height
document.body.appendChild(_canvas);
ctx = _canvas.getContext('2d'),
ctx.strokeStyle = '#0000FF';
ctx.lineWidth = 5;
$(_canvas)
.lasso()
.on("lassoStart", function(e, lassoPoint) {
console.log('lasso start');
var pos = lassoPoint;
ctx.beginPath();
ctx.moveTo(pos[0], pos[1]);
console.log(pos);
})
.on("lassoDone", function(e, lassoPoints) {
console.log('lasso done');
var pos = lassoPoints[0];
ctx.lineTo(pos[0], pos[1]);
ctx.fill();
console.log(pos);
})
.bind("lassoPoint", function(e, lassoPoint) {
var pos = lassoPoint;
ctx.lineTo(pos[0], pos[1]);
ctx.fill();
console.log(pos);
});
}

Categories

Resources