draw line from one canvas to another canvas - html5-canvas

I have multiple canvases and want to draw line between two points on canvas,in which one point is on one canvas and other point is on second(different) canvas.
How to draw line with above condition?
ctx.beginPath();
ctx.moveTo(10,30);//point on canvas1
ctx.lineTo(100,70);//point on canvas2
ctx.stroke();

To do this use the ctx.setTransform function of the second canvas to that of the first
function matchCoordinateSpace(ctx1,ctx2){
// get the window positions of both canvases
var can1Loc = ctx1.canvas.getBoundingClientRect();
var can2Loc = ctx2.canvas.getBoundingClientRect();
// get the relative offset
var can2Rel = {
x:can1Loc.left-can2loc.left,
y:can1Loc.top-can2loc.top,
};
// set the transforms
ctx2.setTransform(1,0,0,1,0,0); // just to make sure the first canvas coordinate space
// is correctly set.
ctx2.setTransform(1,0,0,1,can2Rel.x,can2Rel.y); // translates the canvas to the
// same coordinates as the firstChild
// The second canvas now is in the same relative coordinate space as the first;
// All drawing methods on the second canvas will be in the first canvas coordinate space.
// You only need to do this once, or whenever the two canvases change relative position.
return can2Rel; // incase you need the offset
}
//ctx1 is the first canvas
//ctx2 is the second
var can2Offset = matchCoordinateSpace(ctx1,ctx2);
// You will have to draw the line on each canvas in turn
ctx1.beginPath();
ctx1.moveTo(10,30);
ctx1.lineTo(100,70);
ctx1.stroke();
ctx2.beginPath(); // this line will match up with the line drawn on the first.
ctx2.moveTo(10,30);
ctx2.lineTo(100,70);
ctx2.stroke();

Related

Make children of imported Three.js group follow pointer when their canvas gets hovered

In Three.js, I have a group of meshes that is loaded from outside with help of FBX loader. The group has six meshes inside. My task is to make this meshes follow pointer when they get hovered. More precisely, I'd like to have a sort of magnetic effect (just like navbar items in this pen, but with meshes in Three.js).
I think, firstly, I have to detect, where currently pointer is, i.e. get position of cursor in world coordinates system, and then translate meshes towards it. But when I try to get the position of cursor, it seems to be wrong.
Having said that, I have two questions:
How to get proper cursor's position relative to the world coordinates?
How to change position of each of the group's meshes so that they get translated against the cursor?
Here is what have I done so far:
Hi everyone.
In Three.js, I have a group of meshes that is loaded from outside with help of FBX loader. The group has six meshes inside. My task is to make this meshes follow pointer when their canvas get hovered. More precisely, I'd like to have a sort of magnetic effect (just like navbar items in this pen, but with meshes of Three.js).
I think, firstly, I have to detect, where currently pointer on canvas is, i.e. get position of cursor in world coordinates system, and then translate meshes towards it. But when I try to get the position of cursor, it seems to be wrong.
Having said that, I have two questions:
How to get proper cursor's position relative to the world coordinates?
How to change position of each of the group's meshes so that they get translated against the cursor?
Here is what have I done so far. Function that translates meshes isn't written yet. Mousemove callback returns pretty big digits, though:
// Load object and play a third-party animation
loader.load("Object_001.fbx", (object) => {
mixer = new THREE.AnimationMixer(object);
const action = mixer.clipAction(object.animations[0]);
action.play();
object.traverse((child) => {
if (child.isMesh) {
child.material.map = texture;
child.material.needsUpdate = true;
}
});
scene.add(object);
});
// log coordinates of the pointer
const mouse = new THREE.Vector3();
const position = new THREE.Vector3();
function onMouseMove(event) {
mouse.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5
);
mouse.unproject(camera);
mouse.sub(camera.position).normalize();
const distance = -camera.position.z / mouse.z;
position.copy(camera.position).add(mouse.multiplyScalar(distance));
console.log(position);
}
wrapperElement.addEventListener("mousemove", onMouseMove);
Thanks in advance.
Made a codepen here:
https://codepen.io/cdeep/pen/YzxPPZQ
The cursor only exists in the canvas dom element which is a rendering of the camera view frustum.
The easiest way to make the object follow a mouse is to get the point of intersection of the mouse with another object in the 3d scene and set the object position to the intersection point. The above codepen showcases that.
raycaster.setFromCamera( mouse, camera );
const intersects = raycaster.intersectObjects([ground]);
if(intersects.length) {
const { point } = intersects[0];
cube.position.copy(point.setY(0.5));
}
You could also position it at a fixed distance from the mouse but it looks odd in my opinion:
const distance = 10;
raycaster.setFromCamera( mouse, camera );
const { origin, direction } = raycaster.ray;
cube.position.copy(origin.clone().add(direction.multiplyScalar(distance)));
Documentation for raycaster:
https://threejs.org/docs/index.html?q=ray#api/en/core/Raycaster
Raycasting is used for mouse picking (working out what objects in the
3d space the mouse is over) amongst other things.

