Changeset 0c6b92a for imaps-frontend/src/scripts/main/MapBuilder.js
- Timestamp:
- 12/12/24 17:06:06 (5 weeks ago)
- Branches:
- main
- Parents:
- d565449
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
imaps-frontend/src/scripts/main/MapBuilder.js
rd565449 r0c6b92a 2 2 import Konva from "konva"; 3 3 import HttpService from "../net/HttpService.js"; 4 import { zoomStage } from "../util/zoomStage.js"; 4 import {zoomStage} from "../util/zoomStage.js"; 5 import {addEventHandling} from "../util/addEventHandling.js"; 6 import MapNode from "../base/MapNode.js"; 7 import {json} from "react-router-dom"; 8 import log from "eslint-plugin-react/lib/util/log.js"; 9 import ShapeRegistry from "../util/ShapeRegistry.js"; 10 import shapeRegistry from "../util/ShapeRegistry.js"; 11 import triggerMapSave from "../util/triggerMapSave.js"; 5 12 6 13 export class MapBuilder { 7 constructor(containerId) { 8 this.container = document.getElementById(containerId); 9 this.stage = new Konva.Stage({ 10 container: containerId, 11 width: this.container.clientWidth, 12 height: this.container.clientHeight, 13 }); 14 15 // TODO AKO DRAGNIT NEKOJ OD POCETOK NA STAGE POZICIIVE KE SA ZEZNAT 16 // TODO jwt vo cookie 17 // TODO placed shape i mouseMoveHandler da ne callback ( da ne vrakjat funkcija) 18 19 this.gridLayer = new Konva.Layer(); 20 this.mainLayer = new Konva.Layer(); 21 this.dragLayer = new Konva.Layer(); 22 this.infoPinLayer = new Konva.Layer(); 23 this.textLayer = new Konva.Layer(); 24 this.gridLayer.listening(false); 25 26 this.originalWidth = this.container.clientWidth; 27 this.originalHeight = this.container.clientHeight; 28 29 this.shapes = []; 30 this.blockSize = 10; 31 this.efficientDrawingMode = false; 32 this.roomTypes = []; 33 34 this.gridLine = new Konva.Line({ 35 points: [], 36 stroke: "grey", 37 strokeWidth: 1, 38 opacity: 0.3, 39 }); 40 41 this.gridLine.cache(); 42 43 this.mainTransformer = new Konva.Transformer({ 44 centeredScaling: false, 45 rotationSnaps: [0, 90, 180, 270], 46 anchorSize: 5, 47 padding: 2, 48 anchorFill: "#ef7539", 49 borderStroke: "black", 50 anchorStroke: "black", 51 cornerRadius: 20, 52 anchorCornerRadius: 10, 53 anchorDragBoundFunc: this.transformerSnapFunc(), 54 }); 55 56 this.selectionRectangle = new Konva.Rect({ 57 fill: "rgba(200,0,255,0.5)", 58 visible: false, 59 listening: false, 60 zIndex: 100, 61 }); 62 63 this.x1 = 0; 64 this.y1 = 0; 65 this.x2 = 0; 66 this.y2 = 0; 67 68 this.selecting = false; 69 70 this.initialize(); 71 } 72 73 initialize() { 74 this.drawGrid(); 75 this.mainLayer.add(this.mainTransformer); 76 this.mainLayer.add(this.selectionRectangle); 77 this.stage.add(this.gridLayer); 78 this.stage.add(this.dragLayer); 79 this.stage.add(this.mainLayer); 80 this.stage.add(this.infoPinLayer); 81 this.stage.add(this.textLayer); 82 this.setupEventListeners(); 83 } 84 85 setupEventListeners() { 86 document.getElementById("shapeOptions").addEventListener("click", this.selectShape.bind(this)); 87 document.getElementById("render-button").addEventListener("click", this.render.bind(this)); 88 window.addEventListener("keydown", this.handleExitSelection.bind(this)); 89 window.addEventListener("keydown", this.handleDelete.bind(this)); 90 window.addEventListener("keydown", this.rotateShapesBy90Deg.bind(this)); 91 window.addEventListener("keydown", this.toggleEfficientDrawingMode.bind(this)); 92 window.addEventListener("resize", this.handleResize.bind(this)); 93 this.stage.on("mousedown touchstart", this.handleMouseDown.bind(this)); 94 this.stage.on("mousemove touchmove", this.handleMouseMove.bind(this)); 95 this.stage.on("mouseup touchend", this.handleMouseUp.bind(this)); 96 this.stage.on("click tap", this.handleStageClick.bind(this)); 97 this.stage.on("contextmenu", this.placeInfoPin.bind(this)); 98 this.stage.on("dragmove", this.dragStage.bind(this)); 99 this.stage.on("wheel", this.zoom.bind(this)); 100 } 101 102 detachKeyPressEventListeners() { 103 window.removeEventListener("keydown", this.handleExitSelection.bind(this)); 104 window.removeEventListener("keydown", this.handleDelete.bind(this)); 105 window.removeEventListener("keydown", this.rotateShapesBy90Deg.bind(this)); 106 window.removeEventListener("keydown", this.toggleEfficientDrawingMode.bind(this)); 107 } 108 attachKeyPressEventListeners() { 109 window.addEventListener("keydown", this.handleExitSelection.bind(this)); 110 window.addEventListener("keydown", this.handleDelete.bind(this)); 111 window.addEventListener("keydown", this.rotateShapesBy90Deg.bind(this)); 112 window.addEventListener("keydown", this.toggleEfficientDrawingMode.bind(this)); 113 } 114 115 dragStage(e) { 116 if (!e.evt.shiftKey) return; 117 this.drawGrid(); 118 } 119 120 transformerSnapFunc() { 121 return (oldPos, newPos) => { 122 const snapDistance = 8; 123 124 if (this.mainTransformer.getActiveAnchor() === "rotater") { 125 return newPos; 126 } 127 128 const distance = Math.sqrt(Math.pow(newPos.x - oldPos.x, 2) + Math.pow(newPos.y - oldPos.y, 2)); 129 130 if (distance > snapDistance) { 131 return newPos; 132 } 133 134 const nextX = Math.round(newPos.x / this.blockSize) * this.blockSize; 135 const diffX = Math.abs(newPos.x - nextX); 136 137 const nextY = Math.round(newPos.y / this.blockSize) * this.blockSize; 138 const diffY = Math.abs(newPos.y - nextY); 139 140 const snapToX = diffX < snapDistance; 141 const snapToY = diffY < snapDistance; 142 143 if (snapToX && !snapToY) { 14 constructor(containerId,floorNum,mapName) { 15 this.container = document.getElementById(containerId); 16 this.stage = new Konva.Stage({ 17 container: containerId, 18 width: this.container.clientWidth, 19 height: this.container.clientHeight, 20 }); 21 22 // TODO AKO DRAGNIT NEKOJ OD POCETOK NA STAGE POZICIIVE KE SA ZEZNAT 23 // TODO jwt vo cookie 24 // TODO placed shape i mouseMoveHandler da ne callback ( da ne vrakjat funkcija) 25 // TODO text na top layer sekogas 26 27 this._floorNum = floorNum; 28 this.mapName = mapName; 29 30 this.gridLayer = new Konva.Layer(); 31 this.mainLayer = new Konva.Layer(); 32 this.dragLayer = new Konva.Layer(); 33 this.infoPinLayer = new Konva.Layer(); 34 this.prioLayer = new Konva.Layer(); 35 this.textLayer = new Konva.Layer(); 36 this.gridLayer.listening(false); 37 38 39 this.othStairs = []; 40 41 this.blockSize = 10; 42 this.efficientDrawingMode = false; 43 this.roomTypes = []; 44 45 this.gridLine = new Konva.Line({ 46 points: [], 47 stroke: "grey", 48 strokeWidth: 1, 49 opacity: 0.3, 50 }); 51 52 this.gridLine.cache(); 53 54 this.mainTransformer = new Konva.Transformer({ 55 centeredScaling: false, 56 rotationSnaps: [0, 90, 180, 270], 57 anchorSize: 5, 58 padding: 2, 59 anchorFill: "#f6031f", 60 borderStroke: "black", 61 anchorStroke: "black", 62 cornerRadius: 20, 63 anchorCornerRadius: 10, 64 anchorDragBoundFunc: this.transformerSnapFunc(), 65 }); 66 67 this.selectionRectangle = new Konva.Rect({ 68 fill: "rgba(56,194,245,0.5)", 69 visible: false, 70 listening: false, 71 zIndex: 100, 72 }); 73 74 this.x1 = 0; 75 this.y1 = 0; 76 this.x2 = 0; 77 this.y2 = 0; 78 79 this.selecting = false; 80 81 this.initialize(); 82 } 83 84 initialize() { 85 this.drawGrid(); 86 this.mainLayer.add(this.mainTransformer); 87 this.mainLayer.add(this.selectionRectangle); 88 this.stage.add(this.gridLayer); 89 this.stage.add(this.dragLayer); 90 this.stage.add(this.mainLayer); 91 this.stage.add(this.infoPinLayer); 92 this.stage.add(this.textLayer); 93 this.setupEventListeners(); 94 } 95 96 setupEventListeners() { 97 document.getElementById("shapeOptions").addEventListener("click", this.selectShape.bind(this)); 98 window.addEventListener("keydown", this.handleExitSelection.bind(this)); 99 window.addEventListener("keydown", this.handleDelete.bind(this)); 100 window.addEventListener("keydown", this.rotateShapesBy90Deg.bind(this)); 101 window.addEventListener("keydown", this.toggleEfficientDrawingMode.bind(this)); 102 window.addEventListener("resize", this.handleResize.bind(this)); 103 104 this.boundEscapeEventListener = this.handleExitSelection.bind(this); 105 this.boundDeleteEventListener = this.handleDelete.bind(this); 106 this.boundRotateShapeEventListener = this.rotateShapesBy90Deg.bind(this) 107 this.boundEfficientDrawingModeEventListener = this.toggleEfficientDrawingMode.bind(this); 108 109 //this.attachKeyPressEventListeners(); 110 111 this.stage.on("mousedown touchstart", this.handleMouseDown.bind(this)); 112 this.stage.on("mousemove touchmove", this.handleMouseMove.bind(this)); 113 this.stage.on("mouseup touchend", this.handleMouseUp.bind(this)); 114 this.stage.on("click tap", this.handleStageClick.bind(this)); 115 this.stage.on("contextmenu", this.placeInfoPin.bind(this)); 116 this.stage.on("dragmove", this.dragStage.bind(this)); 117 this.stage.on("wheel", this.zoom.bind(this)); 118 } 119 120 detachKeyPressEventListeners() { 121 window.removeEventListener("keydown", this.boundEscapeEventListener); 122 window.removeEventListener("keydown", this.boundDeleteEventListener); 123 window.removeEventListener("keydown", this.boundRotateShapeEventListener); 124 window.removeEventListener("keydown", this.boundEfficientDrawingModeEventListener); 125 } 126 127 attachKeyPressEventListeners() { 128 window.addEventListener("keydown", this.boundEscapeEventListener); 129 window.addEventListener("keydown", this.boundDeleteEventListener); 130 window.addEventListener("keydown", this.boundRotateShapeEventListener); 131 window.addEventListener("keydown", this.boundEfficientDrawingModeEventListener); 132 } 133 134 dragStage(e) { 135 if (!e.evt.shiftKey) return; 136 this.drawGrid(); 137 } 138 139 transformerSnapFunc() { 140 return (oldPos, newPos) => { 141 const snapDistance = 8; 142 143 if (this.mainTransformer.getActiveAnchor() === "rotater") { 144 return newPos; 145 } 146 147 const distance = Math.sqrt(Math.pow(newPos.x - oldPos.x, 2) + Math.pow(newPos.y - oldPos.y, 2)); 148 149 if (distance > snapDistance) { 150 return newPos; 151 } 152 153 const nextX = Math.round(newPos.x / this.blockSize) * this.blockSize; 154 const diffX = Math.abs(newPos.x - nextX); 155 156 const nextY = Math.round(newPos.y / this.blockSize) * this.blockSize; 157 const diffY = Math.abs(newPos.y - nextY); 158 159 const snapToX = diffX < snapDistance; 160 const snapToY = diffY < snapDistance; 161 162 if (snapToX && !snapToY) { 163 return { 164 x: nextX, 165 y: oldPos.y, 166 }; 167 } else if (!snapToX && snapToY) { 168 return { 169 x: oldPos.x, 170 y: nextY, 171 }; 172 } else if (snapToX && snapToY) { 173 return { 174 x: nextX, 175 y: nextY, 176 }; 177 } 178 179 return newPos; 180 }; 181 } 182 183 handleResize() { 184 this.stage.width(this.container.offsetWidth); 185 this.stage.height(this.container.offsetHeight); 186 this.drawGrid(); 187 } 188 189 zoom(e) { 190 zoomStage(e, this.stage, true); 191 this.drawGrid(); 192 } 193 194 get floorNum(){ 195 return this._floorNum; 196 } 197 198 set floorNum(val){ 199 this._floorNum = val; 200 } 201 202 drawGrid() { 203 this.gridLayer.destroyChildren(); 204 205 let width = this.stage.width(); 206 let height = this.stage.height(); 207 208 //presmetka od globalen koordinaten sistem vo lokalen na canvasot 209 let transform = this.stage.getAbsoluteTransform().copy().invert(); 210 let topLeft = transform.point({ 211 x: 0, 212 y: 0, 213 }); 214 215 let bottomRight = transform.point({ 216 x: width, 217 y: height, 218 }); 219 220 let startX = Math.floor(topLeft.x / this.blockSize) * this.blockSize; 221 let startY = Math.floor(topLeft.y / this.blockSize) * this.blockSize; 222 223 let endX = Math.ceil(bottomRight.x / this.blockSize) * this.blockSize; 224 let endY = Math.ceil(bottomRight.y / this.blockSize) * this.blockSize; 225 226 for (let x = startX; x <= endX; x += this.blockSize) { 227 let line = this.gridLine.clone({ 228 points: [x + 0.5, topLeft.y - this.blockSize, x + 0.5, bottomRight.y + this.blockSize], 229 }); 230 231 line.transformsEnabled("position"); 232 line.perfectDrawEnabled(false); 233 line.shadowForStrokeEnabled(false); 234 235 this.gridLayer.add(line); 236 } 237 238 for (let y = startY; y <= endY; y += this.blockSize) { 239 let line = this.gridLine.clone({ 240 points: [topLeft.x - this.blockSize, y + 0.5, bottomRight.x + this.blockSize, y + 0.5], 241 }); 242 243 line.perfectDrawEnabled(false); 244 line.shadowForStrokeEnabled(false); 245 line.transformsEnabled("position"); 246 this.gridLayer.add(line); 247 } 248 249 this.mainLayer.moveToTop(); 250 this.infoPinLayer.moveToTop(); 251 252 this.gridLayer.batchDraw(); 253 } 254 255 placeInfoPin(e) { 256 e.evt.preventDefault(); 257 let mousePos = this.stage.getRelativePointerPosition(); 258 const attrs = { 259 type: "InfoPin", 260 position: mousePos, 261 blockSize: this.blockSize, 262 layer: this.mainLayer, 263 rotation: 0, 264 scaleX: 1, 265 scaleY: 1, 266 increment: true, 267 floorNum: this.floorNum 268 }; 269 let infoPin = Factory.createShape("InfoPin", attrs); 270 addEventHandling(infoPin, this, "dblclick"); 271 //this.shapes.push(infoPin); 272 ShapeRegistry.add(infoPin) 273 this.mainLayer.add(infoPin); 274 infoPin.displayName(this.textLayer); 275 triggerMapSave() 276 277 console.log(infoPin.name()); 278 } 279 280 toggleEfficientDrawingMode(e) { 281 if (e.key === "e" || e.key === "E") { 282 this.efficientDrawingMode = !this.efficientDrawingMode; 283 console.log("EFFICIENT DRAWING MODE is: ", this.efficientDrawingMode); 284 285 if (!this.efficientDrawingMode) { 286 this.stopDrawing(); 287 } 288 } 289 } 290 291 placeShape() { 292 const mousePos = this.stage.getRelativePointerPosition(); 293 const attrs = { 294 position: mousePos, 295 width: this.blockSize, 296 height: this.blockSize, 297 layer: this.mainLayer, 298 rotation: this.hoverObj.rotation(), 299 scaleX: 1, 300 scaleY: 1, 301 increment: true, 302 snap: true, 303 fromLoad: false, 304 blockSize: this.blockSize, 305 floorNum: this.floorNum 306 }; 307 308 const placedObj = Factory.createShape(this.hoverObj.type, attrs); 309 if (!placedObj) return; 310 311 console.info("ATTRS FNUM",attrs.floorNum) 312 313 this.mainLayer.add(placedObj); 314 //this.shapes.push(placedObj); 315 console.log("VO PLACED SHAEPS WALL ZITI SE: " + placedObj.className); 316 ShapeRegistry.add(placedObj); 317 addEventHandling(placedObj, this, "dblclick"); 318 this.mainLayer.draw(); 319 320 // site ovie func da se vo edna funkcija vo shape. 321 322 placedObj.displayName(this.textLayer); 323 placedObj.snapToGrid(); 324 325 triggerMapSave(); 326 327 if (!this.efficientDrawingMode) { 328 this.stopDrawing(); 329 } 330 } 331 332 stopDrawing() { 333 this.mainTransformer.nodes([]); 334 if (this.hoverObj != null) this.hoverObj.remove(); 335 this.dragLayer.removeChildren(); 336 this.stage.off("mousemove", this.boundMouseMoveHandler); 337 this.stage.off("click", this.boundPlaceShapeHandler); 338 } 339 340 mouseMoveHandler() { 341 const mousePos = this.stage.getRelativePointerPosition(); 342 this.hoverObj.position({x: mousePos.x, y: mousePos.y}); 343 this.hoverObj.visible(true); 344 } 345 346 startDrawing(shapeType) { 347 const attrs = { 348 position: {x: 0, y: 0}, 349 width: this.blockSize, 350 height: this.blockSize, 351 layer: this.mainLayer, 352 rotation: 0, 353 scaleX: 1, 354 scaleY: 1, 355 increment: false, 356 snap: true, 357 fromLoad: false, 358 blockSize: this.blockSize 359 }; 360 this.hoverObj = Factory.createShape(shapeType, attrs); 361 362 console.log("HOVBER OBK:", this.hoverObj) 363 364 this.hoverObj.visible(false); 365 this.dragLayer.add(this.hoverObj); 366 this.dragLayer.moveToTop(); 367 this.boundMouseMoveHandler = this.mouseMoveHandler.bind(this); 368 this.boundPlaceShapeHandler = this.placeShape.bind(this); 369 370 this.stage.on("mousemove", this.boundMouseMoveHandler); 371 this.stage.on("click", this.boundPlaceShapeHandler); 372 } 373 374 selectShape(e) { 375 if (e.target.tagName === "LI") { 376 const shapeType = e.target.getAttribute("data-info"); 377 this.startDrawing(shapeType); 378 this.mainTransformer.nodes([]); 379 } 380 } 381 382 rotateShapesBy90Deg(e) { 383 if (e.key === "r" || e.key === "R") { 384 if (this.hoverObj) { 385 this.hoverObj.rotate(90); 386 } 387 this.mainTransformer.nodes().forEach((node) => { 388 node.rotate(90); 389 }); 390 } 391 } 392 393 handleDelete(e) { 394 if (e.key === "Delete") { 395 this.mainTransformer.nodes().forEach((node) => { 396 node.remove(); 397 node.destroy(); 398 ShapeRegistry.delete(node); 399 triggerMapSave(); 400 }); 401 this.mainTransformer.nodes([]); 402 this.mainLayer.batchDraw(); 403 } 404 } 405 406 handleExitSelection(e) { 407 if (e.key === "Escape") { 408 this.mainTransformer.nodes([]); 409 this.stopDrawing(); 410 } 411 } 412 413 handleMouseDown(e) { 414 this.stage.draggable(e.evt.shiftKey); 415 416 if (e.target !== this.stage) { 417 return; 418 } 419 420 e.evt.preventDefault(); 421 this.x1 = this.stage.getRelativePointerPosition().x; 422 this.y1 = this.stage.getRelativePointerPosition().y; 423 this.x2 = this.stage.getRelativePointerPosition().x; 424 this.y2 = this.stage.getRelativePointerPosition().y; 425 426 this.selectionRectangle.width(0); 427 this.selectionRectangle.height(0); 428 this.selecting = true; 429 } 430 431 handleMouseMove(e) { 432 if (!this.selecting) { 433 return; 434 } 435 e.evt.preventDefault(); 436 this.x2 = this.stage.getRelativePointerPosition().x; 437 this.y2 = this.stage.getRelativePointerPosition().y; 438 439 this.selectionRectangle.setAttrs({ 440 visible: true, 441 x: Math.min(this.x1, this.x2), 442 y: Math.min(this.y1, this.y2), 443 width: Math.abs(this.x2 - this.x1), 444 height: Math.abs(this.y2 - this.y1), 445 }); 446 } 447 448 handleMouseUp(e) { 449 this.selecting = false; 450 this.stage.draggable(false); 451 452 if (!this.selectionRectangle.visible()) { 453 return; 454 } 455 456 e.evt.preventDefault(); 457 this.selectionRectangle.visible(false); 458 const shapes = this.stage.find(".mapObj"); 459 const box = this.selectionRectangle.getClientRect(); 460 const selected = shapes.filter((shape) => Konva.Util.haveIntersection(box, shape.getClientRect())); 461 this.mainTransformer.nodes(selected); 462 console.log(this.mainTransformer.nodes()); 463 } 464 465 saveShapeDetails() { 466 // this.shapes.forEach(shape => { 467 // shape.saveShapeDetails(); 468 // console.log(shape.info); 469 // }); 470 ShapeRegistry.saveDetails(); 471 console.log("thisflornum",this.floorNum) 472 return { 473 shapes: ShapeRegistry.getShapes(this.floorNum), 474 roomTypes: JSON.stringify(this.roomTypes), 475 mapName: this.mapName, 476 floorNum: this.floorNum 477 } 478 } 479 480 getPayload(){ 481 this.saveShapeDetails(); 144 482 return { 145 x: nextX, 146 y: oldPos.y, 147 }; 148 } else if (!snapToX && snapToY) { 149 return { 150 x: oldPos.x, 151 y: nextY, 152 }; 153 } else if (snapToX && snapToY) { 154 return { 155 x: nextX, 156 y: nextY, 157 }; 158 } 159 160 return newPos; 161 }; 162 } 163 164 handleResize() { 165 this.stage.width(this.container.offsetWidth); 166 this.stage.height(this.container.offsetHeight); 167 this.drawGrid(); 168 } 169 170 zoom(e) { 171 zoomStage(e,this.stage); 172 this.drawGrid(); 173 } 174 175 drawGrid() { 176 this.gridLayer.destroyChildren(); 177 178 let width = this.stage.width(); 179 let height = this.stage.height(); 180 181 //presmetka od globalen koordinaten sistem vo lokalen na canvasot 182 let transform = this.stage.getAbsoluteTransform().copy().invert(); 183 let topLeft = transform.point({ 184 x: 0, 185 y: 0, 186 }); 187 188 let bottomRight = transform.point({ 189 x: width, 190 y: height, 191 }); 192 193 let startX = Math.floor(topLeft.x / this.blockSize) * this.blockSize; 194 let startY = Math.floor(topLeft.y / this.blockSize) * this.blockSize; 195 196 let endX = Math.ceil(bottomRight.x / this.blockSize) * this.blockSize; 197 let endY = Math.ceil(bottomRight.y / this.blockSize) * this.blockSize; 198 199 for (let x = startX; x <= endX; x += this.blockSize) { 200 let line = this.gridLine.clone({ 201 points: [x + 0.5, topLeft.y - this.blockSize, x + 0.5, bottomRight.y + this.blockSize], 202 }); 203 204 line.transformsEnabled("position"); 205 line.perfectDrawEnabled(false); 206 line.shadowForStrokeEnabled(false); 207 208 this.gridLayer.add(line); 209 } 210 211 for (let y = startY; y <= endY; y += this.blockSize) { 212 let line = this.gridLine.clone({ 213 points: [topLeft.x - this.blockSize, y + 0.5, bottomRight.x + this.blockSize, y + 0.5], 214 }); 215 216 line.perfectDrawEnabled(false); 217 line.shadowForStrokeEnabled(false); 218 line.transformsEnabled("position"); 219 this.gridLayer.add(line); 220 } 221 222 this.mainLayer.moveToTop(); 223 this.infoPinLayer.moveToTop(); 224 225 this.gridLayer.batchDraw(); 226 } 227 228 placeInfoPin(e) { 229 e.evt.preventDefault(); 230 let mousePos = this.stage.getRelativePointerPosition(); 231 let infoPin = Factory.createShape("InfoPin", mousePos, this.blockSize, this.mainLayer, 0,1,1,true); 232 this.addModalHandling(infoPin); 233 this.shapes.push(infoPin); 234 this.mainLayer.add(infoPin) 235 infoPin.displayName(this.textLayer); 236 console.log(infoPin.name()); 237 } 238 239 toggleEfficientDrawingMode(e) { 240 if (e.key === "e" || e.key === "E") { 241 this.efficientDrawingMode = !this.efficientDrawingMode; 242 console.log("EFFICIENT DRAWING MODE is: ", this.efficientDrawingMode); 243 244 if (!this.efficientDrawingMode) { 245 this.stopDrawing(); 246 } 247 } 248 } 249 250 placeShape() { 251 const mousePos = this.stage.getRelativePointerPosition(); 252 const placedObj = Factory.createShape( 253 this.hoverObj.type, 254 mousePos, 255 this.blockSize, 256 this.mainLayer, 257 this.hoverObj.rotation(), 258 1, 259 1, 260 true 261 ); 262 263 if (!placedObj) return; 264 265 this.mainLayer.add(placedObj); 266 this.shapes.push(placedObj); 267 this.addModalHandling(placedObj); 268 this.mainLayer.draw(); 269 placedObj.displayName(this.textLayer); 270 placedObj.snapToGrid(); 271 272 if (!this.efficientDrawingMode) { 273 this.stopDrawing(); 274 } 275 } 276 277 stopDrawing() { 278 this.mainTransformer.nodes([]); 279 this.hoverObj.remove(); 280 this.dragLayer.removeChildren(); 281 this.stage.off("mousemove", this.boundMouseMoveHandler); 282 this.stage.off("click", this.boundPlaceShapeHandler); 283 } 284 285 mouseMoveHandler() { 286 const mousePos = this.stage.getRelativePointerPosition(); 287 this.hoverObj.position({ x: mousePos.x, y: mousePos.y }); 288 this.hoverObj.visible(true); 289 } 290 291 startDrawing(shapeType) { 292 let pos = { x: 0, y: 0 }; 293 this.hoverObj = Factory.createShape(shapeType, pos, this.blockSize, this.dragLayer, 0); 294 295 this.hoverObj.visible(false); 296 this.dragLayer.add(this.hoverObj); 297 this.dragLayer.moveToTop(); 298 this.boundMouseMoveHandler = this.mouseMoveHandler.bind(this); 299 this.boundPlaceShapeHandler = this.placeShape.bind(this); 300 301 this.stage.on("mousemove", this.boundMouseMoveHandler); 302 this.stage.on("click", this.boundPlaceShapeHandler); 303 } 304 305 selectShape(e) { 306 if (e.target.tagName === "LI") { 307 const shapeType = e.target.getAttribute("data-info"); 308 this.startDrawing(shapeType); 309 this.mainTransformer.nodes([]); 310 } 311 } 312 313 addModalHandling(shape) { 314 shape.on("dblclick", () => { 315 const eventName = shape.modalEventName; 316 if (eventName) { 317 const data = { 318 room: shape, 319 map: this, 320 }; 321 const event = new CustomEvent(eventName, { detail: data }); 322 window.dispatchEvent(event); 323 } 324 }); 325 } 326 327 rotateShapesBy90Deg(e) { 328 if (e.key === "r" || e.key === "R") { 329 if (this.hoverObj) { 330 this.hoverObj.rotate(90); 331 } 332 this.mainTransformer.nodes().forEach((node) => { 333 node.rotate(90); 334 }); 335 } 336 } 337 338 handleDelete(e) { 339 if (e.key === "Delete") { 340 this.mainTransformer.nodes().forEach((node) => { 341 node.remove(); 342 this.shapes.splice(this.shapes.indexOf(node), 1); 343 }); 344 this.mainTransformer.nodes([]); 345 this.mainLayer.batchDraw(); 346 } 347 } 348 349 handleExitSelection(e) { 350 if (e.key === "Escape") { 351 this.mainTransformer.nodes([]); 352 this.stopDrawing(); 353 } 354 } 355 356 handleMouseDown(e) { 357 this.stage.draggable(e.evt.shiftKey); 358 359 if (e.target !== this.stage) { 360 return; 361 } 362 363 e.evt.preventDefault(); 364 this.x1 = this.stage.getRelativePointerPosition().x; 365 this.y1 = this.stage.getRelativePointerPosition().y; 366 this.x2 = this.stage.getRelativePointerPosition().x; 367 this.y2 = this.stage.getRelativePointerPosition().y; 368 369 this.selectionRectangle.width(0); 370 this.selectionRectangle.height(0); 371 this.selecting = true; 372 } 373 374 handleMouseMove(e) { 375 if (!this.selecting) { 376 return; 377 } 378 e.evt.preventDefault(); 379 this.x2 = this.stage.getRelativePointerPosition().x; 380 this.y2 = this.stage.getRelativePointerPosition().y; 381 382 this.selectionRectangle.setAttrs({ 383 visible: true, 384 x: Math.min(this.x1, this.x2), 385 y: Math.min(this.y1, this.y2), 386 width: Math.abs(this.x2 - this.x1), 387 height: Math.abs(this.y2 - this.y1), 388 }); 389 } 390 391 handleMouseUp(e) { 392 this.selecting = false; 393 this.stage.draggable(false); 394 395 if (!this.selectionRectangle.visible()) { 396 return; 397 } 398 399 e.evt.preventDefault(); 400 this.selectionRectangle.visible(false); 401 const shapes = this.stage.find(".mapObj"); 402 const box = this.selectionRectangle.getClientRect(); 403 const selected = shapes.filter((shape) => Konva.Util.haveIntersection(box, shape.getClientRect())); 404 this.mainTransformer.nodes(selected); 405 console.log(this.mainTransformer.nodes()); 406 } 407 408 saveShapeDetails() { 409 this.shapes.forEach((room) => { 410 room.saveShapeDetails(); 411 console.log(room.info); 412 }); 413 } 414 415 async render() { 416 this.saveShapeDetails(); 417 const httpService = new HttpService("http://localhost:8080/api/protected", true); 418 try { 419 const response = await httpService.post("/render", this.shapes); 420 console.log(response); 421 } catch (err) { 422 console.log("ERROR --> Could not render map --->", err); 423 } 424 } 425 426 async saveMap(mapName) { 427 this.saveShapeDetails(); 428 const httpService = new HttpService("http://localhost:8080/api/protected/maps", true); 429 try { 430 const response = await httpService.put(`/save?mapName=${mapName}`, this.shapes); 431 console.log(response, "resp in builder"); 432 } catch (err) { 433 console.log("ERROR --> Could not Save map --->", err); 434 } 435 } 436 437 handleStageClick(e) { 438 if (this.selectionRectangle.visible()) { 439 return; 440 } 441 442 if (e.target === this.stage) { 443 this.mainTransformer.nodes([]); 444 return; 445 } 446 447 if (!e.target.hasName("mapObj")) { 448 return; 449 } 450 451 const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey; 452 const isSelected = this.mainTransformer.nodes().indexOf(e.target) >= 0; 453 454 if (!metaPressed && !isSelected) { 455 this.mainTransformer.nodes([e.target]); 456 console.log("Sel 1"); 457 } else if (metaPressed && isSelected) { 458 const nodes = this.mainTransformer.nodes().slice(); 459 nodes.splice(nodes.indexOf(e.target), 1); 460 this.mainTransformer.nodes(nodes); 461 } else if (metaPressed && !isSelected) { 462 const nodes = this.mainTransformer.nodes().concat([e.target]); 463 this.mainTransformer.nodes(nodes); 464 } 465 } 466 467 addRoomType(roomType) { 468 this.roomTypes.push(roomType); 469 } 470 471 getRoomTypes() { 472 return this.roomTypes; 473 } 474 475 getRooms() { 476 return this.getShapeInfoByType("Room"); 477 } 478 479 getPins() { 480 return this.getShapeInfoByType("InfoPin"); 481 } 482 483 getEntrances() { 484 return this.getShapeInfoByType("Entrance"); 485 } 486 487 getConnections() { 488 const pins = this.getShapeInfoByType("InfoPin"); 489 const entrances = this.getShapeInfoByType("Entrance"); 490 return [...pins, ...entrances]; 491 } 492 493 getShapeInfoByType(type) { 494 return this.shapes.filter((shape) => shape.className === type).map((shape) => shape.info); 495 } 496 497 updateConnections() { 498 console.log("Update"); 499 500 this.shapes.forEach((shape) => { 501 if (shape.className === "InfoPin" || shape.className === "Entrance") { 502 shape.info.selectedPins.forEach((connectedShapeName) => { 503 const connectedShape = this.shapes.find((s) => s.info.name === connectedShapeName); 504 if ( 505 connectedShape && 506 (connectedShape.className === "InfoPin" || connectedShape.className === "Entrance") 507 ) { 508 if (!connectedShape.info.selectedPins.includes(shape.info.name)) { 509 connectedShape.info.selectedPins.push(shape.info.name); 510 } 511 } 512 }); 513 } 514 }); 515 } 516 517 removeConnection(from, to) { 518 this.shapes 519 .filter((s) => s.info.name === from || s.info.name === to) 520 .forEach((s) => { 521 s.info.selectedPins = s.info.selectedPins.filter((pin) => pin !== from && pin !== to); 522 }); 523 console.log("Remove"); 524 } 525 526 updateRoomNames() { 527 this.textLayer.removeChildren(); 528 this.shapes.forEach((shape) => { 529 shape.displayName(this.textLayer); 530 }); 531 this.textLayer.children.forEach((child) => console.log(child)); 532 } 533 534 clearMap() { 535 this.mainLayer.removeChildren(); 536 this.shapes = []; 537 this.hoverObj = null; 538 } 539 540 deserializeMap(data) { 541 console.log("DESERIALIZING: ", data); 542 this.clearMap(); 543 let dsrData = JSON.parse(data); 544 dsrData.forEach((child) => { 545 const shape = JSON.parse(child); 546 const loadedShape = Factory.createShape( 547 shape.className, 548 { x: shape.attrs.x, y: shape.attrs.y }, 549 this.blockSize, 550 this.mainLayer, 551 shape.attrs.rotation, 552 shape.attrs.scaleX, 553 shape.attrs.scaleY 554 ); 555 loadedShape.loadInfo(shape.attrs); 556 this.shapes.push(loadedShape); 557 this.addModalHandling(loadedShape); 558 this.mainLayer.add(loadedShape); 559 }); 560 this.mainTransformer.nodes([]); 561 this.mainLayer.add(this.mainTransformer); 562 this.mainLayer.add(this.selectionRectangle); 563 564 this.shapes.forEach((shape) => shape.displayName(this.textLayer)); 565 } 483 shapes: ShapeRegistry.getShapes(this.floorNum), 484 roomTypes: JSON.stringify(this.roomTypes), 485 mapName: this.mapName, 486 floorNum: this.floorNum 487 } 488 } 489 490 491 handleStageClick(e) { 492 if (this.selectionRectangle.visible()) { 493 return; 494 } 495 496 if (e.target === this.stage) { 497 this.mainTransformer.nodes([]); 498 return; 499 } 500 501 if (!e.target.hasName("mapObj")) { 502 return; 503 } 504 505 const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey; 506 const isSelected = this.mainTransformer.nodes().indexOf(e.target) >= 0; 507 508 if (!metaPressed && !isSelected) { 509 this.mainTransformer.nodes([e.target]); 510 console.log("Sel 1"); 511 } else if (metaPressed && isSelected) { 512 const nodes = this.mainTransformer.nodes().slice(); 513 nodes.splice(nodes.indexOf(e.target), 1); 514 this.mainTransformer.nodes(nodes); 515 } else if (metaPressed && !isSelected) { 516 const nodes = this.mainTransformer.nodes().concat([e.target]); 517 this.mainTransformer.nodes(nodes); 518 } 519 } 520 521 addRoomType(type) { 522 this.roomTypes.push(type); 523 } 524 525 removeRoomType(targetType) { 526 this.roomTypes = this.roomTypes.filter(type => type !== targetType); 527 } 528 529 getRoomTypes() { 530 return this.roomTypes; 531 } 532 533 getRooms() { 534 return this.getShapeInfoByType("Room"); 535 } 536 537 getPins() { 538 return this.getShapeInfoByType("InfoPin"); 539 } 540 541 getEntrances() { 542 return this.getShapeInfoByType("Entrance"); 543 } 544 545 546 547 getShapeInfoByType(type) { 548 return ShapeRegistry.getShapes(this.floorNum).filter((shape) => shape.className === type).map((shape) => shape.info); 549 } 550 551 552 drawConnection(node1Name, node2Name) { 553 554 ShapeRegistry.drawConnection(node1Name,node2Name); 555 } 556 557 getNodeByName(name) { 558 return ShapeRegistry.getShapes(this.floorNum).filter(shape => shape instanceof MapNode && shape.info.name === name)[0]; 559 } 560 561 removeConnection(from, to) { 562 ShapeRegistry.removeConnection(from,to); 563 } 564 565 updateRoomNames() { 566 this.textLayer.removeChildren(); 567 ShapeRegistry.getShapes(this.floorNum).forEach((shape) => { 568 shape.displayName(this.textLayer); 569 }); 570 571 } 572 573 isMainEntranceSelected() { 574 console.log(this.getEntrances().forEach((en) => console.log(en.isMainEntrance, "asdsad"))); 575 576 let hasMainEntrance = false; 577 578 this.getEntrances().forEach((entrance) => { 579 if (entrance.isMainEntrance === true) hasMainEntrance = true; 580 }); 581 582 return hasMainEntrance; 583 584 } 585 586 clearMap() { 587 this.mainLayer.removeChildren(); 588 this.hoverObj = null; 589 } 590 591 592 // ova klasa i map display da nasledbat od glavna klasa 593 594 loadNewFloor(floor) { 595 596 this._floorNum = floor?.num; 597 let data = floor?.mapData; 598 599 if (data == null || data === "") return; 600 601 this.deserializeMap(data); 602 shapeRegistry.getShapes(this.floorNum).forEach((shape) => { 603 this.mainLayer.add(shape); 604 }); 605 606 607 } 608 609 //nov 610 deserializeMap(data) { 611 console.log("DESERIALIZING: ", data); 612 ShapeRegistry.clear(this.floorNum); 613 614 if (data != null) { 615 const dsrData = JSON.parse(data); 616 //load shapes 617 dsrData.forEach((shape) => { 618 const attrs = { 619 position: {x: shape.attrs.x, y: shape.attrs.y}, 620 width: shape.attrs.width, 621 height: shape.attrs.height, 622 layer: this.mainLayer, 623 blockSize: this.blockSize, 624 rotation: shape.attrs.rotation, 625 scaleX: shape.attrs.scaleX, 626 scaleY: shape.attrs.scaleY, 627 increment: false, 628 snap: true, 629 fromLoad: true, 630 floorNum: this.floorNum 631 }; 632 633 const loadedShape = Factory.createShape(shape.className, attrs); 634 loadedShape.loadInfo(shape.attrs); 635 ShapeRegistry.add(loadedShape); 636 // na destroy trebit events da sa trgnat 637 addEventHandling(loadedShape, this, "dblclick"); 638 }); 639 let nodes = ShapeRegistry.getShapes(this.floorNum).filter((shape) => shape.className === "InfoPin" || shape.className === "Entrance" || shape.className === "Stairs"); 640 nodes.forEach((pin) => { 641 let connectedPins = pin.info.selectedPins; 642 if (connectedPins) { 643 connectedPins.forEach((slPin) => { 644 console.log("CONN node1: " + pin + "conn node2: " + slPin) 645 this.drawConnection(pin.info.name, slPin); 646 }); 647 } 648 }); 649 } 650 651 this.mainTransformer.nodes([]); 652 this.mainLayer.add(this.mainTransformer); 653 this.mainLayer.add(this.selectionRectangle); 654 655 ShapeRegistry.getShapes(this.floorNum).forEach((shape) => shape.displayName(this.textLayer)); 656 } 657 566 658 }
Note:
See TracChangeset
for help on using the changeset viewer.