Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

paper.js - achieving smoother edges with closed paths

This is a box with an irregular shape that I have generated:

And this is the end effect I'd like to achieve (note the smooth edges):

enter image description here

Here's the code for my sharp version:

var path1 = new Path({
    segments: [[123, 6], [290, 6], [304, 142], [112, 142]],
    strokeColor: 'white',
    closed: true,
    strokeWidth: 3,
    strokeJoin: 'round'
});

Thing is, I'm already using the strokeJoin: 'round' option and the difference is hardly noticeable with a stroke width of 3px. It's a small thing but could turn into a game breaker as there are going to be multiple objects like this and the difference is huge.

Is there any way to achieve that with paper.js without overdoing it?

like image 332
Paweł Duda Avatar asked Oct 25 '25 01:10

Paweł Duda


2 Answers

As markE mentioned, strokeJoin only changes the canvas style of a path's stroke. Paper.js does not come with a corner-rounding function, you'll have to make your own.

Here's a quick function that you can use a starting point. It will negatively offset the points of a polygon by a given distance and add the appropriate handles.

function roundPath(path,radius) {
    var segments = path.segments.slice(0);
    path.segments = [];
    for(var i = 0, l = segments.length; i < l; i++) {
        var curPoint = segments[i].point;
        var nextPoint = segments[i + 1 == l ? 0 : i + 1].point;
        var prevPoint = segments[i - 1 < 0 ? segments.length - 1 : i - 1].point;
        var nextDelta = curPoint - nextPoint;
        var prevDelta = curPoint - prevPoint;
        nextDelta.length = radius;
        prevDelta.length = radius;
        path.add({
            point:curPoint - prevDelta,
            handleOut: prevDelta/2
        });
        path.add({
            point:curPoint - nextDelta,
            handleIn: nextDelta/2
        });
    }
    path.closed = true;
    return path;
}

Here it is in action.

like image 192
Alex Blackwood Avatar answered Oct 26 '25 16:10

Alex Blackwood


I was looking for an exact implementation, as described here: http://shanfanhuang.com/everything/2015/10/27/rounding-corners

My implementation works as follows:

  • curPoint is the corner, prevPoint and nextPoint as above
  • nextNorm and prevNorm are the normalized versions of the points
  • angle is the angle of the corner, derived from the dot product
  • delta is the distance from the corner points to where the control points need to be inserted, this is derived from a right triangle formed by the control point, curPoint and the center of the corner arc. The corner is a half angle, and the side opposing that corner is the radius
  • prevDelta and nextDelta are the new endpoints of the sides, between those an arc is inserted
  • through is a point halfway on the arc, found by getting the hypotenuse of the above triangle and subtracting the radius.

    var segments = path.segments.slice(0);
    path.segments = [];
    for(var i = 0, l = segments.length; i < l; i++) {
        var curPoint = segments[i].point;
        var nextPoint = segments[i + 1 == l ? 0 : i + 1].point;
        var prevPoint = segments[i - 1 < 0 ? segments.length - 1 : i - 1].point;
    
        var nextNorm = (curPoint - nextPoint).normalize();
        var prevNorm = (curPoint - prevPoint).normalize();
    
        var angle = Math.acos(nextNorm.dot(prevNorm));
    
        var delta = radius/Math.tan(angle/2);
        var prevDelta = prevNorm.normalize(delta);
        var nextDelta = nextNorm.normalize(delta);
    
        var through = curPoint - (prevNorm + nextNorm).normalize(Math.sqrt(delta*delta + radius*radius) - radius);
    
        path.add(curPoint - prevDelta);
        path.arcTo(through, curPoint - nextDelta);
    }
    
like image 21
rikkertkoppes Avatar answered Oct 26 '25 17:10

rikkertkoppes