Canvas polygons do not touch properly

So, I was messing around with the canvas element, and it looks like I came across a situation that is very annoying, but I could not find a solution. Let's say that two polygons are drawn on the canvas and that they should touch each other. Where one polygon looks like this:

ctx.beginPath(); ctx.moveTo(oX,oY); ctx.lineTo(oX=oX+k,oY=oY-h); ctx.lineTo(oX=oX+k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY-h); ctx.fill(); 

A simple version is implemented in this fiddle .

As you can see, there is a thin line between these figures. How can i avoid this? I tried the solutions here , but they don’t actually refer to this case, because I am dealing with diagonal lines.

+4
source share
2 answers

One solution

You can always use the move line trick, but depending on your goal:

If you show many polygons next to each other, you can look at the polygons as simple squares.

  • Draw them as such in an external canvas next to each other. This will produce a result without spaces.
  • Then convert the main canvas to the position where you want these polygons to appear. Add twist and / or skew depending on the purpose.
  • Finally, draw a screen canvas on the main canvas as an image. The problem has disappeared.

This will give you an accurate result without any extra steps when stroking, and the calculations for the boxes will become very simple and fast (think a 2d grid).

However, you must use a screen canvas. If you transform the main canvas and draw the figures, you will encounter the same problem as now. This is due to the fact that each point is converted, and if there is a need for interpolation, it will be calculated separately for each shape of the path. Drawing on the image will add interpolation over the entire surface and only where there are gaps (opaque alpha). Since we are already “space-free”, this is no longer a problem.

This will require an additional step in planning for their proper placement, but this is a simple step.

Example

Step 1 - pull the boxes into the screen canvas:

This code relies on a screen canvas, resulting in two rectangles without a space:

snap

(the example uses a screen to display the result, see the next step to use the screen saver)

 var ctx = document.querySelector("canvas").getContext("2d"); ctx.fillStyle = "red"; ctx.fillRect(10, 10, 50, 50); ctx.fillRect(60, 10, 50, 50); 
 <canvas/> 

Step 2 - transforming the main canvas and drawing off-screen

When entering into the main canvas with a set of transformations, the result will be (a pseudo-random transformation is just for display):

snap2

 var ctx = document.querySelector("canvas").getContext("2d"); // off-screen canvas var octx = document.createElement("canvas").getContext("2d"); octx.fillStyle = "red"; octx.fillRect(10, 10, 50, 50); octx.fillRect(60, 10, 50, 50); // transform and draw to main ctx.translate(80, 0); ctx.rotate(0.5, Math.PI); ctx.transform(1, 0, Math.tan(-0.5),1, 0,0); // skew ctx.drawImage(octx.canvas, 0, 0); 
 <canvas /> 

Step 3 (optional) - Interaction

If you want to interact with the fields, you just apply the same transformation, then add the path to the box and check it for mouse position. Redraw one state, erase by clearing and pulling the screen canvas from above:

 var ctx = document.querySelector("canvas").getContext("2d"); // off-screen canvas var octx = document.createElement("canvas").getContext("2d"); octx.fillStyle = "red"; octx.fillRect(10, 10, 50, 50); octx.fillRect(60, 10, 50, 50); // allow us to reuse some of the steps: function getTransforms() { ctx.setTransform(1,0,0,1,0,0); ctx.translate(80, 0); ctx.rotate(0.5, Math.PI); ctx.transform(1, 0, Math.tan(-0.5),1, 0,0); // skew } function clear() { ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,300,150); } function redraw() { ctx.drawImage(octx.canvas, 0, 0); } getTransforms(); redraw(); ctx.canvas.onmousemove = function(e) { var r = this.getBoundingClientRect(), x = e.clientX - r.left, y = e.clientY - r.top; // box 1 (for many, use array) ctx.beginPath(); ctx.rect(10, 10, 50, 50); clear(); // these can be optimized to use state-flags getTransforms(); // so they aren't redraw for every move... redraw(); // just one box check here if (ctx.isPointInPath(x, y)) { ctx.fill(); } }; 
 <canvas /> 
+5
source

Yes, it is annoying when filled polygons lead to this tiny gap. This is especially true for diagonals that should theoretically occur.

A common workaround is to place a half-pixel dot stroke around the polygons:

 //Some basic setup ... var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var oX = 50; var oY = 50; var h = 33; var k = 50; ctx.fillStyle = 'red'; ctx.strokeStyle='red'; ctx.lineWidth=0.50; //Draw one polygon ctx.beginPath(); ctx.moveTo(oX,oY); ctx.lineTo(oX=oX+k,oY=oY-h); ctx.lineTo(oX=oX+k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY-h); ctx.fill(); ctx.stroke(); //Draw another polygon oX = oX+k; oY = oY+h; ctx.beginPath(); ctx.moveTo(oX,oY); ctx.lineTo(oX=oX+k,oY=oY-h); ctx.lineTo(oX=oX+k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY-h); ctx.fill(); ctx.stroke(); 

 var canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d"); //Some basic setup ... var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var oX = 50; var oY = 50; var h = 33; var k = 50; ctx.fillStyle = 'red'; ctx.strokeStyle='red'; ctx.lineWidth=0.50; //Draw one polygon ctx.beginPath(); ctx.moveTo(oX,oY); ctx.lineTo(oX=oX+k,oY=oY-h); ctx.lineTo(oX=oX+k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY-h); ctx.fill(); ctx.stroke(); //Draw another polygon oX = oX+k; oY = oY+h; ctx.beginPath(); ctx.moveTo(oX,oY); ctx.lineTo(oX=oX+k,oY=oY-h); ctx.lineTo(oX=oX+k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY+h); ctx.lineTo(oX=oX-k,oY=oY-h); ctx.fill(); ctx.stroke(); 
 #canvas{border:1px solid red;} 
 <canvas id="canvas" width=300 height=300></canvas> 
+1
source

Source: https://habr.com/ru/post/1238518/


All Articles