Examples of 2d Canvas smoke with mouse interaction. - html5-canvas

I'm looking for an example of a smoke effect using HTML 2D Canvas where the smoke is effected by the user moving their mouse though the smoke so that the smoke is disturbed.

I don't know if you are still interesting in this but, here take a look at this
So, instead of checking if particle touched the edge you should provide mouse x,y coordinates:
function init() {
var canvas = document.getElementById('myCanvas');
if (canvas.getContext) {
// Set the context variable so it can be re-used
context = canvas.getContext('2d');
// Create the particles and set their initial positions and velocities
for(var i=0; i < particleCount; ++i){
var particle = new Particle(context);
// Set the position to be inside the canvas bounds
particle.setPosition(generateRandom(0, canvasWidth), generateRandom(0, canvasHeight));
// Set the initial velocity to be either random and either negative or positive
particle.setVelocity(generateRandom(-maxVelocity, maxVelocity), generateRandom(-maxVelocity, maxVelocity));
particles.push(particle);
}
}
else {
alert("Please use a modern browser");
}
}

Related

3D three.js Create the ground surface of a 3D building

Following my post last week three.js How to programatically produce a plane from dataset I come back to the community to solve a problem of definition of surface occupied on the ground by a 3D building.
The solution proposed in comments in this post works for this building but is not universal.
To make it universal I chose the following method: when the walls are built I create their clone in another group (see this previous post for walls creation)
// prepare the clones
var clones = new THREE.Group();
scene.add(clones);
var num=0;
// drawing the real walls
var wallGeometry = new THREE.PlaneGeometry(size,(hstair*batims[i][1]));
val = 0xFFFFFF;
opa = 0.5;
if(deltaX > deltaY){val = 0x000000; opa = 0.05;} // shaded wall
var wallMaterial = new THREE.MeshBasicMaterial({color:val,transparent:true, opacity:opa, side:THREE.DoubleSide});
var walls = new THREE.Mesh(wallGeometry, wallMaterial);
walls.position.set((startleft+endleft)/2,(hstair*batims[i][1])/2,(startop+endtop)/2);
walls.rotation.y = -rads;
scene.add(walls);
// add the pseudo-walls to scene
var cloneGeometry=new THREE.PlaneGeometry(long,3);
var cloneMaterial=new THREE.MeshBasicMaterial({color:0xff0000,transparent:true,opacity:0.5,side:THREE.DoubleSide});
var clone=new THREE.Mesh(pseudomursGeometry,pseudomursMaterial);
clone.position.set((startleft+endleft)/2,3,(startop+endtop)/2);
clone.rotation.y=-rads;
clones.add(clone);
num++;
The idea is now to rotate this pseudo-building so that the longest wall is vertical, which allows me to determine the exact floor area occupied with its boundingBox:
var angle=turn=0;
for(i=0; i<dists.length; i++) { // dists is the array of wall lengths
if(dists[i]==longs[0]){ // longs is the reordered lengths array
angle=angles[i][1]; // angle of the longest wall
}
}
// we can now rotate the whole group to put the longest wall vertical
if(angle>0){
turn = angle*-1+(Math.PI/2);
}
else {
turn = angle+(Math.PI/2);
}
clones.rotation.y=turn;
It works perfectly as long as the building has a right angle, whatever its shape: triangle, rectangle, bevel, right angle polygons,
var boundingBox = new THREE.Box3().setFromObject(clones);
var thisarea = boundingBox.getSize();
// area size gives the expected result
console.log('AREA SIZE = '+thisarea.x+' '+thisarea.y+' '+thisarea.z);
...but not when there are no more right angles, for example a trapezoid
The reason is that we rotate the group, and not the cloned walls. I can access and rotate each wall by
for(n=0;n<num;n++){
thisangle = clones.children[n].rotation.y;
clones.children[n].rotation.y = turn-thisangle;
}
But the result is wrong for the others pseudo-walls:
So the question is: how to turn each red pseudo-wall so that the longest one is vertical and the others remain correctly positioned in relation to it? In this way, any building with any shape can be reproduced in 3D with its internal equipment. Any idea on how to achieve this result?
A weird & ugly but well-working solution:
// 1. determines which is the longest side
for(i=0; i<dists.length; i++) {
if(dists[i]==longs[0]){
longest=i;
break; // avoid 2 values if rectangle
}
}
// 2. the group is rotated until the longest side has an angle in degrees
// close to 0 or 180
var letsturn = setInterval(function() {
clones.rotation.y += 0.01;
var group_rotation = THREE.Math.radToDeg(clones.rotation.y); // degrees
var stop = Math.round(angles[longest][0] - group_rotation);
// 3. stop when longest wall is vertical
if( (stop>=179 && stop<=181) || (stop>=-1 && stop<=1) ) {
clearInterval(letsturn);
createPlane() // we can now use boundingBox in reliability
}
}, 1);
et voilĂ .

