paper.rect(0, 0, settings.width, settings.height, settings.radius);
Creates a nice rectangle with rounded corners. Is it possible to create a rectangle with just one round corner?
If you use Raphael JS:
Raphael.fn.roundedRectangle = function (x, y, w, h, r1, r2, r3, r4){
var array = [];
array = array.concat(["M",x,r1+y, "Q",x,y, x+r1,y]); //A
array = array.concat(["L",x+w-r2,y, "Q",x+w,y, x+w,y+r2]); //B
array = array.concat(["L",x+w,y+h-r3, "Q",x+w,y+h, x+w-r3,y+h]); //C
array = array.concat(["L",x+r4,y+h, "Q",x,y+h, x,y+h-r4, "Z"]); //D
return this.path(array);
};
To have a rectangle with only the upper-right corner rounded
var paper = Raphael("canvas", 840, 480);
paper.roundedRectangle(10, 10, 80, 80, 0, 20, 0, 0);
Source and online example: http://www.remy-mellet.com/blog/179-draw-rectangle-with-123-or-4-rounded-corner/
The rounded corners feature maps directly on to the underlying SVG rx and ry attributes, they apply to whole rectangles and so there's no possibility of just setting it on a single corner.
This blog post discusses an approach in SVG of basically covering up the corners you don't want rounded. Although his examples appear to be offline now, the approach should be fairly easy to reverse engineer into SVG.
An alternative approach would be to use a path instead of a rectangle object and draw the whole outline yourself. The syntax is a little obscure but is easy enough once you understand what's going on. Try Jakob Jenkov's SVG Path tutorial for an introduction.
Very old question, here's a better path. I converted it to relative coordinates, which should be better at animations...
Raphael.fn.roundedRectangle = function (x, y, w, h, r1, r2, r3, r4){
var array = [];
array = array.concat(["M",x+r1,y]);
array = array.concat(['l',w-r1-r2,0]);//T
array = array.concat(["q",r2,0, r2,r2]); //TR
array = array.concat(['l',0,h-r3-r2]);//R
array = array.concat(["q",0,r3, -r3,r3]); //BR
array = array.concat(['l',-w+r4+r3,0]);//B
array = array.concat(["q",-r4,0, -r4,-r4]); //BL
array = array.concat(['l',0,-h+r4+r1]);//L
array = array.concat(["q",0,-r1, r1,-r1]); //TL
array = array.concat(["z"]); //end
return this.path(array);
};
Related
I'm looking for a way to check if two path2D are intersecting but can't find a way...
Exemple :
// My circle
let circlePath = new Path2D();
circlePath.ellipse(x, y, radiusX, radiusY, 0, 0, Math.PI*2, false);
// My rectangle
let rectPath = new Path2D();
rectPath.rect(x, y, width, height);
// Intersect boolean
let intersect = circlePath.intersect(rectPath); // Does not exists
Is there a function to do that ?
I found isPointInPath(path2D, x, y) (that I use with my paths to check intersect with mouse) but can't use it between two paths.
Or maybe a way to get an array of all points in a Path2D to use isPointInPath with all points ?
Edit:
FYI, I want this for a game development, I want to be able to check collision between my entities (an entity is defined by some data and a Path2D). My entities can be squares, circles or more complex shapes.
There currently is nothing in the API to do this.
The Path2D interface is still just an opaque object, from which we can't even extract the path-data. I actually did start working on a future proposal to expose this path-data and add a few more methods to the Path2D object, like a getPointAtLength(), or an export to SVG path commands, which would help doing what you want, but I wouldn't hold my breath until it's part of the API, and I must admit I didn't even consider including such a path-intersect method...
However as part of this effort I did build a prototype of that API that currently exposes only a few of the expected methods: https://github.com/Kaiido/path2D-inspection/
Among these methods there is a toSVGString() that can be used with this project (that I didn't extensively test).
So we could build a Path2D#intersects() by merging both libraries. However note that at least mine (path2d-inspection) hasn't been extensively tested yet and I don't recommend using it in production just yet.
Anyway, here is a very quickly made hack as a proof-of-concept:
const circlePath = new Path2D();
circlePath.ellipse(rand(100, 20), rand(100, 20), rand(80, 10), rand(80, 10), rand(Math.PI*2), 0, Math.PI*2, false);
// My rectangle
const rectPath = new Path2D();
rectPath.rect(rand(150), rand(100), rand(200), rand(200));
// Intersect boolean
const intersect = rectPath.intersects(circlePath);
console.log("intersect:", intersect);
// List of intersection points
const intersections = rectPath.getIntersections(circlePath);
console.log(intersections);
// Render on a canvas to verify
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = "green";
ctx.stroke(circlePath);
ctx.strokeStyle = "red";
ctx.stroke(rectPath);
function rand(max=1, min=0) {
return Math.random() * (max - min) + min;
}
.as-console-wrapper { max-height: 50px !important }
<script src="https://cdn.jsdelivr.net/gh/Kaiido/path2D-inspection#master/build/path2D-inspection.min.js"></script>
<script>
// a really quick hack to grasp 'intersect' from their esm
globalThis.module = {};
</script>
<script src="https://cdn.jsdelivr.net/gh/bpmn-io/path-intersection#master/intersect.js"></script>
<script>
// part 2 of the hack to grasp 'intersect', let's make it a Path2D method :P
{
const intersect = module.exports;
delete globalThis.module;
Path2D.prototype.getIntersections = function(path2) {
return intersect(this.toSVGString(), path2.toSVGString());
};
Path2D.prototype.intersects = function(path2) {
return this.getIntersections(path2).length > 0;
};
}
</script>
<canvas></canvas>
There are plenty of examples on how to draw lines on canvas, in js.
But for only educational purposes i want to draw line using algorithm. basically method gets two Vector2 points, from them it finds middle point, then it continues like that recursively until minimum distance of 2 pixels is reached.
I have DrawPoint method to basically draw 1 point on canvas, and DrawLine method that does all the job.
For now I have 2 problems:
1: points are not colored red, as they should be.
2:
It doesnt look like a line.
For Vector2 i used "Victor.js" plugin, and it seems to be working well.
this is code i have:
JS:
var point2 = new Victor(100, 100);
var point3 = new Victor(150, 150);
DrawLine(point2, point3);
function DrawLine(vec0, vec1)
{
var point0 = new Victor(vec0.x, vec0.y);
var point1 = new Victor(vec1.x, vec1.y);
var dist = point1.distance(point0);
if (dist < 2)
return;
//this is how it should look like in c# var middlePoint = point0 + (point1 - point0)/2; But looks like i cant just divide by 2 using victor js because i can only divide vector by vector.
var middlePoint = point0.add(point1.subtract(point0).divide(new Victor(2,2)));
DrawPoint(middlePoint);
DrawLine(point0, middlePoint);
DrawLine(middlePoint, point1);
}
function DrawPoint(point){
var c = document.getElementById("screen");
var ctx = c.getContext("2d");
ctx.fillStyle = "FF0000";
ctx.fillRect(point.x, point.y, 3,1);
}
I really appreciate any help you can provide.
The victor.js documentation shows that most functions of Victors do not return new Victors, but operate on the current instance. In a way, v1.add(v2) is semantically more like v1 += v2 and not v1 + v2.
The problem is with calculating the midpoint. You could use the mix() method, which blends two vectors with a weight. You must clone() the Victor first, otherwise point0will be midofied:
var middlePoint = point0.clone().mix(point1, 0.5);
If you don't change the original Vectors, you don't need to create new instances of Victors from the arguments, you can use the arguments directly:
function DrawLine(point0, point1)
{
var dist = point1.distance(point0);
if (dist < 2) return;
var middlePoint = point0.clone().mix(point1, 0.5);
DrawPoint(middlePoint);
DrawLine(point0, middlePoint);
DrawLine(middlePoint, point1);
}
Finally, as Sven the Surfer has already said in a comment, "FF0000" isn't a valid colour. Use "#FF0000", note the hash mark, or one of the named web colours such as "crimson".
I see this question and I dont know how I can set id for each circles and access them from javascript codes and css codes? (e.g. click)
You can solve this by defining click objects when drawing the circles. Inside the loop drawing the circles (ref. the fiddle made by #MonicaOlejniczak):
...
// push circle info as objects:
circles.push({
id: i + "," + j, // some ID
x: x,
y: y,
radius: radius
});
...
Then:
add a click handler to canvas
correct mouse position
loop through the objects finding if (x,y) is inside the circle:
Function example:
canvas.onclick = function(e) {
// correct mouse coordinates:
var rect = canvas.getBoundingClientRect(), // make x/y relative to canvas
x = e.clientX - rect.left,
y = e.clientY - rect.top,
i = 0, circle;
// check which circle:
while(circle = circles[i++]) {
context.beginPath(); // we build a path to check with, but not to draw
context.arc(circle.x, circle.y, circle.radius, 0, 2*Math.PI);
if (context.isPointInPath(x, y)) {
alert("Clicked circle: " + circle.id);
break;
}
}
};
You can optionally use math instead of the isPointInPath(), but the latter is simpler and is fast enough for this purpose.
Modified version of the same fiddle
You can't set an ID on something that has been drawn to a canvas.
The element on its own is just a bitmap and does not provide information about any drawn objects.
If you need to interact with the items inside the canvas you need to manually keep a reference to where everything is drawn, or use a system like "object picking" or using the built in hit regions.
I'd like to make a minimap of my rpg game.
Is making a minimap as simple as dividing all object dimensions, velocities, and coordinates by however large you want the minimap?
For example below... You have a size of 1000x1000px, a canvas (viewport) of 500x500px, the player is located in the center of the viewport... If you wanted a minimap half the size of the actual world, you would do:
Player/Viewport x,y velocity/2
Player/Viewport x,y coordinates/2
Canvas, world, and all objects' width and height are divided by 2
etc...
That way the rendering of the minimap on the world and the velocities are scaled accurately? Am I missing anything?
Thanks!
EDIT: Something like this?
function miniMap() {
$(".minimapHolder").show();
$("#mini_map").text("hide minimap");
var minicanvas = document.getElementById("miniMap");
ministage = new createjs.Stage("miniMap");
minicam = new createjs.Shape();
minicam.graphics.beginStroke("white").drawRoundRect(0, 0, 100, 40, 5);
//blip representation of Player
player_blip = new createjs.Shape();
player_blip.graphics.beginFill("yellow").drawRoundRect(0, 0, 11.2, 12, 1);
animal_blip = new createjs.Shape();
animal_blip.graphics.beginFill("red").drawRoundRect(0, 0, 24.4, 21.6, 1);
player_blip.x = players_Array[0].x/5;
player_blip.y = players_Array[0].y/5;
animal_blip.x = animalContainer.x/5;
animal_blip.y = animalContainer.y/5;
minicam.x = players_Array[0].x-110;
minicam.y = players_Array[0].y-110;
ministage.addChild(player_blip, animal_blip, minicam);
ministage.update();
}
function updateMiniMap() {
player_blip.x = players_Array[0].x/5;
player_blip.y = players_Array[0].y/5;
if (ContainerOfAnimals.children[0] != null) {
var pt = ContainerOfAnimals.localToGlobal(ContainerOfAnimals.children[0].x, ContainerOfAnimals.children[0].y);
console.log(pt.x);
animal_blip.x = pt.x/5;
animal_blip.y = pt.y/5;
} else {
ministage.removeChild(animal_blip);
}
minicam.x = player_blip.x-40;
minicam.y = player_blip.y-15;
ministage.update();
}
Gives:
Short anwswer: "It will(most likely) work." ... but:
What you are trying to achieve is just scaling the stage/container, so you could also just use a copy of everything and put it into a container and scale it down to 0.5, but that is not the purpose of a minimap.
Objects of the minimap should only be a representation of the object in the 'real' world and should therefore not have any velocity ect.(that should especially not be updated separately from the 'real' world) - while your approach will probably work, you'd allways have to keep track and update every property, this will get messy quickly or even lead to differences if you miss some tiny things.
A more 'clean'(and simple) approach to this would be, that each minimap-object has a reference to the object in the 'real' world and on each tick, it just reads the x/y-coordinates and updates its' own coordinates based on the minimap-scale.
Another thing is the graphics: Scaling-operations can be costly(performance wise), especially when they are done each frame, so IF you use the same graphics for the minimap you should at least used a cached DisplayObject and not have the graphics scaled each frame.
I have a canvas on which i'm drawing 4 lines.
I need to then compare the accuracy of those lines drawn against the coordinates of 4 predefined lines.
Can i do this with canvas?
thanks.
As JJPA says, give it a try!
One route you could take would be to store the coordinates of the drawn line and the expected line. Each line forms a vector. You can take the Euclidean distance between the lines using a simple formula. The closer the distance, the more accurate the lines.
I just wrote this.
var expected = {from: {x:10, y:10}, to:{x:20, y:20}}
var identical = {from: {x:10, y:10}, to:{x:20, y:20}}
var close = {from: {x:9, y:9}, to:{x:21, y:21}}
var lessClose = {from: {x:8, y:13}, to:{x:25, y:15}}
var distance = function(a, b) {
return Math.sqrt(Math.pow(a.from.x - b.from.y, 2) +
Math.pow(a.to.x - b.to.y, 2))
}
console.log("identical", distance(expected, identical))
console.log("close", distance(expected, close))
console.log("lessclose", distance(expected, lessClose))
gives
identical 0
close 1.4142135623730951
lessclose 5.830951894845301
If you don't know which way round the lines are, you can swap start and end points and take the minimum distance.
Well, you can get the pixels of your canvas in an rgb-array in javascript:
var pixelarray= canvas.getImageData(x, y, width, height).data;
then pixelarray[0] as integer is the red-value of the first pixel, pixelarray[3] the red-value of the second and so on.
In a canvas of 100px width (pixel-index 0 to 299 in first line), pixelarray[299] would be the blue-value of the last pixel in the first line and pixelarray[300] the red-value of the first in second line.
Then you could get either the functions representing the 4 lines you want to compare to and do something like
function isblack(x,y,width,height){
return pixelarray[(y*width+x)*3]==255&&pixelarray[(y*width+x)*3+1]==255&&pixelarray[(y*width+x)*3+2]==255||x<0||x>width||y<0||y>height;
}
function f(x){return a*x+b;}
var lineblackness=0;
for(var x=0;x<picturewidth;x++)if(isblack(x,f(x),picturewidth,pictureheight))lineblackness++;
if(lineblackness==picturewidth)line_is_drawn();
Where fx(x) would represent the line ...
well, if you use a picture or coordinates for the line, it might get easier:
getcoord(data){
var ar=new array();
for(var i=0;i<data.length/3;i++)if(data[i*3]==255)ar.push(i);
return ar;
}
var comparecoords=getcoord(comparecanvas.getImageData(x, y, width, height).data);
var thisdata=canvas.getImageData(x, y, width, height).data;
var linehits=0;
for(var i=0;i<comparecoords.length;i++){if(thisdata[comparecoords[i]*3]==255)linehits++;}
if(comparecoords.length==linehits)all_coords_are_hit();
Here I only compared the red-value, assuming you have a black&White-image.
If you want to compare lines that aren't exactly fits, you can change the ==255 with a few additional ifs e.g.
function isblack(x,y,width,height,blurr,treshhold){
for(var i=-blurr;i<blurr;i++)if(x-i>0&&x+i<width&&pixelarray[(y*width+(x+i))*3]>=treshhold)return true;
for(var i=-blurr;i<blurr;i++)if(y-i>0&&y+i<height&&pixelarray[((y+i)*width+(x))*3]>=treshhold)return true;
return false;
}
This function uses also only red, but uses a treshhold to let also gray be valid. it also accepts a blurr-value for checking in x-direction before/after the pixel and then again in y-direction.
You could also combine the two loops for checking in an rectangle around the pixel.
Changing the return true into return i will also give you the distance from the pixel you want to check. if you combine the two loops into an i and a j-value, return sqrt(i^2+j^2) and sum all checks up, you can get an indicator how accurate your line is.
To implement sqareroot-error, only comparing the y-distance and summing up the squared distance i^2 should be enough.
Please note that these programs are suggestions, better implementations may exists and my codes could still contain errors.