Shape transformation - circular section - javascript

Is there any simple transformation (transformation matrix...) that will transform section A into section B? The only requirement is that both sections should have the same size X.
I plan to fill section A with an image and then to transform it into section B including the image. Right now I use javascript KineticJS but I am willing to change the framework if necessary.
Is that doable?

Your solution is not trivial.
Consider this illustration:
Lines radiate 360 degrees outward from a centerpoint and pass through both arcs A and B.
This illustration shows many more lines around the centerpoint.
Think of this as moving every pixel from A to a corresponding pixel in B. This uses linear interpolation (lerping) to move each pixel from A to B. The total number of colored pixels in A & B are the same--no pixels have been "magically" added to B.
Here’s code for this illustration:
var cx=150;
var cy=150;
ctx.lineWidth=1;
for(var a=0;a<Math.PI*2;a+=Math.PI/240){
for(var r=25;r<50;r++){
var x1=cx+r*Math.cos(a);
var y1=cy+r*Math.sin(a);
var x2=cx+(r+25)*Math.cos(a);
var y2=cy+(r+25)*Math.sin(a);
ctx.fillStyle="blue";
ctx.fillRect(x1,y1,1,1);
ctx.fillStyle="blue";
ctx.fillRect(x2,y2,1,1);
}
}
ctx.lineWidth=2;
ctx.beginPath();
ctx.arc(cx,cy,25,0,Math.PI*2);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(cx,cy,50,0,Math.PI*2);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(cx,cy,75,0,Math.PI*2);
ctx.closePath();
ctx.stroke();
The problem
Notice that most of arc A is filled with blue pixels while some parts of B are “moired” (not completely filled). That’s because A’s quantity of pixels will not completely fill B’s larger space.
Your image would appear the same. It would appear "correctly" in arc A but would have missing pixels in arc B.
What you need to do is “fill in the gaps in B” with appropriately determined pixels.
The solution
You can do this with an algorithm like bilinear interpolation which fills in the gaps in B by selecting a “best” pixel color. It does this by comparing 4 adjacent pixels in arc A. This algorithm enlarges a smaller image into a larger image by filling in "missing" pixels inside the larger space.
You will have to slightly adjust this algorithm to do non-axis-aligned interpolation. Here’s an link to a nice example of bilinear interpolation:
http://strauss.pas.nu/js-bilinear-interpolation.html

Related

Verify two shapes are equal in canvas

i'm about to create small challenge for my team which is similar with challenge honeycomb candy in series Squid Game of Netflix.
The idea is user will use mouse click to make a dot around a shape, then i will use api of canvas context to draw another shape from those dots.
Example:
var canvas = document.body.appendChild(document.createElement("CANVAS")), context = canvas.getContext("2d");
context.beginPath();
// all points are given as x (from left to right), y (from top to bottom)
context.moveTo(10, 20); // x = 10 (10 px from the left edge of the canvas), y = 20 (20 px from the top edge of the canvas)
context.lineTo(100, 97); // x = 100, y = 97
context.lineTo(50, 105); // x = 50, y = 100
context.stroke();
finally i will compare this shape with the original shape, if they're same then user win.
anybody know how to compare two shape are same in js? or i need to compare each of dot of two shapes?
There is no straightforward way to compare shapes that are drawn on the canvas.
One possibility would be to store information about shape in const or array(with all shapes) and read the data of the shape that has been created by the user.
Suggested steps :
get your standard shape coordinates
get the coordinates of the points done by the user
compute the figure bounding box (min/max on x/y basically).
draw the shape and fill it with color (on hidden canvas)
set globalCompositeOperation of context to xor, and draw what would be the standard figure on this canvas.
<canvas id="visibleCanvas" width="630" height="500"></canvas>
<canvas id="hiddenCanvas" width="630" height="500" style="display:none;"></canvas>
//later in code
hiddenContext.globalCompositeOperation = 'xor';
that should create similar output:
ctx.getImageData(sx, sy, sw, sh);
of the canvas and count pixels.
compute the number out of non-null pixels. Generally speaking the more empty pixels the better the score is
Here is perfect art about canvas globalComposite operations https://techglimpse.com/canvas-types-source-destination-operations/

Canvas 2D maze torch effect

