diff --git a/src/canvasManager.js b/src/canvasManager.js index d90c715..873a784 100644 --- a/src/canvasManager.js +++ b/src/canvasManager.js @@ -1,16 +1,8 @@ // js/canvasManager.js -let backgroundCanvas; -let lastShapesArray = []; +import { Circle } from './shapes/Circle'; +import { Triangle } from './shapes/Triangle'; -const SVG_NS = "http://www.w3.org/2000/svg"; - -/** - * Renders all shapes into an SVG container - * @param {SVGElement} container - * @param {Array} shapesArray - * @param {Array} patches - */ export function createOrUpdateShapes(container, shapesArray, patches = []) { if (!container || !(container instanceof SVGElement)) { return; @@ -20,7 +12,6 @@ export function createOrUpdateShapes(container, shapesArray, patches = []) { const patchArray = patches?.patches || patches || []; if (!Array.isArray(patchArray)) { console.warn("Expected patches to be an array, got:", typeof patchArray, patchArray); - // Fall back to full redraw patches = []; } @@ -32,18 +23,10 @@ export function createOrUpdateShapes(container, shapesArray, patches = []) { } // Draw all shapes - shapesArray.forEach(shape => { - let element; - if (shape.type === "circle") { - element = document.createElementNS(SVG_NS, "circle"); - element.id = shape.id; - updateCircleAttributes(element, shape); - } else if (shape.type === "triangle") { - element = document.createElementNS(SVG_NS, "polygon"); - element.id = shape.id; - updateTriangleAttributes(element, shape); - } - if (element) { + shapesArray.forEach(shapeData => { + const shape = createShapeInstance(shapeData); + if (shape) { + const element = shape.createSVGElement(); container.appendChild(element); } }); @@ -52,38 +35,25 @@ export function createOrUpdateShapes(container, shapesArray, patches = []) { // Process patches patchArray.forEach(patch => { - // Check if this patch affects the shapes array if (patch.path[0] === 'shapes') { if (patch.action === 'put') { const shapeIndex = patch.path[1]; - const shape = shapesArray[shapeIndex]; + const shapeData = shapesArray[shapeIndex]; + if (!shapeData) return; + + const shape = createShapeInstance(shapeData); if (!shape) return; - let element = document.getElementById(shape.id); + let element = document.getElementById(shapeData.id); if (!element) { // Create new element - if (shape.type === "circle") { - element = document.createElementNS(SVG_NS, "circle"); - element.id = shape.id; - updateCircleAttributes(element, shape); - } else if (shape.type === "triangle") { - element = document.createElementNS(SVG_NS, "polygon"); - element.id = shape.id; - updateTriangleAttributes(element, shape); - } - if (element) { - container.appendChild(element); - } + element = shape.createSVGElement(); + container.appendChild(element); } else { // Update existing element - if (shape.type === "circle") { - updateCircleAttributes(element, shape); - } else if (shape.type === "triangle") { - updateTriangleAttributes(element, shape); - } + shape.updateAttributes(element); } } else if (patch.action === 'del') { - // Shape removed const element = document.getElementById(patch.path[1]); if (element) { container.removeChild(element); @@ -93,50 +63,15 @@ export function createOrUpdateShapes(container, shapesArray, patches = []) { }); } -function updateSpecificAttribute(element, shape, path) { - if (shape.type === "circle") { - switch (path[path.length - 1]) { - case 'x': - element.setAttributeNS(null, "cx", shape.x); - break; - case 'y': - element.setAttributeNS(null, "cy", shape.y); - break; - case 'radius': - element.setAttributeNS(null, "r", shape.radius); - break; - case 'color': - element.setAttributeNS(null, "fill", shape.color || "black"); - break; - } - } else if (shape.type === "triangle") { - if (path.includes('coordinates')) { - const points = shape.coordinates - .map(p => `${p.x},${p.y}`) - .join(" "); - element.setAttributeNS(null, "points", points); - } else if (path.includes('color')) { - element.setAttributeNS(null, "fill", shape.color || "black"); - } +function createShapeInstance(shapeData) { + switch (shapeData.type) { + case "circle": + return new Circle(shapeData.id, shapeData.x, shapeData.y, shapeData.radius, shapeData.color); + case "triangle": + return new Triangle(shapeData.id, shapeData.coordinates, shapeData.color); + default: + console.warn(`Unknown shape type: ${shapeData.type}`); + return null; } } -function updateCircleAttributes(element, shape) { - element.setAttributeNS(null, "cx", shape.x); - element.setAttributeNS(null, "cy", shape.y); - element.setAttributeNS(null, "r", shape.radius); - element.setAttributeNS(null, "fill", shape.color || "black"); - element.setAttributeNS(null, "stroke", "black"); - element.setAttributeNS(null, "stroke-width", "1"); -} - -function updateTriangleAttributes(element, shape) { - const points = shape.coordinates - .map(p => `${p.x},${p.y}`) - .join(" "); - element.setAttributeNS(null, "points", points); - element.setAttributeNS(null, "fill", shape.color || "black"); - element.setAttributeNS(null, "stroke", "black"); - element.setAttributeNS(null, "stroke-width", "1"); -} - diff --git a/src/docManager.js b/src/docManager.js index a57a3a2..ac08b51 100644 --- a/src/docManager.js +++ b/src/docManager.js @@ -21,12 +21,12 @@ export async function initRepo() { // Check for existing docId in URL hash const rootDocUrl = document.location.hash.substring(1) - + if (rootDocUrl && isValidAutomergeUrl(rootDocUrl)) { handle = repo.find(rootDocUrl) // Wait for handle to be ready await handle.whenReady() - + // Check and upgrade schema if needed handle.change(doc => { if (!doc.schemaVersion) { @@ -83,7 +83,7 @@ export function addTriangle(x1, y1, x2, y2, x3, y3) { coordinates: [ { x: x1, y: y1 }, { x: x2, y: y2 }, - { x: x3, y: y3 }, + { x: x3, y: y3 } ], color: "blue" }); @@ -102,8 +102,26 @@ export function moveShape(shapeId, newX, newY) { // TODO faster implementation than for loop for (let i = 0; i < doc.shapes.length; i++) { if (doc.shapes[i].id === Number(shapeId)) { - doc.shapes[i].x = newX; - doc.shapes[i].y = newY; + if (doc.shapes[i].type === "circle") { + doc.shapes[i].x = newX; + doc.shapes[i].y = newY; + } else if (doc.shapes[i].type === "triangle") { + // Calculate the current center + const center = { + x: doc.shapes[i].coordinates.reduce((sum, p) => sum + p.x, 0) / 3, + y: doc.shapes[i].coordinates.reduce((sum, p) => sum + p.y, 0) / 3 + }; + // Calculate the movement delta + const dx = newX - center.x; + const dy = newY - center.y; + + // Move all points + doc.shapes[i].coordinates = doc.shapes[i].coordinates.map(p => ({ + x: p.x + dx, + y: p.y + dy + })); + + } break; } } diff --git a/src/shapes/Circle.js b/src/shapes/Circle.js new file mode 100644 index 0000000..3dd7b18 --- /dev/null +++ b/src/shapes/Circle.js @@ -0,0 +1,22 @@ +import { Shape } from './Shape'; + +export class Circle extends Shape { + constructor(id, x, y, radius, color = "red") { + super(id, "circle", color); + this.x = x; + this.y = y; + this.radius = radius; + } + + getSVGElementType() { + return "circle"; + } + + updateAttributes(element) { + element.setAttributeNS(null, "cx", this.x); + element.setAttributeNS(null, "cy", this.y); + element.setAttributeNS(null, "r", this.radius); + element.setAttributeNS(null, "fill", this.color); + } + +} \ No newline at end of file diff --git a/src/shapes/Shape.js b/src/shapes/Shape.js new file mode 100644 index 0000000..5e96498 --- /dev/null +++ b/src/shapes/Shape.js @@ -0,0 +1,29 @@ +const SVG_NS = "http://www.w3.org/2000/svg"; + +export class Shape { + constructor(id, type, color = "black") { + this.id = id; + this.type = type; + this.color = color; + } + + createSVGElement() { + const element = document.createElementNS(SVG_NS, this.getSVGElementType()); + element.id = this.id; + this.updateAttributes(element); + return element; + } + + // Abstract methods + getSVGElementType() { + throw new Error("Must be implemented by derived class"); + } + // this update also takes care of movements. The actual movement is done in documentManager.js + updateAttributes(element) { + throw new Error("Must be implemented by derived class"); + } + + move(newX, newY) { + throw new Error("Must be implemented by derived class"); + } +} \ No newline at end of file diff --git a/src/shapes/Triangle.js b/src/shapes/Triangle.js new file mode 100644 index 0000000..2c81311 --- /dev/null +++ b/src/shapes/Triangle.js @@ -0,0 +1,22 @@ +import { Shape } from './Shape'; + +export class Triangle extends Shape { + constructor(id, coordinates, color = "blue") { + super(id, "triangle", color); + this.coordinates = coordinates; + } + + getSVGElementType() { + return "polygon"; + } + + updateAttributes(element) { + const points = this.coordinates + .map(p => `${p.x},${p.y}`) + .join(" "); + element.setAttributeNS(null, "points", points); + element.setAttributeNS(null, "fill", this.color); + } + + +} \ No newline at end of file