Hairline/Empty Space when Drawing a Triangulated Rectangle on Canvas - javascript

I was exploring some drawing on the HTML canvas, and quickly landed at dissecting my shapes into triangles, as that seem to be the most common and sure way to ensure that one is handling only simple polygons and not self-intersection ones (which get drawn in a somewhat uncontrollable fashion to me).
But when I wanted to draw a simple rectangle as to triangles, I got the following result:
The approach I am taken is the following:
const rect = [{
x: 200,
y: 225
}, {
x: 490,
y: 225
}, {
x: 490,
y: 375
}, {
x: 200,
y: 375
}];
const triangle1 = [rect[0], rect[1], rect[2]];
const triangle2 = [rect[0], rect[2], rect[3]];
fillPolygon(ctx, triangle1, 'blue');
fillPolygon(ctx, triangle2, 'blue');
See the full example here:
https://jsfiddle.net/JLVva/85/
Since triangles seem to be the basis of graphics drawing, I think I am missing a common workaround applied in these scenarios. How is this normally handled?

If you also add a stroke to the shape as well as a fill it removes that gap. I assume the stroke defaults to transparent or something.
Add this to the end of your fillPolygon function.
ctx.strokeStyle = color;
ctx.stroke();

Related

How right scale canvas? (html)

I've prepared example for my case (just change something into js file for render map).
In short, I have source of map (coordinates, and sprites), and i need to draw this map.
I have following algorithm:
for(let x in data.region.draw_info){
const cellX = data.region.draw_info[x];
for(let y in cellX){
const cellY = cellX[y];
for(let cell of cellY){
ctx.drawImage(image, constans[cell[0]].x*1, constans[cell[0]].y*1, 32, 32, x*32, y*32, 32,32);
}
}
}
As you see, I use:
ctx.drawImage(image, constans[cell[0]].x, constans[cell[0]].y, 32, 32, x*32, y*32, 32,32);
Where: drawImage(image, space_source_x, space_source_y, size_cut_source_x, size_cut_source_y, space_canvas_x, space_canvas_y, size_put_canvas_x, size_put_canvas_y)
I guess, I filled right function drawImage, but I got cut of map (I have map 70x70, but I got render 9x5):
If I scale canvas, I will get full map, than my algorithm is right, but I think I have mistake in using drawImage
If you replace your line with the following, it draws all the images (but at a tiny scale)
ctx.drawImage(image, constans[cell[0]].x, constans[cell[0]].y, 2.2, 2.2, x*2.2, y*2.2, 2.2,2.2);

Different SVG paths are part of segments

I created a SVG file which consists of 35 different paths (train tracks).
Now I want to split the paths/tracks into 16 segments so another SVG path (train) can move along it.
The goal in the end is that the user can determine which paths should be actived, i.e. the segments later on should be clickable by the user in sequence.
To test if it's working the train should randomly move along the tracks.
Currently I tried to assign the paths into segments like this:
const segments = {
1: { x: document.getElementById("track1"), y: document.getElementById("track2")},
2: { x: document.getElementById("track3"), y: document.getElementById("track4")},
3: { x: ocument.getElementById("track5"), y: document.getElementById("track6")},
...
};
This unfortunately is not working. I don't know why since I thought I could just assign the paths into the corresponding segments. Before the SVG file was a normally drawn PNG file in which the segments were manually assigned through their coordinates like this and it worked:
const segments = {
1: { x: 1534, y: 534 },
2: { x: 2278, y: 630 },
3: { x: 2488, y: 1179 },
...
};
I'm new to coding in general, so I unfortunately don't know what to look for. The solutions I found were not exactly suited for my problem.
Thank you in advance for helping me.
document.getElementById("track1") will just return a path element. If you need the coordinates of the start of the path, you would need to do something like:
1: { x: document.getElementById("track1").getPointAtLength(0).x,
y: document.getElementById("track1").getPointAtLength(0).y }

How to animate a line scaling in Phaser 3 with a tween?

