"use strict";
///
///
///
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) {
/// 根据指定节点ID获取该结点使用Raphael绘制的对象Set
/// 流程编号
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) {
/// 根据绘制的节点中的ICON对象的id获取该结点使用Raphael绘制的对象
/// 流程编号
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 () {
/// 清除当前选中的对象有选中状态
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 = {
///
/// 普通
///
Ordinary : 0,
///
/// 合流
///
HL : 1,
///
/// 分流
///
FL : 2,
///
/// 分合流
///
FHL:3,
///
/// 子线程.
///
SubThread:4,
///
/// 虚节点
///
VirtualStart:5,
VirtualEnd:6,
UnKnown:100
};
function CCNode(id, name, iX, iY) {
/// 节点
/// 节点ID
/// 节点名称
/// 节点中心点X坐标
/// 节点中心点Y坐标
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) {
/// 标签
/// MyPk
/// 标签文本
/// 标签左上角X坐标
/// 标签左上角Y坐标
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) {
/// 结点连接线
/// 开始节点ID
/// 结束节点ID
/// 节点类型 0-前进 1-返回
/// 是否可以原路返回
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 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) {
/// 获取两点之间连线的路径,带箭头
/// 开始点X坐标
/// 开始点Y坐标
/// 结束点X坐标
/// 结束点Y坐标
/// 箭头长度
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) {
/// 获取两个绘图元素之间的连接端点的坐标
/// 连接线开始的绘图元素
/// 连接线结束的绘图元素
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
**/
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) {
///
/// 返回圆外点投射到圆心的圆边上坐标
///
/// 圆的中心点坐标
/// 圆外点坐标
/// 圆半径
///
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){
///
/// 返回矩形外点投射到矩形中心的边上的坐标
///
/// 矩形的中心点坐标
/// 矩形外点坐标
/// 矩形的宽
/// 矩形的高
///
//需要判断宽和高的比率问题
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) {
///
/// 根据两活动的中心点,返回往返的边缘坐标
///
/// 起始活动中心点
/// 结束活动中心点
/// 往返之间的半径
/// 往返类型
/// 活动的宽
/// 活动的高
///
//获取新的起点
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){
///
/// 返回 P1 P2 与 P3 P4 的交叉点,如果不在 P3 P4 范围内,则返回 new point()
///
///
///
///
///
///
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){
///
/// 返回双向路由的新点
///
/// 起点
/// 终点
/// 双向箭头之间的半径
/// 走向
/// 返回的是起始点还是终结点
///
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;
}