Oleg Sh 6c4e18ce99 Add dialog to select original graph or autosaved graph.
Refactor graph styles holding objects.
Small style fixes.
2025-01-05 21:05:35 +01:00

240 lines
6.6 KiB
JavaScript

/**
* This is edge model
*
*/
var EdgeModels = {"line": 0, "curve" : 1};
const defaultEdgeWidth = 4;
function EdgeModel()
{
this.width = globalApplication.GetDefaultEdgeWidth();
this.type = EdgeModels.line;
this.curveValue = EdgeModel.prototype.defaultCurve;
this.default = true;
this.sizeOfLoop = 24;
this.loopShiftAngel = Math.PI / 6;
}
EdgeModel.prototype.defaultCurve = 0.1;
EdgeModel.prototype.copyFrom = function(other)
{
this.width = other.width;
this.type = other.type;
this.curveValue = other.curveValue;
this.default = other.default;
}
EdgeModel.prototype.SaveToXML = function ()
{
return "model_width=\"" + this.width + "\" " +
"model_type=\"" + this.type + "\" " +
"model_curveValue=\"" + this.curveValue + "\" " +
"model_default=\"" + this.default + "\" ";
}
EdgeModel.prototype.LoadFromXML = function (xml, graph)
{
this.width = xml.attr('model_width') == null ? this.width : parseFloat(xml.attr("model_width"));
this.type = xml.attr('model_type') == null ? this.type : xml.attr("model_type");
this.curveValue = xml.attr('model_curveValue') == null ? this.curveValue : parseFloat(xml.attr("model_curveValue"));
this.default = xml.attr('model_default') == null ? this.default : xml.attr("model_default") == "true";
}
EdgeModel.prototype.GetCurvePoint = function(position1, position2, t)
{
var points = this.GetBezierPoints(position1, position2);
var firstBezierPoint = points[0];
var secondBezierPoint = points[1];
var B0_t = Math.pow(1-t, 3);
var B1_t = 3 * t * Math.pow(1-t, 2);
var B2_t = 3 * t*t * (1-t)
var B3_t = t*t*t;
var ax = position1.x;
var ay = position1.y;
var dx = position2.x;
var dy = position2.y;
var bx = firstBezierPoint.x;
var by = firstBezierPoint.y;
var cx = secondBezierPoint.x;
var cy = secondBezierPoint.y;
var px_t = (B0_t * ax) + (B1_t * bx) + (B2_t * cx) + (B3_t * dx);
var py_t = (B0_t * ay) + (B1_t * by) + (B2_t * cy) + (B3_t * dy);
return new Point(px_t, py_t);
}
EdgeModel.prototype.GetBezierPoints = function(position1, position2)
{
var direction = position2.subtract(position1);
var delta = direction.length();
direction.normalize(1.0);
var normal = direction.normal();
var deltaOffsetPixels = delta * this.curveValue;
var yOffset = normal.multiply(deltaOffsetPixels);
var firstBezierPointShift = (direction.multiply(delta * 0.2)).add(yOffset);
var secondBezierPointShift = (direction.multiply(-delta * 0.2)).add(yOffset);
var firstBezierPoint = position1.add(firstBezierPointShift);
var secondBezierPoint = position2.add(secondBezierPointShift);
return [firstBezierPoint, secondBezierPoint];
}
EdgeModel.prototype.HitTest = function(position1, position2, mousePos)
{
if (this.type == EdgeModels.line)
return this.HitTestLine(position1, position2, mousePos);
else if (this.type == EdgeModels.curve)
return this.HitTestCurve(position1, position2, mousePos);
return false;
}
EdgeModel.prototype.HitTestLine = function(position1, position2, mousePos, factor)
{
if (factor === undefined)
{
factor = 1.0;
}
var pos1 = position1;
var pos2 = position2;
var pos0 = mousePos;
// Self loop case
if (pos1.equals(pos2))
{
var xCenter = pos1.x - Math.cos(this.GetLoopShiftAngel()) * this.GetLoopSize();
var yCenter = pos1.y - Math.sin(this.GetLoopShiftAngel()) * this.GetLoopSize();
return Math.abs((Point.distance(new Point(xCenter, yCenter), pos0)) - this.GetLoopSize()) <= this.width * 1.5 * factor;
}
var r1 = pos0.distance(pos1);
var r2 = pos0.distance(pos2);
var r12 = pos1.distance(pos2);
if (r1 >= (new Point(r2, r12)).length() || r2 >= (new Point(r1,r12)).length())
{
}
else
{
var distance = ((pos1.y - pos2.y) * pos0.x + (pos2.x - pos1.x) * pos0.y + (pos1.x * pos2.y - pos2.x * pos1.y)) / r12;
if (Math.abs(distance) <= this.width * 1.5 * factor)
{
return true;
}
}
return false;
}
EdgeModel.prototype.HitTestCurve = function(position1, position2, mousePos)
{
var pos1 = position1;
var pos2 = position2;
var pos0 = mousePos;
// Self loop case
if (pos1.equals(pos2))
{
var xCenter = pos1.x - Math.cos(this.GetLoopShiftAngel()) * this.GetLoopSize();
var yCenter = pos1.y - Math.sin(this.GetLoopShiftAngel()) * this.GetLoopSize();
return Math.abs((Point.distance(new Point(xCenter, yCenter), pos0)) - this.GetLoopSize()) <= this.width * 1.5;
}
var interval_count = position1.distance(position2) / 100 * 30;
var start = position1;
for (var i = 0; i < interval_count; i ++)
{
var finish = this.GetCurvePoint(position1, position2, i / interval_count);
if (this.HitTestLine(start, finish, mousePos, 2.0))
return true;
start = finish;
}
return false;
}
EdgeModel.prototype.ChangeCurveValue = function (delta)
{
if (this.type == EdgeModels.line)
{
this.type = EdgeModels.curve;
this.curveValue = 0.0;
}
this.curveValue = this.curveValue + delta;
if (Math.abs(this.curveValue) <= 0.01)
this.type = EdgeModels.line;
this.default = false;
}
EdgeModel.prototype.SetCurveValue = function (value)
{
if (this.type == EdgeModels.line)
{
this.type = EdgeModels.curve;
this.curveValue = 0.0;
}
this.curveValue = value;
if (Math.abs(this.curveValue) <= 0.01)
this.type = EdgeModels.line;
this.default = false;
}
EdgeModel.prototype.GetLoopSize = function ()
{
if (Math.abs(this.curveValue) <= 0.01)
{
// without this condition arc disappears when curveValue=0
return this.sizeOfLoop;
}
else
{
// bigger curveValue -> bigger loop size
let normalCurve = this.curveValue;
if (this.type == EdgeModels.line) {
normalCurve = this.defaultCurve;
}
else if (normalCurve >= 0.0) {
normalCurve += this.defaultCurve
}
return this.sizeOfLoop * Math.abs(normalCurve) * (1 / this.defaultCurve);
}
}
EdgeModel.prototype.GetLoopShiftAngel = function ()
{
if (this.type == EdgeModels.line || this.curveValue >= 0.0)
{ // shift to top-left
return this.loopShiftAngel;
}
else
{ // shift to bottom-right
return this.loopShiftAngel + Math.PI;
}
}