Imagine a very simple scene:
canvas of 800x640
red circle in the center
two blue lines in a 9:00 position
private create(): void {
const circ = this.add.circle(
400, 320, 200, 0xff0000
);
const l1 = this.add.line(
0, 0, 400, 320, 400, 100, 0x0000ff
).setOrigin(0);
const l2 = this.add.line(
0, 0, 400, 320, 200, 320, 0x0000ff
).setOrigin(0);
}
So far so good.
Now I want to scale this simple figure, same config 1.5x its current size:
private create(): void {
const circ = this.add.circle(
400, 320, 200, 0xff0000
);
const l1 = this.add.line(
0, 0, 400, 320, 400, 100, 0x0000ff
).setOrigin(0);
const l2 = this.add.line(
0, 0, 400, 320, 200, 320, 0x0000ff
).setOrigin(0);
this.tweens.add({
targets: [circ, l1, l2],
scale: 1.5,
yoyo: false,
duration: 2000,
ease: 'Sine.easeInOut'
});
}
Expected behavior:
the circle expands from the center
the lines expand as well, ideally where they meet
Actual behavior:
As things stand, only #1 fit my expectations. The lines, however, translate as opposed to merely scaling. And the translation seems affected by the scale parameter passed to tweens.add. What gives? What am I missing here?
Given the various configurations for "origin" wrt lines in Phaser 3, the worst I was expecting was that the lines would emanate/grow differently than the circle (which emanates from the center/origin). But I definitely expect the lines to stay still/keep their intersection at the circle's center.
Can you explain what exactly Phaser is doing here and what might I do to get my desired effect?
There are a bunch of things that might be unintuitive with the Line GameObject in Phaser 3, all contributing to this behavior. Let's break them down.
Lines and Origins
First of all, make the distinction between line as a geometric entity and line as a Phaser 3 Game Object. They are related but the confusion stems from the subtleties.
For the Phaser 3 line Game Object, there are three ordered pairs p (x, y) you need to keep in mind:
P (X, Y), the origin of the line, relative to the game world (and we are going to define the term in a while); in the game world, (0, 0) is at the upper-left corner.
P1 (X1, Y1), the first point that defines a line, relative to P
P2 (X2, Y2), the second point that defines a line, relative to P
Another important thing is that the game world is basically a Cartesian coordinate system flipped around the x-axis. So negative x values still go to the left but negative y values go up and positive y values go down.
Now let's define the origin, which I will just paraphrase from the official docs:
An object's origin is a normalized value in the range [0, 1].
For the x-axis, 0 means the left of the Game Object and 1 means the right.
For the y-axis, 0 means the top of the Game Object and 1 means the bottom.
By default, the origin is set at 0.5, the center of the object.
Interpreting the example and the tween
Let's take the horizontal line l2 in the example and figure out how it's being drawn.
const l2 = this.add.line(
0, 0, 400, 320, 200, 320, 0x0000ff
).setOrigin(0);
The origin is to the left of the game object.
With respect to the game world, this origin lies at 0, 0 or the upper-left corner.
The line is defined by two points relative to the origin, (400, 320) and (200, 320).
The important observation here is that the origin is completely outside the geometric line defined. In effect, there is a considerable invisible space in the rectangular area between your origin (at (0, 0)), left of the game object and the right, lowermost corner (at (400, 320)).
Now, when the tween is invoked, it actually scales the game object rather than just the geometric line. And since most of the game object is "invisible" (the geometric line being the only part visible), the effect is as if the geometric line is translating!
Solution
To get the desired effect, constrain the game object to exactly the area taken up by the geometric line. One such way to do this would be:
since we want the lines to "radiate" in the same way as the circle, set P at (400, 320), the center of the circle. Additionally, set P1 to (0, 0)---remember this is relative to P, so in the game world, we just made P = P1.
let's leave the lines' origins at 0 but now we have to redefine their P2 values to (0, -100) and (0, -200) respectively.
Putting it all together,
private create(): void {
const circ = this.add.circle(
400, 320, 200, 0xff0000
);
const l1 = this.add.line(
400, 320, 0, 0, 0, -100, 0x0000ff
).setOrigin(0);
const l2 = this.add.line(
400, 320, 0, 0, -200, 0, 0x0000ff
).setOrigin(0);
this.tweens.add({
targets: [circ, l1, l2],
scale: 1.5,
yoyo: false,
duration: 2000,
ease: 'Sine.easeInOut'
});
}

How to animate image in a canvas using tweenlite

Been searching for a while now and I haven't seen any article on how to animate (loop continuously from right to left) an imaged drawn in a canvas
var context = document.getElementById('my-canvas').getContext('2d');
var cloud = new Image();
cloud.src = 'images/cloud.png';
cloud.onload = function () {
context.drawImage(cloud, 0, 0)
TweenLite.to({x:0,y:0}, 2, {x: 200, y: 200});
}
What am I getting wrong?
edit:
Like so but using tweenlite : fiddle
Your first argument is wrong, it needs to be the target element/object according to their documentation:
TweenLite.to(target, duration, vars);
where target is an object/element, duration as a number, and vars an object.
Example:
TweenLite.to(canvas, 2, {x: 200, y:200});
Just note that if you intend to use the canvas element as target you could might as well just animate the image directly when loaded.
If you intend to move it across the canvas' bitmap without moving the canvas itself you will need to use a custom object (as I understand their docs) as target holding f.ex. the x and y properties, and use the onUpdate callback mechanism. Check their documentation for all the gory details.
var myObj = {
x: 0, // start position
y: 0,
image: cloud
};
TweenLite.to(myObj, 2, {x: 200, y:200, onUpdate: drawImage});
function drawImage() {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.drawImage(myObj.image, myObj.x, myObj,y);
}
Just replace what you have inside the onload handler with the code above.
Disclaimer: highly untested..

Way to reference objects in Raphael JS?

I am trying to draw shapes based off of where on the screen existing shapes are, so that I only have to change the coordinates of one shape for all the others to shift appropriately. Is there some way to reference a rectangle's x coordinate when constructing another?
For example, the following code does not work as I thought it would:
var paper = Raphael(0, 0, 1000,600);
var rectangleOne = paper.rect(100, 100, 100, 50);
var rectangleTwo = paper.rect(rectangleOne.x, rectangleOne.y + 40, rectangleOne.width + 50, rectangleOne.height);
I get some error that "rectangleOne.x" is undefined and defaults to zero...Any ideas how to fix this? Thanks all!
var rectangleTwo = paper.rect(rectangleOne.attr('x'), rectangleOne.attr('y') + 40, rectangleOne.attr('width') + 50, rectangleOne.attr('height'));

Categories