diff --git a/SBOLCanvasFrontend/src/app/glyph-menu/glyph-menu.component.html b/SBOLCanvasFrontend/src/app/glyph-menu/glyph-menu.component.html index 7c09352d..67b57a2d 100644 --- a/SBOLCanvasFrontend/src/app/glyph-menu/glyph-menu.component.html +++ b/SBOLCanvasFrontend/src/app/glyph-menu/glyph-menu.component.html @@ -79,7 +79,7 @@ - + diff --git a/SBOLCanvasFrontend/src/app/glyph-menu/glyph-menu.component.ts b/SBOLCanvasFrontend/src/app/glyph-menu/glyph-menu.component.ts index 03a630c4..016c636a 100644 --- a/SBOLCanvasFrontend/src/app/glyph-menu/glyph-menu.component.ts +++ b/SBOLCanvasFrontend/src/app/glyph-menu/glyph-menu.component.ts @@ -27,6 +27,7 @@ export class GlyphMenuComponent implements OnInit, AfterViewInit { SEQUENCE_FEATURE: "Sequence Feature", MOLECULAR_SPECIES: "Molecular Species", INTERACTION: "Interaction", + INTERACTION_NODE: "Interaction Node", } @ViewChildren('canvasElement') canvasElements: QueryList; @@ -53,8 +54,8 @@ export class GlyphMenuComponent implements OnInit, AfterViewInit { } onInteractionNodeGlyphClicked(name: string){ - // name = name.charAt(0).toUpperCase()+name.slice(1); - // this.graphService.addInteractionNode(name); + name = name.charAt(0).toUpperCase()+name.slice(1); + this.graphService.addInteractionNode(name); } onInteractionGlyphClicked(name: string) { @@ -102,6 +103,9 @@ export class GlyphMenuComponent implements OnInit, AfterViewInit { case this.elementTypes.INTERACTION: this.graphService.makeInteractionDragsource(elt, elt.getAttribute('glyphStyle')); break; + case this.elementTypes.INTERACTION_NODE: + this.graphService.makeInteractionNodeDragsource(elt, elt.getAttribute('glyphStyle')); + break; } } diff --git a/SBOLCanvasFrontend/src/app/glyph.service.ts b/SBOLCanvasFrontend/src/app/glyph.service.ts index baabe81f..8fd1376d 100644 --- a/SBOLCanvasFrontend/src/app/glyph.service.ts +++ b/SBOLCanvasFrontend/src/app/glyph.service.ts @@ -60,6 +60,7 @@ export class GlyphService { 'assets/glyph_stencils/molecular_species/small-molecule.xml', 'assets/glyph_stencils/molecular_species/no-glyph-assigned-ms.xml', 'assets/glyph_stencils/molecular_species/replacement-glyph.xml', + 'assets/glyph_stencils/molecular_species/complex.xml', ]; private interactionXMLs: string[] = [ diff --git a/SBOLCanvasFrontend/src/app/graph-base.ts b/SBOLCanvasFrontend/src/app/graph-base.ts index b99841ad..cd993e78 100644 --- a/SBOLCanvasFrontend/src/app/graph-base.ts +++ b/SBOLCanvasFrontend/src/app/graph-base.ts @@ -39,6 +39,9 @@ export class GraphBase { static readonly molecularSpeciesGlyphWidth = 50; static readonly molecularSpeciesGlyphHeight = 50; + static readonly interactionNodeGlyphWidth = 50; + static readonly interactionNodeGlyphHeight = 50; + static readonly defaultTextWidth = 120; static readonly defaultTextHeight = 80; @@ -56,6 +59,7 @@ export class GraphBase { static readonly STYLE_MOLECULAR_SPECIES = 'molecularSpeciesGlyph'; static readonly STYLE_SEQUENCE_FEATURE = 'sequenceFeatureGlyph'; static readonly STYLE_INTERACTION = 'interactionGlyph'; + static readonly STYLE_INTERACTION_NODE = 'interactionNodeGlyph'; static readonly STYLE_MODULE_VIEW = "moduleViewCell"; static readonly STYLE_COMPONENT_VIEW = "componentViewCell"; static readonly STYLE_INDICATOR = "indicator"; @@ -81,6 +85,7 @@ export class GraphBase { baseMolecularSpeciesGlyphStyle: any; baseSequenceFeatureGlyphStyle: any; + baseInteractionNodeGlyphStyle: any; // This object handles the hotkeys for the graph. keyHandler: any; @@ -362,6 +367,10 @@ export class GraphBase { return this.isStyle(GraphBase.STYLE_SCAR); }; + mx.mxCell.prototype.isInteractionNode = function() { + return this.isStyle(GraphBase.STYLE_INTERACTION_NODE); + } + mx.mxCell.prototype.isInteraction = function () { return this.isStyle(GraphBase.STYLE_INTERACTION); } @@ -642,6 +651,8 @@ export class GraphBase { this.baseSequenceFeatureGlyphStyle = mx.mxUtils.clone(this.baseMolecularSpeciesGlyphStyle); this.baseSequenceFeatureGlyphStyle[mx.mxConstants.STYLE_PORT_CONSTRAINT] = [mx.mxConstants.DIRECTION_NORTH, mx.mxConstants.DIRECTION_SOUTH]; + this.baseInteractionNodeGlyphStyle = mx.mxUtils.clone(this.baseMolecularSpeciesGlyphStyle); + const textBoxStyle = {}; textBoxStyle[mx.mxConstants.STYLE_SHAPE] = mx.mxConstants.SHAPE_LABEL; textBoxStyle[mx.mxConstants.STYLE_FILLCOLOR] = '#ffffff'; @@ -807,6 +818,18 @@ export class GraphBase { this.graph.getStylesheet().putCellStyle(GraphBase.STYLE_MOLECULAR_SPECIES + name, newGlyphStyle); } + // interaction nodes are basically identical to molecular species + stencils = this.glyphService.getInteractionNodeGlyphs(); + for(const name in stencils){ + const stencil = stencils[name][0]; + let customStencil = new mx.mxStencil(stencil.desc); + mx.mxStencilRegistry.addStencil(name, customStencil); + + const newGlyphStyle = mx.mxUtils.clone(this.baseInteractionNodeGlyphStyle); + newGlyphStyle[mx.mxConstants.STYLE_SHAPE] = name; + this.graph.getStylesheet().putCellStyle(GraphBase.STYLE_INTERACTION_NODE+name, newGlyphStyle); + } + // indicators like composit and combinatorial stencils = this.glyphService.getIndicatorGlyphs(); for(const name in stencils){ @@ -903,9 +926,11 @@ export class GraphBase { this.graph.addListener(mx.mxEvent.CONNECT_CELL, mx.mxUtils.bind(this, async function(sender, evt){ // if the terminal is a module, we need to prompt what it should be changed to, otherwise just clear it + //TODO find the previous terminal and replace it in the toURI or fromURI let edge = evt.getProperty("edge"); let terminal = evt.getProperty("terminal"); + let previous = evt.getProperty("previous"); let source = evt.getProperty("source"); let cancelled = false; diff --git a/SBOLCanvasFrontend/src/app/graph-helpers.ts b/SBOLCanvasFrontend/src/app/graph-helpers.ts index b124430a..b9733152 100644 --- a/SBOLCanvasFrontend/src/app/graph-helpers.ts +++ b/SBOLCanvasFrontend/src/app/graph-helpers.ts @@ -769,14 +769,15 @@ export class GraphHelpers extends GraphBase { this.graph.getModel().remove(interaction); continue; } - if (interaction.value.fromURI === oldReference) { + let index; + if ((index = interaction.value.fromURI.indexOf(oldReference)) > -1) { let infoCopy = interaction.value.makeCopy(); - infoCopy.fromURI = newReference; + infoCopy.fromURI[index] = newReference; this.graph.getModel().execute(new GraphEdits.interactionEdit(interaction, infoCopy)); } - if (interaction.value.toURI === oldReference) { + if ((index = interaction.value.toURI.indexOf(oldReference)) > -1) { let infoCopy = interaction.value.makeCopy(); - infoCopy.toURI = newReference; + infoCopy.toURI[index] = newReference; this.graph.getModel().execute(new GraphEdits.interactionEdit(interaction, infoCopy)); } } @@ -1072,7 +1073,7 @@ export class GraphHelpers extends GraphBase { this.metadataService.setSelectedGlyphInfo(glyphInfo.makeCopy()); } } - else if (cell.isInteraction()) { + else if (cell.isInteraction() || cell.isInteractionNode()) { let interactionInfo = cell.value; if (interactionInfo) { this.metadataService.setSelectedInteractionInfo(interactionInfo.makeCopy()); diff --git a/SBOLCanvasFrontend/src/app/graph.service.ts b/SBOLCanvasFrontend/src/app/graph.service.ts index 51234ed7..d17d34b1 100644 --- a/SBOLCanvasFrontend/src/app/graph.service.ts +++ b/SBOLCanvasFrontend/src/app/graph.service.ts @@ -714,6 +714,33 @@ export class GraphService extends GraphHelpers { console.log(this.graph.getModel().cells); } + makeInteractionNodeDragsource(element, stylename){ + const insertGlyph = mx.mxUtils.bind(this, function (graph, evt, target, x, y) { + this.addInteractionNodeAt(stylename, x - GraphBase.interactionNodeGlyphWidth / 2, y - GraphBase.interactionNodeGlyphHeight / 2); + }); + this.makeGeneralDragsource(element, insertGlyph); + } + + addInteractionNode(name){ + const pt = this.getDefaultNewCellCoords(); + this.addInteractionNodeAt(name, pt.x, pt.y); + } + + addInteractionNodeAt(name: string, x, y){ + this.graph.getModel().beginUpdate(); + try{ + const interactionNodeGlyph = this.graph.insertVertex(this.graph.getDefaultParent(), null, null, x, y, + GraphBase.interactionNodeGlyphWidth, GraphBase.interactionNodeGlyphHeight, GraphBase.STYLE_INTERACTION_NODE + name); + interactionNodeGlyph.setConnectable(true); + + // The new glyph should be selected + this.graph.clearSelection(); + this.graph.setSelectionCell(interactionNodeGlyph); + }finally{ + this.graph.getModel().endUpdate(); + } + } + /** * Turns the given HTML element into a dragsource for creating interaction glyphs */ @@ -765,7 +792,7 @@ export class GraphService extends GraphHelpers { let result = await this.promptChooseFunctionalComponent(selectedCell, true); if (!result) return; - cell.value.fromURI = result; + cell.value.fromURI.push(result+"_"+cell.getId()); } cell.geometry.setTerminalPoint(new mx.mxPoint(x, y - GraphBase.defaultInteractionSize), false); cell.edge = true; @@ -777,13 +804,13 @@ export class GraphService extends GraphHelpers { let result = await this.promptChooseFunctionalComponent(sourceCell, true); if (!result) return; - cell.value.fromURI = result; + cell.value.fromURI.push(result+"_"+cell.getId()); } if (destCell.isModule()) { let result = await this.promptChooseFunctionalComponent(destCell, false); if (!result) return; - cell.value.toURI = result; + cell.value.toURI.push(result+"_"+cell.getId()); } cell.edge = true; this.graph.addEdge(cell, this.graph.getCurrentRoot(), sourceCell, destCell); diff --git a/SBOLCanvasFrontend/src/app/interactionInfo.ts b/SBOLCanvasFrontend/src/app/interactionInfo.ts index 1203c6ba..d05a5504 100644 --- a/SBOLCanvasFrontend/src/app/interactionInfo.ts +++ b/SBOLCanvasFrontend/src/app/interactionInfo.ts @@ -5,13 +5,13 @@ export class InteractionInfo extends Info { // Remember that when you change this you need to change the encode function in graph service static counter: number = 0; interactionType: string; - fromURI: string; - toURI: string; + fromURI: string[]; + toURI: string[]; constructor() { super(); this.displayID = 'Interaction' + (InteractionInfo.counter++); - this.interactionType = "Yo momma"; + this.interactionType; } makeCopy() { diff --git a/SBOLCanvasFrontend/src/assets/glyph_stencils/interaction_nodes/association.xml b/SBOLCanvasFrontend/src/assets/glyph_stencils/interaction_nodes/association.xml index 7a46c716..18b06fb5 100644 --- a/SBOLCanvasFrontend/src/assets/glyph_stencils/interaction_nodes/association.xml +++ b/SBOLCanvasFrontend/src/assets/glyph_stencils/interaction_nodes/association.xml @@ -6,8 +6,6 @@ - - diff --git a/SBOLCanvasFrontend/src/assets/glyph_stencils/molecular_species/complex.xml b/SBOLCanvasFrontend/src/assets/glyph_stencils/molecular_species/complex.xml new file mode 100644 index 00000000..6a6ae5eb --- /dev/null +++ b/SBOLCanvasFrontend/src/assets/glyph_stencils/molecular_species/complex.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/complex.xml b/resources/complex.xml new file mode 100644 index 00000000..a29a4829 --- /dev/null +++ b/resources/complex.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/documents.xml b/resources/documents.xml new file mode 100644 index 00000000..c773903c --- /dev/null +++ b/resources/documents.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file