How to correctly position html elements in three js coordinate system?

I hopefully have a simple problem I can't get an answer to.
I have three js geometric spheres which move in a box. I place this box at the centre of the scene. The mechanics of how the spheres stay in the box is irrelevant. What is important is the spheres move about the origin (0,0) and the canvas always fills the page.
I want to draw a line from the moving spheres to a div or img element on the page. To do this I would assume I have to transform the css coordinates to three js coordinates. I found something I thought did something like this (Note: Over use of somethings to signify I am probably mistaken)
I can add a html element to the same scene/camera as webgl renderer but obviously using a different renderer but I am unsure how to proceed from there?
Basically I want to know:
How should I change the size of the div preserving aspect ratio if need be?
In essence I want the div or element to fill screen at some camera depth.
How to place the div at the centre of the scene by default?
Mines seems to be shifted 1000 in z direction but this might be the size of the div(img) which I have to bring into view.
How to draw a line between the webgl sphere and html div/img?
thanks in advance!
Unfortunately you have asked 3 questions, it is tricky to address them all at once.
I will explain how to position DIV element on top of some 3D object. My example would be a tooltip that appears when you hover the object by mouse: http://jsfiddle.net/mmalex/ycnh0wze/
So let's get started,
First of all you need to subscribe mouse events and convert 2D coordinates of a mouse to relative coordinates on the viewport. Very well explained you will find it here: Get mouse clicked point's 3D coordinate in three.js
Having 2D coordinates, raycast the object. These steps are quite trivial, but for completeness I provide the code chunk.
var raycaster = new THREE.Raycaster();
function handleManipulationUpdate() {
// cleanup previous results, mouse moved and they're obsolete now
latestMouseIntersection = undefined;
hoveredObj = undefined;
raycaster.setFromCamera(mouse, camera);
{
var intersects = raycaster.intersectObjects(tooltipEnabledObjects);
if (intersects.length > 0) {
// keep point in 3D for next steps
latestMouseIntersection = intersects[0].point;
// remember what object was hovered, as we will need to extract tooltip text from it
hoveredObj = intersects[0].object;
}
}
... // do anything else
//with some conditions it may show or hide tooltip
showTooltip();
}
// Following two functions will convert mouse coordinates
// from screen to three.js system (where [0,0] is in the middle of the screen)
function updateMouseCoords(event, coordsObj) {
coordsObj.x = ((event.clientX - renderer.domElement.offsetLeft + 0.5) / window.innerWidth) * 2 - 1;
coordsObj.y = -((event.clientY - renderer.domElement.offsetTop + 0.5) / window.innerHeight) * 2 + 1;
}
function onMouseMove(event) {
updateMouseCoords(event, mouse);
handleManipulationUpdate();
}
window.addEventListener('mousemove', onMouseMove, false);
And finally see the most important part, DIV element placement. To understand the code it is essential to get convenient with Vector3.project method.
The sequence of calculations is as follows:
Get 2D mouse coordinates,
Raycast object and remember 3D coordinate of intersection (if any),
Project 3D coordinate back into 2D (this step may seem redundant here, but what if you want to trigger object tooltip programmatically? You won't have mouse coordinates)
Mess around to place DIV centered above 2D point, with nice margin.
// This will move tooltip to the current mouse position and show it by timer.
function showTooltip() {
var divElement = $("#tooltip");
//element found and mouse hovers some object?
if (divElement && latestMouseIntersection) {
//hide until tooltip is ready (prevents some visual artifacts)
divElement.css({
display: "block",
opacity: 0.0
});
//!!! === IMPORTANT ===
// DIV element is positioned here
var canvasHalfWidth = renderer.domElement.offsetWidth / 2;
var canvasHalfHeight = renderer.domElement.offsetHeight / 2;
var tooltipPosition = latestMouseProjection.clone().project(camera);
tooltipPosition.x = (tooltipPosition.x * canvasHalfWidth) + canvasHalfWidth + renderer.domElement.offsetLeft;
tooltipPosition.y = -(tooltipPosition.y * canvasHalfHeight) + canvasHalfHeight + renderer.domElement.offsetTop;
var tootipWidth = divElement[0].offsetWidth;
var tootipHeight = divElement[0].offsetHeight;
divElement.css({
left: `${tooltipPosition.x - tootipWidth/2}px`,
top: `${tooltipPosition.y - tootipHeight - 5}px`
});
//get text from hovered object (we store it in .userData)
divElement.text(hoveredObj.userData.tooltipText);
divElement.css({
opacity: 1.0
});
}
}

How to accelerate calculations when update messive position from 3d to screen (hud)

I want to update hud positon form 3d position to 2d when mouse moving. Since it may have a large number of 3d objects to project to the screen position, I meet a performance problem.
Are there any way to accelerate calculations? The following is how I calculate 3d object position on 2d screen.
function toScreenPosition(obj) {
var vector = new THREE.Vector3();
//calculate screen half size
var widthHalf = 0.5 * renderer.context.canvas.width;
var heightHalf = 0.5 * renderer.context.canvas.height;
//get 3d object position
obj.updateMatrixWorld();
vector.setFromMatrixPosition(obj.matrixWorld);
vector.project(this.camera);
//get 2d position on screen
vector.x = (vector.x * widthHalf) + widthHalf;
vector.y = -(vector.y * heightHalf) + heightHalf;
return {
x: vector.x,
y: vector.y
};
}
Rather than repositioning your HUD in world space every time your camera moves, add your HUD object(s) to your camera object, and position them only once. Then, when your camera moves, your HUD moves along with it, because the camera's transformation is cascaded to it's children.
yourCamera.add(yourHUD);
yourHUD.position.z = 10;
Note that doing it this way (or even positioning it the way you were) may allow scene objects to clip through your HUD geometry, or even appear between your HUD and the camera, obscuring the HUD. If that's what you want, great! If not, you could move your HUD to a second render pass, allowing it to remain "on top."
First, here is an example of your function rewritten for (almost) optimal performance as written in the comments above, the renderloop is obviously just an example to illustrate where to do which calls:
var width = renderer.context.canvas.width;
var height = renderer.context.canvas.height;
// has to be called whenever the canvas-size changes
function onCanvasResize() {
width = renderer.context.canvas.width;
height = renderer.context.canvas.height;
});
var projMatrix = new THREE.Matrix4();
// renderloop-function, called per animation-frame
function render() {
// just needed once per frame (even better would be
// once per camera-movement)
projMatrix.multiplyMatrices(
camera.projectionMatrix,
projMatrix.getInverse(camera.matrixWorld)
);
hudObjects.forEach(function(obj) {
toScreenPosition(obj, projMatrix);
});
}
// wrapped in IIFE to store the local vector-variable (this pattern
// is used everywhere in three.js)
var toScreenPosition = (function() {
var vector = new THREE.Vector3();
return function __toScreenPosition(obj, projectionMatrix) {
// this could potentially be left away, but isn't too
// expensive as there are 'needsUpdate'-checks in place
obj.updateMatrixWorld();
vector.setFromMatrixPosition(obj.matrixWorld);
vector.applyMatrix4(projectionMatrix);
vector.x = (vector.x + 1) * width / 2;
vector.y = (1 - vector.y) * height / 2;
// might want to consider returning a Vector3-instance
// instead, depends on how the result is used
return {x: vector.x, y: vector.y};
}
}) ();
But, considering you want to render a HUD, it would be better to do that independently of the main-scene, making all of the above computations obsolete and also allowing you to choose a different coordinate-system for sizing and positioning of HUD-elements.
I have an example for this here: https://codepen.io/usefulthink/pen/ZKPvPB. There I used an orthographic camera and a seperate scene to render HUD-Elements on top of the 3d-scene. No extra computations required. Plus I can specify the size and position of HUD-elements conveniently in pixel-units (The same would work using a perspective camera, only requires a bit more trigonometry to get it right).

