From c74c5d8ff2c438bdedeb9bc1cee59dc9b5267dac Mon Sep 17 00:00:00 2001 From: saschamuellercerpro Date: Mon, 8 Jul 2024 18:01:37 +0200 Subject: [PATCH 1/2] done --- src/components/Board/Board.tsx | 103 +++++++++++++++++---------------- src/components/Board/types.tsx | 1 + src/fabric/classes.ts | 52 +++++++++++++++++ 3 files changed, 107 insertions(+), 49 deletions(-) create mode 100644 src/fabric/classes.ts diff --git a/src/components/Board/Board.tsx b/src/components/Board/Board.tsx index 8b7c8ef..521fde5 100644 --- a/src/components/Board/Board.tsx +++ b/src/components/Board/Board.tsx @@ -7,6 +7,7 @@ import * as fabricUtils from "../../fabric/utils"; import * as fabricActions from "../../fabric/actions"; import * as fabricTypes from "../../fabric/types"; import parse from "color-parse"; +import { CustomCornerObject } from "../../fabric/classes"; export type BoardProps = { items: CanvasObject[]; @@ -107,18 +108,20 @@ const Board = React.forwardRef( const customObjects = fabricUtils.retrieveObjects(canvas); if (!customObjects) return []; - return customObjects.map((co) => { - const info = getObjectInfo(co, includeContent); - - return { - id: co.name!, - category: "TODO_category", - color: "TODO_color", - value: "TODO_value", - coords: info.coords, - content: info.content, - }; - }); + return customObjects + .filter((el) => el.name) + .map((co) => { + const info = getObjectInfo(co, includeContent); + + return { + id: co.name!, + category: "TODO_category", + color: "TODO_color", + value: "TODO_value", + coords: info.coords, + content: info.content, + }; + }); } return []; }, @@ -206,6 +209,40 @@ const Board = React.forwardRef( }); }; + const addCornerObjectToPolygon = ( + polygon: fabric.Object, + index: number, + ) => { + if (!editor?.canvas) return; + + const cornerObject = new CustomCornerObject({ + number: index + 1, + }); + const bounds = polygon.getBoundingRect(); + + cornerObject.set({ + left: bounds.left + bounds.width, + top: bounds.top + bounds.height, + }); + + editor.canvas.add(cornerObject); + + // Ensure the corner object moves with the polygon + polygon.on("moving", () => { + const newBounds = polygon.getBoundingRect(); + cornerObject.set({ + left: newBounds.left + newBounds.width, + top: newBounds.top + newBounds.height, + }); + editor.canvas.renderAll(); + }); + + // Ensure the corner object is removed when the polygon is removed + polygon.on("removed", () => { + editor.canvas?.remove(cornerObject); + }); + }; + useEffect(() => { const parentCanvasElement = document.getElementById( "react-annotator-canvas", @@ -558,7 +595,7 @@ const Board = React.forwardRef( // Clear all objects from canvas fabricActions.deleteAll(editor?.canvas); - for (const item of items) { + items.forEach((item, index) => { const scaledCoords = item.coords.map((p) => fabricUtils.toScaledCoord({ cInfo: { width: canvas.getWidth(), height: canvas.getHeight() }, @@ -586,43 +623,11 @@ const Board = React.forwardRef( scaledCoords.length === 4, // Is a rectangle ); - // const renderIcon = ( - // ctx: CanvasRenderingContext2D, - // left: number, - // top: number, - // styleOverride: unknown, - // fabricObject: fabric.Object, - // ) => { - // const deleteIcon = - // "data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3E%3Csvg version='1.1' id='Ebene_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='595.275px' height='595.275px' viewBox='200 215 230 470' xml:space='preserve'%3E%3Ccircle style='fill:%23F44336;' cx='299.76' cy='439.067' r='218.516'/%3E%3Cg%3E%3Crect x='267.162' y='307.978' transform='matrix(0.7071 -0.7071 0.7071 0.7071 -222.6202 340.6915)' style='fill:white;' width='65.545' height='262.18'/%3E%3Crect x='266.988' y='308.153' transform='matrix(0.7071 0.7071 -0.7071 0.7071 398.3889 -83.3116)' style='fill:white;' width='65.544' height='262.179'/%3E%3C/g%3E%3C/svg%3E"; - - // const img = document.createElement("img"); - // img.src = deleteIcon; - // const size = 24; - // ctx.save(); - // ctx.translate(left, top); - // if (fabricObject.angle) - // ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle)); - // ctx.drawImage(img, -size / 2, -size / 2, size, size); - // ctx.restore(); - // }; - - // polygon.controls = { - // ...polygon.controls, - // onDelete: new fabric.Control({ - // x: 0.5, - // y: -0.5, - // offsetY: 16, - // cursorStyle: "pointer", - // mouseUpHandler: () => { - // console.log("deleted up!"); - // return true; - // }, - // render: renderIcon, - // }), - // }; canvas.add(polygon); - } + if (item.hasNumberFlag) { + addCornerObjectToPolygon(polygon, index); + } + }); }, [editor?.canvas, imageSize.width, imageSize.height, items, scaleRatio]); const renderObjectHelper = () => { diff --git a/src/components/Board/types.tsx b/src/components/Board/types.tsx index da048b9..1f69424 100644 --- a/src/components/Board/types.tsx +++ b/src/components/Board/types.tsx @@ -11,4 +11,5 @@ export type CanvasObject = { coords: { x: number; y: number }[]; opacity?: number; content?: string; + hasNumberFlag?: boolean; }; diff --git a/src/fabric/classes.ts b/src/fabric/classes.ts new file mode 100644 index 0000000..e3420cf --- /dev/null +++ b/src/fabric/classes.ts @@ -0,0 +1,52 @@ +import { fabric } from "fabric"; + +interface CustomCornerObjectOptions extends fabric.IGroupOptions { + number?: number; + color?: string; +} + +export class CustomCornerObject extends fabric.Group { + constructor(options: CustomCornerObjectOptions = {}) { + const number = + options.number !== undefined ? options.number.toString() : ""; + const size = 5; + const circle = new fabric.Circle({ + radius: size / 2, + fill: "white", + stroke: options.color ?? "green", + strokeWidth: 0.8, + originX: "center", + originY: "center", + left: 1.5, + top: -1, + }); + + const text = new fabric.Text(number, { + fontSize: size / 2, + fontFamily: "Arial", + originX: "center", + originY: "center", + fill: "black", + left: 1.5, + top: -1, + }); + + const pointer = new fabric.Triangle({ + width: size / 3, + height: size / 3, + fill: options.color ?? "green", + stroke: options.color ?? "green", + strokeWidth: 1, + originX: "center", + originY: "bottom", + top: -size / 2, + angle: -45, + }); + + const objects = [circle, text, pointer]; + super(objects, options); + + this.set("selectable", false); + this.set("evented", true); + } +} From cddd8d3aeaef571179b6264855c6d719b1d566cf Mon Sep 17 00:00:00 2001 From: saschamuellercerpro Date: Mon, 8 Jul 2024 18:05:11 +0200 Subject: [PATCH 2/2] fix --- src/components/Board/Board.tsx | 6 +++--- src/components/Board/types.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Board/Board.tsx b/src/components/Board/Board.tsx index 521fde5..61ff508 100644 --- a/src/components/Board/Board.tsx +++ b/src/components/Board/Board.tsx @@ -595,7 +595,7 @@ const Board = React.forwardRef( // Clear all objects from canvas fabricActions.deleteAll(editor?.canvas); - items.forEach((item, index) => { + items.forEach((item) => { const scaledCoords = item.coords.map((p) => fabricUtils.toScaledCoord({ cInfo: { width: canvas.getWidth(), height: canvas.getHeight() }, @@ -624,8 +624,8 @@ const Board = React.forwardRef( ); canvas.add(polygon); - if (item.hasNumberFlag) { - addCornerObjectToPolygon(polygon, index); + if (item.numberFlag) { + addCornerObjectToPolygon(polygon, item.numberFlag); } }); }, [editor?.canvas, imageSize.width, imageSize.height, items, scaleRatio]); diff --git a/src/components/Board/types.tsx b/src/components/Board/types.tsx index 1f69424..90505af 100644 --- a/src/components/Board/types.tsx +++ b/src/components/Board/types.tsx @@ -11,5 +11,5 @@ export type CanvasObject = { coords: { x: number; y: number }[]; opacity?: number; content?: string; - hasNumberFlag?: boolean; + numberFlag?: number; };