diff --git a/SBOLCanvasBackend/src/utils/Converter.java b/SBOLCanvasBackend/src/utils/Converter.java index a46cd101..8fb29c9b 100644 --- a/SBOLCanvasBackend/src/utils/Converter.java +++ b/SBOLCanvasBackend/src/utils/Converter.java @@ -659,8 +659,7 @@ private Set createModuleView(SBOLDocument document, mxGraph mxCell glyphCell = this.getGraphicalLayout(graph, glyphArray[glyphIndex].getIdentity()); if (glyphCell != null) { glyphCell.setValue(glyphComponent.getDefinition().getIdentity().toString()); - //TODO fix the base style name - glyphCell.setStyle(STYLE_SEQUENCE_FEATURE); + glyphCell.setStyle(STYLE_SEQUENCE_FEATURE + ";" + glyphCell.getStyle()); model.add(container, glyphCell, glyphIndex); } else { glyphCell = (mxCell) graph.insertVertex(container, null, @@ -691,9 +690,9 @@ private Set createModuleView(SBOLDocument document, mxGraph Set interactions = modDef.getInteractions(); for (Interaction interaction : interactions) { Annotation interactionAnn = interaction.getAnnotation(new QName(uriPrefix, "edge", annPrefix)); - mxCell edge = null; - if (interactionAnn != null) { - edge = (mxCell) decodeMxGraphObject(interactionAnn.getStringValue()); + mxCell edge = this.getGraphicalLayout(graph, interaction.getIdentity()); + if (edge != null) { + edge.setStyle(STYLE_INTERACTION + ";"+edge.getStyle()); edge = (mxCell) model.add(rootViewCell, edge, 0); } else { edge = (mxCell) graph.insertEdge(rootViewCell, null, null, null, null); @@ -797,6 +796,7 @@ private void createComponentView(SBOLDocument document, mxGraph graph, Component private mxGraph parseGraph(InputStream graphStream) throws IOException { mxGraph graph = new mxGraph(); + ((mxGraphModel) graph.getModel()).setMaintainEdgeParent(false); Document document = mxXmlUtils.parseXml(mxUtils.readInputStream(graphStream)); mxCodec codec = new mxCodec(document); codec.decode(document.getDocumentElement(), graph.getModel()); @@ -909,44 +909,37 @@ private void createGraphicalLayout(mxGraph graph, mxCell cell, URI reference) annList.add(new Annotation(new QName(uriPrefix, "height", annPrefix), cellGeometry.getHeight())); // styling - String strokeColor = (String) styles.get(mxConstants.STYLE_STROKECOLOR); - if (strokeColor != null) - annList.add( - new Annotation(new QName(uriPrefix, "strokeColor", annPrefix), strokeColor)); + if (cell.getStyle().contains(mxConstants.STYLE_STROKECOLOR)) + annList.add(new Annotation(new QName(uriPrefix, "strokeColor", annPrefix), + (String) styles.get(mxConstants.STYLE_STROKECOLOR))); - String strokeOpacity = (String) styles.get(mxConstants.STYLE_STROKE_OPACITY); - if (strokeOpacity != null) + if (cell.getStyle().contains(mxConstants.STYLE_STROKE_OPACITY)) annList.add(new Annotation(new QName(uriPrefix, "strokeOpacity", annPrefix), - strokeOpacity)); + (String) styles.get(mxConstants.STYLE_STROKE_OPACITY))); - String strokeWidth = (String) styles.get(mxConstants.STYLE_STROKEWIDTH); - if (strokeWidth != null) - annList.add( - new Annotation(new QName(uriPrefix, "strokeWidth", annPrefix), strokeWidth)); + if (cell.getStyle().contains(mxConstants.STYLE_STROKEWIDTH)) + annList.add(new Annotation(new QName(uriPrefix, "strokeWidth", annPrefix), + (String) styles.get(mxConstants.STYLE_STROKEWIDTH))); - String fillColor = (String) styles.get(mxConstants.STYLE_FILLCOLOR); - if (fillColor != null) - annList.add(new Annotation(new QName(uriPrefix, "fillColor", annPrefix), fillColor)); + if (cell.getStyle().contains(mxConstants.STYLE_FILLCOLOR)) + annList.add(new Annotation(new QName(uriPrefix, "fillColor", annPrefix), + (String) styles.get(mxConstants.STYLE_FILLCOLOR))); - String fillOpacity = (String) styles.get(mxConstants.STYLE_FILL_OPACITY); - if (fillOpacity != null) - annList.add( - new Annotation(new QName(uriPrefix, "fillOpacity", annPrefix), fillOpacity)); + if (cell.getStyle().contains(mxConstants.STYLE_FILL_OPACITY)) + annList.add(new Annotation(new QName(uriPrefix, "fillOpacity", annPrefix), + (String) styles.get(mxConstants.STYLE_FILL_OPACITY))); - String fontColor = (String) styles.get(mxConstants.STYLE_FONTCOLOR); - if (fontColor != null) - annList.add(new Annotation(new QName(uriPrefix, "fontColor", annPrefix), fontColor)); + if (cell.getStyle().contains(mxConstants.STYLE_FONTCOLOR)) + annList.add(new Annotation(new QName(uriPrefix, "fontColor", annPrefix), + (String) styles.get(mxConstants.STYLE_FONTCOLOR))); - String fontSize = (String) styles.get(mxConstants.STYLE_FONTSIZE); - if (fontSize != null) - annList.add(new Annotation(new QName(uriPrefix, "fontSize", annPrefix), fontSize)); + if (cell.getStyle().contains(mxConstants.STYLE_FONTSIZE)) + annList.add(new Annotation(new QName(uriPrefix, "fontSize", annPrefix), + (String) styles.get(mxConstants.STYLE_FONTSIZE))); if (cell.getStyle().contains(STYLE_TEXTBOX)) annList.add(new Annotation(new QName(uriPrefix, "text", annPrefix), (String) cell.getValue())); - // annList.add(new Annotation(new QName(uriPrefix, "reference", annPrefix), - // reference)); - Annotation nodeGlyph = layout.createAnnotation(new QName(uriPrefix, "NodeGlyph", annPrefix), new QName(uriPrefix, "attributes", annPrefix), "attributes", annList); nodeGlyph.setNestedIdentity(reference); @@ -956,50 +949,41 @@ private void createGraphicalLayout(mxGraph graph, mxCell cell, URI reference) List annList = new ArrayList(); // styling - String strokeColor = (String) styles.get(mxConstants.STYLE_STROKECOLOR); - if (strokeColor != null) - annList.add( - new Annotation(new QName(uriPrefix, "strokeColor", annPrefix), strokeColor)); + if (cell.getStyle().contains(mxConstants.STYLE_STROKECOLOR)) + annList.add(new Annotation(new QName(uriPrefix, "strokeColor", annPrefix), + (String) styles.get(mxConstants.STYLE_STROKECOLOR))); - String strokeOpacity = (String) styles.get(mxConstants.STYLE_STROKE_OPACITY); - if (strokeOpacity != null) + if (cell.getStyle().contains(mxConstants.STYLE_STROKE_OPACITY)) annList.add(new Annotation(new QName(uriPrefix, "strokeOpacity", annPrefix), - strokeOpacity)); + (String) styles.get(mxConstants.STYLE_STROKE_OPACITY))); - String strokeWidth = (String) styles.get(mxConstants.STYLE_STROKEWIDTH); - if (strokeWidth != null) - annList.add( - new Annotation(new QName(uriPrefix, "strokeWidth", annPrefix), strokeWidth)); + if (cell.getStyle().contains(mxConstants.STYLE_STROKEWIDTH)) + annList.add(new Annotation(new QName(uriPrefix, "strokeWidth", annPrefix), + (String) styles.get(mxConstants.STYLE_STROKEWIDTH))); - String arrowSize = (String) styles.get(mxConstants.STYLE_ENDSIZE); - if (arrowSize != null) - annList.add(new Annotation(new QName(uriPrefix, "endSize", annPrefix), arrowSize)); + if (cell.getStyle().contains(mxConstants.STYLE_ENDSIZE)) + annList.add(new Annotation(new QName(uriPrefix, "endSize", annPrefix), + (String) styles.get(mxConstants.STYLE_ENDSIZE))); - String sourceMargin = (String) styles.get(mxConstants.STYLE_SOURCE_PERIMETER_SPACING); - if (sourceMargin != null) + if (cell.getStyle().contains(mxConstants.STYLE_SOURCE_PERIMETER_SPACING)) annList.add(new Annotation(new QName(uriPrefix, "sourceSpacing", annPrefix), - sourceMargin)); + (String) styles.get(mxConstants.STYLE_SOURCE_PERIMETER_SPACING))); - String targetMargin = (String) styles.get(mxConstants.STYLE_TARGET_PERIMETER_SPACING); - if (targetMargin != null) + if (cell.getStyle().contains(mxConstants.STYLE_TARGET_PERIMETER_SPACING)) annList.add(new Annotation(new QName(uriPrefix, "targetSpacing", annPrefix), - targetMargin)); + (String) styles.get(mxConstants.STYLE_TARGET_PERIMETER_SPACING))); - String edgeStyle = (String) styles.get(mxConstants.STYLE_EDGE); - if (edgeStyle != null) - annList.add(new Annotation(new QName(uriPrefix, "edge", annPrefix), edgeStyle)); + if (cell.getStyle().contains(mxConstants.STYLE_EDGE)) + annList.add(new Annotation(new QName(uriPrefix, "edge", annPrefix), + (String) styles.get(mxConstants.STYLE_EDGE))); - String rounded = (String) styles.get(mxConstants.STYLE_ROUNDED); - if (rounded != null) + if (cell.getStyle().contains(mxConstants.STYLE_ROUNDED)) annList.add(new Annotation(new QName(uriPrefix, "rounded", annPrefix), - Integer.parseInt(rounded) == 1)); - - String curved = (String) styles.get("curved"); - if (curved != null) - annList.add(new Annotation(new QName(uriPrefix, "curved", annPrefix), Integer.parseInt(curved) == 1)); + Integer.parseInt((String) styles.get(mxConstants.STYLE_ROUNDED)) == 1)); - // annList.add(new Annotation(new QName(uriPrefix, "reference", annPrefix), - // reference)); + if (cell.getStyle().contains("curved")) + annList.add(new Annotation(new QName(uriPrefix, "curved", annPrefix), + Integer.parseInt((String) styles.get("curved")) == 1)); Annotation edgeGlyphAnn = layout.createAnnotation(new QName(uriPrefix, "EdgeGlyph", annPrefix), new QName(uriPrefix, "attributes", annPrefix), "attributes", annList); @@ -1047,95 +1031,128 @@ private mxCell getGraphicalLayout(mxGraph graph, URI refference) { break; } } - if(ann == null) + if (ann == null) return null; - mxCell[] cellArr = {cell}; + mxCell[] cellArr = { cell }; cell.setGeometry(new mxGeometry()); - if (ann.getQName().getLocalPart().equals("NodeGlyph")) { + if (ann.getQName().getLocalPart().equals("NodeGlyph")) { + cell.setVertex(true); for (Annotation attributeAnn : ann.getAnnotations()) { String value = attributeAnn.getStringValue(); switch (attributeAnn.getQName().getLocalPart()) { case "x": - cell.getGeometry().setX(Double.parseDouble(value)); break; + cell.getGeometry().setX(Double.parseDouble(value)); + break; case "y": - cell.getGeometry().setY(Double.parseDouble(value)); break; + cell.getGeometry().setY(Double.parseDouble(value)); + break; case "width": - cell.getGeometry().setWidth(Double.parseDouble(value)); break; + cell.getGeometry().setWidth(Double.parseDouble(value)); + break; case "height": - cell.getGeometry().setHeight(Double.parseDouble(value)); break; + cell.getGeometry().setHeight(Double.parseDouble(value)); + break; case "strokeColor": - graph.setCellStyles(mxConstants.STYLE_STROKECOLOR, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_STROKECOLOR, value, cellArr); + break; case "strokeOpacity": - graph.setCellStyles(mxConstants.STYLE_STROKE_OPACITY, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_STROKE_OPACITY, value, cellArr); + break; case "strokeWidth": - graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, cellArr); + break; case "fillColor": - graph.setCellStyles(mxConstants.STYLE_FILLCOLOR, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_FILLCOLOR, value, cellArr); + break; case "fillOpacity": - graph.setCellStyles(mxConstants.STYLE_FILL_OPACITY, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_FILL_OPACITY, value, cellArr); + break; case "fontColor": - graph.setCellStyles(mxConstants.STYLE_FONTCOLOR, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_FONTCOLOR, value, cellArr); + break; case "fontSize": - graph.setCellStyles(mxConstants.STYLE_FONTSIZE, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_FONTSIZE, value, cellArr); + break; case "text": - cell.setValue(value); break; + cell.setValue(value); + break; } } } else if (ann.getQName().getLocalPart().equals("EdgeGlyph")) { - for(Annotation attributeAnn : ann.getAnnotations()) { + cell.setEdge(true); + for (Annotation attributeAnn : ann.getAnnotations()) { String value = attributeAnn.getStringValue(); - switch(attributeAnn.getQName().getLocalPart()) { + switch (attributeAnn.getQName().getLocalPart()) { case "strokeColor": - graph.setCellStyles(mxConstants.STYLE_STROKECOLOR, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_STROKECOLOR, value, cellArr); + break; case "strokeOpacity": - graph.setCellStyles(mxConstants.STYLE_STROKE_OPACITY, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_STROKE_OPACITY, value, cellArr); + break; case "strokeWidth": - graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, cellArr); + break; case "endSize": - graph.setCellStyles(mxConstants.STYLE_ENDSIZE, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_ENDSIZE, value, cellArr); + break; case "sourceSpacing": - graph.setCellStyles(mxConstants.STYLE_SOURCE_PERIMETER_SPACING, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_SOURCE_PERIMETER_SPACING, value, cellArr); + break; case "targetSpacing": - graph.setCellStyles(mxConstants.STYLE_TARGET_PERIMETER_SPACING, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_TARGET_PERIMETER_SPACING, value, cellArr); + break; case "edge": - graph.setCellStyles(mxConstants.STYLE_EDGE, value, cellArr); break; + graph.setCellStyles(mxConstants.STYLE_EDGE, value, cellArr); + break; case "rounded": - graph.setCellStyles(mxConstants.STYLE_ROUNDED, value, cellArr); break; + if(value.equals("true")) { + graph.setCellStyles(mxConstants.STYLE_ROUNDED, "1", cellArr); + } + break; case "curved": - graph.setCellStyles("curved", value, cellArr); break; + if(value.equals("true")) { + graph.setCellStyles("curved", "1", cellArr); + } + break; case "sourcePoint": mxPoint sourcePoint = new mxPoint(); - for(Annotation sourceAnn: attributeAnn.getAnnotations()) { - switch(sourceAnn.getQName().getLocalPart()) { + for (Annotation sourceAnn : attributeAnn.getAnnotations()) { + switch (sourceAnn.getQName().getLocalPart()) { case "x": - sourcePoint.setX(Double.parseDouble(sourceAnn.getStringValue())); break; + sourcePoint.setX(Double.parseDouble(sourceAnn.getStringValue())); + break; case "y": - sourcePoint.setY(Double.parseDouble(sourceAnn.getStringValue())); break; + sourcePoint.setY(Double.parseDouble(sourceAnn.getStringValue())); + break; } } cell.getGeometry().setSourcePoint(sourcePoint); break; case "targetPoint": mxPoint targetPoint = new mxPoint(); - for(Annotation sourceAnn: attributeAnn.getAnnotations()) { - switch(sourceAnn.getQName().getLocalPart()) { + for (Annotation sourceAnn : attributeAnn.getAnnotations()) { + switch (sourceAnn.getQName().getLocalPart()) { case "x": - targetPoint.setX(Double.parseDouble(sourceAnn.getStringValue())); break; + targetPoint.setX(Double.parseDouble(sourceAnn.getStringValue())); + break; case "y": - targetPoint.setY(Double.parseDouble(sourceAnn.getStringValue())); break; + targetPoint.setY(Double.parseDouble(sourceAnn.getStringValue())); + break; } } cell.getGeometry().setSourcePoint(targetPoint); break; case "point": mxPoint point = new mxPoint(); - for(Annotation sourceAnn: attributeAnn.getAnnotations()) { - switch(sourceAnn.getQName().getLocalPart()) { + for (Annotation sourceAnn : attributeAnn.getAnnotations()) { + switch (sourceAnn.getQName().getLocalPart()) { case "x": - point.setX(Double.parseDouble(sourceAnn.getStringValue())); break; + point.setX(Double.parseDouble(sourceAnn.getStringValue())); + break; case "y": - point.setY(Double.parseDouble(sourceAnn.getStringValue())); break; + point.setY(Double.parseDouble(sourceAnn.getStringValue())); + break; } } cell.getGeometry().getPoints().add(point); diff --git a/SBOLCanvasFrontend/src/app/graph-base.ts b/SBOLCanvasFrontend/src/app/graph-base.ts index 26a360e7..38b4fcb0 100644 --- a/SBOLCanvasFrontend/src/app/graph-base.ts +++ b/SBOLCanvasFrontend/src/app/graph-base.ts @@ -96,6 +96,8 @@ export class GraphBase { // it's used mainly for 'actions', which for now means delete, later will mean undoing this.editor = new mx.mxEditor(); this.graph = this.editor.graph; + // edges shouldn't find the nearest common ancestor + this.graph.getModel().maintainEdgeParent = false; this.editor.setGraphContainer(this.graphContainer); this.graph.setCellsCloneable(false); @@ -222,8 +224,18 @@ export class GraphBase { this.anyForeignCellsFound = true; } + let reconstructCellStyle = false; + if (cell && cell.id > 1 && !cell.isViewCell()) { + if (!cell.style || cell.style.length == 0) + reconstructCellStyle = true; + else if (cell.getGeometry().height == 0) + reconstructCellStyle = true; + else if (cell.style.includes(GraphBase.STYLE_SEQUENCE_FEATURE) && !cell.style.includes(glyphDict[cell.value].partRole)) + reconstructCellStyle = true; + } + // reconstruct the cell style - if (cell && cell.id > 1 && (cell.style == null || cell.style.length == 0 || (!cell.isViewCell() && cell.getGeometry().height == 0))) { + if (reconstructCellStyle) { if (glyphDict[cell.value] != null) { if (glyphDict[cell.value].partType === 'DNA region') { // sequence feature @@ -232,8 +244,10 @@ export class GraphBase { } else { cell.style = cell.style.replace(GraphBase.STYLE_SEQUENCE_FEATURE, GraphBase.STYLE_SEQUENCE_FEATURE + glyphDict[cell.value].partRole); } - cell.geometry.width = GraphBase.sequenceFeatureGlyphWidth; - cell.geometry.height = GraphBase.sequenceFeatureGlyphHeight; + if (cell.geometry.width == 0) + cell.geometry.width = GraphBase.sequenceFeatureGlyphWidth; + if (cell.geometry.height == 0) + cell.geometry.height = GraphBase.sequenceFeatureGlyphHeight; } else { // molecular species cell.style = GraphBase.STYLE_MOLECULAR_SPECIES + "macromolecule"; @@ -246,7 +260,11 @@ export class GraphBase { if (name == "Biochemical Reaction" || name == "Non-Covalent Binding" || name == "Genetic Production") { name = "Process"; } - cell.style = GraphBase.STYLE_INTERACTION + name; + if(!cell.style){ + cell.style = GraphBase.STYLE_INTERACTION + name; + }else{ + cell.style = cell.style.replace(GraphBase.STYLE_INTERACTION, GraphBase.STYLE_INTERACTION+name); + } } } @@ -544,7 +562,7 @@ export class GraphBase { * Can only be called before this.graph is initialized */ initStyles() { - + // Main glyph settings. These are applied to sequence feature glyphs and molecular species glyphs this.baseMolecularSpeciesGlyphStyle = {}; this.baseMolecularSpeciesGlyphStyle[mx.mxConstants.STYLE_FILLCOLOR] = '#ffffff'; @@ -846,12 +864,12 @@ export class GraphBase { // sync circuit containers let circuitContainers = new Set(); - for(let movedCell of movedCells){ - if(movedCell.isSequenceFeatureGlyph()){ + for (let movedCell of movedCells) { + if (movedCell.isSequenceFeatureGlyph()) { circuitContainers.add(movedCell.getParent()); } } - for(let circuitContainer of Array.from(circuitContainers.values())){ + for (let circuitContainer of Array.from(circuitContainers.values())) { this.syncCircuitContainer(circuitContainer); } diff --git a/SBOLCanvasFrontend/src/app/graph.service.ts b/SBOLCanvasFrontend/src/app/graph.service.ts index 2484cf55..98ec0259 100644 --- a/SBOLCanvasFrontend/src/app/graph.service.ts +++ b/SBOLCanvasFrontend/src/app/graph.service.ts @@ -677,17 +677,17 @@ export class GraphService extends GraphHelpers { const selectedCell = this.graph.getSelectionCell(); cell.geometry.setTerminalPoint(new mx.mxPoint(x, y - GraphBase.defaultInteractionSize), false); cell.edge = true; - this.graph.addEdge(cell, null, selectedCell, null); + this.graph.addEdge(cell, this.graph.getCurrentRoot(), selectedCell, null); } else if (selectionCells.length == 2) { const sourceCell = selectionCells[0]; const destCell = selectionCells[1]; cell.edge = true; - this.graph.addEdge(cell, null, sourceCell, destCell); + this.graph.addEdge(cell, this.graph.getCurrentRoot(), sourceCell, destCell); } else { cell.geometry.setTerminalPoint(new mx.mxPoint(x, y + GraphBase.defaultInteractionSize), true); cell.geometry.setTerminalPoint(new mx.mxPoint(x + GraphBase.defaultInteractionSize, y), false); cell.edge = true; - this.graph.addEdge(cell, null, null, null); + this.graph.addEdge(cell, this.graph.getCurrentRoot(), null, null); } diff --git a/resources/README.txt b/resources/README.txt index 54e7ab27..2666285e 100644 --- a/resources/README.txt +++ b/resources/README.txt @@ -1,3 +1,5 @@ RESOURCES Tools or assets important to developing sbol-canvas, but which should not be distributed with the final project. + +svg2xml.jar taken from https://github.com/jgraph/svg2xml \ No newline at end of file diff --git a/resources/svg2xml.jar b/resources/svg2xml.jar new file mode 100644 index 00000000..7e890653 Binary files /dev/null and b/resources/svg2xml.jar differ