# Edit in JSFiddle

```const TAU = Math.PI * 2

const mapToEllipse = ({ x, y }, rx, ry, cosphi, sinphi, centerx, centery) => {
x *= rx
y *= ry

const xp = cosphi * x - sinphi * y
const yp = sinphi * x + cosphi * y

return {
x: xp + centerx,
y: yp + centery
}
}

const approxUnitArc = (ang1, ang2) => {
const a = 4 / 3 * Math.tan(ang2 / 4)

const x1 = Math.cos(ang1)
const y1 = Math.sin(ang1)
const x2 = Math.cos(ang1 + ang2)
const y2 = Math.sin(ang1 + ang2)

return [
{
x: x1 - y1 * a,
y: y1 + x1 * a
},
{
x: x2 + y2 * a,
y: y2 - x2 * a
},
{
x: x2,
y: y2
}
]
}

const vectorAngle = (ux, uy, vx, vy) => {
const sign = (ux * vy - uy * vx < 0) ? -1 : 1
const umag = Math.sqrt(ux * ux + uy * uy)
const vmag = Math.sqrt(ux * ux + uy * uy)
const dot = ux * vx + uy * vy

let div = dot / (umag * vmag)

if (div > 1) {
div = 1
}

if (div < -1) {
div = -1
}

return sign * Math.acos(div)
}

const getArcCenter = (
px,
py,
cx,
cy,
rx,
ry,
largeArcFlag,
sweepFlag,
sinphi,
cosphi,
pxp,
pyp
) => {
const rxsq = Math.pow(rx, 2)
const rysq = Math.pow(ry, 2)
const pxpsq = Math.pow(pxp, 2)
const pypsq = Math.pow(pyp, 2)

let radicant = (rxsq * rysq) - (rxsq * pypsq) - (rysq * pxpsq)

}

radicant /= (rxsq * pypsq) + (rysq * pxpsq)

const centerxp = radicant * rx / ry * pyp
const centeryp = radicant * -ry / rx * pxp

const centerx = cosphi * centerxp - sinphi * centeryp + (px + cx) / 2
const centery = sinphi * centerxp + cosphi * centeryp + (py + cy) / 2

const vx1 = (pxp - centerxp) / rx
const vy1 = (pyp - centeryp) / ry
const vx2 = (-pxp - centerxp) / rx
const vy2 = (-pyp - centeryp) / ry

let ang1 = vectorAngle(1, 0, vx1, vy1)
let ang2 = vectorAngle(vx1, vy1, vx2, vy2)

if (sweepFlag === 0 && ang2 > 0) {
ang2 -= TAU
}

if (sweepFlag === 1 && ang2 < 0) {
ang2 += TAU
}

return [ centerx, centery, ang1, ang2 ]
}

const arcToBezier = ({
px,
py,
cx,
cy,
rx,
ry,
xAxisRotation = 0,
largeArcFlag = 0,
sweepFlag = 0
}) => {
const curves = []

if (rx === 0 || ry === 0) {
return []
}

const sinphi = Math.sin(xAxisRotation * TAU / 360)
const cosphi = Math.cos(xAxisRotation * TAU / 360)

const pxp = cosphi * (px - cx) / 2 + sinphi * (py - cy) / 2
const pyp = -sinphi * (px - cx) / 2 + cosphi * (py - cy) / 2

if (pxp === 0 && pyp === 0) {
return []
}

rx = Math.abs(rx)
ry = Math.abs(ry)

const lambda =
Math.pow(pxp, 2) / Math.pow(rx, 2) +
Math.pow(pyp, 2) / Math.pow(ry, 2)

if (lambda > 1) {
rx *= Math.sqrt(lambda)
ry *= Math.sqrt(lambda)
}

let [ centerx, centery, ang1, ang2 ] = getArcCenter(
px,
py,
cx,
cy,
rx,
ry,
largeArcFlag,
sweepFlag,
sinphi,
cosphi,
pxp,
pyp
)

const segments = Math.max(Math.ceil(Math.abs(ang2) / (TAU / 4)), 1)

ang2 /= segments

for (let i = 0; i < segments; i++) {
curves.push(approxUnitArc(ang1, ang2))
ang1 += ang2
}

return curves.map(curve => {
const { x: x1, y: y1 } = mapToEllipse(curve[ 0 ], rx, ry, cosphi, sinphi, centerx, centery)
const { x: x2, y: y2 } = mapToEllipse(curve[ 1 ], rx, ry, cosphi, sinphi, centerx, centery)
const { x, y } = mapToEllipse(curve[ 2 ], rx, ry, cosphi, sinphi, centerx, centery)

return { x1, y1, x2, y2, x, y }
})
}

//Use Sprite repalce Graph

//Encapsulation PiXi => export PiCi
//Aliases
let Container = PIXI.Container,
Application = PIXI.Application,
Sprite = PIXI.Sprite,
Text = PIXI.Text,
Filters = PIXI.filters,
Graphics = PIXI.Graphics;

let renderer = new Application(window.innerWidth, window.innerHeight, {
antialias: true,// antialias: true,//这抗锯齿一开整个世界都变了  =>bug(just Chrome,FF is ok): renderer = new PIXI.WebGLRenderer + renderer.render(stage);
forceFXAA: true,//For WebglRender AA
backgroundColor: 0x1099bb
}),//Todo=> parameter
stage = renderer.stage,
edgeContainer = new Container(),
arrowContainer = new Container(),
nodeContainer = new Container(),//Node above edge
textContainer = new Container(),//Text above node
dragContainer = new Container();//Hightest level

//Canvas(defalut is Webgl) Use for Render test
renderer.renderer = new PIXI.CanvasRenderer(window.innerWidth, window.innerHeight, {
backgroundColor: 0x1099bb
})

document.body.appendChild(renderer.view);

const SCALE_MAX = 10, SCALE_MIN = 0.4;//For scale limmit
let nodeWidth = 30;//defalut node radius
let point = {};//Todo 这里以后指针的形状也可以自定义
let movePosBegin = {};

let nodeList = {},//Save node
edgeList = {},//Cache edge
arrowList = {},//Save arrow
textList = {},//Save text
edgeInfoList = {};//Save edge info

let circleList = {};
let bezierList = {};//Deal with 2 bezierCurve
let midPos;//Now is just for Bezier,TODO is for all(include straight use midPos to Calculate)

function PiCi(opts) {
opts = Object.assign({}, opts);
//Extrac nodes/edges information from opts
let elements = opts.elements;
let nodes = [];
let edges = [];
if (!elements) elements = [];
if (elements.length > 0) {//init Node
for (let i = 0, l = elements.length; i < l; i++) {
let data = elements[i].data,
id = data.id;

//Simple check
if (data == null) data = {};
if (id == null) {//Id is neccesary
//Check whether id is already exists
if ((nodeList[id] != undefined) || edgeList[id] != undefined) {
console.error("id已存在");
break;
} else {
console.error("id是必须参数");
break;
}
}

if (!data.source && !data.target) {//Node
nodeList[data.id] = data;
nodes.push(data);
} else {
edges.push(data);
//Save this edge's info
edgeInfoList[data.id] = data;
}
}
}

initializeNodes(nodes, edges);

for (let i = 0, l = elements.length; i < l; i++) {//init Edge
let data = elements[i].data,
id = data.id;
//Save this edge's info
edgeInfoList[data.id] = data;
if (data.source && data.target) {//Edge
//Get position info
let source = nodeList[data.source];
let target = nodeList[data.target];

drawArrowAndEdge(data, source, target);
}
}
//层级顺序
}

function drawArrowAndEdge(data, source, target) {
//Remove old edge (drawArrowShape会擦除旧arrow)
if (edgeList[data.id]) edgeContainer.removeChild(edgeList[data.id]);
//Draw Arrow
let newSourcePos, newTargetPos;
if (data.targetShape) {
switch (data.curveStyle) {
case "bezier":
//三阶贝塞尔曲线
let bMidPos = CacBezierCurveMidPos(source, target, 100);
if (bezierList[source + '+' + target]) {
bezierList[source + '+' + target] = 1;//'source+target'
} else {
bezierList[source + '+' + target]++;
}
let pos2 = { x: bMidPos.x2, y: bMidPos.y2 }
//drawCircle(pos2.x, pos2.y, 5);
newTargetPos = drawArrowShape(data.id, data.targetShape, pos2, target, source, target, true);
break;
//二阶贝塞尔曲线
let cMidPos = CacQuadraticCurveMidPos(source, target, 100);
//drawCircle(cMidPos.x, cMidPos.y, 5);
newTargetPos = drawArrowShape(data.id, data.targetShape, cMidPos, target, source, target, true);
break;
default:
newTargetPos = drawArrowShape(data.id, data.targetShape, source, target, source, target, true);
break;
}
}
if (data.sourceShape) {
switch (data.curveStyle) {
case "bezier":
//三阶贝塞尔曲线
let bMidPos = CacBezierCurveMidPos(source, target, 100);
let pos1 = { x: bMidPos.x1, y: bMidPos.y1 }
//drawCircle(pos1.x, pos1.y, 5);
newSourcePos = drawArrowShape(data.id, data.sourceShape, source, pos1, source, target, false);
break;
//二阶贝塞尔曲线
let cMidPos = CacQuadraticCurveMidPos(source, target, 100);
//drawCircle(cMidPos.x, cMidPos.y, 5);
newSourcePos = drawArrowShape(data.id, data.sourceShape, source, cMidPos, source, target, false);
break;
default:
newSourcePos = drawArrowShape(data.id, data.sourceShape, source, target, source, target, false);
break;
}
}

let tempSourcePos = newSourcePos ? newSourcePos : source;
let tempTargetPos = newTargetPos ? newTargetPos : target;

//Draw edge
let line = new Graphics();
line.lineStyle(4, 0xFFFFFF, 1);

line.moveTo(tempSourcePos.x, tempSourcePos.y);
switch (data.curveStyle) {
case "bezier":
//三阶贝塞尔曲线
let cPos = CacBezierCurveMidPos(tempSourcePos, tempTargetPos, 100);
line.bezierCurveTo(cPos.x1, cPos.y1, cPos.x2, cPos.y2, cPos.x, cPos.y);
break;
//二阶贝塞尔曲线
let bPos = CacQuadraticCurveMidPos(tempSourcePos, tempTargetPos, 100);
break;
default:
line.lineTo(tempTargetPos.x, tempTargetPos.y);
break;
}

edgeList[data.id] = line;//保存边引用

}

//Dragular Layout
function initializeNodes(nodes, edges) {
for (let i=0,node;i<nodes.length;i++) {
node = nodes[i];
console.log(node);
node.x = Math.random()*500;
node.y = Math.random()*400;
let width = nodeWidth;
if (node.width) width = node.width;
let scale = width / 600;
let circle = generateSprite(node.shape, scale);//600=>todo=>param

//change to tint
if (node.color) {
circle.tint = node.color;
} else {
circle.tint = 0x000000;
}

//Draw NodeText
let textOpts;
if(node.textOpts)textOpts = node.textOpts
if(node.text) drawText(node.text,textOpts,node.id,node.x,node.y);

circle = setNode(circle, node.id);

//Move the graph to its designated position
//Todo => Node坐标随机分布
circle.x = node.x;
circle.y = node.y;

}
}
let offsetX,offsetY;
function drawText(text, opts, id, x, y){
if(text===''||opts===undefined) return;
let newText = new PIXI.Text(text, opts);
if(!offsetX)offsetX = newText.width/2;//讲道理这里应该可以用缓存值吧
if(!offsetY)offsetY = newText.height/2;//但是node的半径是不是可以动态改变的呢？
newText.x = x-offsetX;
newText.y = y-offsetY;
newText.scale.set(0.25,0.25);
textList[id] = newText;
}

function updateText(id, newPos){
textList[id].x = newPos.x-offsetX;
textList[id].y = newPos.y-offsetY;
}

function setNode(sprite, id) {

let onDragStart = function (event) {
// store a reference to the data
// the reason for this is because of multitouch
// we want to track the movement of this particular touch
event.stopPropagation();
this.data = event.data;
this.dragging = true;
}

let onDragEnd = function () {
this.dragging = false;
// set the interaction data to null
this.data = null;
//归位
dragContainer.removeChild(this);
dragContainer.removeChild(textList[id]);
}

let onDragMove = function () {
if (this.dragging) {
let newPosition = this.data.getLocalPosition(this.parent);
this.x = newPosition.x;
this.y = newPosition.y;
updateEdge(id, newPosition);//闭包的缘故，id是能访问得到的
updateText(id, newPosition);
textContainer.removeChild(textList[id]);
nodeContainer.removeChild(this);
}
}

let updateEdge = function (id, newPos) {
for (let element in edgeInfoList) {//一个节点可能连接着多根线
if (edgeInfoList[element].target === id) {
drawNewEdge(edgeInfoList[element], true, newPos);
} else if (edgeInfoList[element].source === id) {
drawNewEdge(edgeInfoList[element], false, newPos);
}
};
}

let drawNewEdge = function (data, targetFlag, newPos) {
let oldLine = edgeList[data.id];//在线的引用保存对象里找到线
edgeContainer.removeChild(oldLine);//删除线重新画
//不落帧
if (targetFlag) {
nodeList[data.target].x = newPos.x;
nodeList[data.target].y = newPos.y;
} else {
nodeList[data.source].x = newPos.x;
nodeList[data.source].y = newPos.y;
}

let source = nodeList[data.source],//起点（node坐标）
target = nodeList[data.target];//终点（node坐标）

//Redraw
drawArrowAndEdge(data, source, target);

// //Save position change
if (targetFlag) {
//保存修改了的target Node坐标
nodeList[data.target].x = newPos.x;
nodeList[data.target].y = newPos.y;
} else {
//保存修改了的source Node坐标
nodeList[data.source].x = newPos.x;
nodeList[data.source].y = newPos.y;
}

}

sprite
.on('pointerdown', onDragStart)
.on('pointerup', onDragEnd)
.on('pointerupoutside', onDragEnd)
.on('pointermove', onDragMove);

return sprite;
}

//脑残画法
function drawArrowShape(id, shape, sourcePos, targetPos, source, target, targetFlag) {

switch (shape) {
case 'circle':
if (!targetFlag && sourcePos.width) c_nodeRadius = sourcePos.width;
if (targetFlag && targetPos.width) c_nodeRadius = targetPos.width;

//边界判定 => 贴一起了就别显示啦
if ((Math.abs(source.y - target.y) < c_nodeRadius * 1.5) &&
(Math.abs(source.x - target.x) < c_nodeRadius * 1.5)) {
}

let srcPos = targetFlag ? targetPos : sourcePos;
let tgtPos = targetFlag ? sourcePos : targetPos;

let c_angle = Math.atan(Math.abs(srcPos.y - tgtPos.y) / Math.abs(srcPos.x - tgtPos.x))
let circleWidth = c_nodeRadius / 2;
//posX和posY就是circle的最终中心坐标
let posX = (c_nodeRadius + circleWidth) * Math.cos(c_angle),
posY = (c_nodeRadius + circleWidth) * Math.sin(c_angle);

//分类讨论target和source的相对左右位置
if (srcPos.x > tgtPos.x) {//source节点在右边
posX = srcPos.x - posX;
} else {
posX = srcPos.x + posX;
}
if (srcPos.y > tgtPos.y) {//source节点在上边
posY = srcPos.y - posY;
} else {
posY = srcPos.y + posY;
}

//Draw circle
let circle = new Graphics();
circle.beginFill(0x66CCFF);

circle.drawCircle(0, 0, circleWidth);
circle.endFill();

circle.x = posX;
circle.y = posY;

//updateArrow
updateArrow(id, circle, targetFlag);

return {
x: posX,
y: posY
}

case 'triangle':
//这个三角形默认按顶角为50°，两个底角为65°来算，两边长先按一半nodeWidth来算吧
//先画出来再想抽象的事
if (!targetFlag && sourcePos.width) t_nodeRadius = sourcePos.width;
if (targetFlag && targetPos.width) t_nodeRadius = targetPos.width;

//边界判定 => 贴一起了就别显示啦
if ((Math.abs(source.y - target.y) < t_nodeRadius * 1.5) &&
(Math.abs(source.x - target.x) < t_nodeRadius * 1.5)) {
}

let t_srcPos = targetFlag ? sourcePos : targetPos;
let t_tgtPos = targetFlag ? targetPos : sourcePos;

let topAngle = Math.PI / 180 * 50,//角度转弧度，注意Math的那些方法的单位是弧度
halfBottomEdge = Math.sin(topAngle / 2) * sideEdge,
centerEdge = Math.cos(topAngle / 2) * sideEdge;
//angle是一样的，先按node中心算，arrow中心算之后再说，先todo(直线版看出不这个问题，曲线就崩了)
let angle = Math.atan(Math.abs(t_srcPos.y - t_tgtPos.y) / Math.abs(t_srcPos.x - t_tgtPos.x));
let beginPosX = t_nodeRadius * Math.cos(angle),
pos1X, pos1Y, pos2X, pos2Y,
centerX = (t_nodeRadius + centerEdge) * Math.cos(angle),
centerY = (t_nodeRadius + centerEdge) * Math.sin(angle);

pos1X = pos2X = Math.sin(angle) * halfBottomEdge;
pos1Y = pos2Y = Math.cos(angle) * halfBottomEdge;//简单的几何知识(手动抽搐😖)

//还需要分类讨论target和source的左右位置的各种情况
//1234代表target相对source所在象限
if (t_srcPos.x > t_tgtPos.x) {//source节点在右
if (t_srcPos.y > t_tgtPos.y) {//下 ----> 1
beginPosX = t_tgtPos.x + beginPosX;
beginPosY = t_tgtPos.y + beginPosY;

centerX = t_tgtPos.x + centerX;
centerY = t_tgtPos.y + centerY;

pos1X = centerX + pos1X;
pos1Y = centerY - pos1Y;//+ -

pos2X = centerX - pos2X;
pos2Y = centerY + pos2Y;//- +
} else {//上 ----> 4
beginPosX = t_tgtPos.x + beginPosX;
beginPosY = t_tgtPos.y - beginPosY;

centerX = t_tgtPos.x + centerX;
centerY = t_tgtPos.y - centerY;

pos1X = centerX + pos1X;
pos1Y = centerY + pos1Y;//+ +

pos2X = centerX - pos2X;
pos2Y = centerY - pos2Y;//- -
}

} else {//source节点在左
if (t_srcPos.y > t_tgtPos.y) {//下 ----> 2
beginPosX = t_tgtPos.x - beginPosX;
beginPosY = t_tgtPos.y + beginPosY;

centerX = t_tgtPos.x - centerX;
centerY = t_tgtPos.y + centerY;

pos1X = centerX - pos1X;
pos1Y = centerY - pos1Y;//- -

pos2X = centerX + pos2X;
pos2Y = centerY + pos2Y;//+ +
} else {//上 ----> 3
beginPosX = t_tgtPos.x - beginPosX;
beginPosY = t_tgtPos.y - beginPosY;

centerX = t_tgtPos.x - centerX;
centerY = t_tgtPos.y - centerY;

pos1X = centerX - pos1X;
pos1Y = centerY + pos1Y;//- +

pos2X = centerX + pos2X;
pos2Y = centerY - pos2Y;//+ -
}
}

//Draw triangle
let triangle = new Graphics();

triangle.beginFill(0x66CCFF);
triangle.lineStyle(0, 0x66CCFF, 1);
triangle.moveTo(beginPosX, beginPosY);
triangle.lineTo(pos1X, pos1Y);
triangle.lineTo(pos2X, pos2Y);
triangle.endFill();

updateArrow(id, triangle, targetFlag);

return {
x: centerX,
y: centerY
}
}
}

function updateArrow(id, shape, targetFlag) {
if (!arrowList[id]) arrowList[id] = {};
if (!targetFlag) {//Source arrow
if (arrowList[id].sourceArrow) arrowContainer.removeChild(arrowList[id].sourceArrow);
//save newArrow
arrowList[id].sourceArrow = shape;
} else {//Target arrow
if (arrowList[id].targetArrow) arrowContainer.removeChild(arrowList[id].targetArrow);
//save newArrow
arrowList[id].targetArrow = shape;
}
}

function generateSprite(shape, scale = 0.2) {
let sprite;

switch (shape) {
default:
sprite = Sprite.fromImage('http://oykqabi76.bkt.clouddn.com/Circle_White.png');
break;
}
sprite.anchor.set(0.5);
sprite.scale.set(scale*2);//>>??????莫名其妙和graph差两倍
sprite.filters = [new Filters.BlurFilter(1)];//效果拔群！！

// Opt-in to interactivity
sprite.interactive = true;

// Shows hand cursor
sprite.buttonMode = true;

return sprite;

}

// Scale/Zoom
if (e.deltaY < 0) {
zooming(true, e.pageX, e.pageY);
} else {
zooming(false, e.pageX, e.pageY);
}
});

function zooming(zoomFlag, x, y) {
//Current scale
let scale = stage.scale.x;
let point = toLocalPos(x, y);
//Zooming
if (zoomFlag) {
if (scale < SCALE_MAX) {
scale += 0.1;
//moving
stage.position.set(stage.x - (point.x * 0.1), stage.y - (point.y * 0.1))
}
} else {
if (scale > SCALE_MIN) {
scale -= 0.1;
//moving
stage.position.set(stage.x - (point.x * -0.1), stage.y - (point.y * -0.1))
}
}
stage.scale.set(scale, scale);
}

function drawCircle(x, y, r = 30) {
let circle = new Graphics();
circle.beginFill(0x000000, 0.2);

circle.drawCircle(0, 0, r);
circle.endFill();
let localPos = toLocalPos(x, y);
circle.x = localPos.x;
circle.y = localPos.y;
point.circle = circle;
}

// Drag/Move
let startMousePos = {};

stage.hitArea = new PIXI.Rectangle(0, 0, window.innerWidth, window.innerHeight);
stage.interactive = true;
stage.buttonMode = true;

stage.on('pointerdown', stagePointerDown)
.on('pointerup', stagePointerUp)
.on('pointerupoutside', stagePointerUp)
.on('pointermove', stagePointerMove);

function stagePointerDown(event) {
this.dragging = true;
movePosBegin.x = stage.x;
movePosBegin.y = stage.y;
let newPosition = event.data.global;
let x = newPosition.x;
let y = newPosition.y;
startMousePos.x = x; startMousePos.y = y;
//Draw circle
let r = 30 / stage.scale.x;
drawCircle(x, y, r);
}

function stagePointerUp(event) {
this.dragging = false;
//Remove  circle
if (point.circle) dragContainer.removeChild(point.circle);
}

function stagePointerMove(event) {
if (this.dragging) {
//Move  circle
let newPosition = event.data.global;
let x = newPosition.x;
let y = newPosition.y;

//Remove  circle first
if (point.circle) dragContainer.removeChild(point.circle);
//Redraw circle
//Current scale
let scale = stage.scale.x;
let r = 30 / scale;
drawCircle(x, y, r);

//需要注意这里的差值必须要拿global坐标来算而不是to stageLocalPos来算
//因为stage.x/y按照的是global坐标来移动的
let offsetX = x - startMousePos.x,//差值
offsetY = y - startMousePos.y;

stage.x = movePosBegin.x + offsetX;
stage.y = movePosBegin.y + offsetY;//修正差值
}
}

function toLocalPos(x, y) {
let mouse = new PIXI.Point(x, y);
let localPos = stage.toLocal(mouse);
return localPos;
}

//三阶贝塞尔曲线---引用库:arcTobezier
function CacBezierCurveMidPos(tempSourcePos, tempTargetPos, height = 100) {
let dx = tempSourcePos.x - tempTargetPos.x,
dy = tempSourcePos.y - tempTargetPos.y,
dr = Math.sqrt(dx * dx + dy * dy);

const curve = {
type: 'arc',
rx: dr,
ry: dr,
largeArcFlag: 0,
sweepFlag: 1,
xAxisRotation: 0,
}

const curves = arcToBezier({
px: tempSourcePos.x,
py: tempSourcePos.y,
cx: tempTargetPos.x,
cy: tempTargetPos.y,
rx: curve.rx,
ry: curve.ry,
xAxisRotation: curve.xAxisRotation,
largeArcFlag: curve.largeArcFlag,
sweepFlag: curve.sweepFlag,
});

return curves[0];
}

//Result/Goal
function autoGenera(nodeNum,edgeNum){//先node再edge
let res = [];
for(let i=0;i<nodeNum;i++){
let temp = {};
let data = temp.data = {};
//Ascii => A => 65
data.id = String.fromCharCode(65+i);
data.width = (Math.random()*30)+20;
data.text = "Text";
data.textOpts = {
fontFamily: 'Arial',
fontSize: 100,
fontStyle: 'italic',
fontWeight: 'bold',
stroke: '#4a1850',
strokeThickness: 5,
wordWrap: true,
wordWrapWidth: 440
};
//data.shape 默认为圆形
if(i%2===0)data.color=0x66CCFF;
res.push(temp);
}
for(let i=0;i<edgeNum;i++){
let randomNode1 = Math.floor(Math.random()*nodeNum);
let randomNode2 = Math.floor(Math.random()*nodeNum);
//toFix => Node can arrow itself
if(randomNode1 === randomNode2){
if(randomNode1===1){
randomNode1--;
}else{
randomNode2++;
}
};
let temp = {};
let data = temp.data = {};
data.id = String.fromCharCode(65+nodeNum+i);
data.source = res[randomNode1].data.id;
data.target = res[randomNode2].data.id;

res.push(temp);
}
return res;
}

PiCi({
container: document.getElementById('cy'),
elements: autoGenera(6,4)

})```
```<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.5.1/pixi.min.js"></script>
```