import { GraphicsItem , MouseDownEvent , MouseMoveEvent , MouseUpEvent } from "./graphicsitem" ; import { NodeGraphicsItem , NodeGraphicsItemRenderState } from "./nodegraphicsitem" ; import { ConnectionGraphicsItem } from "./connectiongraphicsitem" ; import { AddConnectionAction } from "../actions/addconnectionaction" ; import { UndoStack } from "../undostack" ; import { RemoveConnectionAction } from "../actions/removeconnectionaction" ; import { ConnectionSwitchAction , SwitchConnectionAction } from "../actions/switchconnectionaction" ; export enum SocketType { In , Out } export class SocketGraphicsItem extends GraphicsItem { public id ! : string ; public title ! : string ; public node ! : NodeGraphicsItem ; public socketType ! : SocketType ; radius = 8 ; conns : ConnectionGraphicsItem [ ] = [ ] ; hit : boolean ; hitSocket : SocketGraphicsItem ; hitConnection : ConnectionGraphicsItem ; mouseDragX : number ; mouseDragY : number ; addConnection ( con ) { this . conns . push ( con ) ; } removeConnection ( con ) { this . conns . splice ( this . conns . indexOf ( con ) , 1 ) ; } getConnection ( index ) { return this . conns [ index ] ; } getConnectionFrom ( socketA ) { for ( const con of this . conns ) { if ( con . socketA == socketA ) return con ; } return null ; } getConnectionTo ( socketB ) { for ( const con of this . conns ) { if ( con . socketB == socketB ) return con ; } return null ; } hasConnections ( ) { return this . conns . length > 0 ; } constructor ( ) { super ( ) ; this . width = this . radius * 2 ; this . height = this . radius * 2 ; this . hit = false ; this . hitSocket = null ; } draw ( ctx , renderData = null ) { const renderState = < NodeGraphicsItemRenderState > renderData ; ctx . lineWidth = 3 ; ctx . beginPath ( ) ; ctx . fillStyle = "rgb(150,150,150)" ; ctx . arc ( this . centerX ( ) , this . centerY ( ) , this . radius , 0 , 2 * Math . PI ) ; ctx . fill ( ) ; ctx . beginPath ( ) ; ctx . arc ( this . centerX ( ) , this . centerY ( ) , this . radius , 0 , 2 * Math . PI ) ; ctx . strokeStyle = "rgb(0, 0, 0)" ; ctx . stroke ( ) ; if ( this . hasConnections ( ) ) { ctx . beginPath ( ) ; ctx . fillStyle = "rgb(100,100,100)" ; ctx . arc ( this . centerX ( ) , this . centerY ( ) , this . radius / 3 , 0 , 2 * Math . PI ) ; ctx . fill ( ) ; } this . drawActiveConnection ( ctx ) ; if ( renderState . hovered ) { ctx . fillStyle = "rgb(150,150,150)" ; ctx . font = "9px 'Open Sans'" ; if ( this . socketType == SocketType . Out ) { const w = ctx . measureText ( this . title ) . width ; ctx . fillText ( this . title , this . x + this . width + 4 , this . y + 12 ) ; } else { const w = ctx . measureText ( this . title ) . width ; ctx . fillText ( this . title , this . x - 4 - w , this . y + 12 ) ; } } } drawActiveConnection ( ctx ) { if ( this . hitSocket ) { const mouseX = this . mouseDragX ; const mouseY = this . mouseDragY ; ctx . beginPath ( ) ; ctx . strokeStyle = "rgb(200, 200, 200)" ; ctx . lineWidth = 4 ; ctx . moveTo ( this . hitSocket . centerX ( ) , this . hitSocket . centerY ( ) ) ; if ( this . hitSocket . socketType == SocketType . Out ) { ctx . bezierCurveTo ( this . hitSocket . centerX ( ) + 60 , this . hitSocket . centerY ( ) , mouseX - 60 , mouseY , mouseX , mouseY ) ; } else { ctx . bezierCurveTo ( this . hitSocket . centerX ( ) - 60 , this . hitSocket . centerY ( ) , mouseX + 60 , mouseY , mouseX , mouseY ) ; } ctx . setLineDash ( [ 5 , 3 ] ) ; ctx . stroke ( ) ; ctx . setLineDash ( [ ] ) ; } } public mouseDown ( evt ) { this . hit = true ; this . hitSocket = null ; this . mouseDragX = evt . globalX ; this . mouseDragY = evt . globalY ; if ( this . socketType == SocketType . In && this . hasConnections ( ) ) { this . hitSocket = this . getConnection ( 0 ) . socketA ; this . hitConnection = this . getConnection ( 0 ) ; } else this . hitSocket = this ; } public mouseMove ( evt ) { if ( this . hit ) { this . mouseDragX = evt . globalX ; this . mouseDragY = evt . globalY ; } } public mouseUp ( evt ) { const mouseX = evt . globalX ; const mouseY = evt . globalY ; const movedCons = [ ] ; const actions = [ ] ; if ( this . hitSocket ) { const closeSock = this . scene . getHitSocket ( mouseX , mouseY ) ; let remainsDAG = false ; if ( closeSock ) { if ( this . hitSocket . socketType == SocketType . Out && closeSock . socketType == SocketType . In ) remainsDAG = this . scene . remainsDAG ( this . hitSocket . node , closeSock . node ) || remainsDAG ; else if ( this . hitSocket . socketType == SocketType . In && closeSock . socketType == SocketType . Out ) remainsDAG = this . scene . remainsDAG ( closeSock . node , this . hitSocket . node ) || remainsDAG ; if ( ! remainsDAG ) { this . hit = false ; this . hitSocket = null ; this . hitConnection = null ; return ; } } if ( this . hitConnection ) { this . scene . removeConnection ( this . hitConnection ) ; movedCons . push ( this . hitConnection ) ; actions . push ( ConnectionSwitchAction . Remove ) ; this . hitConnection = null ; } if ( closeSock && closeSock != this . hitSocket && closeSock . socketType != this . hitSocket . socketType && closeSock . node != this . hitSocket . node ) { const con = new ConnectionGraphicsItem ( ) ; if ( this . hitSocket . socketType == SocketType . Out ) { con . socketA = this . hitSocket ; con . socketB = closeSock ; if ( closeSock . hasConnections ( ) ) { const removeCon = closeSock . getConnection ( 0 ) ; this . scene . removeConnection ( removeCon ) ; movedCons . push ( removeCon ) ; actions . push ( ConnectionSwitchAction . Remove ) ; } } else { con . socketA = closeSock ; con . socketB = this . hitSocket ; } this . scene . addConnection ( con ) ; movedCons . push ( con ) ; actions . push ( ConnectionSwitchAction . Add ) ; } else if ( ! closeSock ) { if ( this . hitSocket . socketType == SocketType . Out ) { if ( this . hitConnection ) { this . scene . removeConnection ( this . hitConnection ) ; movedCons . push ( this . hitConnection ) ; actions . push ( ConnectionSwitchAction . Remove ) ; } } } } this . hit = false ; this . hitSocket = null ; this . hitConnection = null ; const action = new SwitchConnectionAction ( this . scene , movedCons , actions ) ; UndoStack . current . push ( action ) ; } }