3D from 2D canvas content - javascript

I have a 2D occupancy map like this (left side)
So far it is content drawn pixel by pixel in a canvas of the robot's web interface.
What I would like to achive is a pan/tilt/rotatable pseudo 3D view of it. Where each occupied block gets a rect. So something like this.
I don't even know where to start. Is this doable in pure html5? … Any guidance would help.
Some facts on my setup:
webserver of the robot is a flask (python) server on a raspberry pi
client browsers are Chrome (on windows) or Safari on iPad
calculation should take place on client side
Thanks
Robert

Is this doable in pure html5?
Sure since 3d is just a 2d projection. Check WebGL and http://threejs.org/ if you need a javascript library.

You can "fake" an extrusion in 2d canvas by drawing repeated-offset version of your floorplan.
You can lighten the corners a bit for effect.
Demo: http://jsfiddle.net/m1erickson/7wxGS/
Example code:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.strokeStyle="gray";
draw();
ctx.strokeStyle="black"
for(var i=.5;i<10;i+=.5){
ctx.save();
ctx.translate(-i,-i);
draw();
ctx.restore();
}
function draw(){
ctx.beginPath();
ctx.moveTo(100,100);
ctx.lineTo(125,100);
ctx.lineTo(125,125);
ctx.lineTo(145,125);
ctx.lineTo(145,145);
ctx.lineTo(125,145);
ctx.lineTo(125,175);
ctx.lineTo(100,175);
ctx.closePath();
ctx.stroke();
// exterior corners
corner(100,100);
corner(145,145);
corner(125,175);
}
function corner(x,y){
ctx.save();
ctx.beginPath();
ctx.fillStyle="#ddd";
ctx.fillRect(x,y,1,1);
ctx.restore();
}

Related

Make canvas fill include stroke in shapes

Ok, so i am making a 3D rendering engine in pure javascript, as a challenge of course - to test my linear algebra skills. I am not using webgl, so please do not say "use webgl".
Anyways, the software will take in triangles, a camera and local transformations, and render the data onto the screen (i even made it interactive)
There are only 6 lines of rendering code, however, which are:
// some shading and math calculations then this:
context.fillStyle = color;
context.strokeStyle = color;
context.beginPath();
context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x0, y0);
context.closePath();
context.fill();
context.stroke();
And while that works, it drops to 10fps with 4k+ faces on my Chromebook. (60fps on a regular computer)
Anyways, that outputs this:
But to make it faster, and because canvas state changes are slow, i removed the stroke, making the rendering code:
// some shading and math calculations then this:
context.fillStyle = color;
//context.strokeStyle = color;
context.beginPath();
context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x0, y0);
context.closePath();
context.fill();
//context.stroke();
which runs twice as fast, but the resulting thing that gets rendered to the screen is this: (different model)
which has ugly lines everywhere at the edges of the triangles (which get removed when I re-add the stroke)
However, the fps doubles and performance gains are great...
So i believe the lines are caused because the canvas fill doesnt include the area where it would have stroked (the outline, as you may say).
I have tried to fix it with math, and although it works there are some edge cases where it doesn't
So my question is as follows:
Is there a way to make the context fill include the stroke area without stroking, because it is very expensive?
Using both stroke and fill will force the rasterization twice which explains the approximate double time.
The reason why you get glitches between the triangles is because of rounding errors and anti-aliasing. There is not a straight-forward solution to this; the stroke will cover the glitches of course, but to do it without the stroke will require you to offset and expand at least every other triangle.
However, you could use a small trick to cover up the gap and that is to redraw the entire image (as bitmap) on top offset just a single pixel (you might get away with 0.5 pixel but then anti-aliasing is needed). This adds to the time, but far less than rasterization or recalculation of the paths.
Say that the result on the left is what you have (simulated here) with a clear gap. Redrawing it on top as shown in the right will cover the gap without too much distortion.
Simply use:
ctx.drawImage(sourceCanvas, 1, 1);
Tip: when only calling fill() you don't need closePath() as it is called implicit, saving one op. Microscopic gain perhaps but still (with more complex geometry it even might have an influence :) ).
Note: drawing to itself will cause an internal allocation of a temporary bitmap copy. However, you will only need to do one extra drawImage() operation. The option is to use off-canvas render but draw twice to a main displayed canvas. Either way...
var ctx = c.getContext("2d");
ctx.fillStyle = "#777";
tri(10,10, 72,17, 40.2, 100);
// simulates gap
ctx.fillStyle = "#222";
tri(72.5,17.5, 40.7,100.5, 90,25);
// fill entire image back again, drawn twice here for demo
ctx.drawImage(c, 100, 0);
ctx.drawImage(c, 0, 0, 100, 150, 101, 1, 100, 150);
ctx.fillText("Raster", 5, 8);
ctx.fillText("Offset self", 105, 8);
function tri(x0,y0,x1,y1,x2,y2) {
ctx.beginPath();
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.fill();
}
<canvas id=c></canvas>

Create 3D shape from user input?

Im trying to build a simple 3d shape builder (it would always be a rectangle / square that needs to be built) all I want is for the user to be able enter the height, width and depth and the shape is built in real time for them as a line drawing. I have been looking online and found this jsfiddle which is great however I would need it to work in 3d (not just 2d) http://jsfiddle.net/m1erickson/f6E6Y/ but along those lines. Im however completly stuck and left scratching my head on this one...
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var width=2;
var height=35;
var $width=document.getElementById('width');
var $height=document.getElementById('height')
$width.value=width;
$height.value=height;
draw();
$width.addEventListener("keyup", function(){
width=this.value;
draw();
}, false);
$height.addEventListener("keyup", function(){
height=this.value;
draw();
}, false);
function draw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillRect(40,40,width,height);
}
Any ideas?
Dealing with 3D is much easier with a library. Three.js is a popular one. They also have a demo for what you've described: http://threejs.org/docs/scenes/geometry-browser.html#BoxGeometry
The controls on the top-right will allow you to adjust the dimensions of the object in real-time. Modifying this example will be much more easier than writing it from scratch, unless you'd like to learn vanilla WebGL.

