Monday, 10 April 2017

Draw lines and circles and fill the hole shape in JS

I read the contents of a dxf-file (only 2D) in NodeJS with a dxf parser (https://github.com/bjnortier/dxf) and then i get an array with the following output:

  • LINE: start.x, start.y, end.x, end.y
  • CIRCLE: x, y, radius
  • ARC: x, y ,radius, startAngle, endAngle

I wrote 3 functions based on the Bresenham-Algorithm to set the needed pixels in an array, which i want to use later to draw an canvas.

The input-parameters are

  1. data: the denorm dxf data in an array
  2. coordSystem: the array where to set the needed pixels
module.exports: {

    processLINE: function(data, coordSystem) {

        var setPixel = function(x, y) {
            x = Math.ceil(x);
            y = Math.ceil(y);

            coordSystem[x][y] = 1;
        }

        var line = function(x0, y0, x1, y1) {
           var dx = Math.abs(x1-x0);
           var dy = Math.abs(y1-y0);
           var sx = (x0 < x1) ? 1 : -1;
           var sy = (y0 < y1) ? 1 : -1;
           var err = dx-dy;
           var e2;

           while(true) {
             setPixel(x0,y0);
             if ((x0===x1) && (y0===y1)) break;
             e2 = 2*err;
             if (e2 >-dy){ err -= dy; x0  += sx; }
             if (e2 < dx){ err += dx; y0  += sy; }
           }
        }

        line(Math.ceil(data.start.x), Math.ceil(data.start.y), Math.ceil(data.end.x), Math.ceil(data.end.y))

        return coordSystem;


    },

    processCIRCLE: function(data, coordSystem) {

        var setPixel = function(x, y) {

            x = Math.ceil(x);
            y = Math.ceil(y);

            coordSystem[x][y] = 1;
        }

        var createCircle = function(x0, y0, radius)
        {
            var f = 1 - radius;
            var ddF_x = 0;
            var ddF_y = -2 * radius;
            var x = 0;
            var y = radius;

            setPixel(x0, y0 + radius);
            setPixel(x0, y0 - radius);
            setPixel(x0 + radius, y0);
            setPixel(x0 - radius, y0);

            while(x < y) 
            {
              if(f >= 0) 
              {
                y--;
                ddF_y += 2;
                f += ddF_y;
              }
              x++;
              ddF_x += 2;
              f += ddF_x + 1;

              setPixel(x0 + x, y0 + y);
              setPixel(x0 - x, y0 + y);
              setPixel(x0 + x, y0 - y);
              setPixel(x0 - x, y0 - y);
              setPixel(x0 + y, y0 + x);
              setPixel(x0 - y, y0 + x);
              setPixel(x0 + y, y0 - x);
              setPixel(x0 - y, y0 - x);
            }
        }

        createCircle(data.x, data.y, data.r);

        return coordSystem;

    },

    processARC: function(data, coordSystem) {

        var setPixel = function(x, y, coordinates) {

            x = Math.ceil(x);
            y = Math.ceil(y);

            coordSystem[x][y] = 1;
        }

        var createPartialcircle = function()
        {

            startAngle = data.startAngle*180/Math.PI;
            endAngle = data.endAngle*180/Math.PI;

            if(startAngle>endAngle) {
                for (var i=startAngle; i>endAngle; i--) {
                    var radians = i * Math.PI / 180;
                    var px = data.x - data.r * Math.cos(radians);
                    var py = data.y - data.r * Math.sin(radians);
                    setPixel(px, py, coordinates);
                }
            } else {
                for (var i=startAngle; i<endAngle; i++) {
                    var radians = i * Math.PI / 180;
                    var px = data.x + data.r * Math.cos(radians);
                    var py = data.y + data.r * Math.sin(radians);
                    setPixel(px, py, coordinates);
                }
            }
        }

        createPartialcircle(data.x, data.y, data.r);

        return coordSystem;
    }
}

With this i get the following shape: As you can see it works, but there are some "holes" and because of this my last function which should fill the hole shape (scan-line-algorithm), doesn't work well...

shape

Here is how i fill the shape I took this code from HERE and wrote it in JavaScript-Style.

function scanLineFill(config, data, x, y, fillColor) {

            function getPixel(x,y) {
                return data[x][y];
            }

            function setPixel(x,y) {
                data[x][y] = fillColor;
            }

            // Config
            var nMinX = 0;
            var nMinY = 0;
            var nMaxX = config.maxValues.x;
            var nMaxY = config.maxValues.y;
            var seedColor = getPixel(x,y);


            function lineFill(x1, x2, y) {

                var xL,xR;
                if( y < nMinY || nMaxY < y || x1 < nMinX || nMaxX < x1 || x2 < nMinX || nMaxX < x2 )
                    return;
                for( xL = x1; xL >= nMinX; --xL ) { // scan left
                    if( getPixel(xL,y) !== seedColor )
                        break;
                    setPixel(xL,y);
                }
                if( xL < x1 ) {
                    lineFill(xL, x1, y-1); // fill child
                    lineFill(xL, x1, y+1); // fill child
                    ++x1;
                }
                for( xR = x2;  xR <= nMaxX; ++xR ) { // scan right
                    console.log('FOR: xR --> ', xR)
                    if( getPixel(xR,y) !== seedColor )
                        break;
                    setPixel(xR,y);
                }
                if(  xR > x2 ) {
                    lineFill(x2, xR, y-1); // fill child
                    lineFill(x2, xR, y+1); // fill child
                    --x2;
                }
                for( xR = x1; xR <= x2 && xR <= nMaxX; ++xR ) {  // scan betweens
                    if( getPixel(xR,y) === seedColor )
                        setPixel(xR,y);
                    else {
                        if( x1 < xR ) {
                            // fill child
                            lineFill(x1, xR-1, y-1);
                            // fill child
                            lineFill(x1, xR-1, y+1);
                            x1 = xR;
                        }
                        // Note: This function still works if this step is removed.
                        for( ; xR <= x2 && xR <= nMaxX; ++xR) { // skip over border
                            if( getPixel(xR,y) === seedColor ) {
                                x1 = xR--;
                                break;
                            }
                        }
                    }
                }

            }


            if( fillColor !== seedColor ) {
                lineFill(x, x, y);
            }

            return data;

        }

And the result is this:

shapeFilled

I think if the shape has no holes, the fill-function would fill the shape correct. But how can i achieve this?



via Sebastian Ortmann

No comments:

Post a Comment