svg: generate 'outline path'

前端 未结 3 724
别跟我提以往
别跟我提以往 2020-12-05 15:23

I have a set of coordinates that I turn into an svg path (using cubic beziers to make it smooth). When I apply a certain stroke width, I get the following result (the blue d

相关标签:
3条回答
  • 2020-12-05 15:51

    This is very demanding. Honestly I doubt that you will able to write a sorce code that will do that, whithout a lib that will do it for you.

    This technic is sometimes called "buffer". Like create a buffer around an object.

    If you can use Programming lang C then you should look at LEDA library. In java I dont know anything, maybe the GeoTools lib.

    0 讨论(0)
  • 2020-12-05 15:53

    I'm not sure whether this solves your problem. It depends on what you want to do with the outline path.

    PostScript has functionality for computing an outline for stroked paths which, when filling it, will produce the same visual output as stroking the original path. However, the resulting path data might be less elegant than you'd expect.

    The following PostScript program (let's call it path2outlines.ps turns a PostScript path into a "fillable" SVG path:

    %!
    % First, we're converting to outlines.
    strokepath
    
    % This is just a string buffer
    /S 99 string def
    
    % This defines a procedure for printing coordinates to stdout
    /printCoordinates {     % coord* number command
      print                 % coord* number
      array astore          % array
      {                     % coord
        ( )print            % coord
        //S cvs             % string
        print               % 
      }forall
      (\n)print
    } bind def
    
    % This iterates over the path segments and prints the SVG path data.
    {2(M) printCoordinates}
    {2(L) printCoordinates}
    {6(C) printCoordinates}
    {(Z\n)print}
    pathforall
    
    quit
    

    You have to feed it data files like this (% starts a comment). Let's call this data.ps:

    %!
    % First, set up the graphics state parameters
    % Equivalent to stroke-width="10"
    10 setlinewidth 
    
    % Equivalent to stroke-linejoin="round" (0 = miter, 1 = round, 2 = bevel)
    1 setlinejoin
    
    % Now, we're defining the path.
    % Use postfix notation, i.e. first coordinates, then command.
    % m/M = moveto
    % l = rlineto
    % L = lineto
    % c = rcurveto
    % C = curveto
    
    238 50 moveto
    5.67569 -1.01351 11.8327 -3.8229 20.92029 -2.14724 rcurveto
    8.68106 0.69732 14.21173 4.90255 18.07971 7.14724 rcurveto
    6.23697 3.61945 13.47556 9.5931 15 18 rcurveto
    1.07056 5.90372 1.17343 10.97649 -4 16 rcurveto
    -6.76816 6.57204 -19.45392 9.57738 -25.69687 10.59046 rcurveto
    -3.94836 0.64074 4.73492 3.29883 10.69687 5.40954 rcurveto
    8.05417 2.85142 15 8 21 14 rcurveto
    6 6 5.26578 10.94739 5.26578 17.03015 rcurveto
    -2.4541 7.30975 -4.23343 11.08675 -11.26578 12.96985 rcurveto
    -3.98279 1.0665 -11.92578 3.49756 -17 4 rcurveto
    -8.95618 0.88684 -15.80411 2.97838 -26 0 rcurveto
    -9.19197 -3.44464 rlineto
    

    Depending on the platform you're using, you can use Ghostscript to invoke it somewhat similar to:

    gs -q data.ps path2outlines.ps > outlinePath.txt
    

    Give it a try, but I'm not sure whether you will be satisfied. The complexity of the output might give a hint about the actual complexity of the problem. Especially self-intersecting paths are a problem.

    Edit (speaking of problems): I believe that in general it's mathematically not possible to create a Bézier curve that is perfectly "parallel" to a given Bézier curve.

    0 讨论(0)
  • 2020-12-05 15:57

    I created the svgContour function that is thought for scenarios similar to this,
    the resulting contour offset is not related to the stroke-width value and must be set as a parameter to the function.

    At the moment it will find one offset side at a time, but running it once per sides you can solve this issue.

    TLDR
    Actually through svgContour you can find the contour of any svg shapes, currently it doesn't support fill-modes but one of the next goals is to implement that. it relies on getPathData() to get the pathData of any SVGGeometryElement, then this data goes through three phases:

    • redrawSteepCurve
      Premise: drawing a beizer-curve parallel to another isn't obtained just offsetting the points/control-points of the curve unless the curve is sufficiently flat (in this case the visual rendering will be fine); this method takes a SVGPathData and in case it finds a steep curve it splits it up until it's flat enough (return a visually equivalent SVGPathData).

    • contourPathData
      In this stage the pathData is dissambled in points, points are connected in segments, each segment is offsetted, an intersection point is then found for each contiguous segment ( what we get back is a list of offsetted points).

    • drawLine
      This phase places the points from step 2 in the pathData coming from step 1, and finally draws the contour.

    An example:

    const path = document.querySelector('path')
    
    svgContour(path, 5)
    svg {
      width: 100vw;
      height: 100vh
    }
    <script src="https://cdn.rawgit.com/fracalo/svg-contour/master/dist/svg-contour.js"></script>
    
    <svg viewBox='300 0 100 200'>
      <g>
        <title>number 3</title>
        <path d="m238,50c5.67569,-1.01351 11.8327,-3.8229 20.92029,-2.14724c8.68106,0.69732 14.21173,4.90255 18.07971,7.14724c6.23697,3.61945 13.47556,9.5931 15,18c1.07056,5.90372 1.17343,10.97649 -4,16c-6.76816,6.57204 -19.45392,9.57738 -25.69687,10.59046c-3.94836,0.64074 4.73492,3.29883 10.69687,5.40954c8.05417,2.85142 15,8 21,14c6,6 5.26578,10.94739 5.26578,17.03015c-2.4541,7.30975 -4.23343,11.08675 -11.26578,12.96985c-3.98279,1.0665 -11.92578,3.49756 -17,4c-8.95618,0.88684 -15.80411,2.97838 -26,0l-9.19197,-3.44464"
        id="svg_1" opacity="0.5" stroke-width="10" stroke-linejoin="round" stroke="#000000" fill="none" />
      </g>
    </svg>

    the contour is traced !

    0 讨论(0)
提交回复
热议问题