UNPKG

react-floorplanner

Version:

react-floorplanner is a React Component for plans design. Draw a 2D floorplan and navigate it in 3D mode.

370 lines (306 loc) 11.6 kB
import {List, Map} from 'immutable'; import { SELECT_TOOL_DRAWING_LINE, BEGIN_DRAWING_LINE, UPDATE_DRAWING_LINE, END_DRAWING_LINE, BEGIN_DRAGGING_LINE, UPDATE_DRAGGING_LINE, END_DRAGGING_LINE, SELECT_LINE, MODE_IDLE, MODE_WAITING_DRAWING_LINE, MODE_DRAWING_LINE, MODE_DRAGGING_LINE } from '../constants'; import * as Geometry from '../utils/geometry'; import { addLine, replaceLineVertex, removeLine, select, unselect, addLineAvoidingIntersections, unselectAll, detectAndUpdateAreas, mergeEqualsVertices, } from '../utils/layer-operations'; import {nearestSnap, addPointSnap, addLineSnap, addLineSegmentSnap} from '../utils/snap'; import {sceneSnapElements} from '../utils/snap-scene'; import {samePoints} from "../utils/geometry"; export default function (state, action) { switch (action.type) { case SELECT_TOOL_DRAWING_LINE: return selectToolDrawingLine(state, action.sceneComponentType); case BEGIN_DRAWING_LINE: return beginDrawingLine(state, action.layerID, action.x, action.y); case UPDATE_DRAWING_LINE: return updateDrawingLine(state, action.x, action.y); case END_DRAWING_LINE: return endDrawingLine(state, action.x, action.y); case BEGIN_DRAGGING_LINE: return beginDraggingLine(state, action.layerID, action.lineID, action.x, action.y); case UPDATE_DRAGGING_LINE: return updateDraggingLine(state, action.x, action.y); case END_DRAGGING_LINE: return endDraggingLine(state, action.x, action.y); case SELECT_LINE: return selectLine(state, action.layerID, action.lineID); default: return state; } } function selectToolDrawingLine(state, sceneComponentType) { return state.merge({ mode: MODE_WAITING_DRAWING_LINE, drawingSupport: Map({ type: sceneComponentType }) }); } /** lines operations **/ function beginDrawingLine(state, layerID, x, y) { let catalog = state.catalog; let snapElements = sceneSnapElements(state.scene, new List(), state.snapMask); let snap = null; if (state.snapMask && !state.snapMask.isEmpty()) { snap = nearestSnap(snapElements, x, y, state.snapMask); if (snap) ({x, y} = snap.point); snapElements = snapElements.withMutations(snapElements => { let a, b, c; ({a, b, c} = Geometry.horizontalLine(y)); addLineSnap(snapElements, a, b, c, 10, 3, null); ({a, b, c} = Geometry.verticalLine(x)); addLineSnap(snapElements, a, b, c, 10, 3, null); }); } let drawingSupport = state.get('drawingSupport').set('layerID', layerID); let scene = state.scene.updateIn(['layers', layerID], layer => layer.withMutations(layer => { unselectAll(layer); let {line} = addLine(layer, drawingSupport.get('type'), x, y, x, y, catalog); select(layer, 'lines', line.id); select(layer, 'vertices', line.vertices.get(0)); select(layer, 'vertices', line.vertices.get(1)); })); return state.merge({ mode: MODE_DRAWING_LINE, scene, snapElements, activeSnapElement: snap ? snap.snap : null, drawingSupport }); } function updateDrawingLine(state, x, y) { let snap = null; if (state.snapMask && !state.snapMask.isEmpty()) { snap = nearestSnap(state.snapElements, x, y, state.snapMask); if (snap) ({x, y} = snap.point); } let layerID = state.getIn(['drawingSupport', 'layerID']); let scene = state.scene.updateIn(['layers', layerID], layer => layer.withMutations(layer => { let lineID = layer.getIn(['selected', 'lines']).first(); let vertex; ({layer, vertex} = replaceLineVertex(layer, lineID, 1, x, y)); select(layer, 'vertices', vertex.id); return layer; })); return state.merge({ scene, activeSnapElement: snap ? snap.snap : null, }); } function endDrawingLine(state, x, y) { let catalog = state.catalog; if (state.snapMask && !state.snapMask.isEmpty()) { let snap = nearestSnap(state.snapElements, x, y, state.snapMask); if (snap) ({x, y} = snap.point); } let layerID = state.getIn(['drawingSupport', 'layerID']); let scene = state.scene.updateIn(['layers', layerID], layer => layer.withMutations(layer => { let lineID = layer.getIn(['selected', 'lines']).first(); let line = layer.getIn(['lines', lineID]); let v0 = layer.vertices.get(line.vertices.get(0)); unselect(layer, 'lines', lineID); unselect(layer, 'vertices', line.vertices.get(0)); unselect(layer, 'vertices', line.vertices.get(1)); removeLine(layer, lineID); addLineAvoidingIntersections(layer, line.type, v0.x, v0.y, x, y, catalog); detectAndUpdateAreas(layer, catalog); })); return state.merge({ mode: MODE_WAITING_DRAWING_LINE, scene, snapElements: new List(), activeSnapElement: null, sceneHistory: state.sceneHistory.push(scene) }); } function beginDraggingLine(state, layerID, lineID, x, y) { let snapElements = sceneSnapElements(state.scene, new List(), state.snapMask); let layer = state.scene.layers.get(layerID); let line = layer.lines.get(lineID); let vertex0 = layer.vertices.get(line.vertices.get(0)); let vertex1 = layer.vertices.get(line.vertices.get(1)); return state.merge({ mode: MODE_DRAGGING_LINE, snapElements, draggingSupport: Map({ layerID, lineID, startPointX: x, startPointY: y, startVertex0X: vertex0.x, startVertex0Y: vertex0.y, startVertex1X: vertex1.x, startVertex1Y: vertex1.y, }) }) } function updateDraggingLine(state, x, y) { let draggingSupport = state.draggingSupport; let snapElements = state.snapElements; let layerID = draggingSupport.get('layerID'); let lineID = draggingSupport.get('lineID'); let diffX = x - draggingSupport.get('startPointX'); let diffY = y - draggingSupport.get('startPointY'); let newVertex0X = draggingSupport.get('startVertex0X') + diffX; let newVertex0Y = draggingSupport.get('startVertex0Y') + diffY; let newVertex1X = draggingSupport.get('startVertex1X') + diffX; let newVertex1Y = draggingSupport.get('startVertex1Y') + diffY; let activeSnapElement = null; let curSnap0 = null, curSnap1 = null; if (state.snapMask && !state.snapMask.isEmpty()) { curSnap0 = nearestSnap(snapElements, newVertex0X, newVertex0Y, state.snapMask); curSnap1 = nearestSnap(snapElements, newVertex1X, newVertex1Y, state.snapMask); } let deltaX = 0, deltaY = 0; if (curSnap0 && curSnap1) { if (curSnap0.point.distance < curSnap1.point.distance) { deltaX = curSnap0.point.x - newVertex0X; deltaY = curSnap0.point.y - newVertex0Y; activeSnapElement = curSnap0.snap; } else { deltaX = curSnap1.point.x - newVertex1X; deltaY = curSnap1.point.y - newVertex1Y; activeSnapElement = curSnap1.snap; } } else { if (curSnap0) { deltaX = curSnap0.point.x - newVertex0X; deltaY = curSnap0.point.y - newVertex0Y; activeSnapElement = curSnap0.snap; } if (curSnap1) { deltaX = curSnap1.point.x - newVertex1X; deltaY = curSnap1.point.y - newVertex1Y; activeSnapElement = curSnap1.snap; } } newVertex0X += deltaX; newVertex0Y += deltaY; newVertex1X += deltaX; newVertex1Y += deltaY; return state.merge({ activeSnapElement, scene: state.scene.updateIn(['layers', layerID], layer => layer.withMutations(layer => { let lineVertices = layer.getIn(['lines', lineID, 'vertices']); layer.updateIn(['vertices', lineVertices.get(0)], vertex => vertex.merge({x: newVertex0X, y: newVertex0Y})); layer.updateIn(['vertices', lineVertices.get(1)], vertex => vertex.merge({x: newVertex1X, y: newVertex1Y})); return layer; })) }); } function endDraggingLine(state, x, y) { let catalog = state.catalog; let {draggingSupport} = state; let layerID = draggingSupport.get('layerID'); let layer = state.scene.layers.get(layerID); let lineID = draggingSupport.get('lineID'); let line = layer.lines.get(lineID); let vertex0 = layer.vertices.get(line.vertices.get(0)); let vertex1 = layer.vertices.get(line.vertices.get(1)); let maxV = Geometry.maxVertex(vertex0, vertex1); let minV = Geometry.minVertex(vertex0, vertex1); let lineLength = Geometry.verticesDistance(minV,maxV); let alpha = Math.atan2(maxV.y - minV.y, maxV.x - minV.x); let holesWithOffsetPosition = []; layer.lines.get(lineID).holes.forEach(holeID => { let hole = layer.holes.get(holeID); let pointOnLine = lineLength * hole.offset; let offsetPosition = { x: pointOnLine * Math.cos(alpha) + minV.x, y: pointOnLine * Math.sin(alpha) + minV.y }; holesWithOffsetPosition.push({hole, offsetPosition}); }); return state.withMutations(state => { let scene = state.scene.updateIn(['layers', layerID], layer => layer.withMutations(layer => { let diffX = x - draggingSupport.get('startPointX'); let diffY = y - draggingSupport.get('startPointY'); let newVertex0X = draggingSupport.get('startVertex0X') + diffX; let newVertex0Y = draggingSupport.get('startVertex0Y') + diffY; let newVertex1X = draggingSupport.get('startVertex1X') + diffX; let newVertex1Y = draggingSupport.get('startVertex1Y') + diffY; if (state.snapMask && !state.snapMask.isEmpty()) { let curSnap0 = nearestSnap(state.snapElements, newVertex0X, newVertex0Y, state.snapMask); let curSnap1 = nearestSnap(state.snapElements, newVertex1X, newVertex1Y, state.snapMask); let deltaX = 0, deltaY = 0; if (curSnap0 && curSnap1) { if (curSnap0.point.distance < curSnap1.point.distance) { deltaX = curSnap0.point.x - newVertex0X; deltaY = curSnap0.point.y - newVertex0Y; } else { deltaX = curSnap1.point.x - newVertex1X; deltaY = curSnap1.point.y - newVertex1Y; } } else { if (curSnap0) { deltaX = curSnap0.point.x - newVertex0X; deltaY = curSnap0.point.y - newVertex0Y; } if (curSnap1) { deltaX = curSnap1.point.x - newVertex1X; deltaY = curSnap1.point.y - newVertex1Y; } } newVertex0X += deltaX; newVertex0Y += deltaY; newVertex1X += deltaX; newVertex1Y += deltaY; } mergeEqualsVertices(layer, line.vertices.get(0)); mergeEqualsVertices(layer, line.vertices.get(1)); removeLine(layer, lineID); if(!samePoints({newVertex0X, newVertex0Y}, {newVertex1X, newVertex1Y})) { addLineAvoidingIntersections(layer, line.type, newVertex0X, newVertex0Y, newVertex1X, newVertex1Y, catalog, line.properties, holesWithOffsetPosition); } detectAndUpdateAreas(layer, catalog); })); state.merge({ mode: MODE_IDLE, scene, draggingSupport: null, activeSnapElement: null, snapElements: new List(), sceneHistory: state.sceneHistory.push(scene) }); }); } function selectLine(state, layerID, lineID) { let scene = state.scene; scene = scene.merge({ layers: scene.layers.map(unselectAll), selectedLayer: layerID }); scene = scene.updateIn(['layers', layerID], layer => layer.withMutations(layer => { let line = layer.getIn(['lines', lineID]); select(layer, 'lines', lineID); select(layer, 'vertices', line.vertices.get(0)); select(layer, 'vertices', line.vertices.get(1)); }) ); return state.merge({ scene, sceneHistory: state.sceneHistory.push(scene) }) }