I am working on a school project that includes these conditions:
Make maze with using only JS, HTML5 and CSS.
Make a torch effect around the character. You cannot light through walls.
I started making this game with the use of canvas.
I have succeeded to make a torch effect around the character as shown here:
http://people.inf.elte.hu/tunyooo/web2/HTML5-Maze.html
However, I cannot make it NOT to light through walls.
I am fairly sure I should do something like this:
Start a loop in all directions from the current position of the character up until it reaches the view distance OR if the context.getImageData() returns [0,0,0,255]. This way, I could get the character's distance from northern, eastern, western and southern walls.
Then, I could light the maze around the character with a (viewdistance-DistanceFrom*Wall) rectangle.
Unfortunately though, after 15 hours of thinking about this I am running out of ideas how to make this work.
Any tips are appreciated.
A simpler way of doing this is (ps: I get a "forbidden" error on the link provided so i cannot see what you did):
Have a matte version of the maze, a transparent and white image where white represent allowed drawing areas. This matte image should match the maze image in size and placement.
Create an off-screen canvas the size of the torch image
When you need to draw the torch first draw the matte image into the off-screen canvas. Draw it offset so the correct part of the matte is drawn. For example: if the torch will be drawn at position 100, 100 on the maze then draw the matte into the off-screen canvas at -100,-100 - or simply create the canvas the same size as the maze and draw in the matte at 0,0 and the torch at the relative position. More memory is used but simpler to maintain.
Change composite mode to source-in and then draw the torch. Change composite mode back to copy for the next draw.
Now your torch is clipped to fit within the wall. Now simply draw the off-screen canvas to your main canvas instead of the torch.
Note: it's important that the torch is made such as it cannot reach the other side of the wall (diameter size) or it will instead shine "under" the maze walls - this can be solved other ways though by using matte for different zones which is chosen depending on player position (not shown here).
To move in the demo below just move the mouse over the canvas area.
Live demo
function mousemoved(e) {
var rect = canvas.getBoundingClientRect(), // adjust mouse pos.:
x = e.clientX - rect.left - iTorch.width * 0.5, // center of torch
y = e.clientY - rect.top - iTorch.height * 0.5;
octx.drawImage(iMatte, 0, 0); // draw matte to off-screen
octx.globalCompositeOperation = 'source-in'; // change comp mode
octx.drawImage(iTorch, x, y); // clip torch
octx.globalCompositeOperation = 'copy'; // change comp mode for next
ctx.drawImage(iMaze, 0, 0); // redraw maze
ctx.drawImage(ocanvas, 0, 0); // draw clipped torch on top
}
In the demo the torch is of more or less random size, a bit too big in fact - something I made quick and dirty. But try to move within the maze path to see it being clipped. The off-screen canvas is added on the size of the main canvas to show what goes on.
An added bonus is that you could use the same matte for hit-testing.
Make your maze hallways into clipping paths.
Your torch effects will be contained within the clipping paths.
[ Addition to answer based on questioner's comments ]
To create a clipping path from your existing maze image:
Open up your maze image in a Paint program. The mouse cursors X/Y position are usually displayed as you move over the maze image.
Record the top-left and bottom-right of each maze hallway in an array.
var hallways=[];
hallways.push({left:100, y:50, right: 150, bottom: 65}); // for each hallway
Listen for mouse events and determine which hallway the mouse is in.
// hallwayIndex is the index of the hallway the mouse is inside
var hallwayIndex=-1;
// x=mouse's x coordinate, y=mouse's y coordinate
for(var i=0;i<hallways;i++){
var hall=hallways[i];
if(x>=hall.left &&
x<=hall.right &&
y>=hall.top &&
y<=hall.bottom)
{ hallwayIndex=i; }
}
Redraw the maze on the canvas
Create a clipping path for the current hallway:
var width=hall.right-hall.left;
var height=hall.bottom-hall.top;
ctx.beginPath();
ctx.Rect(hall.left,hall.top,width,height);
ctx.clip();
Draw the player+torch into the hallway (the torch will not glow thru the walls).
There is a brilliant article on this topic: http://www.redblobgames.com/articles/visibility/
Doing it accurately like that, however, is a lot of work. If you want to go with a quick and dirty solution, I would suggest the following. Build the world from large blocks (think retro pixels). This makes collision detection simpler too. Now you can consider all points within the torch radius. Walk in a straight line from the character to the point. If you reach the point without hitting a wall, make it bright.
(You could do the same with 1-pixel blocks too, but you might hit performance issues.)

JavaScript canvas, getting flat lines

I'm working on project which have to draw graphs. Everything is quite good, but noticed one problem, the lines are showing strange.. it seems like someone draw my graph with brush holding horizontally.. when line goes down everything is OK, but when line is going horizontal the it becomes much smaller ... I can't find what's the problem could be..
Please help, because I started to get wrong graphs when it needs to draw horizontal line...
Here is the link to my project:
http://www.unolita.lt/images/signalai/Documents/Koreliacine%20funkcija.html
You can clearly see my problem on 1st picture..
Here is it's code:
function drawSignal()
{
var canvas = document.getElementById("canvSignal");
if (canvas.getContext)
{
var ctx = canvas.getContext("2d");
ctx.lineWidth = 3;
function Signalas()
{
<...>
ctx.beginPath();
ctx.moveTo(x, y);
ctx.strokeStyle = "black";
<...>
y=250- Sn[n] ;
ctx.lineTo(x, y);
ctx.stroke(x, y);
<...>
To put all code here was too much problematic..
This is due to the fact lines are drawn over all pixels they're over (on canvas positionning is in float). When you want to draw precise vertical or horizontal lines in javascript on a canvas, you'd better have them in half ints.
Possible Solution : If you have to draw a line with an odd numbered width, then you will have to offset the center of your line by 0.5 up or down. That way rendering will happen at boundary of pixel and not in middle, and you will always have a sharp line with no residue at the end edges.
So add 0.5 for odd numbered line width so that your points should be half numbered
ctx.lineTo(x+0.5, y+0.5);
ctx.stroke(x+0.5, y+0.5);
I have modified your code like this in line number 134 and 135 and got a output like this . Hope, this helps
Refer Here :
incorrect display lineWidth=1 at html5 canvas
HTML5 Canvas and Line Width
Line Width in Canvas

HTML5 canvas - controlling what happens when drawing on same place

How can i control what happens when i draw a line in the same coordinates as previous line?
Currently the color becomes brighter, but i want it to stay the same (or more accurately - i want the second line to on top of the first line).
I tried to do:
_context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
But it seems to work only when drawing canvas on canvas, not when drawing on the same canvas.
Supposing your color has a full alpha, your problem is probably related to the fact you draw lines of width 1 at integer coordinates.
Look at the following fiddle : http://jsfiddle.net/RAgak/
Drawing at integer coordinates makes the line wider and fuzzy. And drawing a second time makes it brighter. But this doesn't happen when I draw the line at half-integer coordinates.
var y = 10;
c.beginPath();
c.moveTo(0, y);
c.lineTo(30, y);
c.stroke(); // fuzzy
c.beginPath();
c.moveTo(50, y+0.5);
c.lineTo(80, y+0.5);
c.stroke(); // ok
This is due to the fact lines are drawn over all pixels they're over (on canvas positionning is in float). When you want to draw precise vertical or horizontal lines in javascript on a canvas, you'd better have them in half ints.
See illustration : The first horizontal line was drawn with a y position of 1. This line is fuzzy and wide. The second horizontal line was drawn with a y position of 4.5. It is thin and precise.
The solution, at least when drawing horizontal or vertical lines (or rects), is to take the width of the line into account and draw at integer or half-integer coordinates.

clear pixels under a shape in HTML canvas

I am using an HTML canvas and javascript and I need to clear all of the pixels underneath a shape created by closing a path (for example, I am using flot, and I want to make rounded corners, and to do this, I first need to remove the square corners by drawing a curve on top of the corner to remove the desired pixels).
Right now, I am doing this by just filling the shape with the same color as the background, which can imitate what I want to do, but, it is not ideal as it makes it impossible to place the chart on top of non-solid backgrounds without seeing the square corners. I know that there is a clearRect method that would do what I want to do, but with only rectangles, I need to do it with any closed shape. Is it possible, and if so, how would I do it?
brainjam's code was heading in the right direction, but didn't fully solve the problem. Here's the solution:
context.save();
context.globalCompositeOperation = 'copy';
context.fillStyle = 'rgba(0,0,0,0)';
//draw shape to cover up stuff underneath
context.fill();
context.restore();
Here's an example of a function that will clear a circle from a canvas:
var clearCircle = function(x, y, radius)
{
context.save();
context.globalCompositeOperation = 'destination-out';
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
context.fill();
context.restore();
};
I think what you want is a clipping region, defined by the clip() function. The latter takes a bunch of paths. Here's an example.
This is a little different from what you are specifically asking (which is to remove pixels after drawing them), but actually not drawing the pixels in the first place is probably better, if I understand your requirements correctly.
Edit: I now think I understand that what you want to do is clear pixels to transparent black. To do that, after having defined your paths, do something like this:
context.fillStyle = 'rgba(0,0,0,0)';
context.fill();
The first statement sets the fill color to transparent black.
Use globalCompositeOperation = 'destination-out' instead of 'copy', it will erase all pixels of the shape in the canvas.
See all kinds of composition here
very usefull !

Categories

Resources