-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4a68a2a
commit ee5823a
Showing
7 changed files
with
449 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
OPENAI_API_KEY="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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { asTRBL, getMid } from 'diagram-js/lib/layout/LayoutUtil'; | ||
|
||
import { getDistancePointPoint } from 'diagram-js/lib/features/bendpoints/GeometricUtil'; | ||
import { is } from '../../util/ModelUtil'; | ||
|
||
const AUTO_CONNECT_PADDING = 200; | ||
|
||
export default function AutoConnect(elementDetection, modeling, rules) { | ||
this._elementDetection = elementDetection; | ||
this._modeling = modeling; | ||
this._rules = rules; | ||
} | ||
|
||
AutoConnect.prototype.canConnect = function(element) { | ||
const elementTrbl = asTRBL(element); | ||
|
||
const rects = [ | ||
{ | ||
top: elementTrbl.top - AUTO_CONNECT_PADDING, | ||
right: elementTrbl.right + AUTO_CONNECT_PADDING, | ||
bottom: elementTrbl.bottom + AUTO_CONNECT_PADDING, | ||
left: elementTrbl.right + 10 | ||
}, | ||
{ | ||
top: elementTrbl.top - AUTO_CONNECT_PADDING, | ||
right: elementTrbl.right + AUTO_CONNECT_PADDING, | ||
bottom: elementTrbl.bottom + AUTO_CONNECT_PADDING, | ||
left: elementTrbl.left - 10 | ||
} | ||
]; | ||
|
||
return rects.reduce((target, rect) => { | ||
return target || this._findTarget(element, rect); | ||
}, false); | ||
}; | ||
|
||
AutoConnect.prototype._findTarget = function(element, rect) { | ||
const possibleTargets = this._elementDetection.detectAt(rect).filter(possibleTarget => { | ||
return possibleTarget !== element && !isConnection(possibleTarget) && !isLabel(possibleTarget); | ||
}); | ||
|
||
// find closest element to connect to | ||
return possibleTargets.reduce((target, possibleTarget) => { | ||
if (isConnected(element, possibleTarget) || hasMaxConnections(element)) { | ||
return target; | ||
} | ||
|
||
const canConnect = this.getConnectionType(element, possibleTarget); | ||
|
||
if (!canConnect) { | ||
return target; | ||
} | ||
|
||
const distance = getDistancePointPoint(getMid(element), getMid(possibleTarget)); | ||
|
||
if (!target || distance < getDistancePointPoint(getMid(element), getMid(target))) { | ||
return possibleTarget; | ||
} | ||
|
||
return target; | ||
}, null); | ||
}; | ||
|
||
AutoConnect.prototype.getConnectionType = function(source, target) { | ||
return this._rules.allowed('connection.create', { | ||
source, | ||
target | ||
}); | ||
}; | ||
|
||
AutoConnect.prototype.connect = function(element) { | ||
const target = this.canConnect(element); | ||
|
||
if (!target) { | ||
return; | ||
} | ||
|
||
this._modeling.connect(element, target); | ||
}; | ||
|
||
AutoConnect.$inject = [ | ||
'elementDetection', | ||
'modeling', | ||
'rules' | ||
]; | ||
|
||
function isConnection(element) { | ||
return element.waypoints; | ||
} | ||
|
||
function isLabel(element) { | ||
return element.labelTarget; | ||
} | ||
|
||
function isConnected(source, target) { | ||
return source.incoming.some(connection => connection.source === target) | ||
|| source.outgoing.some(connection => connection.target === target); | ||
} | ||
|
||
function hasMaxConnections(element) { | ||
return !is(element, 'bpmn:Gateway') && element.outgoing.length; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
import { | ||
isNumber, | ||
isUndefined, | ||
some | ||
} from 'min-dash'; | ||
|
||
import { | ||
append as svgAppend, | ||
attr as svgAttr, | ||
clear as svgClear, | ||
create as svgCreate | ||
} from 'tiny-svg'; | ||
|
||
const THRESHOLD = 2; | ||
|
||
|
||
export default class ElementDetection { | ||
constructor(canvas, elementRegistry) { | ||
this._canvas = canvas; | ||
this._elementRegistry = elementRegistry; | ||
} | ||
|
||
/** | ||
* Detect elements intersecting point or rect. | ||
* Uses element registry. | ||
* | ||
* @param {Object} pointOrRect | ||
* | ||
* @return {Array} | ||
*/ | ||
detectAt(pointOrRect) { | ||
if (isPoint(pointOrRect)) { | ||
pointOrRect = { | ||
...pointOrRect, | ||
width: 0, | ||
height: 0 | ||
}; | ||
} | ||
|
||
if (!isTRBL(pointOrRect)) { | ||
pointOrRect = toTRBL(pointOrRect); | ||
} | ||
|
||
// drawRect(pointOrRect, this._canvas); | ||
|
||
const elements = this._elementRegistry.filter(elementIntersects(pointOrRect)); | ||
|
||
return elements; | ||
} | ||
} | ||
|
||
ElementDetection.$inject = [ | ||
'canvas', | ||
'elementRegistry' | ||
]; | ||
|
||
// helpers ////////// | ||
|
||
function elementIntersects(rect) { | ||
return function(element) { | ||
if (isConnection(element)) { | ||
return connectionIntersects(element, rect); | ||
} | ||
|
||
return element.x <= rect.right && | ||
element.x + element.width >= rect.left && | ||
element.y <= rect.bottom && | ||
element.y + element.height >= rect.top; | ||
} | ||
} | ||
|
||
function isConnection(element) { | ||
return !!element.waypoints; | ||
} | ||
|
||
function connectionIntersects(connection, rect) { | ||
const segments = getSegments(connection); | ||
|
||
return some(segments, segmentIntersects(rect)); | ||
} | ||
|
||
function getSegments({ waypoints }) { | ||
return waypoints.reduce((segments, waypoint, index) => { | ||
if (isLastIndex(index, waypoints)) { | ||
return segments; | ||
} | ||
|
||
return [ | ||
...segments, | ||
{ | ||
start: waypoint, | ||
end: waypoints[ index + 1] | ||
} | ||
]; | ||
}, []); | ||
} | ||
|
||
function segmentIntersects(rect) { | ||
return function(segment) { | ||
let { start, end } = segment; | ||
|
||
if (isHorizontal(segment)) { | ||
if (start.x > end.x) { | ||
start = segment.end; | ||
end = segment.start; | ||
} | ||
|
||
return start.y >= rect.top && | ||
start.y <= rect.bottom && | ||
start.x <= rect.right && | ||
end.x >= rect.left; | ||
} else { | ||
if (start.y > end.y) { | ||
start = segment.end; | ||
end = segment.start; | ||
} | ||
|
||
return start.x >= rect.left && | ||
start.x <= rect.right && | ||
start.y <= rect.bottom && | ||
end.y >= rect.top; | ||
} | ||
}; | ||
} | ||
|
||
function isHorizontal({ start, end }) { | ||
return Math.abs(start.y - end.y) <= THRESHOLD; | ||
} | ||
|
||
function isLastIndex(index, array) { | ||
return index + 1 === array.length; | ||
} | ||
|
||
function isPoint(pointOrRect) { | ||
return isNumber(pointOrRect.x) && | ||
isNumber(pointOrRect.y) && | ||
isUndefined(pointOrRect.width) && | ||
isUndefined(pointOrRect.height); | ||
} | ||
|
||
function isTRBL(pointOrRect) { | ||
return isNumber(pointOrRect.top) && | ||
isNumber(pointOrRect.right) && | ||
isNumber(pointOrRect.bottom) && | ||
isNumber(pointOrRect.left); | ||
} | ||
|
||
function toTRBL(rect) { | ||
return { | ||
top: rect.y, | ||
right: rect.x + rect.width, | ||
bottom: rect.y + rect.height, | ||
left: rect.x | ||
}; | ||
} | ||
|
||
let counter = 0; | ||
|
||
const colors = [ 'red', 'green', 'blue' ]; | ||
|
||
function drawRect(rect, canvas) { | ||
const layer = canvas.getLayer('element-detection'); | ||
|
||
// svgClear(layer); | ||
|
||
const gfx = svgCreate('rect'); | ||
|
||
svgAttr(gfx, { | ||
fill: colors[ counter % colors.length ], | ||
fillOpacity: 0.25, | ||
stroke: colors[ counter % colors.length ], | ||
strokeOpacity: 0.25, | ||
strokeWidth: 2, | ||
x: rect.left, | ||
y: rect.top, | ||
width: Math.max(rect.right - rect.left, 2), | ||
height: Math.max(rect.bottom - rect.top, 2) | ||
}); | ||
|
||
svgAppend(layer, gfx); | ||
|
||
setTimeout(() => { | ||
svgClear(layer); | ||
}, 1000); | ||
|
||
counter++; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import AutoConnect from './AutoConnect'; | ||
import ElementDetection from './ElementDetection'; | ||
|
||
export default { | ||
_init_: [ 'autoConnect' ], | ||
autoConnect: [ 'type', AutoConnect ], | ||
elementDetection: [ 'type', ElementDetection ] | ||
}; |
Oops, something went wrong.