UNPKG

react-planner

Version:

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

354 lines (262 loc) 13.9 kB
import { Project, Line, Hole, Item, Area, Layer, Vertex } from './export'; import { Map, List } from 'immutable'; import { Group as GroupModel } from '../models'; import { IDBroker, MathUtils, GeometryUtils } from '../utils/export'; class Group{ static select( state, groupID ){ let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]); state = Project.setAlterate( state ).updatedState; layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => { state = Layer.unselectAll( state, groupLayerID ).updatedState; let lines = groupLayerElements.get('lines'); let holes = groupLayerElements.get('holes'); let items = groupLayerElements.get('items'); let areas = groupLayerElements.get('areas'); if( lines ) lines.forEach( lineID => { state = Line.select( state, groupLayerID, lineID ).updatedState; }); if( holes ) holes.forEach( holeID => { state = Hole.select( state, groupLayerID, holeID ).updatedState; }); if( items ) items.forEach( itemID => { state = Item.select( state, groupLayerID, itemID ).updatedState; }); if( areas ) areas.forEach( areaID => { state = Area.select( state, groupLayerID, areaID ).updatedState; }); }); state = Project.setAlterate( state ).updatedState; let groups = state.getIn(['scene', 'groups']).map( g => g.set('selected', false) ); state = state.setIn(['scene', 'groups'], groups).setIn([ 'scene', 'groups', groupID, 'selected' ], true); return { updatedState: state }; } static unselect( state, groupID ){ let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]); let reduced = layerList.reduce( ( newState, layer, layerID ) => Layer.unselectAll( newState, layerID ).updatedState, state ); state = reduced.setIn([ 'scene', 'groups', groupID, 'selected' ], false); return { updatedState: state }; } static create( state ){ let groupID = IDBroker.acquireID(); state = state.setIn(['scene', 'groups', groupID], new GroupModel({ id: groupID, name: groupID}) ); return { updatedState: state }; } static createFromSelectedElements( state ){ let groupID = IDBroker.acquireID(); state = state.setIn(['scene', 'groups', groupID], new GroupModel({ id: groupID, name: groupID}) ); state.getIn(['scene', 'layers']).forEach((layer) => { let layerID = layer.get('id'); let layerElements = { 'lines': layer.get('lines').filter( el => el.get('selected') ), 'items': layer.get('items').filter( el => el.get('selected') ), 'holes': layer.get('holes').filter( el => el.get('selected') ), 'areas': layer.get('areas').filter( el => el.get('selected') ) }; for( let elementPrototype in layerElements ) { layerElements[elementPrototype].forEach( el => state = this.addElement( state, groupID, layerID, elementPrototype, el.get('id') ).updatedState ); } }); return {updatedState: state}; } static addElement( state, groupID, layerID, elementPrototype, elementID ){ let actualList = state.getIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype]) || new List(); if( !actualList.contains(elementID) ) { state = state.setIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype], actualList.push(elementID)); state = this.reloadBaricenter( state, groupID ).updatedState; } return { updatedState: state }; } static setBarycenter( state, groupID, x, y ) { if (typeof x !== 'undefined') state = state.setIn(['scene', 'groups', groupID, 'x'], x); if (typeof y !== 'undefined') state = state.setIn(['scene', 'groups', groupID, 'y'], y); return { updatedState: state }; } static reloadBaricenter( state, groupID ) { let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]); let { a, b, c, d, e, f, SVGHeight } = state.get('viewer2D').toJS(); let m1 = [ [ a, b, c ], [ d, e, f ], [ 0, 0, 1 ] ]; let xBar = 0; let yBar = 0; let elementCount = 0; layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => { state = Layer.unselectAll( state, groupLayerID ).updatedState; let lines = groupLayerElements.get('lines'); let holes = groupLayerElements.get('holes'); let items = groupLayerElements.get('items'); let areas = groupLayerElements.get('areas'); if( lines ) lines.forEach( ( lineID ) => { let vertices = state.getIn(['scene', 'layers', groupLayerID, 'lines', lineID, 'vertices']) .map( vID => state.getIn(['scene', 'layers', groupLayerID, 'vertices', vID]) ); let { x: x1, y: y1 } = vertices.get(0); let { x: x2, y: y2 } = vertices.get(1); let { x: xM, y: yM } = GeometryUtils.midPoint( x1, y1, x2, y2 ); xBar += xM; yBar += yM; elementCount++; }); if( holes ) holes.forEach( holeID => { let hole = state.getIn(['scene', 'layers', groupLayerID, 'holes', holeID]); let lineVertices = state.getIn(['scene', 'layers', groupLayerID, 'lines', hole.line, 'vertices']) .map( vID => state.getIn(['scene', 'layers', groupLayerID, 'vertices', vID]) ); let { x: x1, y: y1 } = lineVertices.get(0); let { x: x2, y: y2 } = lineVertices.get(1); let { x, y } = GeometryUtils.extendLine( x1, y1, x2, y2, hole.offset * GeometryUtils.pointsDistance( x1, y1, x2, y2 ) ); xBar += x; yBar += y; elementCount++; }); if( items ) items.forEach( itemID => { let { x, y } = state.getIn(['scene', 'layers', groupLayerID, 'items', itemID]); xBar += x; yBar += y; elementCount++; }); if( areas ) areas.forEach( areaID => { let areaVertices = state.getIn(['scene', 'layers', groupLayerID, 'areas', areaID, 'vertices']) .map( vID => state.getIn(['scene', 'layers', groupLayerID, 'vertices', vID]) ).toJS(); let { x, y } = GeometryUtils.verticesMidPoint( areaVertices ); xBar += x; yBar += y; elementCount++; }); }); if( elementCount ) { state = this.setBarycenter( state, groupID, xBar / elementCount, yBar / elementCount ).updatedState; } return { updatedState: state }; } static removeElement( state, groupID, layerID, elementPrototype, elementID ) { let actualList = state.getIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype]); if( !actualList || !actualList.contains(elementID) ) { return { updatedState: state }; } state = state.setIn(['scene', 'groups', groupID, 'elements', layerID, elementPrototype], actualList.filterNot( el => el === elementID )); return { updatedState : state }; } static setAttributes( state, groupID, attributes ){ state = state.mergeIn(['scene', 'groups', groupID], attributes); return { updatedState : state }; } static setProperties( state, groupID, properties ){ state = state.mergeIn(['scene', 'groups', groupID, 'properties'], properties); return { updatedState : state }; } static remove( state, groupID ) { state = state.removeIn(['scene', 'groups', groupID]); return { updatedState : state }; } static removeAndDeleteElements( state, groupID ) { let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]); layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => { state = Layer.unselectAll( state, groupLayerID ).updatedState; let lines = groupLayerElements.get('lines'); let holes = groupLayerElements.get('holes'); let items = groupLayerElements.get('items'); let areas = groupLayerElements.get('areas'); if( lines ) { lines.forEach( lineID => { state = Line.remove( state, groupLayerID, lineID ).updatedState; state = Layer.detectAndUpdateAreas( state, groupLayerID ).updatedState; }); } if( holes ) holes.forEach( holeID => { state = Hole.remove( state, groupLayerID, holeID ).updatedState; }); if( items ) items.forEach( itemID => { state = Item.remove( state, groupLayerID, itemID ).updatedState; }); //( actually ) no effect by area's destruction if( false && areas ) areas.forEach( areaID => { state = Area.remove( state, groupLayerID, areaID ).updatedState; }); }); state = state.deleteIn([ 'scene', 'groups', groupID ]); return { updatedState: state }; } static translate( state, groupID, x, y ) { let deltaX = x - state.getIn(['scene', 'groups', groupID, 'x']); let deltaY = y - state.getIn(['scene', 'groups', groupID, 'y']); let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]); layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => { let lines = groupLayerElements.get('lines'); //let holes = groupLayerElements.get('holes'); let items = groupLayerElements.get('items'); //let areas = groupLayerElements.get('areas'); //move vertices instead lines avoiding multiple vertex translation if( lines ) { let vertices = {}; lines.forEach( lineID => { let line = state.getIn(['scene', 'layers', groupLayerID, 'lines', lineID]); if( !vertices[ line.vertices.get(0) ] ) vertices[ line.vertices.get(0) ] = state.getIn(['scene', 'layers', groupLayerID, 'vertices', line.vertices.get(0)]) if( !vertices[ line.vertices.get(1) ] ) vertices[ line.vertices.get(1) ] = state.getIn(['scene', 'layers', groupLayerID, 'vertices', line.vertices.get(1)]) }); for( let vertexID in vertices ) { let { x: xV, y: yV } = vertices[ vertexID ]; state = Vertex.setAttributes( state, groupLayerID, vertexID, new Map({ x: xV + deltaX, y: yV + deltaY }) ).updatedState; } //need to be separated from setAttributes cycle for( let vertexID in vertices ) { state = Vertex.beginDraggingVertex( state, groupLayerID, vertexID ).updatedState; state = Vertex.endDraggingVertex( state ).updatedState; } } if( items ) state = items .map( itemID => state.getIn(['scene', 'layers', groupLayerID, 'items', itemID]) ) .reduce( ( newState, item ) => { let { x: xI, y: yI } = item; return Item.setAttributes( newState, groupLayerID, item.id, new Map({ x: xI + deltaX, y: yI + deltaY }) ).updatedState; }, state ); //translation of holes and areas should not take any effect //if( holes ) holes.forEach( holeID => { state = Hole.select( state, groupLayerID, holeID ).updatedState; }); //if( areas ) areas.forEach( areaID => { state = Area.select( state, groupLayerID, areaID ).updatedState; }); state = Layer.detectAndUpdateAreas( state, groupLayerID ).updatedState; }); state = this.setBarycenter( state, groupID, x, y ).updatedState; state = Group.select( state, groupID ).updatedState; return { updatedState: state }; } static rotate( state, groupID, newAlpha ) { let { x: barX, y: barY, rotation } = state.getIn(['scene', 'groups', groupID]); let alpha = newAlpha - rotation; state = Group.setAttributes( state, groupID, new Map({ rotation: newAlpha }) ).updatedState; let layerList = state.getIn([ 'scene', 'groups', groupID, 'elements' ]); layerList.entrySeq().forEach( ([groupLayerID, groupLayerElements]) => { let lines = groupLayerElements.get('lines'); let holes = groupLayerElements.get('holes'); let items = groupLayerElements.get('items'); let areas = groupLayerElements.get('areas'); //move vertices instead lines avoiding multiple vertex translation if( lines ) { let vertices = {}; lines.forEach( lineID => { let line = state.getIn(['scene', 'layers', groupLayerID, 'lines', lineID]); if( !vertices[ line.vertices.get(0) ] ) vertices[ line.vertices.get(0) ] = state.getIn(['scene', 'layers', groupLayerID, 'vertices', line.vertices.get(0)]) if( !vertices[ line.vertices.get(1) ] ) vertices[ line.vertices.get(1) ] = state.getIn(['scene', 'layers', groupLayerID, 'vertices', line.vertices.get(1)]) }); for( let vertexID in vertices ) { let { x: xV, y: yV } = vertices[ vertexID ]; let { x: newX, y: newY } = GeometryUtils.rotatePointAroundPoint( xV, yV, barX, barY, alpha ); state = Vertex.setAttributes( state, groupLayerID, vertexID, new Map({ x: newX, y: newY }) ).updatedState; } //need to be separated from setAttributes cycle for( let vertexID in vertices ) { state = Vertex.beginDraggingVertex( state, groupLayerID, vertexID ).updatedState; state = Vertex.endDraggingVertex( state ).updatedState; } } if( items ) state = items .map( itemID => state.getIn(['scene', 'layers', groupLayerID, 'items', itemID]) ) .reduce( ( newState, item ) => { let { x: xI, y: yI, rotation: rI } = item; let { x: newX, y: newY } = GeometryUtils.rotatePointAroundPoint( xI, yI, barX, barY, alpha ); return Item.setAttributes( newState, groupLayerID, item.id, new Map({ x: newX, y: newY, rotation: rI + alpha }) ).updatedState; }, state ); //rotation of holes and areas should not take any effect //if( holes ) holes.forEach( holeID => { state = Hole.select( state, groupLayerID, holeID ).updatedState; }); //if( areas ) areas.forEach( areaID => { state = Area.select( state, groupLayerID, areaID ).updatedState; }); state = Layer.detectAndUpdateAreas( state, groupLayerID ).updatedState; }); state = Group.select( state, groupID ).updatedState; return { updatedState: state }; } } export { Group as default };