Three.js: Object3D added in scene, attached to another object3D doesn't update position on translation

The logic of my code is such as the object3D which are included in the scene, on double click get added with a Line (with BufferGeometry).
I am getting the object3D by using Raycaster intersect.
The way I am adding it is:
scene.add( newLine );
newLine.updateMatrixWorld();
THREE.SceneUtils.attach( newLine, scene, intersects[0].object );
The following is my mousemove code which helps me moving the object3D in XZ plane.
function onDocumentMouseMove( event ) {
var mouseX = ( event.clientX / window.innerWidth ) * 2 - 1;
var mouseY = - ( event.clientY / window.innerHeight ) * 2 + 1;
var mouse = new THREE.Vector2( mouseX, mouseY );
raycaster.setFromCamera( mouse, camera);
if (selection) {
var intersects = raycaster.intersectObject( plane );
selection.position.copy( intersects[0].point.sub( offset ));
}
}
Nothing complicated. Simple code. And the movement is happening well. I can easily move the object3D around.
When I am checking the console for change in position of object3D on grabbing and moving it, it is changing which is what should happen. But I do not see any change at in the position of the Line, i.e., newLine as in my code. The issue is unless I am calling .updateWorldMatrix() as well which as per THREE docs, should automatically be called in each render cycle. Still I am calling that. Why am not able to get the position of my newLine when clearly its position is moving along with the object3D when I am dragging object3D around?
Why is it needed? Unless the position of the line can show as changing, I can't update an HTML element, which I am attaching to the end of that line. Hence, the position change is imperative. Gif attached which shows, when the cube/ sphere/ cone is moved, render(..) gives me changing position log of that. However, when logging the same for the Line it doesn't change. If any can help me with the issue, it will be amazing. Thanks much.
EDIT
When I am attaching the HTMLElement directly to parent object3D it shows expected result. It moves when I move object3D. This is because as said its position is being updated continuously in the render cycle when I move it.
Gif:
The line's .position attribute is not changing because its local position remains the same. Since the line is attached to the parent, its relative position to the parent doesn't change, only the global position does. To get the global position of line, you can use the .getWorldPosition() method:
// Declare var to store world position
var worldPos = new THREE.Vector3();
// Get world position of line
line.getWorldPosition(worldPos);
// Now global position is stored in Vec3
console.log(worldPos);

Canvas javascript clear path

