|
|
"use strict";
|
|
|
/// <reference path="raphael.js" />
|
|
|
/// <reference path="../easyUI/jquery-1.8.0.min.js" />
|
|
|
/// <reference path="../easyUI/jquery.easyui.min.js" />
|
|
|
|
|
|
var
|
|
|
// global value for designer
|
|
|
diagram = {
|
|
|
isChanged: false, // 标识当前流程是否更新
|
|
|
flow: null // current instance of CCFlow
|
|
|
},
|
|
|
container = null, // 当前CCFlow的UI对象,一个CCFlow
|
|
|
ACTION = {
|
|
|
NONE: "none",
|
|
|
NODE_CREATE: "node_create",
|
|
|
NODE_SELECTED: "node_selected",
|
|
|
NODE_MOVED :'node_moved',
|
|
|
DIRECTION_CREATE: "direction_create",
|
|
|
DIRECTION_SELECTED: "direction_selected",
|
|
|
DIRECTION_PICK_FIRST: "direction_pick_first",
|
|
|
DIRECTION_PICK_SECOND: "direction_pick_second",
|
|
|
DIRECTION_MOVE_POINT: "direction_move_point",
|
|
|
|
|
|
SELECTING_MULTIPLE: "selecting_multiple",
|
|
|
CONTAINER_SELECTED: "container_selected",
|
|
|
TEXT_EDITING: "text_editing"
|
|
|
},
|
|
|
state = ACTION.NONE;
|
|
|
|
|
|
|
|
|
//定义样式变量,可在此统一修改流程样式
|
|
|
var DATA_USER_ICON_PATH = '../../../DataUser/UserIcon/';
|
|
|
var DATA_USER_ICON_DEFAULT = 'Default.png';
|
|
|
var NODE_WIDTH = 40,
|
|
|
NODE_WIDTH_CENTER = 6,
|
|
|
NODE_HEIGHT = 40,
|
|
|
NODE_BORDER_RADIUS = 5;
|
|
|
var NODE_ICON_PATH = '../../../DataUser/NodeIcon/';
|
|
|
var NODE_ICON_DEFAULT = 'Default.jpg';
|
|
|
|
|
|
|
|
|
var LABEL_FONT_SIZE = 14;
|
|
|
var LABEL_FONT_COLOR_FORE = 'none',
|
|
|
LABEL_FONT_COLOR_HOVER = 'blue',
|
|
|
LABEL_FONT_COLOR_DRAG = 'blue',
|
|
|
LABEL_BORDER_COLOR_NORAML = 'none',
|
|
|
LABEL_BORDER_COLOR_HOVER = 'blue',
|
|
|
LABEL_BORDER_COLOR_DRAG = 'blue';
|
|
|
|
|
|
var STYLE_LINE_HOVER_COLOR = 'blue',
|
|
|
STYLE_LINE_TRACK_COLOR = 'red';
|
|
|
|
|
|
var
|
|
|
STYLE_NORMAL = {
|
|
|
BORDER_COLOR: 'black',
|
|
|
BORDER_WIDTH: 2,
|
|
|
LINE_WIDTH : 2,
|
|
|
FONT_FORE: "black",
|
|
|
FONT_SIZE:12
|
|
|
},
|
|
|
STYLE_HOVER = {
|
|
|
BORDER_COLOR: 'green',
|
|
|
BORDER_WIDTH: 2,
|
|
|
FONT_FORE: "blue"
|
|
|
},
|
|
|
STYLE_FOCUSED = {
|
|
|
BORDER: 'blue',
|
|
|
BORDER_WIDTH :2,
|
|
|
FONT_FORE: "blue"
|
|
|
};
|
|
|
|
|
|
// CCFlow ENUMS
|
|
|
var PageEditType = {
|
|
|
Add: 0,
|
|
|
Modify: 1,
|
|
|
None: 2
|
|
|
},
|
|
|
WorkState = {
|
|
|
Run: 'Run',
|
|
|
Designer: 'Designer'
|
|
|
},
|
|
|
ElementType = {
|
|
|
FLOW :'flow',
|
|
|
NODE : 'node',
|
|
|
DIRECTION : 'direction',
|
|
|
LABNOTE : 'labNote'
|
|
|
},
|
|
|
CONTAINERID = 'holder';// GET UIOBJECT OF CURRENT CCFLOW
|
|
|
|
|
|
function CCFlow(n, ct, id, wID) {// 构造函数默认值
|
|
|
this.Name = n;
|
|
|
this.ChartType = ct;
|
|
|
this.FK_Flow = id;
|
|
|
this.WorkID = wID;
|
|
|
|
|
|
|
|
|
this.nodes = new Array();
|
|
|
this.directions = new Array();
|
|
|
this.labNotes = new Array();
|
|
|
this.params = null;
|
|
|
this.wF_SelectorAcceptor = new Array();
|
|
|
this.wF_GenerWorkList = new Array();
|
|
|
|
|
|
this.workState = ''; // run、designer
|
|
|
|
|
|
this.focusElement = null; //记录当前选中的控件,{type:'',ele:obj}
|
|
|
this.oType = ElementType.FLOW;
|
|
|
};
|
|
|
CCFlow.load = function (data, w, h) {
|
|
|
// flow property
|
|
|
var f = null;
|
|
|
if (data.WF_FLOW.length == 1) {
|
|
|
var flow = data.WF_FLOW[0];
|
|
|
f = new CCFlow(flow.NAME, flow.CHARTTYPE, flow.NO, flow.WORKID);
|
|
|
f.params = flow.PARAS;
|
|
|
diagram.flow = f;
|
|
|
container = CCFlow.getContainer(w, h);
|
|
|
// 如果是设计状态,需要添加绑定事件
|
|
|
f.WorkState = WorkState.Designer;
|
|
|
if (f.WorkState == WorkState.Designer) {
|
|
|
CCFlow.bindContainerAction();
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
$.messager.alert('error', '流程数据只允许有一条数据!', 'info');
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// element property
|
|
|
// json串全部用大写,兼容数据库问题
|
|
|
f.nodes = CCNode.loadArray(data.WF_NODE);
|
|
|
f.directions = CCDirection.loadArray(data.WF_DIRECTION);
|
|
|
f.labNotes = CCLabNote.loadArray(data.WF_LABNOTE);
|
|
|
|
|
|
// f.wF_SelectorAcceptor = WF_SelectorAcceptor.loadArray(f.WF_SELECTORACCEPTOR);
|
|
|
// f.wF_GenerWorkList = WF_GenerWorkList.loadArray(fl.GENERWORKLIST);
|
|
|
|
|
|
f._initialized = true;
|
|
|
return f;
|
|
|
};
|
|
|
CCFlow.getContainer = function (w, h) {
|
|
|
|
|
|
// 画布容器
|
|
|
// 背景
|
|
|
// 网格
|
|
|
if (w == undefined) {
|
|
|
w = window.screen.width;
|
|
|
h = window.screen.height;
|
|
|
|
|
|
w = 1500 > w ? 1200 : w;
|
|
|
h = 600 > h ? 600 : h;
|
|
|
}
|
|
|
|
|
|
var c = new Raphael(CONTAINERID, w, h);
|
|
|
return c;
|
|
|
};
|
|
|
CCFlow.bindContainerAction = function () {
|
|
|
$('#' + CONTAINERID)
|
|
|
.bind('mousedown', onContainerMouseDown)
|
|
|
.bind('mousemove', onContainerMouseMove)
|
|
|
.bind('mouseup', onContainerMouseUp)
|
|
|
.bind("contextmenu", onContainerContextMenu);
|
|
|
};
|
|
|
CCFlow.prototype = {
|
|
|
|
|
|
constructor: CCFlow, getSize: function () {
|
|
|
return this.size
|
|
|
},
|
|
|
draw: function () {
|
|
|
|
|
|
//绘制节点
|
|
|
for (var b = 0; b < this.nodes.length; b++) {
|
|
|
this.nodes[b].draw();
|
|
|
this.nodes[b].bindAction();
|
|
|
}
|
|
|
|
|
|
//绘制节点的连线
|
|
|
for (var b = 0; b < this.directions.length; b++) {
|
|
|
this.directions[b].draw();
|
|
|
this.directions[b].bindAction();
|
|
|
}
|
|
|
|
|
|
//绘制标签
|
|
|
for (var b = 0; b < this.labNotes.length; b++) {
|
|
|
this.labNotes[b].draw();
|
|
|
this.labNotes[b].bindAction();
|
|
|
}
|
|
|
},
|
|
|
add: function () {
|
|
|
|
|
|
},
|
|
|
del: function () {
|
|
|
|
|
|
},
|
|
|
save: function () {
|
|
|
|
|
|
// if (!diagram.isChanged) {
|
|
|
// return true;
|
|
|
// }
|
|
|
loading();
|
|
|
var params = new Params(),
|
|
|
nodes = "",
|
|
|
dirs = "",
|
|
|
labes = "";
|
|
|
params.push('fk_flow', this.FK_Flow);
|
|
|
|
|
|
for (var b = 0; b < this.nodes.length; b++) {
|
|
|
var n = this.nodes[b],
|
|
|
x = n.x + NODE_WIDTH / 2,
|
|
|
y = n.y + NODE_HEIGHT / 2;
|
|
|
|
|
|
nodes += "~@Name=" + n.name
|
|
|
+ "@X=" + x
|
|
|
+ "@Y=" + y
|
|
|
+ "@NodeID=" + n.id
|
|
|
+ "@HisRunModel=" + n.nodeType
|
|
|
+ "@Icon=" + n.icon
|
|
|
+ "@HisPosType=" + n.nodePosType;
|
|
|
}
|
|
|
params.push('nodes', nodes);
|
|
|
|
|
|
|
|
|
for (var b = 0; b < this.directions.length; b++) {
|
|
|
var d = this.directions[b];
|
|
|
if (d.toNode != null && d.toNode != 0 && d.node != null) //不为虚开始
|
|
|
{
|
|
|
// 指向同一点的连线不保存
|
|
|
if (d.toNode == d.node)
|
|
|
continue;
|
|
|
//中间节与虚结束节点有连线时忽略不保存
|
|
|
if (d.toNode == 1)
|
|
|
continue;
|
|
|
var canBack = d.isCanBack ? 1 : 0,
|
|
|
myPk = d.node + "_" + d.toNode + "_" + d.dirType,
|
|
|
dots = '';
|
|
|
if (d.lineType == DirectionUIType.Polyline) {
|
|
|
dots = "#" + d.pTurn1.X + "," + d.pTurn1.Y
|
|
|
+ "#" + d.pTurn2.X + "," + d.pTurn2.Y;
|
|
|
}
|
|
|
|
|
|
dirs += "~@Node=" + d.node
|
|
|
+ "@ToNode=" + d.toNode
|
|
|
+ "@DirType=" + d.dirType
|
|
|
+ "@IsCanBack=" + canBack
|
|
|
+ "@Dots=" + dots
|
|
|
+ "@MyPK=" + myPk;
|
|
|
}
|
|
|
}
|
|
|
params.push('dirs', dirs);
|
|
|
|
|
|
|
|
|
for (var b = 0; b < this.labNotes.length; b++) {
|
|
|
var l = this.labNotes[b];
|
|
|
labes += "~@FK_Flow=" + diagram.flow.FK_Flow
|
|
|
+ "@X=" + l.x + "@Y=" + l.y
|
|
|
+ "@MyPK=" + l.mypk + "@Label=" + l.name;
|
|
|
}
|
|
|
params.push('labes', labes);
|
|
|
|
|
|
var ff = params.toJsonDataString();
|
|
|
|
|
|
ajaxService('flow', 'FlowSave', ff, function (data) {
|
|
|
loaded();
|
|
|
var jdata = $.parseJSON(data);
|
|
|
if (!jdata.success) {
|
|
|
$.messager.alert('失败', jdata.msg, 'error');
|
|
|
}
|
|
|
else {
|
|
|
diagram.isChanged = false;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
},
|
|
|
getNodeById: function (nId) {
|
|
|
/// <summary>根据指定节点ID获取该结点使用Raphael绘制的对象Set</summary>
|
|
|
/// <param name="nodeid" Type="Int">流程编号</param>
|
|
|
for (var i = 0, j = this.nodes.length; i < j; i++) {
|
|
|
if (this.nodes[i].id == nId) {
|
|
|
return this.nodes[i];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
},
|
|
|
getNodeByIconId: function (iconId) {
|
|
|
/// <summary>根据绘制的节点中的ICON对象的id获取该结点使用Raphael绘制的对象</summary>
|
|
|
/// <param name="raphaelid" Type="Int">流程编号</param>
|
|
|
for (var i = 0, j = this.nodes.length; i < j; i++) {
|
|
|
if (this.nodes[i].rIcon.id == iconId) {
|
|
|
return this.nodes[i];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
},
|
|
|
clearFocus: function () {
|
|
|
/// <summary>清除当前选中的对象有选中状态</summary>
|
|
|
if (this.focusElement) {
|
|
|
this.focusElement.clearFocus();
|
|
|
this.focusElement = null;
|
|
|
}
|
|
|
},
|
|
|
setFocus: function (ele) {
|
|
|
var changed = false;
|
|
|
if (ele != null && ele != this.focusElement) {
|
|
|
ele.focus();
|
|
|
changed = true;
|
|
|
}
|
|
|
|
|
|
if (changed) {
|
|
|
if (this.focusElement != null)
|
|
|
this.focusElement.clearFocus();
|
|
|
this.focusElement = ele;
|
|
|
}
|
|
|
},
|
|
|
getNewElementName: function (eleType) {
|
|
|
var name;
|
|
|
var maxid = 0;
|
|
|
var idstr;
|
|
|
var currId;
|
|
|
switch (eleType) {
|
|
|
case ElementType.NODE:
|
|
|
name = '新建节点 ';
|
|
|
|
|
|
$.each(diagram.flow.nodes, function () {
|
|
|
if (this.name.length > 5 && this.name.substr(0, 5) == name) {
|
|
|
currId = parseInt(this.name.substr(5));
|
|
|
if (!isNaN(currId)) {
|
|
|
maxid = Math.max(maxid, currId);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
break;
|
|
|
case ElementType.LABNOTE:
|
|
|
name = '新建标签 ';
|
|
|
$.each(diagram.flow.labNotes, function () {
|
|
|
if (this.name.length > 5 && this.name.substr(0, 5) == name) {
|
|
|
currId = parseInt(this.name.substr(5));
|
|
|
if (!isNaN(currId)) {
|
|
|
maxid = Math.max(maxid, currId);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
return name + (maxid + 1);
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
// CCNODES ENUMS
|
|
|
var FlowNodePosType = {
|
|
|
Start: 0,
|
|
|
Mid: 1,
|
|
|
End: 2
|
|
|
},
|
|
|
FlowNode_BORDER_COLOR = {
|
|
|
Start: 'green',
|
|
|
End: 'red',
|
|
|
Mid: 'black'
|
|
|
},
|
|
|
FlowNodeType = {
|
|
|
/// <summary>
|
|
|
/// 普通
|
|
|
/// </summary>
|
|
|
Ordinary : 0,
|
|
|
/// <summary>
|
|
|
/// 合流
|
|
|
/// </summary>
|
|
|
HL : 1,
|
|
|
/// <summary>
|
|
|
/// 分流
|
|
|
/// </summary>
|
|
|
FL : 2,
|
|
|
/// <summary>
|
|
|
/// 分合流
|
|
|
/// </summary>
|
|
|
FHL:3,
|
|
|
/// <summary>
|
|
|
/// 子线程.
|
|
|
/// </summary>
|
|
|
SubThread:4,
|
|
|
/// <summary>
|
|
|
/// 虚节点
|
|
|
/// </summary>
|
|
|
VirtualStart:5,
|
|
|
VirtualEnd:6,
|
|
|
UnKnown:100
|
|
|
};
|
|
|
function CCNode(id, name, iX, iY) {
|
|
|
/// <summary>节点</summary>
|
|
|
/// <param name="iNodeID" Type="Int">节点ID</param>
|
|
|
/// <param name="sNodeName" Type="String">节点名称</param>
|
|
|
/// <param name="iX" Type="Int">节点中心点X坐标</param>
|
|
|
/// <param name="iY" Type="Int">节点中心点Y坐标</param>
|
|
|
this.id = id;
|
|
|
this.name = name;
|
|
|
this.x = iX;
|
|
|
this.y = iY;
|
|
|
this.icon = '';
|
|
|
this.nodePosType = FlowNodePosType.Start;
|
|
|
this.hisToNDs = '';
|
|
|
this.nodeType = FlowNodeType.Ordinary;
|
|
|
this.rCenter = null;
|
|
|
this.rBorderColor = '';
|
|
|
this.rBorder = null;
|
|
|
this.rIcon = null;
|
|
|
this.rText = null;
|
|
|
this.rIsFocus = false;
|
|
|
this.isDrag = false;
|
|
|
this.oType = ElementType.NODE;
|
|
|
};
|
|
|
CCNode.load = function (n) {
|
|
|
var x = n.X - NODE_WIDTH / 2,
|
|
|
y = n.Y - NODE_HEIGHT / 2;
|
|
|
var node = new CCNode(n.ID, n.NAME, x, y);
|
|
|
node.icon = n.ICON; //liuxc,20150323
|
|
|
return node;
|
|
|
};
|
|
|
CCNode.loadArray = function (a) {
|
|
|
var c = [];
|
|
|
for (var b = 0; b < a.length; b++) {
|
|
|
c.push(CCNode.load(a[b]))
|
|
|
}
|
|
|
return c
|
|
|
};
|
|
|
CCNode.prototype = {
|
|
|
initProperty: function () {
|
|
|
//BorderColor
|
|
|
switch (this.nodePosType) {
|
|
|
case FlowNodePosType.Start:
|
|
|
this.rBorderColor = FlowNode_BORDER_COLOR.Start;
|
|
|
break;
|
|
|
case FlowNodePosType.End:
|
|
|
this.rBorderColor = FlowNode_BORDER_COLOR.End;
|
|
|
break;
|
|
|
default:
|
|
|
this.rBorderColor = FlowNode_BORDER_COLOR.Mid;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// icon
|
|
|
if (this.icon == null || this.icon.length == 0) {
|
|
|
this.icon = NODE_ICON_PATH + NODE_ICON_DEFAULT;
|
|
|
}
|
|
|
else {
|
|
|
if (this.icon.indexOf('.') == -1) {
|
|
|
this.icon = NODE_ICON_PATH + this.icon + '.png';
|
|
|
}
|
|
|
else {
|
|
|
this.icon = NODE_ICON_PATH + this.icon.substr(this.icon.lastIndexOf('/') + 1);
|
|
|
}
|
|
|
|
|
|
var isExist = checkUrl(encodeURI(this.icon)); //liuxc,20150324
|
|
|
if (isExist == false) {
|
|
|
this.icon = NODE_ICON_PATH + NODE_ICON_DEFAULT;
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
draw: function () {
|
|
|
|
|
|
this.initProperty();
|
|
|
this.rBorder = container.rect(this.x, this.y, NODE_WIDTH, NODE_HEIGHT, NODE_BORDER_RADIUS);
|
|
|
this.rBorder.attr({
|
|
|
"stroke": this.rBorderColor,
|
|
|
"stroke-width": STYLE_NORMAL.BORDER_WIDTH,
|
|
|
"fill":"white",
|
|
|
"fill-opacity": 0.5
|
|
|
});
|
|
|
this.rIcon = container.image(this.icon, this.x + 1, this.y + 1, NODE_WIDTH - 2, NODE_HEIGHT - 2);
|
|
|
this.rIcon.parent = this;
|
|
|
this.rText = container.text(this.x + NODE_WIDTH / 2, this.y + NODE_HEIGHT + 10, this.name);
|
|
|
//this.rText.attr({ "stroke": STYLE_NORMAL.FONT_FORE, "font-size": STYLE_NORMAL.FONT_SIZE });
|
|
|
this.rText.attr({ "stroke": STYLE_NORMAL.FONT_FORE});
|
|
|
this.rCenter = container.circle(this.x + NODE_WIDTH / 2, this.y + NODE_HEIGHT / 2, NODE_WIDTH_CENTER).attr({ "hue": .45, "fill": "green" });
|
|
|
this.rCenter.parent = this;
|
|
|
this.rCenter.hide();
|
|
|
},
|
|
|
bindAction: function () {
|
|
|
this.rIconAction();
|
|
|
this.rCenterAction();
|
|
|
this.rTextAction();
|
|
|
},
|
|
|
rCenterAction: function () {
|
|
|
|
|
|
function rCenterDown(e) {
|
|
|
if (px == 0) {
|
|
|
px = this.parent.rCenter.attr("cx"),
|
|
|
py = this.parent.rCenter.attr("cy");
|
|
|
|
|
|
var recDirBegin = {
|
|
|
x: px,
|
|
|
y: py,
|
|
|
width: NODE_WIDTH_CENTER,
|
|
|
height: NODE_WIDTH_CENTER
|
|
|
};
|
|
|
|
|
|
recDirEnd = recDirBegin;
|
|
|
recDirEnd.x = px + 1;
|
|
|
recDirEnd.y = py + 1;
|
|
|
|
|
|
if (tempDirection == null) {
|
|
|
// 方向添加的时候确定开始节点
|
|
|
name = f.getNewElementName(ElementType.DIRECTION);
|
|
|
var d = new CCDirection(this.parent.id, "", DirType.Backward);
|
|
|
d.FromNode = this.parent;
|
|
|
tempDirection = d;
|
|
|
tempDirection.move(recDirBegin, recDirEnd);
|
|
|
}
|
|
|
}
|
|
|
state = ACTION.DIRECTION_CREATE;
|
|
|
return false;
|
|
|
};
|
|
|
this.rCenter.mousedown(rCenterDown);
|
|
|
this.rCenter.mouseup(this.MouseUp);
|
|
|
|
|
|
},
|
|
|
rIconAction: function () {
|
|
|
var pStartBorder, pStartText, pStartCenter;
|
|
|
function iconMove(dx, dy, x, y, e) {
|
|
|
if (!this.isDrag) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (tempDirection != null) {
|
|
|
onContainerMouseMove(e);
|
|
|
return false;
|
|
|
}
|
|
|
//动态修改与ICON绑定的其他对象的坐标
|
|
|
var att = { x: this.ox + dx, y: this.oy + dy }, ps, path1;
|
|
|
this.attr(att);
|
|
|
|
|
|
this.parent.rBorder.attr({ "x": pStartBorder.x + dx, "y": pStartBorder.y + dy });
|
|
|
this.parent.rText.attr({ "x": pStartText.x + dx, "y": pStartText.y + dy });
|
|
|
this.parent.rCenter.attr({ "cx": pStartCenter.x + dx, "cy": pStartCenter.y + dy });
|
|
|
|
|
|
//重绘与该节点相连的连接线
|
|
|
CCDirection.OnFlowNodeMove(this.parent, e);
|
|
|
};
|
|
|
function iconDown(x, y, e) {
|
|
|
if (state != ACTION.NONE)
|
|
|
return false;
|
|
|
|
|
|
if (!((isie && e.button == 1) || e.button == 0)) {
|
|
|
this.isDrag = false; //此处为解决非IE浏览器一些莫名其妙的问题,增加此变量来解决
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
this.isDrag = true;
|
|
|
this.attr({ "cursor": "hand" });
|
|
|
|
|
|
this.ox = this.attr("x");
|
|
|
this.oy = this.attr("y");
|
|
|
// this.animate({ "fill-opacity": 0.5 }, 500);
|
|
|
|
|
|
//记录与ICON绑定的其他对象的原始坐标
|
|
|
pStartCenter = { x: this.parent.rCenter.attr("cx"), y: this.parent.rCenter.attr("cy") };
|
|
|
pStartBorder = { x: this.parent.rBorder.attr("x"), y: this.parent.rBorder.attr("y") };
|
|
|
pStartText = { x: this.parent.rText.attr("x"), y: this.parent.rText.attr("y") };
|
|
|
|
|
|
};
|
|
|
|
|
|
this.rCenter.mousemove(iconMove);
|
|
|
this.rIcon.drag(iconMove, iconDown, this.MouseUp);
|
|
|
this.rIcon.hover(this.Hover, this.UnHover);
|
|
|
$(this.rIcon.node).bind("contextmenu", this.ContextMenu);
|
|
|
},
|
|
|
rTextAction: function () {
|
|
|
},
|
|
|
Hover: function () {
|
|
|
if (this.parent.rIsFocus) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (state == ACTION.DIRECTION_CREATE)
|
|
|
this.parent.rIcon.attr({ "cursor": "move" });
|
|
|
else
|
|
|
this.parent.rIcon.attr({ "cursor": "arrow" });
|
|
|
|
|
|
this.parent.rBorder.attr({ "stroke": STYLE_HOVER.BORDER_COLOR });
|
|
|
this.parent.rText.attr({ "stroke": STYLE_HOVER.FONT_FORE });
|
|
|
this.parent.rCenter.show();
|
|
|
},
|
|
|
UnHover: function () {
|
|
|
if (this.parent.rIsFocus) {
|
|
|
return false;
|
|
|
}
|
|
|
this.parent.setNormal();
|
|
|
},
|
|
|
MouseUp: function (e) {
|
|
|
var r = addDirection(e,this);
|
|
|
if (!r) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (!this.isDrag) {
|
|
|
return;
|
|
|
} else {
|
|
|
//this.animate({ "fill-opacity": 1 }, 500);
|
|
|
this.parent.x = this.attr("x") - NODE_WIDTH / 2,
|
|
|
this.parent.y = this.attr("y") - NODE_HEIGHT / 2;
|
|
|
}
|
|
|
},
|
|
|
ContextMenu: function (e) {
|
|
|
|
|
|
$('#nodeMenu').menu('show', {
|
|
|
left: e.pageX,
|
|
|
top: e.pageY
|
|
|
});
|
|
|
|
|
|
return false;
|
|
|
},
|
|
|
clearFocus: function () {
|
|
|
this.rIsFocus = false;
|
|
|
this.setNormal();
|
|
|
},
|
|
|
focus: function () {
|
|
|
|
|
|
this.rBorder.attr({ "stroke": STYLE_FOCUSED.BORDER });
|
|
|
this.rText.attr({ "stroke": STYLE_FOCUSED.FONT });
|
|
|
this.rIsFocus = true;
|
|
|
},
|
|
|
setNormal: function () {
|
|
|
this.rCenter.hide();
|
|
|
this.rBorder.attr({ "stroke": this.rBorderColor });
|
|
|
this.rText.attr({ "stroke": STYLE_NORMAL.FONT_FORE });
|
|
|
},
|
|
|
add: function () {
|
|
|
this.draw();
|
|
|
this.bindAction();
|
|
|
diagram.flow.nodes.push(this);
|
|
|
|
|
|
},
|
|
|
del: function () {
|
|
|
|
|
|
//删除关联的连接线
|
|
|
for (var i = 0; i < diagram.flow.directions.length; i++) {
|
|
|
var dir = diagram.flow.directions[i];
|
|
|
if (dir.node == this.id || dir.toNode == this.id)
|
|
|
dir.del();
|
|
|
}
|
|
|
|
|
|
this.rBorder.remove();
|
|
|
this.rIcon.remove();
|
|
|
this.rText.remove();
|
|
|
this.rCenter.remove();
|
|
|
diagram.flow.nodes.remove(this);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
function CCLabNote(sPk, text, iX, iY) {
|
|
|
/// <summary>标签</summary>
|
|
|
/// <param name="sPk" Type="String">MyPk</param>
|
|
|
/// <param name="sLabelName" Type="String">标签文本</param>
|
|
|
/// <param name="iX" Type="Int">标签左上角X坐标</param>
|
|
|
/// <param name="iY" Type="Int">标签左上角Y坐标</param>
|
|
|
this.mypk = sPk;
|
|
|
this.name = text;
|
|
|
this.x = iX;
|
|
|
this.y = iY;
|
|
|
|
|
|
this.rBorder = null;
|
|
|
this.rText = null;
|
|
|
this.rIsFocus = false;
|
|
|
this.oType = ElementType.LABNOTE;
|
|
|
}
|
|
|
CCLabNote.load = function (l) {
|
|
|
|
|
|
var lbl = new CCLabNote(l.MYPK, l.NAME, l.X, l.Y);
|
|
|
|
|
|
return lbl;
|
|
|
};
|
|
|
CCLabNote.loadArray = function (a) {
|
|
|
var c = [];
|
|
|
for (var b = 0; b < a.length; b++) {
|
|
|
c.push(CCLabNote.load(a[b]))
|
|
|
}
|
|
|
return c;
|
|
|
};
|
|
|
CCLabNote.prototype = {
|
|
|
draw: function () {
|
|
|
|
|
|
this.rText = container.text(this.x, this.y, this.name);
|
|
|
this.rText.attr({ "stroke": LABEL_FONT_COLOR_FORE, "font-size": LABEL_FONT_SIZE, "text-anchor": "start" });
|
|
|
this.rText.toFront();
|
|
|
this.rText.parent = this;
|
|
|
var rec = this.rText.getBBox(true);
|
|
|
this.rBorder = container.rect(this.x - 2, rec.y, rec.width + 2, rec.height + 2);
|
|
|
},
|
|
|
bindAction: function () {
|
|
|
|
|
|
var isDrag = false, pStartBorder;
|
|
|
|
|
|
$(this.rText.node).bind("contextmenu", function (e) {
|
|
|
|
|
|
$('#labelMenu').menu('show', {
|
|
|
left: e.pageX,
|
|
|
top: e.pageY
|
|
|
});
|
|
|
|
|
|
return false;
|
|
|
});
|
|
|
|
|
|
this.rText.hover(
|
|
|
function () {
|
|
|
if (this.parent.rIsFocus) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
this.parent.rBorder.attr("stroke", LABEL_FONT_COLOR_HOVER);
|
|
|
this.parent.rText.attr({ "stroke": LABEL_FONT_COLOR_HOVER, "cursor": "move" });
|
|
|
},
|
|
|
function () {
|
|
|
if (this.parent.rIsFocus) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
this.parent.rBorder.attr("stroke", LABEL_FONT_COLOR_FORE);
|
|
|
this.parent.rText.attr({ "stroke": LABEL_FONT_COLOR_FORE, "cursor": "pointer" });
|
|
|
}
|
|
|
);
|
|
|
|
|
|
|
|
|
function txtDown(x, y, e) {
|
|
|
if (!((isie && e.button == 1) || e.button == 0)) {
|
|
|
isDrag = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
isDrag = true;
|
|
|
pStartBorder = { x: this.parent.rBorder.attr("x"), y: this.parent.rBorder.attr("y") };
|
|
|
this.ox = this.attr("x");
|
|
|
this.oy = this.attr("y");
|
|
|
this.animate({ "fill-opacity": 0.5 }, 500);
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
function txtMove(dx, dy) {
|
|
|
if (!isDrag) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
this.attr({ x: this.ox + dx, y: this.oy + dy });
|
|
|
this.parent.rBorder.attr({ x: this.ox + dx, y: pStartBorder.y + dy });
|
|
|
}
|
|
|
|
|
|
function txtUp() {
|
|
|
if (!isDrag) {
|
|
|
return;
|
|
|
}
|
|
|
this.parent.x = this.attr("x"),
|
|
|
this.parent.y = this.attr("y");
|
|
|
this.parent.rBorder.animate({ "fill-opacity": 1 }, 500);
|
|
|
this.parent.rText.animate({ "fill-opacity": 1 }, 500);
|
|
|
}
|
|
|
|
|
|
this.rText.drag(txtMove, txtDown, txtUp);
|
|
|
},
|
|
|
clearFocus: function () {
|
|
|
this.rText.attr({ "stroke": LABEL_FONT_COLOR_FORE });
|
|
|
this.rIsFocus = false;
|
|
|
},
|
|
|
focus: function () {
|
|
|
|
|
|
this.rBorder.attr("stroke", STYLE_FOCUSED.BORDER);
|
|
|
this.rText.attr({ "stroke": STYLE_FOCUSED.FONT, "cursor": "hand" });
|
|
|
this.rIsFocus = true;
|
|
|
},
|
|
|
add: function () {
|
|
|
this.draw();
|
|
|
this.bindAction();
|
|
|
diagram.flow.labNotes.push(this);
|
|
|
},
|
|
|
del: function () {
|
|
|
this.rBorder.remove();
|
|
|
this.rText.remove();
|
|
|
diagram.flow.labNotes.remove(this);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
var DirType = {
|
|
|
Forward: 0, //前进线
|
|
|
Backward: 1, //回退线
|
|
|
Virtual: 2 //虚拟线
|
|
|
},
|
|
|
DirectionUIType =
|
|
|
{
|
|
|
Line: 0,
|
|
|
Polyline: 1
|
|
|
},
|
|
|
DirectionMove =
|
|
|
{
|
|
|
Begin: 0,
|
|
|
Line: 1,
|
|
|
End: 2
|
|
|
};
|
|
|
function CCDirection(nId, tonId, dirType, IsCanback,dots1) {
|
|
|
/// <summary>结点连接线</summary>
|
|
|
/// <param name="iFromNodeID" Type="Int">开始节点ID</param>
|
|
|
/// <param name="iToNodeID" Type="Int">结束节点ID</param>
|
|
|
/// <param name="iDirType" Type="Int">节点类型 0-前进 1-返回</param>
|
|
|
/// <param name="iIsCanBack" Type="Int">是否可以原路返回</param>
|
|
|
this.node = nId;
|
|
|
this.toNode = tonId;
|
|
|
this.dirType = dirType;
|
|
|
this.isCanBack = IsCanback;
|
|
|
|
|
|
if (dots1 !=null && dots1.length>0) {
|
|
|
var dots= new Array();
|
|
|
var strs= new Array();
|
|
|
strs = dots1.split('@');
|
|
|
for(var i=0;i<strs.length;i++) {
|
|
|
str = strs[i];
|
|
|
if (str != null && str != ""){
|
|
|
var tmp= new Array();
|
|
|
tmp = str.split(',');
|
|
|
if (tmp.length() == 2) {
|
|
|
dots.push(Number(tmp[0]));
|
|
|
dots.push(Number(tmp[1]));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
this.pTurn1 = { x: dots[0], y: dots[1] };
|
|
|
this.pTurn2 = { x: dots[2], y: dots[3] };
|
|
|
this.lineType = DirectionUIType.Polyline;
|
|
|
|
|
|
}
|
|
|
else
|
|
|
this.lineType = DirectionUIType.Line;
|
|
|
|
|
|
this.FromNode = diagram.flow.getNodeById(this.node);
|
|
|
this.ToNode = diagram.flow.getNodeById(this.toNode);
|
|
|
|
|
|
this.MoveType = DirectionMove.Line;
|
|
|
this.IsReturnTypeDir = false;
|
|
|
this.rPath = null;
|
|
|
this.rectB = null;
|
|
|
this.rectE = null;
|
|
|
this.rIsFocus = false;
|
|
|
this.oType = ElementType.DIRECTION;
|
|
|
}
|
|
|
CCDirection.load = function (d) {
|
|
|
var dir = new CCDirection(d.NODE, d.TONODE, d.DIRTYPE, d.ISCANBACK, d.DOTS);
|
|
|
return dir;
|
|
|
};
|
|
|
CCDirection.loadArray = function (a) {
|
|
|
var c = [];
|
|
|
for (var b = 0; b < a.length; b++) {
|
|
|
c.push(CCDirection.load(a[b]))
|
|
|
}
|
|
|
return c
|
|
|
};
|
|
|
CCDirection.BidRad = 10.0; //往返箭头之间的半径
|
|
|
CCDirection.OnFlowNodeMove = function (node, e) {
|
|
|
for (var i = 0, j = diagram.flow.directions.length; i < j; i++) {
|
|
|
var dir = diagram.flow.directions[i];
|
|
|
|
|
|
if (node.id != dir.node && node.id != dir.toNode)
|
|
|
continue;
|
|
|
dir.draw();
|
|
|
}
|
|
|
};
|
|
|
CCDirection.prototype = {
|
|
|
draw: function () {
|
|
|
|
|
|
if (this.FromNode == null || this.ToNode == null)
|
|
|
return true; //跳出本次循环
|
|
|
|
|
|
var rectB = container.getBound(this.FromNode.rBorder),
|
|
|
rectE = container.getBound(this.ToNode.rBorder);
|
|
|
|
|
|
this.move(rectB, rectE);
|
|
|
},
|
|
|
move: function (rectB, rectE) {// 节点移动时调用
|
|
|
|
|
|
if (this.lineType != DirectionUIType.Line) {
|
|
|
//
|
|
|
}
|
|
|
else {
|
|
|
|
|
|
var isDbline = false;
|
|
|
for (var i = 0; i < diagram.flow.directions.length; i++) {
|
|
|
var d = diagram.flow.directions[i];
|
|
|
|
|
|
// 双向线
|
|
|
if (d.node == this.toNode && d.toNode == this.node) {
|
|
|
isDbline = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (rectB != null && rectE != null) {
|
|
|
|
|
|
var points = container.getLinePoints(rectB, rectE, this.lineType);
|
|
|
|
|
|
if (isDbline) {
|
|
|
var pStartBack = new Point(points.x1, points.y1),
|
|
|
pEndBack = new Point(points.x2, points.y2);
|
|
|
|
|
|
var pointsNew = PointCount.IntPoint(pStartBack, pEndBack, CCDirection.BidRad, this.dirType, NODE_WIDTH, NODE_HEIGHT);
|
|
|
|
|
|
points.x1 = pointsNew[0].X;
|
|
|
points.y1 = pointsNew[0].Y;
|
|
|
points.x2 = pointsNew[1].X;
|
|
|
points.y2 = pointsNew[1].Y;
|
|
|
|
|
|
}
|
|
|
var path = container.getArrow(points.x1, points.y1, points.x2, points.y2, 8);
|
|
|
if (this.rPath == null) {
|
|
|
this.rPath = container.path(path);
|
|
|
this.rPath.attr({ "stroke": STYLE_NORMAL.BORDER_COLOR, "stroke-width": STYLE_NORMAL.LINE_WIDTH });
|
|
|
this.rPath.parent = this;
|
|
|
|
|
|
this.rectB = container.circle(points.x1, points.y1, 5)
|
|
|
.attr({ "hue": .45, "fill": "green" })
|
|
|
.hide();
|
|
|
this.rectE = container.circle(points.x2, points.y2, 5)
|
|
|
.attr({ "hue": .45, "fill": "green" })
|
|
|
.hide();
|
|
|
}
|
|
|
else {
|
|
|
this.rPath.attr({ path: path });
|
|
|
this.rectB.attr({ "cx": points.x1, "cy": points.y1 });
|
|
|
this.rectE.attr({ "cx": points.x2, "cy": points.y2 });
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
bindAction: function () {
|
|
|
$(this.rPath.node).bind("contextmenu", this.ContextMenu);
|
|
|
|
|
|
this.rPath.hover(
|
|
|
function () {
|
|
|
if (this.parent.rIsFocus) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
this.attr({ "stroke": STYLE_LINE_HOVER_COLOR, "cursor": "move" });
|
|
|
},
|
|
|
function () {
|
|
|
if (this.parent.rIsFocus) {
|
|
|
return;
|
|
|
}
|
|
|
this.attr({ "stroke": STYLE_NORMAL.BORDER_COLOR, "cursor": "pointer" });
|
|
|
}
|
|
|
);
|
|
|
|
|
|
this.rPath.mousedown(function (e) {
|
|
|
if (!((isie && e.button == 1) || e.button == 0)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
});
|
|
|
},
|
|
|
ContextMenu: function (e) {
|
|
|
|
|
|
$('#dirMenu').menu('show', {
|
|
|
left: e.pageX,
|
|
|
top: e.pageY
|
|
|
});
|
|
|
|
|
|
return false;
|
|
|
},
|
|
|
clearFocus: function () {
|
|
|
|
|
|
this.rectB.attr({ "fill": STYLE_NORMAL.BORDER_COLOR }).hide();
|
|
|
this.rectE.attr({ "fill": STYLE_NORMAL.BORDER_COLOR }).hide();
|
|
|
|
|
|
this.rPath.attr({ "stroke": STYLE_NORMAL.BORDER_COLOR });
|
|
|
this.rIsFocus = false;
|
|
|
},
|
|
|
focus: function () {
|
|
|
|
|
|
this.rectB.attr({ "fill": STYLE_FOCUSED.BORDER }).show();
|
|
|
this.rectE.attr({ "fill": STYLE_FOCUSED.BORDER }).show();
|
|
|
this.rPath.attr({ "stroke": STYLE_FOCUSED.BORDER, "cursor": "move" });
|
|
|
this.rIsFocus = true;
|
|
|
},
|
|
|
add: function () {
|
|
|
this.draw();
|
|
|
this.bindAction();
|
|
|
diagram.flow.directions.push(this);
|
|
|
} ,
|
|
|
del:function(){
|
|
|
|
|
|
this.rPath.remove();
|
|
|
this.rectB.remove();
|
|
|
this.rectE.remove();
|
|
|
diagram.flow.directions.remove(this);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
/* drag to add direction between 2 node*/
|
|
|
var px = 0 , py=0,
|
|
|
recDirEnd = {
|
|
|
x: 0,
|
|
|
y: 0,
|
|
|
width: NODE_WIDTH_CENTER,
|
|
|
heigth: NODE_WIDTH_CENTER
|
|
|
},
|
|
|
tempDirection ;
|
|
|
function onContainerMouseDown(e) {
|
|
|
var ele = container.getElementByPoint(e.pageX, e.pageY);
|
|
|
if(ele == null) {
|
|
|
diagram.flow.clearFocus();
|
|
|
}
|
|
|
else if (ele.parent) {
|
|
|
diagram.flow.setFocus(ele.parent);
|
|
|
}
|
|
|
|
|
|
if (!((isie && e.button == 1) || e.button == 0)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (state == ACTION.DIRECTION_CREATE) {
|
|
|
state == ACTION.DIRECTION_MOVE_POINT;
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
}
|
|
|
function onContainerMouseMove(e) {
|
|
|
|
|
|
if (tempDirection != null){
|
|
|
|
|
|
var p = getRaphaelPoint(e.pageX, e.pageY);
|
|
|
|
|
|
var recDirBegin = {
|
|
|
x: px,
|
|
|
y: py,
|
|
|
width: NODE_WIDTH_CENTER,
|
|
|
height: NODE_WIDTH_CENTER
|
|
|
};
|
|
|
|
|
|
if (p.x > 5)
|
|
|
p.x = p.x - 5;
|
|
|
|
|
|
recDirEnd.x = p.x;
|
|
|
recDirEnd.y = p.y;
|
|
|
|
|
|
tempDirection.move(recDirBegin, recDirEnd);
|
|
|
}
|
|
|
}
|
|
|
function onContainerMouseUp(e) {
|
|
|
|
|
|
// if (state != ACTION.DIRECTION_CREATE && state != ACTION.DIRECTION_MOVE_POINT)
|
|
|
// return ;
|
|
|
|
|
|
if(tempDirection == null)
|
|
|
return;
|
|
|
|
|
|
var ele = container.getElementByPoint(e.pageX, e.pageY);
|
|
|
|
|
|
var needAdded = false,
|
|
|
dToNode = null,
|
|
|
dNode = tempDirection.node;
|
|
|
|
|
|
if (ele != null && ele.parent != null && ele.parent.oType == ElementType.NODE) {
|
|
|
dToNode = ele.parent.id;
|
|
|
// 起止点相同
|
|
|
if (dToNode != null && dNode != dToNode) {
|
|
|
needAdded = true;
|
|
|
}
|
|
|
|
|
|
for (var i = 0; i < diagram.flow.directions.length; i++) {
|
|
|
var d = diagram.flow.directions[i];
|
|
|
if (d == this) { // 同一个规则箭头
|
|
|
needAdded = false;
|
|
|
break; ;
|
|
|
}
|
|
|
|
|
|
if (d.node == dNode && d.toNode == dToNode) {//重复
|
|
|
needAdded = false;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// if (d.node == dToNode && d.toNode == dNode) {// 往返箭头
|
|
|
// continue;
|
|
|
// }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (needAdded) {
|
|
|
// 方向添加的时候确定开始结束节点
|
|
|
var dir = new CCDirection(dNode, dToNode, DirType.Forward, false);
|
|
|
|
|
|
dir.isNew = true; //标识是新加的连接线
|
|
|
tempDirection.rPath.remove();
|
|
|
dir.add();
|
|
|
} else {
|
|
|
tempDirection.rPath.remove();
|
|
|
}
|
|
|
|
|
|
tempDirection = null;
|
|
|
px = py = 0;
|
|
|
state = ACTION.NONE;
|
|
|
}
|
|
|
function onContainerContextMenu(e) {
|
|
|
|
|
|
lastMousePoint.x = e.pageX;
|
|
|
lastMousePoint.y = e.pageY;
|
|
|
|
|
|
$('#flowMenu').menu('show', {
|
|
|
left: e.pageX,
|
|
|
top: e.pageY
|
|
|
});
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
// 在节点上松开鼠标时创建方向
|
|
|
function addDirection(e) {
|
|
|
if (tempDirection != null) {
|
|
|
onContainerMouseUp(e);
|
|
|
return false;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
Raphael.fn.getArrow = function (x1, y1, x2, y2, size) {
|
|
|
/// <summary>获取两点之间连线的路径,带箭头</summary>
|
|
|
/// <param name="x1" Type="Int">开始点X坐标</param>
|
|
|
/// <param name="y1" Type="Int">开始点Y坐标</param>
|
|
|
/// <param name="x2" Type="Int">结束点X坐标</param>
|
|
|
/// <param name="y2" Type="Int">结束点Y坐标</param>
|
|
|
/// <param name="size" Type="Int">箭头长度</param>
|
|
|
var angle = Raphael.angle(x1, y1, x2, y2); //得到两点之间的角度
|
|
|
var a45 = Raphael.rad(angle - 45); //角度转换成弧度
|
|
|
var a45m = Raphael.rad(angle + 45);
|
|
|
var x2a = x2 + Math.cos(a45) * size;
|
|
|
var y2a = y2 + Math.sin(a45) * size;
|
|
|
var x2b = x2 + Math.cos(a45m) * size;
|
|
|
var y2b = y2 + Math.sin(a45m) * size;
|
|
|
|
|
|
|
|
|
// M:move表示画笔起点移动到此点
|
|
|
// L:line表示从某点绘制到某点,绘制直线
|
|
|
return ["M", x1, y1, "L", x2, y2, "L", x2a, y2a, "M", x2, y2, "L", x2b, y2b];
|
|
|
}
|
|
|
Raphael.fn.getLinePoints = function (rectB, rectE) {
|
|
|
/// <summary>获取两个绘图元素之间的连接端点的坐标</summary>
|
|
|
/// <param name="rElementFrom" Type="Raphael Element">连接线开始的绘图元素</param>
|
|
|
/// <param name="rElementTo" Type="Raphael Element">连接线结束的绘图元素</param>
|
|
|
|
|
|
var p = [
|
|
|
{ x: rectB.x + rectB.width / 2, y: rectB.y - 1 },
|
|
|
{ x: rectB.x + rectB.width / 2, y: rectB.y + rectB.height + 1 },
|
|
|
{ x: rectB.x - 1, y: rectB.y + rectB.height / 2 },
|
|
|
{ x: rectB.x + rectB.width + 1, y: rectB.y + rectB.height / 2 },
|
|
|
|
|
|
{ x: rectE.x + rectE.width / 2, y: rectE.y - 1 },
|
|
|
{ x: rectE.x + rectE.width / 2, y: rectE.y + rectE.height + 1 },
|
|
|
{ x: rectE.x - 1, y: rectE.y + rectE.height / 2 },
|
|
|
{ x: rectE.x + rectE.width + 1, y: rectE.y + rectE.height / 2 }
|
|
|
];
|
|
|
|
|
|
var d = {}, dis = [];
|
|
|
|
|
|
for (var i = 0; i < 4; i++) {
|
|
|
for (var j = 4; j < 8; j++) {
|
|
|
var dx = Math.abs(p[i].x - p[j].x),
|
|
|
dy = Math.abs(p[i].y - p[j].y);
|
|
|
if (
|
|
|
(i == j - 4)
|
|
|
|| (((i != 3 && j != 6) || p[i].x < p[j].x)
|
|
|
&& ((i != 2 && j != 7) || p[i].x > p[j].x)
|
|
|
&& ((i != 0 && j != 5) || p[i].y > p[j].y)
|
|
|
&& ((i != 1 && j != 4) || p[i].y < p[j].y))
|
|
|
) {
|
|
|
dis.push(dx + dy);
|
|
|
d[dis[dis.length - 1]] = [i, j];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (dis.length == 0) {
|
|
|
var res = [0, 4];
|
|
|
} else {
|
|
|
res = d[Math.min.apply(Math, dis)];
|
|
|
}
|
|
|
|
|
|
return { x1: p[res[0]].x, y1: p[res[0]].y, x2: p[res[1]].x, y2: p[res[1]].y };
|
|
|
}
|
|
|
Raphael.fn.getBound = function (obj) {
|
|
|
var rect = obj.getBBox();
|
|
|
return rect;
|
|
|
};
|
|
|
|
|
|
|
|
|
/* Creates an instance of Point
|
|
|
*
|
|
|
* @constructor
|
|
|
* @this {Point}
|
|
|
* @param {Number} x The x coordinate of point.
|
|
|
* @param {Number} y The y coordinate of point.
|
|
|
* Note: Even if it is named Point this class should be named Dot as Dot is closer
|
|
|
* then Point from math perspective.
|
|
|
**/
|
|
|
function Point(x, y) {
|
|
|
/* coordinate of point*/
|
|
|
this.X = x;
|
|
|
this.Y = y;
|
|
|
|
|
|
/**The {@link Style} of the Point*/
|
|
|
this.style = null;//new Style();
|
|
|
|
|
|
/**Serialization type*/
|
|
|
this.oType = 'Point'; //object type used for JSON deserialization
|
|
|
}
|
|
|
Point.load = function (o) {
|
|
|
/* Creates a {Point} out of JSON parsed object
|
|
|
*@param {JSONObject} o - the JSON parsed object
|
|
|
*@return {Point} a newly constructed Point
|
|
|
**/
|
|
|
|
|
|
var newPoint = new Point(Number(o.X), Number(o.Y));
|
|
|
return newPoint;
|
|
|
}
|
|
|
Point.loadArray = function (v) {
|
|
|
/* Creates an array of points from an array of {JSONObject}s
|
|
|
*@param {Array} v - the array of JSONObjects
|
|
|
*@return an {Array} of {Point}s
|
|
|
**/
|
|
|
|
|
|
var newPoints = [];
|
|
|
for (var i = 0; i < v.length; i++) {
|
|
|
newPoints.push(Point.load(v[i]));
|
|
|
}
|
|
|
return newPoints;
|
|
|
}
|
|
|
Point.cloneArray = function (v) {
|
|
|
/* Clones an array of points
|
|
|
*@param {Array} v - the array of {Point}s
|
|
|
*@return an {Array} of {Point}s
|
|
|
**/
|
|
|
|
|
|
var newPoints = [];
|
|
|
for (var i = 0; i < v.length; i++) {
|
|
|
newPoints.push(v[i].clone());
|
|
|
}
|
|
|
return newPoints;
|
|
|
}
|
|
|
Point.prototype = {
|
|
|
constructor: Point,
|
|
|
|
|
|
/**Paint current {Point} withing a context
|
|
|
*If you want to use a different style then the default one change the style
|
|
|
**/
|
|
|
paint: function (context) {
|
|
|
|
|
|
},
|
|
|
|
|
|
clone: function () {
|
|
|
var newPoint = new Point(this.x, this.y);
|
|
|
return newPoint;
|
|
|
},
|
|
|
|
|
|
/**Tests to see if a point (x, y) is within a range of current Point
|
|
|
*@param {Numeric} x - the x coordinate of tested point
|
|
|
*@param {Numeric} y - the x coordinate of tested point
|
|
|
*@param {Numeric} radius - the radius of the vicinity
|
|
|
*@author Alex Gheorghiu <alex@scriptoid.com>
|
|
|
**/
|
|
|
near: function (x, y, radius) {
|
|
|
var distance = Math.sqrt(Math.pow(this.X - x, 2) + Math.pow(this.Y - y, 2));
|
|
|
|
|
|
return (distance <= radius);
|
|
|
},
|
|
|
|
|
|
contains: function (x, y) {
|
|
|
return this.X == x && this.Y == y;
|
|
|
},
|
|
|
equals: function (value) {
|
|
|
return this.X == value.X && this.Y == value.Y;
|
|
|
},
|
|
|
toString: function () {
|
|
|
return 'point(' + this.X + ',' + this.Y + ')';
|
|
|
},
|
|
|
|
|
|
getPoints: function () {
|
|
|
return [this];
|
|
|
},
|
|
|
|
|
|
getBounds: function () {
|
|
|
return Point.getBounds(this.getPoints());
|
|
|
}
|
|
|
}
|
|
|
Point.getBounds=function(points){
|
|
|
if (!points.length)
|
|
|
return null;
|
|
|
var minX = points[0].X;
|
|
|
var maxX = minX;
|
|
|
var minY = points[0].Y;
|
|
|
var maxY = minY;
|
|
|
for (var i = 1; i < points.length; i++) {
|
|
|
minX = Math.min(minX, points[i].X);
|
|
|
minY = Math.min(minY, points[i].Y);
|
|
|
maxX = Math.max(maxX, points[i].X);
|
|
|
maxY = Math.max(maxY, points[i].Y);
|
|
|
}
|
|
|
return [minX, minY, maxX, maxY];
|
|
|
};
|
|
|
|
|
|
function PointCount(){
|
|
|
|
|
|
}
|
|
|
PointCount.GetPointAtEllipse=function(start, end, r) {
|
|
|
/// <summary>
|
|
|
/// 返回圆外点投射到圆心的圆边上坐标
|
|
|
/// </summary>
|
|
|
/// <param name="start">圆的中心点坐标</param>
|
|
|
/// <param name="end">圆外点坐标</param>
|
|
|
/// <param name="r">圆半径</param>
|
|
|
/// <returns></returns>
|
|
|
|
|
|
var pReturn = new Point();
|
|
|
if (start.X == end.X) {
|
|
|
pReturn.X = start.X;
|
|
|
if (end.Y <= start.Y)
|
|
|
pReturn.Y = start.Y - r;
|
|
|
else
|
|
|
pReturn.Y = start.Y + r;
|
|
|
}
|
|
|
else if (start.Y == end.Y) {
|
|
|
pReturn.Y = start.Y;
|
|
|
if (end.X <= start.X)
|
|
|
pReturn.X = start.X - r;
|
|
|
else
|
|
|
pReturn.X = start.X + r;
|
|
|
}
|
|
|
else {
|
|
|
pReturn.Y = r * (end.Y - start.Y) / Math.sqrt(Math.pow(end.Y - start.Y, 2) + Math.pow(end.X - start.X, 2)) + start.Y;
|
|
|
pReturn.X = (pReturn.Y - start.Y) * (end.X - start.X) / (end.Y - start.Y) + start.X;
|
|
|
}
|
|
|
return pReturn;
|
|
|
};
|
|
|
PointCount.GetPointAtRectangle=function(start, end, width, height){
|
|
|
/// <summary>
|
|
|
/// 返回矩形外点投射到矩形中心的边上的坐标
|
|
|
/// </summary>
|
|
|
/// <param name="start">矩形的中心点坐标</param>
|
|
|
/// <param name="end">矩形外点坐标</param>
|
|
|
/// <param name="width">矩形的宽</param>
|
|
|
/// <param name="height">矩形的高</param>
|
|
|
/// <returns></returns>
|
|
|
|
|
|
//需要判断宽和高的比率问题
|
|
|
var pReturn = new Point();
|
|
|
if (start.X == end.X)
|
|
|
{
|
|
|
pReturn.X = start.X;
|
|
|
if (end.Y <= start.Y)
|
|
|
pReturn.Y = start.Y - height/2;
|
|
|
else
|
|
|
pReturn.Y = start.Y + height / 2;
|
|
|
}
|
|
|
else if (start.Y == end.Y)
|
|
|
{
|
|
|
pReturn.Y = start.Y;
|
|
|
if (end.X <= start.X)
|
|
|
pReturn.X = start.X - width / 2;
|
|
|
else
|
|
|
pReturn.X = start.X + width / 2;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (height / width >= Math.abs(end.Y - start.Y) / Math.abs(end.X - start.X))
|
|
|
{
|
|
|
if (end.X - start.X <= 0)
|
|
|
{
|
|
|
pReturn.X = start.X - width / 2;
|
|
|
pReturn.Y = start.Y + (end.Y - start.Y) * (-width / 2) / (end.X - start.X);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
pReturn.X = start.X + width / 2;
|
|
|
pReturn.Y = start.Y + (end.Y - start.Y) * (width / 2) / (end.X - start.X);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (end.Y - start.Y <= 0)
|
|
|
{
|
|
|
pReturn.Y = start.Y - height / 2;
|
|
|
pReturn.X = (-height / 2) * (end.X - start.X) / (end.Y - start.Y) + start.X;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
pReturn.Y = start.Y + height / 2;
|
|
|
pReturn.X = (height / 2) * (end.X - start.X) / (end.Y - start.Y) + start.X;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return pReturn;
|
|
|
}
|
|
|
PointCount.IntPoint = function (start, end, r, dirType, width, height) {
|
|
|
/// <summary>
|
|
|
/// 根据两活动的中心点,返回往返的边缘坐标
|
|
|
/// </summary>
|
|
|
/// <param name="start">起始活动中心点</param>
|
|
|
/// <param name="end">结束活动中心点</param>
|
|
|
/// <param name="r">往返之间的半径</param>
|
|
|
/// <param name="bid">往返类型</param>
|
|
|
/// <param name="width">活动的宽</param>
|
|
|
/// <param name="height">活动的高</param>
|
|
|
/// <returns></returns>
|
|
|
|
|
|
//获取新的起点
|
|
|
var newStart = PointCount.GetBidPoint(start, end, r, dirType, true);
|
|
|
//获取新的终点
|
|
|
var newEnd = PointCount.GetBidPoint(start, end, r, dirType, false);
|
|
|
|
|
|
var intersection = new Array();
|
|
|
intersection.push(new Point());
|
|
|
intersection.push(new Point());
|
|
|
if (newStart.X == newEnd.X) {
|
|
|
if (newStart.Y >= newEnd.Y) {
|
|
|
intersection[0].X = newStart.X;
|
|
|
intersection[0].Y = newStart.Y - height / 2;
|
|
|
intersection[1].X = newEnd.X;
|
|
|
intersection[1].Y = newEnd.Y + height / 2;
|
|
|
}
|
|
|
else {
|
|
|
intersection[0].X = newStart.X;
|
|
|
intersection[0].Y = newStart.Y + height / 2;
|
|
|
intersection[1].X = newEnd.X;
|
|
|
intersection[1].Y = newEnd.Y - height / 2;
|
|
|
}
|
|
|
}
|
|
|
else if (newStart.Y == newEnd.Y) {
|
|
|
if (newStart.X >= newEnd.X) {
|
|
|
intersection[0].X = newStart.X - width / 2;
|
|
|
intersection[0].Y = newStart.Y;
|
|
|
intersection[1].X = newEnd.X + width / 2;
|
|
|
intersection[1].Y = newEnd.Y;
|
|
|
}
|
|
|
else {
|
|
|
intersection[0].X = newStart.X + width / 2;
|
|
|
intersection[0].Y = newStart.Y;
|
|
|
intersection[1].X = newEnd.X - width / 2;
|
|
|
intersection[1].Y = newEnd.Y;
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
//定义起点的四个角的坐标
|
|
|
var Srec1 = new Point(start.X - width / 2, start.Y - height / 2); //左上角
|
|
|
var Srec2 = new Point(start.X + width / 2, start.Y - height / 2); //右上角
|
|
|
var Srec3 = new Point(start.X + width / 2, start.Y + height / 2); //右下角
|
|
|
var Srec4 = new Point(start.X - width / 2, start.Y + height / 2); //左下角
|
|
|
|
|
|
//定义交叉点落在哪条边上
|
|
|
var Lin1 = 0; //0为上边,1为右边,2为下边,3为左边
|
|
|
|
|
|
//定义结束点点的四个角的坐标
|
|
|
var Erec1 = new Point(end.X - width / 2, end.Y - height / 2); //左上角
|
|
|
var Erec2 = new Point(end.X + width / 2, end.Y - height / 2); //右上角
|
|
|
var Erec3 = new Point(end.X + width / 2, end.Y + height / 2); //右下角
|
|
|
var Erec4 = new Point(end.X - width / 2, end.Y + height / 2); //左下角
|
|
|
|
|
|
//定义交叉点落在哪条边上
|
|
|
var Lin2 = 0; //0为上边,1为右边,2为下边,3为左边
|
|
|
|
|
|
// 计算交叉点落在那条边上
|
|
|
if (newStart.Y < newEnd.Y) {
|
|
|
if (newStart.X > newEnd.X)
|
|
|
if (Math.abs((newStart.Y - newEnd.Y) / (newStart.X - newEnd.X)) >= Math.abs((newStart.Y - Srec4.Y) / (newStart.X - Srec4.X)))
|
|
|
Lin1 = 2;
|
|
|
else
|
|
|
Lin1 = 3;
|
|
|
else
|
|
|
if (Math.abs((newStart.Y - newEnd.Y) / (newStart.X - newEnd.X)) >= Math.abs((newStart.Y - Srec3.Y) / (newStart.X - Srec3.X)))
|
|
|
Lin1 = 2;
|
|
|
else
|
|
|
Lin1 = 1;
|
|
|
|
|
|
if (newStart.X > newEnd.X)
|
|
|
if (Math.abs((newEnd.Y - newStart.Y) / (newEnd.X - newStart.X)) >= Math.abs((newEnd.Y - Erec2.Y) / (newEnd.X - Erec2.X)))
|
|
|
Lin2 = 0;
|
|
|
else
|
|
|
Lin2 = 1;
|
|
|
else
|
|
|
if (Math.abs((newEnd.Y - newStart.Y) / (newEnd.X - newStart.X)) >= Math.abs((newEnd.Y - Erec1.Y) / (newEnd.X - Erec1.X)))
|
|
|
Lin2 = 0;
|
|
|
else
|
|
|
Lin2 = 3;
|
|
|
}
|
|
|
else {
|
|
|
if (newStart.X > newEnd.X)
|
|
|
if (Math.abs((newEnd.Y - newStart.Y) / (newEnd.X - newStart.X)) >= Math.abs((newStart.Y - Srec1.Y) / (newStart.X - Srec1.X)))
|
|
|
Lin1 = 0;
|
|
|
else
|
|
|
Lin1 = 3;
|
|
|
else
|
|
|
if (Math.abs((newEnd.Y - newStart.Y) / (newEnd.X - newStart.X)) >= Math.abs((newStart.Y - Srec2.Y) / (newStart.X - Srec2.X)))
|
|
|
Lin1 = 0;
|
|
|
else
|
|
|
Lin1 = 1;
|
|
|
|
|
|
if (newStart.X > newEnd.X)
|
|
|
if (Math.abs((newStart.Y - newEnd.Y) / (newStart.X - newEnd.X)) >= Math.abs((newEnd.Y - Erec3.Y) / (newEnd.X - Erec3.X)))
|
|
|
Lin2 = 2;
|
|
|
else
|
|
|
Lin2 = 1;
|
|
|
else
|
|
|
if (Math.abs((newStart.Y - newEnd.Y) / (newStart.X - newEnd.X)) >= Math.abs((newEnd.Y - Erec4.Y) / (newEnd.X - Erec4.X)))
|
|
|
Lin2 = 2;
|
|
|
else
|
|
|
Lin2 = 3;
|
|
|
}
|
|
|
|
|
|
|
|
|
// 计算交叉点
|
|
|
var p1 = newStart;
|
|
|
var p2 = newEnd;
|
|
|
var p3 = new Point();
|
|
|
var p4 = new Point();
|
|
|
switch (Lin1) {
|
|
|
case 0:
|
|
|
{
|
|
|
p3 = Srec1;
|
|
|
p4 = Srec2;
|
|
|
break;
|
|
|
}
|
|
|
case 1:
|
|
|
{
|
|
|
p3 = Srec2;
|
|
|
p4 = Srec3;
|
|
|
break;
|
|
|
}
|
|
|
case 2:
|
|
|
{
|
|
|
p3 = Srec3;
|
|
|
p4 = Srec4;
|
|
|
break;
|
|
|
}
|
|
|
case 3:
|
|
|
{
|
|
|
p3 = Srec4;
|
|
|
p4 = Srec1;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
intersection[0] = PointCount.Interaction(p1, p2, p3, p4);
|
|
|
if (intersection[0].equals(new Point()))
|
|
|
intersection[0] = newStart;
|
|
|
switch (Lin2) {
|
|
|
case 0:
|
|
|
{
|
|
|
p3 = Erec1;
|
|
|
p4 = Erec2;
|
|
|
break;
|
|
|
}
|
|
|
case 1:
|
|
|
{
|
|
|
p3 = Erec2;
|
|
|
p4 = Erec3;
|
|
|
break;
|
|
|
}
|
|
|
case 2:
|
|
|
{
|
|
|
p3 = Erec3;
|
|
|
p4 = Erec4;
|
|
|
break;
|
|
|
}
|
|
|
case 3:
|
|
|
{
|
|
|
p3 = Erec4;
|
|
|
p4 = Erec1;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
intersection[1] = PointCount.Interaction(p1, p2, p3, p4);
|
|
|
if (intersection[1].equals(new Point()))
|
|
|
intersection[1] = newEnd;
|
|
|
|
|
|
}
|
|
|
return intersection;
|
|
|
}
|
|
|
PointCount.Interaction=function(p1, p2, p3, p4){
|
|
|
/// <summary>
|
|
|
/// 返回 P1 P2 与 P3 P4 的交叉点,如果不在 P3 P4 范围内,则返回 new point()
|
|
|
/// </summary>
|
|
|
/// <param name="p1"></param>
|
|
|
/// <param name="p2"></param>
|
|
|
/// <param name="p3"></param>
|
|
|
/// <param name="p4"></param>
|
|
|
/// <returns></returns>
|
|
|
|
|
|
var intersection = new Point();
|
|
|
//k为intersection(交点)到p1的距离除以p1到p2的距离,k<0表明intersection在p1之外,k>1表明intersection在p2之外
|
|
|
var k = ((p1.Y - p4.Y) * (p3.X - p4.X) - (p1.X - p4.X) * (p3.Y - p4.Y)) / ((p2.X - p1.X) * (p3.Y - p4.Y) - (p2.Y - p1.Y) * (p3.X - p4.X));
|
|
|
if (k >= 0 && k <= 1) {
|
|
|
intersection.X = p1.X + (p2.X - p1.X) * k;
|
|
|
intersection.Y = p1.Y + (p2.Y - p1.Y) * k;
|
|
|
}
|
|
|
return intersection;
|
|
|
}
|
|
|
PointCount.GetBidPoint=function(start, end, r, dirType, SorE){
|
|
|
/// <summary>
|
|
|
/// 返回双向路由的新点
|
|
|
/// </summary>
|
|
|
/// <param name="start">起点</param>
|
|
|
/// <param name="end">终点</param>
|
|
|
/// <param name="r">双向箭头之间的半径</param>
|
|
|
/// <param name="bid">走向</param>
|
|
|
/// <param name="SorE">返回的是起始点还是终结点</param>
|
|
|
/// <returns></returns>
|
|
|
|
|
|
var pReturn = new Point();
|
|
|
var pBegin = new Point();
|
|
|
if (SorE)
|
|
|
pBegin = start;
|
|
|
else
|
|
|
pBegin = end;
|
|
|
|
|
|
if (start.X == end.X && start.Y == end.Y) {
|
|
|
pReturn.X = start.X + r;
|
|
|
pReturn.Y = start.Y;
|
|
|
}
|
|
|
else {
|
|
|
if (dirType == DirType.Forward) {
|
|
|
if (start.X >= end.X && start.Y > end.Y) {
|
|
|
pReturn.X = pBegin.X + Math.sqrt(Math.pow(r, 2) * Math.pow(start.Y - end.Y, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(start.X - end.X, 2)));
|
|
|
pReturn.Y = pBegin.Y - Math.sqrt(Math.pow(r, 2) * Math.pow(start.X - end.X, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(start.X - end.X, 2)));
|
|
|
}
|
|
|
else if (start.X < end.X && start.Y > end.Y) {
|
|
|
pReturn.X = pBegin.X + Math.sqrt(Math.pow(r, 2) * Math.pow(start.Y - end.Y, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(start.X - end.X, 2)));
|
|
|
pReturn.Y = pBegin.Y + Math.sqrt(Math.pow(r, 2) * Math.pow(start.X - end.X, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(start.X - end.X, 2)));
|
|
|
}
|
|
|
else if (start.X < end.X && start.Y <= end.Y) {
|
|
|
pReturn.X = pBegin.X - Math.sqrt(Math.pow(r, 2) * Math.pow(start.Y - end.Y, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(start.X - end.X, 2)));
|
|
|
pReturn.Y = pBegin.Y + Math.sqrt(Math.pow(r, 2) * Math.pow(start.X - end.X, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(start.X - end.X, 2)));
|
|
|
}
|
|
|
else {
|
|
|
pReturn.X = pBegin.X - Math.sqrt(Math.pow(r, 2) * Math.pow(start.Y - end.Y, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(start.X - end.X, 2)));
|
|
|
pReturn.Y = pBegin.Y - Math.sqrt(Math.pow(r, 2) * Math.pow(start.X - end.X, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(start.X - end.X, 2)));
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
if (start.X <= end.X && start.Y <= end.Y) {
|
|
|
pReturn.X = pBegin.X - Math.sqrt(Math.pow(r, 2) * Math.pow(end.Y - start.Y, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(end.X - start.X, 2)));
|
|
|
pReturn.Y = pBegin.Y + Math.sqrt(Math.pow(r, 2) * Math.pow(end.X - start.X, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(end.X - start.X, 2)));
|
|
|
}
|
|
|
else if (start.X > end.X && start.Y <= end.Y) {
|
|
|
pReturn.X = pBegin.X - Math.sqrt(Math.pow(r, 2) * Math.pow(end.Y - start.Y, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(end.X - start.X, 2)));
|
|
|
pReturn.Y = pBegin.Y - Math.sqrt(Math.pow(r, 2) * Math.pow(end.X - start.X, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(end.X - start.X, 2)));
|
|
|
}
|
|
|
else if (start.X > end.X && start.Y > end.Y) {
|
|
|
pReturn.X = pBegin.X + Math.sqrt(Math.pow(r, 2) * Math.pow(end.Y - start.Y, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(end.X - start.X, 2)));
|
|
|
pReturn.Y = pBegin.Y - Math.sqrt(Math.pow(r, 2) * Math.pow(end.X - start.X, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(end.X - start.X, 2)));
|
|
|
}
|
|
|
else {
|
|
|
pReturn.X = pBegin.X + Math.sqrt(Math.pow(r, 2) * Math.pow(end.Y - start.Y, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(end.X - start.X, 2)));
|
|
|
pReturn.Y = pBegin.Y + Math.sqrt(Math.pow(r, 2) * Math.pow(end.X - start.X, 2) / (Math.pow(start.Y - end.Y, 2) + Math.pow(end.X - start.X, 2)));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return pReturn;
|
|
|
}
|