Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I fill color between 4 lines when it connect in canvas?

I am trying to fill color between lines when it connects in ionic. I want to fill color between line when four-line touch each other. For that, I created a canvas demo using a touch event. Please help to solve my issue.

We have 4 lines in the canvas box and we will drag them and connect each line and give a shape line box. that means our line is connected now fill color between line so the box is filled up with color.

html file:

 <canvas #canvasDraw width="300" height="300" (touchstart)="handleTouchStart($event)"
        (touchmove)="handleTouchmove($event)" 
        (touchend)="handleTouchEnd($event)">
        You need a browser that supports HTML5!
 </canvas>

ts file:

import { Component, ElementRef, ViewChild } from '@angular/core';

@Component({
    selector: 'app-home',
    templateUrl: 'home.page.html',
    styleUrls: ['home.page.scss'],
})
export class HomePage {
    @ViewChild('canvasDraw', { static: false }) canvas: ElementRef;

    canvasElement: any;
    lines: any[];
    isDown: boolean = false;
    startX: number;
    startY: number;
    nearest: any;
    offsetX: any;
    offsetY: any;

    constructor() {
    }

    ngAfterViewInit() {
        this.canvasElement = this.canvas.nativeElement;
        this.lines = [];
        this.lines.push({ x0: 75, y0: 25, x1: 125, y1: 25 });
        this.lines.push({ x0: 75, y0: 100, x1: 125, y1: 100 });
        this.lines.push({ x0: 50, y0: 35, x1: 50, y1: 85 });
        this.lines.push({ x0: 150, y0: 35, x1: 150, y1: 85 });
        this.draw();
        //this.reOffset();
        requestAnimationFrame(() => {
            this.reOffset()
        })
    }

    reOffset() {
        let BB = this.canvasElement.getBoundingClientRect();
        this.offsetX = BB.left;
        this.offsetY = BB.top;
    }

    // select the this.nearest line to the mouse
    closestLine(mx, my) {
        let dist = 100000000;
        let index, pt;
        for (let i = 0; i < this.lines.length; i++) {
            //
            let xy = this.closestXY(this.lines[i], mx, my);
            //
            let dx = mx - xy.x;
            let dy = my - xy.y;
            let thisDist = dx * dx + dy * dy;
            if (thisDist < dist) {
                dist = thisDist;
                pt = xy;
                index = i;
            }
        }
        let line = this.lines[index];
        return ({ pt: pt, line: line, originalLine: { x0: line.x0, y0: line.y0, x1: line.x1, y1: line.y1 } });
    }

    // linear interpolation -- needed in setClosestLine()
    lerp(a, b, x) {
        return (a + x * (b - a));
    }

    // find closest XY on line to mouse XY
    closestXY(line, mx, my) {
        let x0 = line.x0;
        let y0 = line.y0;
        let x1 = line.x1;
        let y1 = line.y1;
        let dx = x1 - x0;
        let dy = y1 - y0;
        let t = ((mx - x0) * dx + (my - y0) * dy) / (dx * dx + dy * dy);
        t = Math.max(0, Math.min(1, t));
        let x = this.lerp(x0, x1, t);
        let y = this.lerp(y0, y1, t);
        return ({ x: x, y: y });
    }

    // draw the scene
    draw() {
        let ctx = this.canvasElement.getContext('2d');
        let cw = this.canvasElement.width;
        let ch = this.canvasElement.height;
        ctx.clearRect(0, 0, cw, ch);
        // draw all lines at their current positions
        for (let i = 0; i < this.lines.length; i++) {
            this.drawLine(this.lines[i], 'black');
        }
        // draw markers if a line is being dragged
        if (this.nearest) {
            // point on line this.nearest to mouse
            ctx.beginPath();
            ctx.arc(this.nearest.pt.x, this.nearest.pt.y, 5, 0, Math.PI * 2);
            ctx.strokeStyle = 'red';
            ctx.stroke();
            // marker for original line before dragging
            this.drawLine(this.nearest.originalLine, 'red');
            // hightlight the line as its dragged
            this.drawLine(this.nearest.line, 'red');
        }        
    }

    drawLine(line, color) {
        let ctx = this.canvasElement.getContext('2d');
        ctx.beginPath();
        ctx.moveTo(line.x0, line.y0);
        ctx.lineTo(line.x1, line.y1);
        ctx.strokeStyle = color;
        ctx.stroke();
    }

    handleTouchStart(e) {
        // tell the browser we're handling this event
        let tch = e.touches[0];
        // tch.preventDefault();
        // tch.stopPropagation();
        // mouse position
        this.startX = tch.clientX - this.offsetX;
        this.startY = tch.clientY - this.offsetY;
        // find this.nearest line to mouse
        this.nearest = this.closestLine(this.startX, this.startY);
        this.draw();
        // set dragging flag
        this.isDown = true;
    }

    handleTouchEnd(e) {
        // tell the browser we're handling this event
        let tch = e.touches[0];
        // tch.preventDefault();
        // tch.stopPropagation();
        // clear dragging flag
        this.isDown = false;
        this.nearest = null;
        this.draw();
    }