I am testing out a little Canvas programming and I have a question about how to clear previous paths when drawing a new one.
Example:
Paint a filled circle with no stroke and a stroked line.
I have the following code:
graphicContext.save();
graphicContext.beginPath();
graphicContext.arc(95,50,40,0,2*Math.PI);
graphicContext.fillStyle="rgb(50, 200, 200)";
graphicContext.fill();
graphicContext.restore();
graphicContext.save();
graphicContext.moveTo(0,0);
graphicContext.lineTo(200,100);
graphicContext.stroke();
graphicContext.restore();
However, both the circle and the line are stroked. How can I stroke the line without also stroking the circle?
First of all, there is no need to use save and restore method.
Second, use beginPath method before drawing the line, as you are creating a new path. beginPath will reset / clear the previous path ...
var graphicContext = document.querySelector('#canvas').getContext('2d')
// circle
graphicContext.beginPath();
graphicContext.arc(95,50,40,0,2*Math.PI);
graphicContext.fillStyle="rgb(50, 200, 200)";
graphicContext.fill();
// line
graphicContext.beginPath();
graphicContext.moveTo(0,0);
graphicContext.lineTo(200,100);
graphicContext.stroke();
<canvas id="canvas"></canvas>

How to hide a html overlay label on a sphere object

I have a sphere model along with html text labels overlayed onto it at fixed positions. How can I hide them when they are behind the object when i am rotating my sphere using orbit controls?
Anyone knows about any references on stackoverflow or any examples which can solve my issue?
See this example how you could do it: http://jsfiddle.net/L0rdzbej/194/
I have put the relevant code in the updateLabelVisibility()-function, it looks like this:
var meshPosition = mesh.getWorldPosition();
var eye = camera.position.clone().sub( meshPosition );
var dot = eye.clone().normalize().dot( meshPosition.normalize() );
//IS TRUE WHEN THE MESH IS OCCLUDED BY THE SPHERE = dot value below 0.0
var ocluded = dot < -0.2;
if ( ocluded ) {
// HIDE LABEL IF CAMERA CANT SEE IT (I.E. WHEN IT IS BEHIND THE GLOBE)
label.style.visibility = "hidden";
}
else {
// DISPLAY LABEL IF MESH IS VISIBLE TO THE CAMERA
label.style.visibility = "visible";
}
Where mesh is the marker where your label is attached to. The dot is basically telling you how far around the sphere it is, within a certain distance of 90 degree. Simply said by values below zero the label is longer visible by going further around the back.
To be fair I based this off some code from: https://zeitgeist-globe.appspot.com/

Need to draw arrows and circles on canvas without clearing the whole canvas

I'm trying to draw arrows and circles on a canvas, currently the whole canvas is cleared on mousemove and mousedown or whenever the draw function is called, I am not able to draw multiple arrows and circles. Is there any other to accomplish this task?
heres a fiddle: http://jsfiddle.net/V7MRL/
Stack two canvases on top of each over, and draw the temporary arrows/circle on the one on top, and do the final draw on the canvas below.
This way you can clear the top canvas with no issue, and your draws 'accumulate' in the lower canvas.
http://jsfiddle.net/V7MRL/5/ (updated)
I changed your draw function so it takes a 'final' flag that states wether the draw is final.
For a final draw, lower canvas is used, for an temporary draw, upper canvas is cleared then used.
function draw(final) {
var ctx = final ? context : tempContext ;
if (final == false) ctx.clearRect(0, 0, canvas.width, canvas.height);
Edit : for the issue #markE mentionned, i just handled mouseout event to cancel the draw if one is ongoing :
function mouseOut(eve) {
if ( mouseIsDown ) {
mouseIsDown = 0;
cancelTempDraw();
}
}
with :
function cancelTempDraw() {
tempContext.clearRect(0, 0, canvas.width, canvas.height);
}
Rq that your undo logic is not working as of now. I changed it a bit so that in draw, if the draw is final, i save the final canvas prior to drawing the latest figure to quickly have a 1-step undo. i just created a third temp canvas to/from which you copy.
Rq also that you cannot store one canvas for each stroke, or the memory will soon explode. Store the figures + their coordinates in an array if you want a full undo... or just allow 1-step undo for now.
i updated the jsfiddle link.

Resources