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);
Related
Using Opencv.js I have acquired the contours in an image.
using some selection function I have collected a subset of these contours.
Say they are in the list of contours:
var suitableContours = [];
I need to know draw these contours using opencv.
If this were python I could do
cv.drawContours(img, [suitableContours[cnt]], 0, [255, 0, 0, 255], 2);
Assume by the point I want to draw the contours I don't have access to the original image nor the mask nor the original contour list, only the selected contours.
How can I draw these contours?
The above code results in: BindingError: Cannot pass "[object Object],[object Object]" as a MatVector
From
https://docs.opencv.org/3.4/d5/daa/tutorial_js_contours_begin.html
Try something like this:
let color = new cv.Scalar(255,0,0,255);
for (let i = 0; i < suitableContours.size(); ++i) {
cv.drawContours(src, suitableContours, i, color, 1, cv.LINE_8, hierarchy, 100);
}
cv.imshow('canvasOutput', src);
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();
I'd been trying to write a small library in Javascript mainly for Canvas drawImage() method.
The main purpose of the library is to pass array values instead of passing single values such as :
// srcPos=[0,0] , size=[90,90], dstPos=[50,50]
function draw_image(context, image, srcPos, size, dstPos, size) {
context.drawImage(image, srcPos[0], srcPos[1], size[0], size[1], dstPos[0], dstPos[1], size[0], size[1]);
}
but when i called this function jzz like this, I'm getting Uncaught ReferenceError :
var canvas = document.getElementById("display"),
frame = canvas.getContext("2d");
var shipInfo = { center:[45, 45], size:[90, 90], radius: 35, angle:45 },
shipImage = new Image(),
pos = [40, 70];
shipImage.src = "ship.png";
function draw() {
draw_image(frame, shipImage, shipInfo.size, pos, shipInfo.size);
}
window.onload = function() {
draw();
}
And Is it possible to implement a method overriding the default drawImage() like this:
frame.draw_image(shipImage, srcPos, shipInfo.size, dstPos, shipInfo.size);
If you want to add a function to the 2d context, javascript makes this easy thanks to the prototype inheritance : You can inject the Context2D object to add or change its function as you wish.
You might want to look at a few addings i made to the context in a small canvas lib i made here : https://github.com/gamealchemist/CanvasLib
Some will tell that injecting is evil, but unless you're on a huge boat i would just say : If you use some graphic library, respect the semantic of existing functions and everything should be fine. If you don't use libs : do whatever it takes !
So, to answer more specifically to your question, your shorter drawImage would give :
CanvasRenderingContext2D.prototype.draw_image = function ( image,
srcPos, size,
dstPos, size) {
this.drawImage(image, srcPos[0], srcPos[1], size[0], size[1],
dstPos[0], dstPos[1], size[0], size[1]);
};
Then you can use the new function on all your contexts :
var canvas = document.getElementById("display"),
frame = canvas.getContext("2d");
frame.draw_image( ... ) ;
Notice that you could use 'rect' objects, which would be arrays with 4 elements, x, y, w, h, and lead to an even shorter syntax.
Edit : i see in your lib that you want to rotate your rect.
First thing is that you don't want to reset the transform. Just save it then restore it.
I would try something closer to this :
var x = dstPos[0],
y = dstPos[1],
halfWidth = dstSize[0]*0.5, // !! not src use >>1 if you know it's an int.
halfHeight = dstSize[1]*0.5, // !! not src ...
angleInRads = angle * Math.PI / 180;
this.save();
this.translate(x+halfWidth,y+halfHeight);
this.rotate(angleInRads);
this.drawImage(image
, center[0], center[1], srcSize[0], srcSize[1]
, -halfWidth, -halfHeight, dstSize[0],dstSize[1]);
this.restore();
Your small image library would fit well inside a javascript object.
A Demo: http://jsfiddle.net/m1erickson/7pZJw/
A javascript object can hold information about your image:
the image itself
the image size (can be automatically calculated for you)
the image centerpoint (can be automatically calculated for you)
Example:
// create a new object
// fill it with the info about the image
var object={
image:shipImage,
width:shipImage.width,
height:shipImage.height,
centerOffsetX:shipImage.width/2,
centerOffsetY:shipImage.height/2,
radius:35,
angle:45,
};
A javascript object can also hold functions that draws the image (as you've done in your code)
Example:
// when you call object.draw the image will be drawn by this function
// which is added to the object itself
draw:function(context,atX,atY,newWidth,newHeight){
context.drawImage(
this.image,
0,0,this.width,this.height,
atX,atY,newWidth,newHeight);
},
A function to create your small image library inside a javascript object might look like this:
function createImageObject(image,radius,angle){
// create a new object
// fill it with the info about the image
var object={
image:image,
width:image.width,
height:image.height,
centerOffsetX:image.width/2,
centerOffsetY:image.height/2,
radius:radius,
angle:angle,
draw:function(context,atX,atY,newWidth,newHeight){
context.drawImage(
this.image,
0,0,this.width,this.height,
atX,atY,newWidth,newHeight);
},
};
return(object);
}
And you can use your ship object library like this:
// create a new ship object
var shipObject=createImageObject(img,35,45);
// draw the ship image using the ship object
// draw at 20,20 with size 75,75
shipObject.draw(frame,20,20,75,75);
BTW, I see you're using the version of drawImage that will scale/clip the source image.
If you just want to draw the full image at its original size you can do this shortcut:
// draw the image full-sized at x,y
context.drawImage(image,x,y);
I'm having an issue with obj.getTransformedBounds() of a Shape().
The getBounds() method should return a rectangle (if it was set using setBounds()) which stores the x/y/w/h of the Shape() within its OWN coordinate space - it's real width & height with no transformations applied.
The getTransformedBounds() method should return a rectangle (again, if set by using setBounds()) which stores the x/y/w/y within its PARENT'S coordinate space - the shape's width & height with transformations applied.
Instead, I see getBounds() returning only the transformed bounds of a shape that has its scaleX and/or scaleY applied:
http://jsfiddle.net/W9Tb8/1/
var shape = new createjs.Shape();
shape.graphics = new createjs.Graphics().beginFill("#f00").drawRect(0,0,100,100);
shape.setBounds(0,0,100,100);
shape.scaleX = shape.scaleY = 0.5;
console.log(shape.getBounds(), shape.getTransformedBounds()); // returns [0, 0, 50, 50], [0, 0, 50, 50]
// shape.getBounds() should return [0, 0, 100, 100]
// shape.getTransformedBounds() should return [0, 0, 50, 50}
Am I missing something?
It looks like what's happen is EaselJS is reusing some rectangle instances to help reduce memory impact. Have a look at the documentation here for more about it:
http://createjs.com/Docs/EaselJS/classes/Shape.html#method_getBounds
If you want to force it to give you the originally set bounds you'll either need to clone or copy them. I've altered your example to show how to output the original bounds using cloning.
http://jsfiddle.net/W9Tb8/2/
console.log(shape.getBounds().clone(), shape.getTransformedBounds());
I am using the code below to create hundreds of lines in my three.js scene
edgeGeometry[i] = new THREE.Geometry();
edgeGeometry[i].vertices[0] = v(x1,y1,z1);
edgeGeometry[i].vertices[1] = v(x2,y2,z2);
edgesMat[i] = new THREE.LineBasicMaterial({
color: 0x6699FF, linewidth: 1, fog:true});
edge[i] = new THREE.Line(edgeGeometry[i], edgesMat[i]);
edge[i].type = THREE.Lines;
scene2.add(edge[i]);
It works just fine, but when i change the value of "linewidth" to a bigger OR smaller value, i see NO difference in the scene.
How should i change the thickness of the lines? Any ideas?
Thanks, Dimitris
1) Use native OpenGL
You can achieve rendering of line thicknesses with a workaround by setting your browser to use native OpenGL instead of ANGLE. You can read here on how to do this on Chrome.
Keep in mind that you will experience performance differences if you swap to native OpenGL.
EDIT:
The master MrDoob himself posted here how to do this for both Chrome and Firefox.
Note: This first option is no longer a valid solution since the latest OpenGL versions no longer support line thickness either. Check also #gman his answer. This means if you want to use line thickness the second option is the way to go.
2) Use THREE.MeshLine class
There is also another solution; this THREE.MeshLine class on github is a nice workaround. It comes with a special THREE.MeshLineMaterial. According to the docs it is as simple as:
Create and populate a geometry
Create a THREE.MeshLine and assign the geometry
Create a THREE.MeshLineMaterial
Use THREE.MeshLine and THREE.MeshLineMaterial to create a THREE.Mesh
Are you using Windows?
I remember this not working on Windows because it wasn't implemented in ANGLE.
This occurs in Windows Chrome and Firefox, both using ANGLE (WebGL to DirectX wrapper).
The issue is still not solved by the ANGLE project. You can star the issue here to get higher priority and get a notification if it's going to be implemented:
https://code.google.com/p/angleproject/issues/detail?id=119
I use TubeGeometry to create a Thick line between two points:
See Green lines in Helix
// line material
var lineMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00 });
let startVector = new THREE.Vector3(
RADI * Math.cos(t),
RADI * Math.sin(t),
3 * t
);
let endVector = new THREE.Vector3(
RADI * Math.cos(t + 10),
RADI * Math.sin(t + 10),
3 * t
);
let linePoints = [];
linePoints.push(startVector, endVector);
// Create Tube Geometry
var tubeGeometry = new THREE.TubeGeometry(
new THREE.CatmullRomCurve3(linePoints),
512,// path segments
0.5,// THICKNESS
8, //Roundness of Tube
false //closed
);
let line = new THREE.Line(tubeGeometry, lineMaterial);
scene.add(line);
This is no longer an issue just in ANGLE it's an issue on all platforms. Browsers needed to switching to the OpenGL 4+ core profile to support WebGL2 and the OpenGL 4+ core profile does not support line widths greater than 1. From the OpenGL 4.0+ spec, section E.2.1
E.2.1 Deprecated But Still Supported Features
The following features are deprecated, but still present in the core profile. They may be removed from a future version of OpenGL, and are removed in a forward compatible context implementing the core profile.
Wide lines - LineWidth values greater than 1.0 will generate an INVALID_VALUE error.
To draw thicker lines you need generate geometry. For three.js there is this library (pointed out by Wilt as well)
https://github.com/spite/THREE.MeshLine
You can use CanvasRenderer instead of Webglrenderer. Check out the ifficial documentation here where each shape has a border of linewidth = 10;
You can achieve the same effect using extrude-polyline to generate a simplicial complex for the thickened (poly)line and three-simplicial-complex to convert this to a three.js Mesh:
const THREE = require('three');
const extrudePolyline = require('extrude-polyline');
const Complex = require('three-simplicial-complex')(THREE);
function thickPolyline(points, lineWidth) {
const simplicialComplex = extrudePolyline({
// Adjust to taste!
thickness: lineWidth,
cap: 'square', // or 'butt'
join: 'bevel', // or 'miter',
miterLimit: 10,
}).build(points);
// Add a z-coordinate.
for (const position of simplicialComplex.positions) {
position[2] = 0;
}
return Complex(simplicialComplex);
}
const vertices = [[0, 0], [10, 0], [10, 10], [20, 10], [30, 00]];
const geometry = thickPolyline(vertices, 10);
const material = new THREE.MeshBasicMaterial({
color: 0x009900,
side: THREE.DoubleSide
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
If you want to texture map the polyline, things get a little more complicated.
Thanks to Wilt's answer for pointing me in the right direction with THREE.MeshLine.
It can be slightly trickier than they make it out to be, however... So here's my solution following their docs and their demo code very carefully... (assuming you've already included Three and MeshLine):
renderer = new THREE.WebGLRenderer({ canvas });
//...
function createCircle(resolution) {
let circleGeometry = new THREE.Geometry();
for (let rotation = 0; rotation <= Math.PI * 2.0; rotation += Math.PI * 0.1) {
circleGeometry.vertices.push(
new THREE.Vector3(Math.cos(rotation), Math.sin(rotation), 0));
}
let circleLine = new MeshLine();
circleLine.setGeometry(circleGeometry);
//Bonus: parabolic width! (See Z rotation below.)
//circleLine.setGeometry(circleGeometry, function(point) {
//return Math.pow(4 * point * (1 - point), 1);
//});
//Note: resolution is *required*!
return new THREE.Mesh(circleLine.geometry,
new MeshLineMaterial({
color: 'blue',
resolution,
sizeAttenuation: 0,
lineWidth: 5.0,
side: THREE.DoubleSide
}));
}
let circle = createCircle(new THREE.Vector2(canvas.width, canvas.height));
circle.rotation.x = Math.PI * 0.5;
circle.position.y = 20.0;
scene.add(circle);
//In update, to rotate the circle (e.g. if using parabola above):
world.circle.rotation.z += 0.05;
With size attenuation off and using THREE.DoubleSide, like I did above, the circle will look like a nice, consistent circle no matter where you're looking at it from (not "true 3D").
For just a line, you can obviously easily adapt.
Why not set the opacity to something like 0.1?
NOTE: This only works if you are giving borders to something, if there's nothing behind it the it won't work.