kevoj/angular-editor-fabric-js

draw a polygon

jeldev12 opened this issue · 9 comments

Hi Sir, good-day. Is it possible to add or a draw a polygon on top of the image?.

kevoj commented

Hi, it sounds interesting, we will keep this feature in mind for future updates 🙌

Hi Sir , I am using your project and modifying some parts particularly to be able to add and draw polygon to the image that was added on the canvas. I would just like to asks if you have an idea how we can solve the issue of being able to add multiple polygons on the canvas and be able to edit the selected polygon and also be able to delete the selected polygon same as on the shapes like square, circle .etc.

I have a working code here which I was able to connect and create polygon and edit it but the thing is I can't solve the adding of multiple polygons and also editing selected polygon and deleting selected polygon. Maybe you have an expertise in fabric js would really be appreciated. Thank you.


getClickCoords(event: any) {
    if (this.isCanvasDrawn && this.isImageDrawn) {
      this.newPt = {
        x: event.layerX,
        y: event.layerY
      };
      this.points.push(this.newPt);
      
      // Create a new fabric.Polygon object with the updated points
      const newPolygon = this.polygon
      
      // Set the fill color for the new polygon
      newPolygon.fill = this.hexToRGBA(this.selectedColor, 0.5);
      
      // Remove the old polygon (if it exists) and add the new one to the canvas
      if (this.polygon) {
        this.canvas.remove(this.polygon);
      }
      this.canvas.add(newPolygon);
      
      // Update this.polygon to the newPolygon for future reference
      this.polygon = newPolygon;
    }
  }

  public edit() {
    function polygonPositionHandler(dim, finalMatrix, fabricObject) {
      let x =
          fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x,
        y = fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y;
      return fabric.util.transformPoint(
        new fabric.Point(x, y),
        fabric.util.multiplyTransformMatrices(
          fabricObject.canvas.viewportTransform,
          fabricObject.calcTransformMatrix()
        )
      );
    }
    function anchorWrapper(anchorIndex, fn) {
      return function(eventData, transform, x, y) {
        var fabricObject = transform.target,
          absolutePoint = fabric.util.transformPoint(
            new fabric.Point(
              fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x,
              fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y
            ),
            fabricObject.calcTransformMatrix()
          ),
          actionPerformed = fn(eventData, transform, x, y),
          newDim = fabricObject._setPositionDimensions({}),
          polygonBaseSize = fabricObject._getNonTransformedDimensions(),
          newX =
            (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) /
            polygonBaseSize.x,
          newY =
            (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) /
            polygonBaseSize.y;
        fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
        return actionPerformed;
      };
    }
    function actionHandler(eventData, transform, x, y) {
      var polygon = transform.target,
        currentControl = polygon.controls[polygon.__corner],
        mouseLocalPosition = polygon.toLocalPoint(
          new fabric.Point(x, y),
          'center',
          'center'
        ),
        polygonBaseSize = polygon._getNonTransformedDimensions(),
        size = polygon._getTransformedDimensions(0, 0),
        finalPointPosition = {
          x:
            (mouseLocalPosition.x * polygonBaseSize.x) / size.x +
            polygon.pathOffset.x,
          y:
            (mouseLocalPosition.y * polygonBaseSize.y) / size.y +
            polygon.pathOffset.y
        };
      polygon.points[currentControl.pointIndex] = finalPointPosition;
      return true;
    }
    let poly = this.canvas.getObjects()[0];
    this.canvas.setActiveObject(poly);
    poly.edit = !poly.edit;
    if (poly.edit) {
      let lastControl = poly.points.length - 1;
      poly.cornerStyle = 'circle';
      poly.cornerColor = 'red ';
      poly.controls = poly.points.reduce(function(acc, point, index) {
        acc['p' + index] = new fabric['Control']({
          pointIndex: index,
          positionHandler: polygonPositionHandler,
          actionHandler: anchorWrapper(
            index > 0 ? index - 1 : lastControl,
            actionHandler
          ),
          actionName: 'modifyPolygon'
        });
        return acc;
      }, {});
    }

    poly.hasBorders = !poly.edit;
    this.canvas.requestRenderAll();
  }

why are you referencing using:

// Update this.polygon to the newPolygon for future reference
this.polygon = newPolygon;

Current delete function should delete select polygon. and edit polygon (selecting any point on selected polygon should be able to change it). Can you make example stackblitz for better understanding of your issue.

the issue of Edit is:
let poly = this.canvas.getObjects()[0]; // you are selecting 1st object on canvas.
it should be let poly = this.canvas.getActiveObject(); // with this you will be able to Edit selected polygon.

@Saqib92 Hi Sir, good day. So basically, I am creating this app based on Mr. Kevoj angular editor using fabric js. So as you can see on the first screenshot. I have this feature where I can add image on the canvas and then there is a menu on the left side for me to add different shapes like square, circle etc and be able to change the color and the size and also be able to delete the shapes.

FIRST SCREENSHOT
image

SECOND SCREENSHOT
image

Now when it comes to the shapes like square, rectable, triangle and circle I already have made it work and it working fine. My current issue is that the I cant make the polygon work the same as those existing shapes.

What I want to achieve is be able to draw a polygon which now is working. (just the draw). So when I click the polygon item from the menu I should be able to draw a polygon and have a button set the created polygon as completed so that I can click in the menu again and be able to create another polygon. Next is be able to select from the created polygon in the canvas and be able to move, edit or delete it. As you can see on the screenshot I have a progress now where I can draw and edit but not the multiple functionality where in we can create multiple polygon and select one to edit or delete.

THIRD SCREENSHOT
image

This is the stackblitz I created but it does not full work because I am not really familiar with the blitz tool but I have all the code

https://stackblitz.com/edit/angular-ivy-fbiqgw?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.module.ts

and for the dialog where In I uses a color picker here is the code

<color-sketch [width]="200" (onChange)="handleChange($event)"></color-sketch>


export class ColorPickerDialogComponent implements OnInit {

  constructor(private dialogRef: MatDialogRef<ColorPickerDialogComponent>, private sanitizer: DomSanitizer) {}


  
  color: Object;
  hexColor: String;
  rgbaColor: Object;
  @HostBinding('class') headerClass: SafeStyle;
  handleChange($event: ColorEvent) {
    this.color = $event.color;
    this.hexColor = $event.color.hex;
    this.rgbaColor = $event.color.rgb;
    this.headerClass = this.sanitizer.bypassSecurityTrustStyle('background-color:'+ this.hexColor +';');
    this.dialogRef.close(this.hexColor );
  }

  ngOnInit(): void {
  }

}

Thank you for the help and ideas. Much Appreciated.

to your question on what I am referencing using: this.polygon = newPolygon;

is

The issue I am facing that time with the fill color getting darker each time getClickCoords is called is likely because I am adding the same polygon object (this.polygon) to the canvas each time, and then modifying its fill color. This means that each time I add the polygon, it retains its previous fill color, and the new fill color is layered on top of the old one.

To solve this issue, I created a new fabric.Polygon object each time getClickCoords is being called.

let poly = this.canvas.getObjects()[0];

let poly = this.canvas.getActiveObject(); causes the following issue Invalid or undefined 'poly' object.

Your stackblitz link is very messy. Please update it with minimal code i.e only polygon with an image.

Hi Sir, I was able to fix the following issues. Thank you for the idea and help @Saqib92 . Very much appreciated.