Skip to content

Commit

Permalink
Merge pull request #140 from kbss-cvut/feature/persist-fta-diagram-la…
Browse files Browse the repository at this point in the history
…yout

Feature/persist fta diagram layout
  • Loading branch information
kostobog authored Jan 16, 2024
2 parents 351c789 + b528f78 commit 4e1b4a7
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 26 deletions.
43 changes: 35 additions & 8 deletions src/components/editor/faultTree/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {ROUTES} from "@utils/constants";
import {extractFragment} from "@services/utils/uriIdentifierUtils";
import {FTABoundary} from "@components/editor/faultTree/shapes/shapesDefinitions";
import * as joint from "jointjs";
import {Rectangle} from "@models/utils/Rectangle";
import {JOINTJS_NODE_MODEL} from "@components/editor/faultTree/shapes/constants";

const Editor = ({setAppBarName}: DashboardTitleProps) => {
const history = useNavigate();
Expand All @@ -30,6 +32,16 @@ const Editor = ({setAppBarName}: DashboardTitleProps) => {
const [highlightedElementView, setHighlightedElementView] = useState(null)
const _localContext = useLocalContext({rootEvent: rootEvent, highlightedElementView: highlightedElementView})

const getRootEvent = () : FaultEvent => {
// @ts-ignore
return _localContext.rootEvent;
}

const getHighlightedElementView = () => {
// @ts-ignore
return _localContext.highlightedElementView
}

useEffect(() => {
if (faultTree) {
setAppBarName(faultTree.name)
Expand Down Expand Up @@ -58,17 +70,16 @@ const Editor = ({setAppBarName}: DashboardTitleProps) => {

const [contextMenuAnchor, setContextMenuAnchor] = useState<ElementContextMenuAnchor>(contextMenuDefaultAnchor)
const handleContextMenu = (elementView, evt) => {
const elementIri = elementView.model.get('custom/faultEventIri');
// @ts-ignore
const foundEvent = findEventByIri(elementIri, _localContext.rootEvent);
const elementIri = elementView.model.get(JOINTJS_NODE_MODEL.faultEventIri);

const foundEvent = findEventByIri(elementIri, getRootEvent());
setContextMenuSelectedEvent(foundEvent);
setContextMenuAnchor({mouseX: evt.pageX, mouseY: evt.pageY,})
}

const handleElementPointerClick = (elementView) => {
const elementIri = elementView.model.get('custom/faultEventIri');
// @ts-ignore
const foundEvent = findEventByIri(elementIri, _localContext.rootEvent);
const elementIri = elementView.model.get(JOINTJS_NODE_MODEL.faultEventIri);
const foundEvent = findEventByIri(elementIri, getRootEvent());

setSidebarSelectedEvent(foundEvent);
setHighlightedElementView(elementView);
Expand All @@ -79,15 +90,30 @@ const Editor = ({setAppBarName}: DashboardTitleProps) => {
hideHighlightedBorders();
}

const handleMoveEvent = (elementView, evt) => {
const faultEventIri = elementView.model.get(JOINTJS_NODE_MODEL.faultEventIri);

const movedEvent = findEventByIri(faultEventIri, getRootEvent());
const rect :Rectangle = movedEvent.rectangle;
const size = elementView.model.attributes.size;
const position = elementView.model.attributes.position;
if(rect.x != position.x || rect.y != position.y || rect.width != size.width || rect.height != size.height) {
rect.x = position.x;
rect.y = position.y;
rect.width = size.width;
rect.height = size.height;
faultEventService.updateEventRectangle(faultEventIri, rect.iri, rect);
}
}

const highlightBorders = (elementView) => {
const tools = new joint.dia.ToolsView({
tools: [FTABoundary.factory()]
});
elementView.addTools(tools);
}
const hideHighlightedBorders = () => {
// @ts-ignore
_localContext.highlightedElementView?.removeTools();
getHighlightedElementView()?.removeTools();
setHighlightedElementView(null);
}

Expand Down Expand Up @@ -134,6 +160,7 @@ const Editor = ({setAppBarName}: DashboardTitleProps) => {
onElementPointerClick={handleElementPointerClick}
onBlankPointerClick={handleBlankPointerClick}
onConvertToTable={() => setFailureModesTableOpen(true)}
onNodeMove={handleMoveEvent}
setHighlightedElement={setHighlightedElementView}
refreshTree={refreshTree}
/>
Expand Down
34 changes: 30 additions & 4 deletions src/components/editor/faultTree/canvas/EditorCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ import * as svgPanZoom from "svg-pan-zoom";
import {SVG_PAN_ZOOM_OPTIONS} from "@utils/constants";
import {saveSvgAsPng} from "save-svg-as-png";
import renderTree from "@components/editor/faultTree/shapes/RenderTree";
import {JOINTJS_NODE_MODEL} from "@components/editor/faultTree/shapes/constants";

enum MOVE_NODE {
DRAGGING = 0,
RELEASING =1
}


interface Props {
treeName: string,
Expand All @@ -24,6 +31,7 @@ interface Props {
onBlankPointerClick: () => void,
onEventUpdated: (faultEvent: FaultEvent) => void,
onConvertToTable: () => void,
onNodeMove: (element: any, evt: any) => void,
refreshTree: () => void,
setHighlightedElement: (element: any) => void,
}
Expand All @@ -37,6 +45,7 @@ const EditorCanvas = ({
onBlankPointerClick,
onEventUpdated,
onConvertToTable,
onNodeMove,
refreshTree,
setHighlightedElement
}: Props) => {
Expand All @@ -51,14 +60,16 @@ const EditorCanvas = ({
const [currentZoom, setCurrentZoom] = useState(1);
const [isExportingImage, setIsExportingImage] = useState(false);

let dragStartPosition = null;

useEffect(() => {
const canvasWidth = containerRef.current.clientWidth;
const canvasHeight = containerRef.current.clientHeight;

const graph = new joint.dia.Graph;
const divContainer = document.getElementById("jointjs-container");
const paper = new joint.dia.Paper({
// @ts-ignore

el: divContainer,
model: graph,
width: canvasWidth,
Expand All @@ -77,14 +88,20 @@ const EditorCanvas = ({
});
setSvgZoom(diagramZoom);

// @ts-ignore
paper.on({
'element:contextmenu': (elementView, evt) => {
onElementContextMenu(elementView, evt)
},
'element:pointerclick': (elementView, evt) => {
onElementPointerClick(elementView, evt)
},
'element:pointermove': (elementView: joint.dia.ElementView, evt: joint.dia.Event, x: number, y: number) =>
{
handleNodeMove(MOVE_NODE.DRAGGING, elementView, evt, x, y);
},
'element:pointerup': (elementView: joint.dia.ElementView, evt: joint.dia.Event, x: number, y: number) => {
handleNodeMove(MOVE_NODE.RELEASING, elementView, evt, x, y);
},
'blank:pointerclick': () => onBlankPointerClick(),
'blank:pointerdown': () => diagramZoom.enablePan(),
'blank:pointerup': () => diagramZoom.disablePan(),
Expand All @@ -94,6 +111,15 @@ const EditorCanvas = ({
setJointPaper(paper);
}, []);

const handleNodeMove = (move: MOVE_NODE, elementView: joint.dia.ElementView, evt: joint.dia.Event, x: number, y: number) => {
if(!dragStartPosition && move === MOVE_NODE.DRAGGING)
dragStartPosition = [x, y];
else if(dragStartPosition && (dragStartPosition[0] != x || dragStartPosition[1] != y) && move === MOVE_NODE.RELEASING){
dragStartPosition = null;
onNodeMove(elementView, evt);
}
}

useEffect(() => {
if (isExportingImage) {
const svgPaper = document.querySelector('#jointjs-container > svg');
Expand All @@ -118,15 +144,15 @@ const EditorCanvas = ({
const autoLayoutElements = [];
const manualLayoutElements = [];
graph.getElements().forEach((el) => {
const faultEventIri = el.get('custom/faultEventIri');
const faultEventIri = el.get(JOINTJS_NODE_MODEL.faultEventIri);
if(faultEventIri && faultEventIri === sidebarSelectedEvent?.iri) {
const elementView = el.findView(jointPaper);
setHighlightedElement(elementView)
}

if (el.get('type') === 'fta.ConditioningEvent') {
manualLayoutElements.push(el);
} else {
} else if(!el.get(JOINTJS_NODE_MODEL.hasPersistentPosition)) {
autoLayoutElements.push(el);
}
});
Expand Down
3 changes: 2 additions & 1 deletion src/components/editor/faultTree/shapes/FaultEventShape.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {createShape} from "@services/jointService";
import {sequenceListToArray} from "@services/faultEventService";
import * as faultEventService from "@services/faultEventService";
import {has} from "lodash";
import {JOINTJS_NODE_MODEL} from "@components/editor/faultTree/shapes/constants";

const FaultEventShape = ({addSelf, treeEvent, parentShape}: JointEventShapeProps) => {
const [currentShape, setCurrentShape] = useState<any>(undefined)
Expand All @@ -28,7 +29,7 @@ const FaultEventShape = ({addSelf, treeEvent, parentShape}: JointEventShapeProps
}

// @ts-ignore
eventShape.set('custom/faultEventIri', treeEvent.iri)
eventShape.set(JOINTJS_NODE_MODEL.faultEventIri, treeEvent.iri)

setCurrentShape(eventShape)

Expand Down
10 changes: 8 additions & 2 deletions src/components/editor/faultTree/shapes/RenderTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as faultEventService from "../../../../services/faultEventService";
import {Link} from "./shapesDefinitions";
import {flatten} from "lodash";
import {has} from "lodash";
import {JOINTJS_NODE_MODEL} from "@components/editor/faultTree/shapes/constants";

const renderLink = (container, source, target) => {
// @ts-ignore
Expand All @@ -26,8 +27,13 @@ const renderTree = (container, node, parentShape = null) => {
nodeShape.attr(['probabilityLabel', 'text'], node.probability.toExponential(2));
}
// @ts-ignore
nodeShape.set('custom/faultEventIri', node.iri)

nodeShape.set(JOINTJS_NODE_MODEL.faultEventIri, node.iri)
const r = node.rectangle;
if(r && r.x && r.y && r.width && r.height){
nodeShape.position(node.rectangle.x, node.rectangle.y);
// @ts-ignore
nodeShape.set(JOINTJS_NODE_MODEL.hasPersistentPosition, true);
}
// Render link
if(parentShape){
renderLink(container, parentShape, nodeShape)
Expand Down
4 changes: 4 additions & 0 deletions src/components/editor/faultTree/shapes/constants.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const JOINTJS_NODE_MODEL = {
faultEventIri: 'custom/faultEventIri',
hasPersistentPosition: 'custom/has-persistent-position'
}
19 changes: 13 additions & 6 deletions src/components/editor/system/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,20 @@ const Editor = ({setAppBarName}: DashboardTitleProps) => {
setContextMenuAnchor({mouseX: evt.pageX, mouseY: evt.pageY,})
}

const getSystem = () => {
// @ts-ignore
return _localContext.system;
}

const getHighlightedElementView = () => {
// @ts-ignore
return _localContext.highlightedElementView
}

const handleContextMenu = (elementView, evt) => {
const componentIri = elementView.model.get('custom/componentIri');

// @ts-ignore
const flattenedComponents = flatten([_localContext.system.components]);
const flattenedComponents = flatten([getSystem().components]);
const index = findIndex(flattenedComponents, el => el.iri === componentIri);
if (index > -1) {
setContextMenuSelectedComponent(flattenedComponents[index]);
Expand All @@ -60,8 +69,7 @@ const Editor = ({setAppBarName}: DashboardTitleProps) => {
const handleElementPointerClick = (elementView) => {
const componentIri = elementView.model.get('custom/componentIri');

// @ts-ignore
const flattenedComponents = flatten([_localContext.system.components]);
const flattenedComponents = flatten([getSystem().components]);
const index = findIndex(flattenedComponents, el => el.iri === componentIri);
if (index > -1) {
setSidebarSelectedComponent(flattenedComponents[index]);
Expand All @@ -82,8 +90,7 @@ const Editor = ({setAppBarName}: DashboardTitleProps) => {
elementView.addTools(tools);
}
const hideHighlightedBorders = () => {
// @ts-ignore
_localContext.highlightedElementView?.removeTools();
getHighlightedElementView()?.removeTools();
setHighlightedElementView(null);
}

Expand Down
2 changes: 0 additions & 2 deletions src/components/editor/system/canvas/EditorCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ const EditorCanvas = ({
const graph = new joint.dia.Graph;
const divContainer = document.getElementById("jointjs-system-container");
const paper = new joint.dia.Paper({
// @ts-ignore
el: divContainer,
model: graph,
width: canvasWidth,
Expand All @@ -73,7 +72,6 @@ const EditorCanvas = ({
});
setSvgZoom(diagramZoom);

// @ts-ignore
paper.on({
'blank:contextmenu': (evt) => {
onBlankContextMenu(evt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ const ComponentEditMenu = ({component, onComponentUpdated}: Props) => {


useEffect(() => {
// @ts-ignore
reset(defaultValues)
}, [component])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ const ComponentFunctionsList = ({ component }) => {
})
}

// @ts-ignore
return (
<React.Fragment>
<List>
Expand Down
5 changes: 4 additions & 1 deletion src/models/eventModel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import VocabularyUtils from "@utils/VocabularyUtils";
import {AbstractModel, CONTEXT as ABSTRACT_CONTEXT} from "@models/abstractModel";
import {FailureMode, CONTEXT as FAILURE_MODE_CONTEXT} from "@models/failureModeModel";
import {Rectangle, CONTEXT as RECTANGLE_CONTEXT, PREFIX as DIAGRAM_PREFIX} from "@models/utils/Rectangle";

const ctx = {
"name": VocabularyUtils.PREFIX + "hasName",
Expand All @@ -14,9 +15,10 @@ const ctx = {
"failureMode": VocabularyUtils.PREFIX + "hasFailureMode",
"sequenceProbability": VocabularyUtils.PREFIX + "hasSequenceProbability",
"childrenSequence": VocabularyUtils.PREFIX + "hasChildrenSequence",
"rectangle": DIAGRAM_PREFIX + "has-rectangle",
};

export const CONTEXT = Object.assign({}, ctx, ABSTRACT_CONTEXT, FAILURE_MODE_CONTEXT);
export const CONTEXT = Object.assign({}, ctx, ABSTRACT_CONTEXT, FAILURE_MODE_CONTEXT, RECTANGLE_CONTEXT);

export enum EventType {
BASIC = "BASIC",
Expand All @@ -36,6 +38,7 @@ export interface FaultEvent extends AbstractModel {
failureMode?: FailureMode,
sequenceProbability?: number,
childrenSequence?: any,
rectangle?: Rectangle,
}

export enum GateType {
Expand Down
20 changes: 20 additions & 0 deletions src/models/utils/Rectangle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const PREFIX = "http://onto.fel.cvut.cz/ontologies/diagram/";

const ctx = {
"iri": "@id",
"types": "@type",
"x": PREFIX + "x",
"y": PREFIX + "y",
"width": PREFIX + "width",
"height": PREFIX + "height"
};

export const CONTEXT = Object.assign({}, ctx);

export interface Rectangle {
iri?: string,
x?: number,
y?: number,
width?: number,
height?: number
}
Loading

0 comments on commit 4e1b4a7

Please sign in to comment.