index.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import { is, isAny } from '../utils/modelUtil';
  2. import { findFreePosition, generateGetNextPosition, getConnectedDistance } from '../autoPlace/YmAutoPlaceUtil';
  3. import { isObject } from 'min-dash';
  4. import { bpmnStart } from '../config/variableName';
  5. import { DEFAULT_CONNECT } from '../config/constants';
  6. var HIGH_PRIORITY = 2000;
  7. function distance(a: any, b: any) {
  8. return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
  9. }
  10. /**
  11. * Convert the given bounds to a { top, left, bottom, right } descriptor.
  12. *
  13. * @param {Point|Rect} bounds
  14. *
  15. * @return {RectTRBL}
  16. */
  17. export function asTRBL(bounds: any) {
  18. return {
  19. top: bounds.y,
  20. right: bounds.x + (bounds.width || 0),
  21. bottom: bounds.y + (bounds.height || 0),
  22. left: bounds.x,
  23. };
  24. }
  25. export function getConnectionMid(connection: any) {
  26. var waypoints = connection.waypoints;
  27. var parts = waypoints.reduce(function (parts: any, point: any, index: any) {
  28. var lastPoint = waypoints[index - 1];
  29. if (lastPoint) {
  30. var lastPart = parts[parts.length - 1];
  31. var startLength = (lastPart && lastPart.endLength) || 0;
  32. var length = distance(lastPoint, point);
  33. parts.push({
  34. start: lastPoint,
  35. end: point,
  36. startLength: startLength,
  37. endLength: startLength + length,
  38. length: length,
  39. });
  40. }
  41. return parts;
  42. }, []);
  43. var totalLength = parts.reduce(function (length: any, part: any) {
  44. return length + part.length;
  45. }, 0);
  46. // find which segement contains middle point
  47. var midLength = totalLength / 2;
  48. var i = 0;
  49. var midSegment = parts[i];
  50. while (midSegment.endLength < midLength) {
  51. midSegment = parts[++i];
  52. }
  53. // calculate relative position on mid segment
  54. var segmentProgress = (midLength - midSegment.startLength) / midSegment.length;
  55. var midPoint = {
  56. x: midSegment.start.x + (midSegment.end.x - midSegment.start.x) * segmentProgress,
  57. y: midSegment.start.y + (midSegment.end.y - midSegment.start.y) * segmentProgress,
  58. };
  59. return midPoint;
  60. }
  61. export function roundPoint(point: any) {
  62. return {
  63. x: Math.round(point.x),
  64. y: Math.round(point.y),
  65. };
  66. }
  67. export function getBoundsMid(bounds: any) {
  68. return roundPoint({
  69. x: bounds.x + (bounds.width || 0) / 2,
  70. y: bounds.y + (bounds.height || 0) / 2,
  71. });
  72. }
  73. export function getMid(element: any) {
  74. if (!!element.waypoints) return getConnectionMid(element);
  75. return getBoundsMid(element);
  76. }
  77. export function getOrientation(rect: any, reference: any, padding: any) {
  78. padding = padding || 0;
  79. if (!isObject(padding)) padding = { x: padding, y: padding };
  80. var rectOrientation = asTRBL(rect),
  81. referenceOrientation = asTRBL(reference);
  82. var top = rectOrientation.bottom + padding.y <= referenceOrientation.top,
  83. right = rectOrientation.left - padding.x >= referenceOrientation.right,
  84. bottom = rectOrientation.top - padding.y >= referenceOrientation.bottom,
  85. left = rectOrientation.right + padding.x <= referenceOrientation.left;
  86. var vertical = top ? 'top' : bottom ? 'bottom' : null,
  87. horizontal = left ? 'left' : right ? 'right' : null;
  88. if (horizontal && vertical) return vertical + '-' + horizontal;
  89. else return horizontal || vertical || 'intersect';
  90. }
  91. function getVerticalDistance(orientation: any, minDistance: any) {
  92. if (orientation.indexOf('top') != -1) return -1 * minDistance;
  93. else if (orientation.indexOf('bottom') != -1) return minDistance;
  94. else return 0;
  95. }
  96. /**
  97. * horizontalDistance 控制距离 计算默认取50 如果有连接过 获取之前的连接距离
  98. * */
  99. export function getFlowNodePosition(source: any, element: any, jnpfData: any) {
  100. var sourceTrbl = asTRBL(source);
  101. var sourceMid = getMid(source);
  102. let layout = jnpfData.data['layout']; // horizontal: 横向, vertical:纵向
  103. var horizontalDistance =
  104. layout?.value === 'horizontal'
  105. ? getConnectedDistance(source, {
  106. filter: function (connection: any) {
  107. return is(connection, 'bpmn:SequenceFlow');
  108. },
  109. })
  110. : 120;
  111. var margin = 30,
  112. minDistance = 50,
  113. orientation = 'left';
  114. if (is(source, 'bpmn:BoundaryEvent')) {
  115. orientation = getOrientation(source, source.host, -25);
  116. if (orientation.indexOf('top') !== -1) margin *= -1;
  117. }
  118. var position: any = {};
  119. let nextPositionDirection: any = {};
  120. // 获取排序 横向还是竖向 横向
  121. if (layout?.value === 'horizontal') {
  122. position = {
  123. x: sourceTrbl.right + DEFAULT_CONNECT + element.width / 2,
  124. y: sourceMid.y + getVerticalDistance(orientation, minDistance),
  125. };
  126. nextPositionDirection = {
  127. y: {
  128. margin: margin,
  129. minDistance: minDistance,
  130. },
  131. };
  132. } else {
  133. position = {
  134. x: sourceTrbl.right + getVerticalDistance(orientation, minDistance),
  135. y: sourceMid.y + (element.height + source.height) / 2 + horizontalDistance,
  136. };
  137. nextPositionDirection = {
  138. x: {
  139. margin: (source.width - element.width) / 2 + 220,
  140. minDistance: minDistance,
  141. },
  142. };
  143. }
  144. return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
  145. }
  146. export function getNewShapePosition(source: any, element: any, jnpfData: any) {
  147. if (is(element, 'bpmn:TextAnnotation')) return getTextAnnotationPosition(source, element);
  148. if (isAny(element, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) return getDataElementPosition(source, element);
  149. if (is(element, 'bpmn:FlowNode')) return getFlowNodePosition(source, element, jnpfData);
  150. }
  151. /**
  152. * Always put element bottom right of source.
  153. */
  154. export function getDataElementPosition(source: any, element: any) {
  155. var sourceTrbl = asTRBL(source);
  156. var position = {
  157. x: sourceTrbl.right - 10 + element.width / 2,
  158. y: sourceTrbl.bottom + 40 + element.width / 2,
  159. };
  160. var nextPositionDirection = {
  161. x: {
  162. margin: 30,
  163. minDistance: 30,
  164. },
  165. };
  166. return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
  167. }
  168. /**
  169. * Always try to place text annotations top right of source.
  170. */
  171. export function getTextAnnotationPosition(source: any, element: any) {
  172. var sourceTrbl = asTRBL(source);
  173. var position = {
  174. x: sourceTrbl.right + element.width / 2,
  175. y: sourceTrbl.top - 50 - element.height / 2,
  176. };
  177. if (!!source.waypoints) {
  178. position = getMid(source);
  179. position.x += 100;
  180. position.y -= 50;
  181. }
  182. var nextPositionDirection = {
  183. y: {
  184. margin: -30,
  185. minDistance: 20,
  186. },
  187. };
  188. return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
  189. }
  190. export default function YmGridSnappingAutoPlaceBehavior(eventBus: any, gridSnapping: any, jnpfData: any) {
  191. eventBus.on('autoPlace', HIGH_PRIORITY, function (context: any) {
  192. var source = context.source,
  193. sourceMid = getMid(source),
  194. shape = context.shape,
  195. layout = jnpfData.data['layout']; // horizontal: 横向, vertical:纵向
  196. var position: any = getNewShapePosition(source, shape, jnpfData);
  197. ['x', 'y'].forEach(function (axis: any) {
  198. var options: any = {};
  199. if (position[axis] === sourceMid[axis]) return;
  200. if (position[axis] > sourceMid[axis]) options.min = position[axis];
  201. else options.max = position[axis];
  202. if (is(shape, 'bpmn:TextAnnotation')) {
  203. if (isHorizontal(axis)) options.offset = -shape.width / 2;
  204. else options.offset = -shape.height / 2;
  205. }
  206. });
  207. if (layout?.value === 'vertical') {
  208. if (source.type === bpmnStart) {
  209. position = {
  210. x: position.x + (source.width - shape.width) / 2 + 10,
  211. y: position.y,
  212. };
  213. } else {
  214. position = {
  215. x: position.x - 100,
  216. y: position.y,
  217. };
  218. }
  219. }
  220. return position;
  221. });
  222. }
  223. YmGridSnappingAutoPlaceBehavior.$inject = ['eventBus', 'gridSnapping', 'jnpfData'];
  224. function isHorizontal(axis: any) {
  225. return axis === 'x';
  226. }