Rotating straight line around point in canvas using javascript

When I run this code it produces the lines "rotating" from 0 degrees to 180, the problem is the previous line does not get cleared (so the ctx.clearRect(0,0,400,200) does not work. In the console log it shows up as running (when I try to debug) but it is not actually clearing it. Does anyone have any idea how to fix this?
var canvas = document.getElementById("radarImage");
var ctx = canvas.getContext("2d");
var angle = 0
function incrementAngle(){
angle++;
if(angle>180){
angle=0;
}
}
function rotateRadar(){
incrementAngle();
ctx.clearRect(0,0,400,200);
ctx.save();
ctx..translate(-200,-200);
ctx.rotate((Math.PI/180)*angle);
ctx.translate(-200,-200);
ctx.moveTo(200,200);
ctx.lineTo(0,200);
ctx.stroke();
ctx.restore;
}
setInterval(rotateRadar,200);
Edit: You should call your updates constantly using request animation frame: http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
If you are open to using a library for working with canvas then easel js is could be a possibility: http://www.createjs.com/easeljs
Another SO user asked a similar question for Easel Js and there are some good answers that may be relavant to you: EaselJS - Rotate a shape around its center
Simply you cant rotate a line cont in canvas, you have to read sin,cos to rotate a line in canvas.
Hope this should help you.JsFiddle
context.clearRect(0,0,400,200);
context.beginPath();
context.moveTo(x,y);
x1=Math.cos(startAngle)*radius+10;
y1=Math.sin(startAngle)*radius+10;
console.log(x1,y1);
context.lineTo(x1,y1);
context.stroke();

How can I "colour" specific part of the screen using coordinates?

I'm just started working with Leap Motion (it is so much fun). The Leap works mainly with vectors. And now I want to create a program where I can visualise where is a vector pointing. The only way I can imagine doing this is by using a small image which appears when this fuction is on and positioning by using the img.style.left , img.style.top instructions. Any other ideas?
If your goal is to represent 2D Vectors,
You can use canvas to draw lines.
A canvas is like a div but you can draw whatever you want in it, I don't know anything about Leap Motion but if you want to draw lines and circles at precise coordinates, it may be a good solution instead of working with the DOM itself.
The JS part looks like this :
var canvas = document.getElementById('my-canvas');
var ctx = canvas.getContext('2d');
//For exemple here is how to draw a rectangle
//fillStyle support all valid css color
ctx.fillStyle = "rgba(50, 255, 24, 0.7)";
//Create the rectangle, with (startX, startY, height, width)
ctx.fillRect(20, 15, 50, 50);
ctx.beginPath(); //Tells canvas we want to draw
ctx.moveTo(250,250); //Moves the cursor to the coordinates (250, 250);
ctx.lineTo(75, 84); //Draws a line from the cursor (250, 250) to (75, 84);
ctx.closePath(); //Tells canvas to 'close' the drawing
ctx.strokeStyle = "red";
ctx.stroke(); //Draws the line stroke
And the HTML is simply :
<canvas id="my-canvas" height="500px" width="500px">
Here is the text displayed when the browser doesnt support canvas.
</canvas>
I made a jsfiddle to show you what simple things we can do with canvas.
http://jsfiddle.net/pq8g0bf0/1/
A nice website to learn canvas : http://www.html5canvastutorials.com/tutorials/html5-canvas-element/
Since it's javascript, you are free to do calculations for your vectors coordinates, addding eventListeners etc ...

How to create nxm HTML5 canvas Grid of objects (colored squares)?

I'm trying to create a grid (matrix) of squares in a given area <canvas> with n columns and m rows, and an option to set a spacing between the squares if desired. The grid should fill squares with random coloring.
I'd also like to add a zoom function, so the colored squares will appear much bigger in a region that is double-clicked.
Can anyone suggest a good method to generate the grid and assign random colors to the squares? If you can suggest how to create the zoom effect too that would be SUPER :)
I'm new to canvas, so any method help would be great!
Thanks
It looks like from the comments you #Spencer Wieczorek have the padded-cell drawing worked out.
The key to the other part of your question is using transforms:
determine the point from which you want to scale (doubleclicking): var scalePtX,scalePtY.
save the untransformed context state: ctx.save();
translate by (1-zoom)*scalePoint: ctx.translate((1-zoom)*scalePtX,(1-zoom)*scalePtY)
scale by the zoom: ctx.scale(zoom,zoom)
draw cells using coordinates as if they were untransformed: fillRect
restore the context to its untransformed state: ctx.restore()
Here's the important code and a Demo: http://jsfiddle.net/m1erickson/e8bfg3h4/
function draw(){
ctx.clearRect(0,0,cw,ch);
ctx.save();
ctx.translate((1-zoom)*scalePtX,(1-zoom)*scalePtY);
ctx.scale(zoom,zoom);
ctx.globalAlpha=0.25;
for(var y=0;y<rows;y++){
for(var x=0;x<cols;x++){
ctx.fillStyle=colors[y*cols+x];
ctx.fillRect(x*(w+padding),y*(h+padding),w,h);
}}
ctx.globalAlpha=1.00;
ctx.beginPath();
ctx.arc(scalePtX,scalePtY,10,0,Math.PI*2);
ctx.closePath();
ctx.stroke();
ctx.restore();
}

Categories

Resources