How to determine a shape touches another shape or not using kineticjs?

I have a number of shapes in my kinetc layer. If I drag and drop a shape somewhere else in the layer, how to determine the dropped shape touches another shape or not?
The thing you need to do is create a mathematical representation of the shapes you have. For most simple collision detection, you can use bounding-boxes.
Basically, if you have a circle, you can create a representation of it as being bounded in a box.
Then if you have a square, you can check if the bounding box of the square (which is the square itself) is intersecting with the bounding box of the circle.
I wrote an answer to this a while ago: HTML5 / kineticJS getIntersection function implementation
function checkCollide(pointX, pointY, objectx, objecty, objectw, objecth) { // pointX, pointY belong to one rectangle, while the object variables belong to another rectangle
var oTop = objecty;
var oLeft = objectx;
var oRight = objectx+objectw;
var oBottom = objecty+objecth;
if(pointX > oLeft && pointX < oRight){
if(pointY > oTop && pointY < oBottom ){
return 1;
}
}
else
return 0;
};
used like this:
var children = layer.getChildren();
for( var i=0; i<children.length; i++){ // for each single shape
for( var j=0; j<children.length; j++){ //check each other shape
if(i != j){ //skip if shape is the same
if(checkCollide(children[i].getX(), children[i].getY(), children[j].getX(), children[j].getY(), children[j].getWidth(), children[j].getHeight()))
alert('top left corner collided');
}
}
}
This works great if the shape you have is a rectangle of some sort, but not that great if you have two circles, as they have a radius. So this suffices for a quick check of collision, next you need another function which will check collisions more precisely.
You can also try using kineticjs with box2d, there are a bunch of tutorials out there on the topic.

