Draw A Stack of Shapes - html

I'm trying to build a web tool to generate an image composed of a stack of shapes. Each shape element is derived from one original shape; the derivation is basically created by expanding by x pixels, then rotating it by y degrees, then placing it at the "end" of the stack. In the picture below, the original shape is a white heptagon, with slightly curved sides. The pink derivation was created by the following steps:
Copy the white heptagon and enlarge it by 10 pixels.
Rotate it by +/- 8 degrees.
Place it behind the white heptagon.
My question is what web technology will be suitable (and the simplest) to do this task? HTML5 canvas, SVG, or WebGL? I am leaning towards the canvas but it only supports one primitive shape i.e. rectangle.
For the curvy sides, I don't think there is a library that does this automatically. I might need to resort to doing it manually e.g. using arcs() in canvas.

Here's code to draw a stack of concentric rotated poly-arc shapes
This code lets you define the number of sides in your polygon as well as the inset size of your arcs.
This code just stacks the shapes from bottom-to-top, but you can always use ctx.globalCompositeOperation="destinationover" to draw behind the current shapes.
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/KkteA/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
drawArcInsetPolygon(7,100,.8,200,150,2,"gray","pink",-90);
drawArcInsetPolygon(7,100,.8,200,150,2,"gray","white",45);
function drawArcInsetPolygon(sideCount,size,arcInset,centerX,centerY,strokeWidth,strokeColor,fillColor,rotationDegrees){
var radians=rotationDegrees*Math.PI/180;
ctx.save();
ctx.translate(centerX,centerY);
ctx.rotate(radians);
ctx.beginPath();
ctx.moveTo (size * Math.cos(0), size * Math.sin(0));
for (var i = 1; i <= sideCount;i += 1) {
var cpX= (size*arcInset) * Math.cos((i-.5) * 2 * Math.PI / sideCount);
var cpY=( size*arcInset) * Math.sin((i-.5) * 2 * Math.PI / sideCount);
var endX= size * Math.cos(i * 2 * Math.PI / sideCount);
var endY= size * Math.sin(i * 2 * Math.PI / sideCount);
ctx.quadraticCurveTo(cpX, cpY, endX,endY);
}
ctx.fillStyle=fillColor;
ctx.strokeStyle = strokeColor;
ctx.lineWidth = strokeWidth;
ctx.stroke();
ctx.fill();
ctx.restore();
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=400 height=300></canvas>
</body>
</html>

Canvas should be very capable of allowing you to easily clone, alter and redraw elements behind your drawings :).
For something simple, as of Mar 2013, I'd recommend using Kinetic JS
Blobs would get you arched sided polygons easily like this:
new Kinetic.Blob({
x: 200,
y: 200,
scale: 2,
offset: [50, 50],
points: [{
x: 36,
y: 0
}, {
x: 81,
y: 10
}, {
x: 100,
y: 50
}, {
x: 81,
y: 90
}, {
x: 36,
y: 100
}, {
x: 0,
y: 73
}, {
x: 0,
y: 27
}],
fill: '#AAFFDD',
strokeWidth:0.2,
tension: 1.1
});
(Link for historical purposes only, It no longer works)
Check It in a fiddle~ jsfiddle.net/jaibuu/VUzKP/

Related

How to create smooth wave in Html5, svg, canvas?

4 hour of search for wave in html, svg or canvas but didn't find anything what would look like in a picture.
How can I create wave like this? with smooth ending and filled with color ?
This shape can be achieved using bezierCurveTo() in canvas. I'm sure the shape is possible in SVG as well, but I'm not as familiar with it, so below is a canvas demonstration.
The snippet is borrowed and adapted from the MDN article. Basically, you want a bezier curve that keeps the control points on the same y-axis as the start and end points. Make the curve more or less dramatic by moving the control points along the x-axis. The farther the first control point and second control point are to the start and end points (respectively) the more dramatic the curve will be.
I would start by building the graph in canvas using straight lines and adapting them to be bezier curves after the fact.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var hw = 10, ho = hw / 2;
var x1 = 50, y1 = 20,
x2 = 230, y2 = 100,
cp1x = 120, cp1y = 20,
cp2x = 160, cp2y = 100;
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2);
ctx.stroke();
ctx.fillStyle = 'blue';
// start point
ctx.fillRect(x1 - ho, y1 - ho, hw, hw);
// end point
ctx.fillRect(x2 - ho, y2 - ho, hw, hw);
ctx.fillStyle = 'red';
// control point one
ctx.fillRect(cp1x - ho, cp1y - ho, hw, hw);
// control point two
ctx.fillRect(cp2x - ho, cp2y - ho, hw, hw);
<canvas id="canvas"></canvas>

Html 5 canvas arc issue - arc gets distorted

I am using the following code to draw a arc using html5 canvas.
This is my code
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="1000" height="550" style="padding-top:20px;border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(293.5, 183.4375, 186.70352183177323, 4.71238898038469, 6.8067840827778845);
ctx.arc(293.5, 190.4375, 93.40238454336179, 6.8067840827778845, 4.71238898038469, true);
ctx.closePath();
ctx.strokeStyle = "white";
ctx.lineWidth = 1;
ctx.fillStyle = "red";
ctx.fill();
ctx.stroke();
</script>
</body>
</html>
This is how the arc looks. IF observed closely, the top left end of the outer arc has a slight flat surface. Could anyone please tell me why this occurs? Why isnt the arc smooth?
The second arc has a rayon greater than the center y position. It's out of the canvas element.
You can see the change here : https://jsfiddle.net/o1rdkj8u/
ctx.arc(293.5, 183.4375, 183.43753/*186.70352183177323*/, 4.71238898038469, 6.8067840827778845);
ctx.arc(293.5, 183.4375/*190.4375*/, 93.40238454336179, 6.8067840827778845, 4.71238898038469, true);
Furthermore, your arcq don't have the same center. If it's not what you want, have a look at it.
Strokes are drawn half-inside and half-outside your arc.
The half-outside part of your arc is being cut off by the top of the canvas.
So if you want the stroke to fully fit inside the canvas then be sure you subtract half the context's linewidth.
radius = radius - context.lineWidth/2;
So for your arc:
ctx.arc(
293.5, 183.4375,
186.70352183177323 - ctx.lineWidth/2, // subtract half linewidth
4.71238898038469, 6.8067840827778845
);