    handleTouchmove(e) {
        if (!this.isDown) { return; }
        // tell the browser we're handling this event
        let tch = e.touches[0];
        // tch.preventDefault();
        // tch.stopPropagation();
        // mouse position
        const mouseX = tch.clientX - this.offsetX;
        const mouseY = tch.clientY - this.offsetY;
        // calc how far mouse has moved since last mousemove event
        let dx = mouseX - this.startX;
        let dy = mouseY - this.startY;
        this.startX = mouseX;
        this.startY = mouseY;
        // change this.nearest line vertices by distance moved
        let line = this.nearest.line;
        line.x0 += dx;
        line.y0 += dy;
        line.x1 += dx;
        line.y1 += dy;
        // redraw
        this.draw();

        let ctx = this.canvasElement.getContext('2d');
        ctx.beginPath();
        ctx.rect(line.x0, line.y0, line.x1, line.y1);
        ctx.fillStyle = "red";
        ctx.fill();
    }

}

enter image description here

How to fill color when four line touch or connect?

like image 408
user9088454 Avatar asked Oct 27 '20 12:10

user9088454


People also ask

Which function is used to fill colors on canvas?

Description. To fill an HTML5 Canvas shape with a solid color, we can set the fillStyle property to a color string such as blue, a hex value such as #0000FF, or an RGB value such as rgb(0,0,255), and then we can use the fill() method to fill the shape.

How do you change the color of a line on a canvas?

To set the color of an HTML5 Canvas line, we can use the strokeStyle property of the canvas context, which can be set to a color string such as red, green, or blue, a hex value such as #FF0000 or #555, or an RGB value such as rgb(255, 0, 0).

How do I fill a rectangle with canvas color?

The fillRect() method draws a "filled" rectangle. The default color of the fill is black. Tip: Use the fillStyle property to set a color, gradient, or pattern used to fill the drawing.

How do you fill a circle in canvas?

The arc() method creates an arc/curve (used to create circles, or parts of circles). Tip: To create a circle with arc(): Set start angle to 0 and end angle to 2*Math. PI. Tip: Use the stroke() or the fill() method to actually draw the arc on the canvas.


Video Answer


1 Answers

How to get a pixel's color

Basically, when you do your touch event, pixels are changing color. You can find out which pixel(s) are/were affected from the event. Having the pixel(s) as input you can find out what color a pixel has: https://jsfiddle.net/ourcodeworld/8swevoxo/

var canvas = document.getElementById("canvas");

function getPosition(obj) {
    var curleft = 0, curtop = 0;
    if (obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return { x: curleft, y: curtop };
    }
    return undefined;
}

function rgbToHex(r, g, b) {
    if (r > 255 || g > 255 || b > 255)
        throw "Invalid color component";
    return ((r << 16) | (g << 8) | b).toString(16);
}

function drawImageFromWebUrl(sourceurl){
      var img = new Image();

      img.addEventListener("load", function () {
          // The image can be drawn from any source
          canvas.getContext("2d").drawImage(img, 0, 0, img.width,    img.height, 0, 0, canvas.width, canvas.height);
          
      });

      img.setAttribute("src", sourceurl);
}
// Draw a base64 image because this is a fiddle, and if we try with an image from URL we'll get tainted canvas error
// Read more about here : http://ourcodeworld.com/articles/read/182/the-canvas-has-been-tainted-by-cross-origin-data-and-tainted-canvases-may-not-be-exported
drawImageFromWebUrl("data:image/gif;base64,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");

canvas.addEventListener("mousemove",function(e){
      var pos = getPosition(this);
    var x = e.pageX - pos.x;
    var y = e.pageY - pos.y;
    var coord = "x=" + x + ", y=" + y;
    var c = this.getContext('2d');
    var p = c.getImageData(x, y, 1, 1).data; 
        
    // If transparency on the image
    if((p[0] == 0) && (p[1] == 0) && (p[2] == 0) && (p[3] == 0)){
                coord += " (Transparent color detected, cannot be converted to HEX)";
    }
    
    var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
    
    document.getElementById("status").innerHTML = coord;
    document.getElementById("color").style.backgroundColor = hex;
},false);
<canvas id="canvas" width="150" height="150"></canvas>
<div id="status"></div><br>
<div id="color" style="width:30px;height:30px;"></div>
<p>
Move the mouse over the BUS !
</p>

This code was not written by me.

Understanding the problem

Now that we know how to get the color pixel by pixel, we need to convert our question to something that is equivalent, but it's easier to compute. I would define the problem as follows:

Traverse pixels starting from a given point in a continuous manner in order to find out whether a cycle can be formed, which contains pixels of a certain (default) color inside the boundary, which should change their color to some fill color.

How to solve it

  • Start a cycle of pixel traversing starting from a given point and always changing coordinate values in each step to a point neighboring the current point or some point visited earlier in the loop
  • Always store the coordinates of the currently visited point so if you would visit the same point, you just ignore it on the second time
  • Use a data structure, like a stack to store all neighbors to visit, but not yet visited, put in the stack all neighbors of each point you visit (unless the point was already visited)
  • If you ever arrive back to the starting point from a point which was not already visited, then register that this is a cycle
  • Always keep track of boundary points
  • When you found out that it's a cycle and you know where it is located, traverse each point in the region and if it has a default color, check whether there is a continuous line by which you could "leave" the cycle by visiting only neighbors of default color, pretty similarly as you have checked whether it is a cycle. If not, then paint the pixel (and all its neighbors of similar color) to the color you need
like image 87
Lajos Arpad Avatar answered Nov 14 '22 01:11

Lajos Arpad