Zooming into canvas via js causes content to be lost?

http://deepschool.kd.io/Pages/Experiments/draw.htm
This is a Image editor I am working on, But it has a bug. Let's say you have created an image a 15x Zoom, when you change the zoom, the image is lost. Why is this? and what is the Remedy?
HTML:
Zoom:
<input type="number" id="zoom" min="1" max="50" onchange="zoomy()">
JS:
var zoomy = function() {
var zoomamount = zoom.value
var canvassize = zoomamount * 16
c.width = canvassize
c.height = canvassize
};
Thanks In Advance
If you want to zoom into canvas, it means you have to redraw it with zoom.
So instead of drawing pixels on click right onto canvas, which is made of pure pixels... You need to first create some representation of your grid of pixels.
var gridOfPixels = [];
Let's say you are ok with static size for now. Make it 8x8 pixels. At start you want to initialize your array:
for (var i=0; i < 8*8; i++) gridOfPixels[i] = 0;
So the grid canvas is ready, now we need to draw it.
function renderGrid() {
for (var y=0; y < 8; y++)
for (var x=0; x < 8; x++)
renderPixel( x, y, gridOfPixels[x+y*8] );
}
You already know how to renderPixel - calculate the rectangle position (posX = x*pixWidth, posY*pixHeight), where pixWidth is canvasWidth/8, etc.. Now you draw all your pixels, using the third parameter for the color.
To finish, you have to connect onclick to put a pixel on grid, and then call renderGrid so the user sees the change.
$('#my-canvas').click(function(e) {
var x = ...;
var y = ...; // calculate the position of pixels from mouse position inside canvas
// dont forget to check that x,y are in the 0-7 range
// dont forget to convert x,y to whole number using parseInt()
gridOfPixels[x+y*8] = 1;
renderGrid(); // update the grid canvas
});
Now, every time you resize the canvas or change some variables, the original canvas content will be saved in your grid, and you can renderGrid() any time you need to. You could even do it in realtime, animating the color of the pixels, etc..
Have fun. :)

Resources