CustomAutoPlaceBehavior.js 6.28 KB
// import { getNewShapePosition } from 'bpmn-js/lib/features/auto-place/BpmnAutoPlaceUtil';
import { is } from 'bpmn-js/lib/util/ModelUtil';
import { isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil';
import {
  getMid,
  asTRBL,
  getOrientation
} from 'diagram-js/lib/layout/LayoutUtil';

import {
  findFreePosition,
  generateGetNextPosition,
  getConnectedDistance
} from 'diagram-js/lib/features/auto-place/AutoPlaceUtil';


var HIGH_PRIORITY = 2500;


export default function AutoPlaceBehavior(eventBus, gridSnapping, toolManager, selection, modeling,
  bpmnFactory, elementRegistry) {
  eventBus.on('connection.added', HIGH_PRIORITY, function (context, element) {
    let source = context.element.source;
    let target = context.element.target;
    let outgoing = source.outgoing;
    // 分支  同步条件节点 条件同步
    let gateWay = ["bpmn:ExclusiveGateway", "bpmn:ParallelGateway", "bpmn:InclusiveGateway"];
    if (gateWay.includes(context.element.source.type) && context.element.waypoints && context.element.waypoints.length == 3) {

      let mid = Math.ceil(outgoing.length / 2);
      // 奇数
      if (outgoing.length % 2 != 0) {
        // 设置中间点的线和任务节点的位置
      }
      // 设置左边和右边的线和任务的位置
      // context.element.waypoints[0] = { x: context.element.waypoints[0].x + source.width / 2, y: context.element.waypoints[0].y - source.height / 2 }
      // context.element.waypoints[2] = { x: context.element.waypoints[2].x + target.width / 2, y: context.element.waypoints[2].y - target.height / 2 }
      // context.element.waypoints[1] = { x: context.element.waypoints[2].x, y: context.element.waypoints[0].y }
    }

  });

  eventBus.on('autoPlace', HIGH_PRIORITY, function (context) {
    var source = context.source,
      sourceMid = getMid(source),
      shape = context.shape;

    var position = getNewShapePosition(source, shape);

    ['x', 'y'].forEach(function (axis) {
      var options = {};

      // do not snap if x/y equal
      if (position[axis] === sourceMid[axis]) {
        return;
      }

      if (position[axis] > sourceMid[axis]) {
        options.min = position[axis];
      } else {
        options.max = position[axis];
      }

      if (is(shape, 'bpmn:TextAnnotation')) {

        if (isHorizontal(axis)) {
          options.offset = -shape.width / 2;
        } else {
          options.offset = -shape.height / 2;
        }

      }

      position[axis] = gridSnapping.snapValue(position[axis], options);

    });

    // must be returned to be considered by auto place
    return position;
  });

  // 消除移动到边界移动的bug
  eventBus.on('drag.move', HIGH_PRIORITY, function (context) {
  });
  // 移除原来的监听器
  eventBus._listeners["drag.move"].next = null;
}

AutoPlaceBehavior.$inject = [
  'eventBus',
  'gridSnapping',
  'toolManager',
  'selection',
  'modeling',
  'bpmnFactory',
  'elementRegistry'
];

// helpers //////////

function isHorizontal(axis) {
  return axis === 'x';
}

// BpmnAutoPlaceUtil.js
/**
 * Find the new position for the target element to
 * connect to source.
 *
 * @param  {djs.model.Shape} source
 * @param  {djs.model.Shape} element
 *
 * @return {Point}
 */
function getNewShapePosition(source, element) {

  if (is(element, 'bpmn:TextAnnotation')) {
    return getTextAnnotationPosition(source, element);
  }

  if (isAny(element, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) {
    return getDataElementPosition(source, element);
  }

  if (is(element, 'bpmn:FlowNode')) {
    return getFlowNodePosition(source, element);
  }
}

/**
 * Always try to place element bottom of source;
 * compute actual distance from previous nodes in flow.
 */
function getFlowNodePosition(source, element) {

  var sourceTrbl = asTRBL(source);
  var sourceMid = getMid(source);

  var horizontalDistance = getConnectedDistance(source, {
    filter: function (connection) {
      return is(connection, 'bpmn:SequenceFlow');
    }
  });

  var margin = 30,
    minDistance = 80,
    orientation = 'left';

  if (is(source, 'bpmn:BoundaryEvent')) {
    orientation = getOrientation(source, source.host, -25);

    if (orientation.indexOf('top') !== -1) {
      margin *= -1;
    }
  }

  // 向右
  // var position = {
  //   x: sourceTrbl.right + horizontalDistance + element.width / 2,
  //   y: sourceMid.y + getVerticalDistance(orientation, minDistance)
  // };
  // 向下
  var position = {
    x: sourceMid.x + getVerticalDistance(orientation, minDistance),
    y: sourceTrbl.bottom + horizontalDistance + element.width / 2,
  };

  var nextPositionDirection = {
    y: {
      margin: margin,
      minDistance: minDistance
    }
  };

  //  如果是已经有分支的话 下一个位置 x坐标偏移
  if ("bpmn:ExclusiveGateway" == source.type) {

    // position = {
    //   x: sourceTrbl.right + horizontalDistance + element.width / 2,
    //   y: sourceMid.y + getVerticalDistance(orientation, minDistance)
    // };

    nextPositionDirection = {
      x: {
        margin: margin * 2,
        minDistance: minDistance
      }
    };
  }

  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
}


function getVerticalDistance(orientation, minDistance) {
  if (orientation.indexOf('top') != -1) {
    return -1 * minDistance;
  } else if (orientation.indexOf('bottom') != -1) {
    return minDistance;
  } else {
    return 0;
  }
}


/**
 * Always try to place text annotations top right of source.
 */
function getTextAnnotationPosition(source, element) {

  var sourceTrbl = asTRBL(source);

  var position = {
    x: sourceTrbl.right + element.width / 2,
    y: sourceTrbl.top - 50 - element.height / 2
  };

  var nextPositionDirection = {
    y: {
      margin: -30,
      minDistance: 20
    }
  };

  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
}


/**
 * Always put element bottom right of source.
 */
function getDataElementPosition(source, element) {

  var sourceTrbl = asTRBL(source);

  var position = {
    x: sourceTrbl.right - 10 + element.width / 2,
    y: sourceTrbl.bottom + 40 + element.width / 2
  };

  var nextPositionDirection = {
    x: {
      margin: 30,
      minDistance: 30
    }
  };

  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
}