html5 canvas - emulate 3d rotation of image

I have an image which has square shape. I want to rotate and squeeze it to get 3d effect like on the pictures below:
Source image:
Rotate to 0 degrees and squeeze:
Rotate to 45 degrees and squeeze:
Something like this.
I have played around Math and tried to change Width and Height of the image by multiplying to Sin and Cos of angle.
var w = image.width*Math.cos(angle* TO_RADIANS);
var h = image.height*Math.sin(angle* TO_RADIANS);
h=h*2/3; //squeezing the height
ctx.drawImage(image, 0, 0, w, h);
But I am not good at mathematics, so I hope somebody may help me to solve this issue.
I've resolved my problem. I squeezed my image in photoshop. So, the image became 300x150 size and looked like the second picture above. Then I applied a function below anytime, when I needed to redraw the image according to the angle:
var TO_RADIANS = Math.PI/180;
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle * TO_RADIANS);
var w = image.width-Math.abs((image.width/2)*Math.sin(angle* TO_RADIANS));
var h = image.height+Math.abs((image.height)*Math.sin(angle * TO_RADIANS));
ctx.translate(-w/2, -h/2);
ctx.drawImage(image, 0, 0, w, h);
ctx.restore();
Now it works pretty well.
You should look into a transform matrix.
Something like this:
ctx.transform(1, 0.6, -.8, .5, 20, 0);
http://jsfiddle.net/ericjbasti/nNZLC/
some links for you :
http://www.w3schools.com/tags/canvas_transform.asp
https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/MatrixTransforms/MatrixTransforms.html

Kinetic.JS - why does layer order change overwrite my colour?

I'm currently playing with Kinetic.JS. I have drawn a rather crude UFO-like shape in two parts, a hull (red) and a disc (grey).
Demo - JSBin
Question: how come when I later arrange the shape ordering so the hull is above the disc, the disc bizarrely goes from grey to the hull's red?
Uncomment the moveToTop() line at the bottom of my JSBin to see what I mean. Here's the pertinent (condensed) code.
//ship hull
var hull = new Kinetic.Shape({
drawFunc: function(ctx) {
ctx.arc(game_dims.w / 2, game_dims.h * 0.6, game_dims.h * 0.45, 0, Math.PI, true);
this.fill(ctx);
},
fill: 'red'
});
//ship disc
var disc = new Kinetic.Circle({
x: game_dims.w / 2,
y: game_dims.h * 0.6,
radius: {x: game_dims.w * 0.45, y: 30},
fill: '#888'
});
//draw
layer.add(hull);
layer.add(disc);
stage.add(layer);
//post-production
hull.moveToTop(); // <-- weirdness - changes disc colour!?
layer.draw();
I am aware I could draw the two shapes in reverse order to get the desired order, but that is not what I want with this question - I'm interested in rearrangement of order after drawing.
Thanks in advance
Your draw function of the hull needs to tell the context it's drawing a new path:
function(ctx) {
ctx.beginPath();
ctx.arc(...);
this.fill(ctx);
}
By adding the beginPath() command you are telling the context that you are not in fact adding to the previous path, but drawing a new one instead. This is also what makes this.fill() fill the previous shape with red, because in your example the context is still referring to the disc when it attempts to fill it

html5 canvas prevent linewidth scaling

If I draw a rectangle of say linewidth=2 and then scale it to double the size of the rectangle, I get a rectangle that has its border double the size of the initial linewidth.
Is there a way to keep the linewidth to the perceived size of 2 or the original size.
In short, I want to just scale the size of the rectangle but keep the linewidth visually of size 2.
I tried setting the linewidth before and after the scale(2,2) command but the border width also increases.
One option is to divide the linewidth by the scale factor and this will work if the x and y scale factors are the same.
I don't have the option to scale the rectangle width and height and I need to use the scale command as I have other objects within the rectangle that need the scaling.
You can define path with transformation and stroke it without one. That way line width won't be transformed.
Example:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.save(); //save context without transformation
ctx.scale(2, 0.5); //make transformation
ctx.beginPath(); //define path
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
ctx.restore(); //restore context without transformation
ctx.stroke(); //stroke path
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
The lineWidth should be scaled down beforehand.
ctx.lineWidth = 2 / Math.max(scaleX, scaleY);
ctx.scale(scaleX, scaleY);
ctx.fillRect(50, 50, 50, 50);
Ok, you have a couple of options:
You can do your own scaling of coordinates and dimensions, e.g.
ctx.strokeRect( scaleX * x, scaleY * y, scaleX * width, scaleY * height) ;
And you'll need to apply the scaling to all the other objects too.
Alternatively you could apply the scaling but not rely on lineWidth for drawing the border of the rectangle. A simple method would be to draw the rectangle by filling the correct region and then removing the interior, minus the border, like so:
var scaleX = 1.5, scaleY = 2;
var lineWidth = 2;
ctx.scale(scaleX, scaleY);
ctx.fillStyle = "#000";
ctx.fillRect(100, 50, 100,
ctx.clearRect(100+lineWidth/scaleX, 50+lineWidth/scaleY, 100-(2*lineWidth)/scaleX, 60-(2*lineWidth)/scaleY);