react-planner-electron
Version:
react-planner-electron is a React Component for plans design. Draw a 2D floorplan and navigate it in 3D mode.
221 lines (173 loc) • 7.91 kB
JavaScript
import { Map, List } from 'immutable';
import { Vertex as VertexModel } from '../models';
import {
IDBroker,
GeometryUtils,
SnapSceneUtils,
SnapUtils
} from '../utils/export';
import {
MODE_DRAGGING_VERTEX,
MODE_IDLE
} from '../constants';
import { Layer, Line, Group } from '../class/export';
class Vertex{
static add( state, layerID, x, y, relatedPrototype, relatedID ) {
let vertex = state.getIn(['scene', 'layers', layerID, 'vertices']).find(vertex => GeometryUtils.samePoints(vertex, {x, y}));
if (vertex) {
vertex = vertex.update(relatedPrototype, related => related.push(relatedID));
}
else {
vertex = new VertexModel({
id: IDBroker.acquireID(),
name: 'Vertex',
x, y,
[relatedPrototype]: new List([relatedID])
});
}
state = state.setIn(['scene', 'layers', layerID, 'vertices', vertex.id], vertex);
return { updatedState: state, vertex };
}
static setAttributes(state, layerID, vertexID, vertexAttributes) {
state = state.mergeIn(['scene', 'layers', layerID, 'vertices', vertexID ], vertexAttributes)
return { updatedState: state };
}
static addElement( state, layerID, vertexID, elementPrototype, elementID ) {
state = state.updateIn(['scene', 'layers', layerID, 'vertices', vertexID, elementPrototype], list => list.push( elementID ) );
return { updatedState: state };
}
static removeElement( state, layerID, vertexID, elementPrototype, elementID ) {
let elementIndex = state.getIn(['scene', 'layers', layerID, 'vertices', vertexID, elementPrototype]).findIndex( el => el === elementID );
if( elementIndex !== -1 ) {
state = state.updateIn(['scene', 'layers', layerID, 'vertices', vertexID, elementPrototype], list => list.remove( elementIndex ) );
}
return { updatedState: state };
}
static select( state, layerID, vertexID ){
state = state.setIn(['scene', 'layers', layerID, 'vertices', vertexID, 'selected'], true);
state = state.updateIn(['scene', 'layers', layerID, 'selected', 'vertices'], elems => elems.push(vertexID));
return { updatedState: state };
}
static unselect( state, layerID, vertexID ){
state = state.setIn(['scene', 'layers', layerID, 'vertices', vertexID, 'selected'], false);
state = state.updateIn(['scene', 'layers', layerID, 'selected', 'vertices'], elems => elems.filter( el => el.id !== vertexID ));
return { updatedState: state };
}
static remove( state, layerID, vertexID, relatedPrototype, relatedID, forceRemove ) {
let vertex = state.getIn(['scene', 'layers', layerID, 'vertices', vertexID]);
if( vertex ) {
if( relatedPrototype && relatedID ) vertex = vertex.update(relatedPrototype, related => {
let index = related.findIndex(ID => relatedID === ID);
return related.delete(index);
});
let inUse = vertex.areas.size || vertex.lines.size;
if( inUse && !forceRemove ) {
state = state.setIn(['scene', 'layers', layerID, 'vertices', vertexID], vertex);
}
else {
state = state.deleteIn(['scene', 'layers', layerID, 'vertices', vertexID]);
}
}
return { updatedState: state };
}
static beginDraggingVertex(state, layerID, vertexID, x, y) {
let snapElements = SnapSceneUtils.sceneSnapElements(state.scene, new List(), state.snapMask);
state = state.merge({
mode: MODE_DRAGGING_VERTEX,
snapElements,
draggingSupport: Map({
layerID,
vertexID,
previousMode: state.get('mode')
})
});
return { updatedState: state };
}
static updateDraggingVertex(state, x, y) {
let { draggingSupport, snapElements, scene } = state;
let snap = null;
if (state.snapMask && !state.snapMask.isEmpty()) {
snap = SnapUtils.nearestSnap(snapElements, x, y, state.snapMask);
if (snap) ({ x, y } = snap.point);
}
let layerID = draggingSupport.get('layerID');
let vertexID = draggingSupport.get('vertexID');
state = state.merge({
activeSnapElement: snap ? snap.snap : null,
scene: scene.mergeIn(['layers', layerID, 'vertices', vertexID], { x, y })
});
return { updatedState: state };
}
static endDraggingVertex(state, x, y) {
let { draggingSupport } = state;
let layerID = draggingSupport.get('layerID');
let vertexID = draggingSupport.get('vertexID');
let lines = state.getIn(['scene', 'layers', layerID, 'vertices', vertexID, 'lines']);
if( lines ) {
state = lines.reduce(
( reducedState, lineID ) =>
{
if( !reducedState.getIn(['scene', 'layers', layerID, 'lines', lineID])) return reducedState;
let v_id0 = reducedState.getIn(['scene', 'layers', layerID, 'lines', lineID, 'vertices', 0]);
let v_id1 = reducedState.getIn(['scene', 'layers', layerID, 'lines', lineID, 'vertices', 1]);
let oldVertexID = v_id0 === vertexID ? v_id1 : v_id0;
let oldVertex = reducedState.getIn(['scene', 'layers', layerID, 'vertices', oldVertexID]);
let vertex = reducedState.getIn(['scene', 'layers', layerID, 'vertices', vertexID]);
let oldHoles = [];
let orderedVertices = GeometryUtils.orderVertices([oldVertex, vertex]);
let holes = reducedState
.getIn(['scene', 'layers', layerID, 'lines', lineID, 'holes'])
.forEach(holeID => {
let hole = reducedState.getIn(['scene', 'layers', layerID, 'holes', holeID]);
let oldLineLength = GeometryUtils.pointsDistance(oldVertex.x, oldVertex.y, vertex.x, vertex.y);
let offset = GeometryUtils.samePoints( orderedVertices[1], reducedState.getIn(['scene', 'layers', layerID, 'lines', lineID, 'vertices', 1]) ) ? ( 1 - hole.offset ) : hole.offset;
let offsetPosition = GeometryUtils.extendLine( oldVertex.x, oldVertex.y, vertex.x, vertex.y, oldLineLength * offset );
oldHoles.push({hole, offsetPosition});
});
let lineType = reducedState.getIn(['scene', 'layers', layerID, 'lines', lineID, 'type']);
let lineProps = reducedState.getIn(['scene', 'layers', layerID, 'lines', lineID, 'properties']);
let lineGroups = reducedState //get groups membership if present
.getIn(['scene', 'groups'])
.filter( group => {
const lines = group.getIn(['elements', layerID, 'lines']);
return lines && lines.contains(lineID);
});
reducedState = Layer.removeZeroLengthLines( reducedState, layerID ).updatedState;
reducedState = Layer.mergeEqualsVertices( reducedState, layerID, vertexID ).updatedState;
reducedState = Line.remove( reducedState, layerID, lineID ).updatedState;
if (!GeometryUtils.samePoints(oldVertex, vertex)) {
let ret = Line.createAvoidingIntersections(
reducedState,
layerID,
lineType,
oldVertex.x,
oldVertex.y,
vertex.x,
vertex.y,
lineProps,
oldHoles
);
reducedState = ret.updatedState;
//re-add to old line's groups if present
ret.lines.forEach( addedLine => {
lineGroups.forEach( oldLineGroup => {
reducedState = Group.addElement( reducedState, oldLineGroup.id, layerID, 'lines', addedLine.id ).updatedState;
});
});
}
return reducedState;
},
state
);
}
state = Layer.detectAndUpdateAreas( state, layerID ).updatedState;
state = state.merge({
mode: draggingSupport.get('previousMode'),
draggingSupport: null,
activeSnapElement: null,
snapElements: new List()
});
return { updatedState: state };
}
}
export { Vertex as default };