1034 lines
37 KiB
JavaScript
1034 lines
37 KiB
JavaScript
/*
|
|
* (c) Copyright Ascensio System SIA 2010-2015
|
|
*
|
|
* This program is a free software product. You can redistribute it and/or
|
|
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
|
* version 3 as published by the Free Software Foundation. In accordance with
|
|
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
|
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
|
* of any third-party rights.
|
|
*
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
|
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
|
*
|
|
* You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
|
|
* EU, LV-1021.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of the Program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU AGPL version 3.
|
|
*
|
|
* Pursuant to Section 7(b) of the License you must retain the original Product
|
|
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
|
* grant you any rights under trademark law for use of our trademarks.
|
|
*
|
|
* All the Product's GUI elements, including illustrations and icon sets, as
|
|
* well as technical writing content are licensed under the terms of the
|
|
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
|
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
|
*
|
|
*/
|
|
"use strict";
|
|
var moveTo = 0,
|
|
lineTo = 1,
|
|
arcTo = 2,
|
|
bezier3 = 3,
|
|
bezier4 = 4,
|
|
close = 5;
|
|
var PATH_COMMAND_START = 257;
|
|
var PATH_COMMAND_END = 258;
|
|
var cToRad = Math.PI / 10800000;
|
|
var cToDeg = 1 / cToRad;
|
|
var cToRad2 = (Math.PI / 60000) / 180;
|
|
function Path() {
|
|
this.stroke = null;
|
|
this.extrusionOk = null;
|
|
this.fill = null;
|
|
this.pathH = null;
|
|
this.pathW = null;
|
|
this.ArrPathCommandInfo = [];
|
|
this.ArrPathCommand = [];
|
|
this.Id = g_oIdCounter.Get_NewId();
|
|
g_oTableId.Add(this, this.Id);
|
|
}
|
|
Path.prototype = {
|
|
Get_Id: function () {
|
|
return this.Id;
|
|
},
|
|
getObjectType: function () {
|
|
return historyitem_type_Path;
|
|
},
|
|
Write_ToBinary2: function (w) {
|
|
w.WriteLong(this.getObjectType());
|
|
w.WriteString2(this.Get_Id());
|
|
},
|
|
Read_FromBinary2: function (r) {
|
|
this.Id = r.GetString2();
|
|
},
|
|
Refresh_RecalcData: function () {},
|
|
createDuplicate: function () {
|
|
var p = new Path();
|
|
p.setStroke(this.stroke);
|
|
p.setExtrusionOk(this.extrusionOk);
|
|
p.setFill(this.fill);
|
|
p.setPathH(this.pathH);
|
|
p.setPathW(this.pathW);
|
|
for (var i = 0; i < this.ArrPathCommandInfo.length; ++i) {
|
|
var command = this.ArrPathCommandInfo[i];
|
|
switch (command.id) {
|
|
case moveTo:
|
|
case lineTo:
|
|
var x = command.X;
|
|
var y = command.Y;
|
|
p.addPathCommand({
|
|
id: command.id,
|
|
X: x,
|
|
Y: y
|
|
});
|
|
break;
|
|
case bezier3:
|
|
var X0 = command.X0;
|
|
var Y0 = command.Y0;
|
|
var X1 = command.X1;
|
|
var Y1 = command.Y1;
|
|
p.addPathCommand({
|
|
id: bezier3,
|
|
X0: X0,
|
|
Y0: Y0,
|
|
X1: X1,
|
|
Y1: Y1
|
|
});
|
|
break;
|
|
case bezier4:
|
|
var X0 = command.X0;
|
|
var Y0 = command.Y0;
|
|
var X1 = command.X1;
|
|
var Y1 = command.Y1;
|
|
var X2 = command.X2;
|
|
var Y2 = command.Y2;
|
|
p.addPathCommand({
|
|
id: bezier4,
|
|
X0: X0,
|
|
Y0: Y0,
|
|
X1: X1,
|
|
Y1: Y1,
|
|
X2: X2,
|
|
Y2: Y2
|
|
});
|
|
break;
|
|
case arcTo:
|
|
var hR = command.hR;
|
|
var wR = command.wR;
|
|
var stAng = command.stAng;
|
|
var swAng = command.swAng;
|
|
p.addPathCommand({
|
|
id: arcTo,
|
|
hR: hR,
|
|
wR: wR,
|
|
stAng: stAng,
|
|
swAng: swAng
|
|
});
|
|
break;
|
|
case close:
|
|
p.addPathCommand({
|
|
id: close
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
return p;
|
|
},
|
|
setStroke: function (pr) {
|
|
History.Add(this, {
|
|
Type: historyitem_PathSetStroke,
|
|
oldPr: this.stroke,
|
|
newPr: pr
|
|
});
|
|
this.stroke = pr;
|
|
},
|
|
setExtrusionOk: function (pr) {
|
|
History.Add(this, {
|
|
Type: historyitem_PathSetExtrusionOk,
|
|
oldPr: this.extrusionOk,
|
|
newPr: pr
|
|
});
|
|
this.extrusionOk = pr;
|
|
},
|
|
setFill: function (pr) {
|
|
History.Add(this, {
|
|
Type: historyitem_PathSetFill,
|
|
oldPr: this.fill,
|
|
newPr: pr
|
|
});
|
|
this.fill = pr;
|
|
},
|
|
setPathH: function (pr) {
|
|
History.Add(this, {
|
|
Type: historyitem_PathSetPathH,
|
|
oldPr: this.pathH,
|
|
newPr: pr
|
|
});
|
|
this.pathH = pr;
|
|
},
|
|
setPathW: function (pr) {
|
|
History.Add(this, {
|
|
Type: historyitem_PathSetPathW,
|
|
oldPr: this.pathW,
|
|
newPr: pr
|
|
});
|
|
this.pathW = pr;
|
|
},
|
|
addPathCommand: function (cmd) {
|
|
History.Add(this, {
|
|
Type: historyitem_PathAddPathCommand,
|
|
newPr: cmd
|
|
});
|
|
this.ArrPathCommandInfo.push(cmd);
|
|
},
|
|
moveTo: function (x, y) {
|
|
this.addPathCommand({
|
|
id: moveTo,
|
|
X: x,
|
|
Y: y
|
|
});
|
|
},
|
|
lnTo: function (x, y) {
|
|
this.addPathCommand({
|
|
id: lineTo,
|
|
X: x,
|
|
Y: y
|
|
});
|
|
},
|
|
arcTo: function (wR, hR, stAng, swAng) {
|
|
this.addPathCommand({
|
|
id: arcTo,
|
|
wR: wR,
|
|
hR: hR,
|
|
stAng: stAng,
|
|
swAng: swAng
|
|
});
|
|
},
|
|
quadBezTo: function (x0, y0, x1, y1) {
|
|
this.addPathCommand({
|
|
id: bezier3,
|
|
X0: x0,
|
|
Y0: y0,
|
|
X1: x1,
|
|
Y1: y1
|
|
});
|
|
},
|
|
cubicBezTo: function (x0, y0, x1, y1, x2, y2) {
|
|
this.addPathCommand({
|
|
id: bezier4,
|
|
X0: x0,
|
|
Y0: y0,
|
|
X1: x1,
|
|
Y1: y1,
|
|
X2: x2,
|
|
Y2: y2
|
|
});
|
|
},
|
|
close: function () {
|
|
this.addPathCommand({
|
|
id: close
|
|
});
|
|
},
|
|
Undo: function (data) {
|
|
switch (data.Type) {
|
|
case historyitem_PathSetStroke:
|
|
this.stroke = data.oldPr;
|
|
break;
|
|
case historyitem_PathSetExtrusionOk:
|
|
this.extrusionOk = data.oldPr;
|
|
break;
|
|
case historyitem_PathSetFill:
|
|
this.fill = data.oldPr;
|
|
break;
|
|
case historyitem_PathSetPathH:
|
|
this.pathH = data.oldPr;
|
|
break;
|
|
case historyitem_PathSetPathW:
|
|
this.pathW = data.oldPr;
|
|
break;
|
|
case historyitem_PathAddPathCommand:
|
|
this.ArrPathCommandInfo.splice(this.ArrPathCommandInfo.length - 1, 1);
|
|
}
|
|
},
|
|
Redo: function (data) {
|
|
switch (data.Type) {
|
|
case historyitem_PathSetStroke:
|
|
this.stroke = data.newPr;
|
|
break;
|
|
case historyitem_PathSetExtrusionOk:
|
|
this.extrusionOk = data.newPr;
|
|
break;
|
|
case historyitem_PathSetFill:
|
|
this.fill = data.newPr;
|
|
break;
|
|
case historyitem_PathSetPathH:
|
|
this.pathH = data.newPr;
|
|
break;
|
|
case historyitem_PathSetPathW:
|
|
this.pathW = data.newPr;
|
|
break;
|
|
case historyitem_PathAddPathCommand:
|
|
this.ArrPathCommandInfo.push(data.newPr);
|
|
}
|
|
},
|
|
Save_Changes: function (data, w) {
|
|
w.WriteLong(data.Type);
|
|
switch (data.Type) {
|
|
case historyitem_PathSetStroke:
|
|
case historyitem_PathSetExtrusionOk:
|
|
writeBool(w, data.newPr);
|
|
break;
|
|
case historyitem_PathSetFill:
|
|
writeString(w, data.newPr);
|
|
break;
|
|
case historyitem_PathSetPathH:
|
|
case historyitem_PathSetPathW:
|
|
writeLong(w, data.newPr);
|
|
break;
|
|
case historyitem_PathAddPathCommand:
|
|
switch (data.newPr.id) {
|
|
case moveTo:
|
|
case lineTo:
|
|
w.WriteBool(true);
|
|
writeLong(w, data.newPr.id);
|
|
writeString(w, data.newPr.X);
|
|
writeString(w, data.newPr.Y);
|
|
break;
|
|
case bezier3:
|
|
w.WriteBool(true);
|
|
writeLong(w, data.newPr.id);
|
|
writeString(w, data.newPr.X0);
|
|
writeString(w, data.newPr.Y0);
|
|
writeString(w, data.newPr.X1);
|
|
writeString(w, data.newPr.Y1);
|
|
break;
|
|
case bezier4:
|
|
w.WriteBool(true);
|
|
writeLong(w, data.newPr.id);
|
|
writeString(w, data.newPr.X0);
|
|
writeString(w, data.newPr.Y0);
|
|
writeString(w, data.newPr.X1);
|
|
writeString(w, data.newPr.Y1);
|
|
writeString(w, data.newPr.X2);
|
|
writeString(w, data.newPr.Y2);
|
|
break;
|
|
case arcTo:
|
|
w.WriteBool(true);
|
|
writeLong(w, data.newPr.id);
|
|
writeString(w, data.newPr.hR);
|
|
writeString(w, data.newPr.wR);
|
|
writeString(w, data.newPr.stAng);
|
|
writeString(w, data.newPr.swAng);
|
|
break;
|
|
case close:
|
|
w.WriteBool(true);
|
|
writeLong(w, data.newPr.id);
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
Load_Changes: function (r) {
|
|
var type = r.GetLong();
|
|
switch (type) {
|
|
case historyitem_PathSetStroke:
|
|
this.stroke = readBool(r);
|
|
break;
|
|
case historyitem_PathSetExtrusionOk:
|
|
this.extrusionOk = readBool(r);
|
|
break;
|
|
case historyitem_PathSetFill:
|
|
this.fill = readString(r);
|
|
break;
|
|
case historyitem_PathSetPathH:
|
|
this.pathH = readLong(r);
|
|
break;
|
|
case historyitem_PathSetPathW:
|
|
this.pathW = readLong(r);
|
|
break;
|
|
case historyitem_PathAddPathCommand:
|
|
if (r.GetBool()) {
|
|
var command_id = readLong(r);
|
|
switch (command_id) {
|
|
case moveTo:
|
|
case lineTo:
|
|
var x = readString(r);
|
|
var y = readString(r);
|
|
this.ArrPathCommandInfo.push({
|
|
id: command_id,
|
|
X: x,
|
|
Y: y
|
|
});
|
|
break;
|
|
case bezier3:
|
|
var X0 = readString(r);
|
|
var Y0 = readString(r);
|
|
var X1 = readString(r);
|
|
var Y1 = readString(r);
|
|
this.ArrPathCommandInfo.push({
|
|
id: bezier3,
|
|
X0: X0,
|
|
Y0: Y0,
|
|
X1: X1,
|
|
Y1: Y1
|
|
});
|
|
break;
|
|
case bezier4:
|
|
var X0 = readString(r);
|
|
var Y0 = readString(r);
|
|
var X1 = readString(r);
|
|
var Y1 = readString(r);
|
|
var X2 = readString(r);
|
|
var Y2 = readString(r);
|
|
this.ArrPathCommandInfo.push({
|
|
id: bezier4,
|
|
X0: X0,
|
|
Y0: Y0,
|
|
X1: X1,
|
|
Y1: Y1,
|
|
X2: X2,
|
|
Y2: Y2
|
|
});
|
|
break;
|
|
case arcTo:
|
|
var hR = readString(r);
|
|
var wR = readString(r);
|
|
var stAng = readString(r);
|
|
var swAng = readString(r);
|
|
this.ArrPathCommandInfo.push({
|
|
id: arcTo,
|
|
hR: hR,
|
|
wR: wR,
|
|
stAng: stAng,
|
|
swAng: swAng
|
|
});
|
|
break;
|
|
case close:
|
|
this.ArrPathCommandInfo.push({
|
|
id: close
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
recalculate: function (gdLst) {
|
|
var ch, cw;
|
|
if (this.pathW != undefined) {
|
|
if (this.pathW > MOVE_DELTA) {
|
|
cw = (gdLst["w"] / this.pathW);
|
|
} else {
|
|
cw = 0;
|
|
}
|
|
} else {
|
|
cw = 1;
|
|
}
|
|
if (this.pathH != undefined) {
|
|
if (this.pathH > MOVE_DELTA) {
|
|
ch = (gdLst["h"] / this.pathH);
|
|
} else {
|
|
ch = 0;
|
|
}
|
|
} else {
|
|
ch = 1;
|
|
}
|
|
var APCI = this.ArrPathCommandInfo,
|
|
n = APCI.length,
|
|
cmd;
|
|
var x0, y0, x1, y1, x2, y2, wR, hR, stAng, swAng, lastX, lastY;
|
|
for (var i = 0; i < n; ++i) {
|
|
cmd = APCI[i];
|
|
switch (cmd.id) {
|
|
case moveTo:
|
|
case lineTo:
|
|
x0 = gdLst[cmd.X];
|
|
if (x0 === undefined) {
|
|
x0 = parseInt(cmd.X, 10);
|
|
}
|
|
y0 = gdLst[cmd.Y];
|
|
if (y0 === undefined) {
|
|
y0 = parseInt(cmd.Y, 10);
|
|
}
|
|
this.ArrPathCommand[i] = {
|
|
id: cmd.id,
|
|
X: x0 * cw,
|
|
Y: y0 * ch
|
|
};
|
|
lastX = x0 * cw;
|
|
lastY = y0 * ch;
|
|
break;
|
|
case bezier3:
|
|
x0 = gdLst[cmd.X0];
|
|
if (x0 === undefined) {
|
|
x0 = parseInt(cmd.X0, 10);
|
|
}
|
|
y0 = gdLst[cmd.Y0];
|
|
if (y0 === undefined) {
|
|
y0 = parseInt(cmd.Y0, 10);
|
|
}
|
|
x1 = gdLst[cmd.X1];
|
|
if (x1 === undefined) {
|
|
x1 = parseInt(cmd.X1, 10);
|
|
}
|
|
y1 = gdLst[cmd.Y1];
|
|
if (y1 === undefined) {
|
|
y1 = parseInt(cmd.Y1, 10);
|
|
}
|
|
this.ArrPathCommand[i] = {
|
|
id: bezier3,
|
|
X0: x0 * cw,
|
|
Y0: y0 * ch,
|
|
X1: x1 * cw,
|
|
Y1: y1 * ch
|
|
};
|
|
lastX = x1 * cw;
|
|
lastY = y1 * ch;
|
|
break;
|
|
case bezier4:
|
|
x0 = gdLst[cmd.X0];
|
|
if (x0 === undefined) {
|
|
x0 = parseInt(cmd.X0, 10);
|
|
}
|
|
y0 = gdLst[cmd.Y0];
|
|
if (y0 === undefined) {
|
|
y0 = parseInt(cmd.Y0, 10);
|
|
}
|
|
x1 = gdLst[cmd.X1];
|
|
if (x1 === undefined) {
|
|
x1 = parseInt(cmd.X1, 10);
|
|
}
|
|
y1 = gdLst[cmd.Y1];
|
|
if (y1 === undefined) {
|
|
y1 = parseInt(cmd.Y1, 10);
|
|
}
|
|
x2 = gdLst[cmd.X2];
|
|
if (x2 === undefined) {
|
|
x2 = parseInt(cmd.X2, 10);
|
|
}
|
|
y2 = gdLst[cmd.Y2];
|
|
if (y2 === undefined) {
|
|
y2 = parseInt(cmd.Y2, 10);
|
|
}
|
|
this.ArrPathCommand[i] = {
|
|
id: bezier4,
|
|
X0: x0 * cw,
|
|
Y0: y0 * ch,
|
|
X1: x1 * cw,
|
|
Y1: y1 * ch,
|
|
X2: x2 * cw,
|
|
Y2: y2 * ch
|
|
};
|
|
lastX = x2 * cw;
|
|
lastY = y2 * ch;
|
|
break;
|
|
case arcTo:
|
|
hR = gdLst[cmd.hR];
|
|
if (hR === undefined) {
|
|
hR = parseInt(cmd.hR, 10);
|
|
}
|
|
wR = gdLst[cmd.wR];
|
|
if (wR === undefined) {
|
|
wR = parseInt(cmd.wR, 10);
|
|
}
|
|
stAng = gdLst[cmd.stAng];
|
|
if (stAng === undefined) {
|
|
stAng = parseInt(cmd.stAng, 10);
|
|
}
|
|
swAng = gdLst[cmd.swAng];
|
|
if (swAng === undefined) {
|
|
swAng = parseInt(cmd.swAng, 10);
|
|
}
|
|
var a1 = stAng;
|
|
var a2 = stAng + swAng;
|
|
var a3 = swAng;
|
|
stAng = Math.atan2(ch * Math.sin(a1 * cToRad), cw * Math.cos(a1 * cToRad)) / cToRad;
|
|
swAng = Math.atan2(ch * Math.sin(a2 * cToRad), cw * Math.cos(a2 * cToRad)) / cToRad - stAng;
|
|
if ((swAng > 0) && (a3 < 0)) {
|
|
swAng -= 21600000;
|
|
}
|
|
if ((swAng < 0) && (a3 > 0)) {
|
|
swAng += 21600000;
|
|
}
|
|
if (swAng == 0 && a3 != 0) {
|
|
swAng = 21600000;
|
|
}
|
|
var a = wR * cw;
|
|
var b = hR * ch;
|
|
var sin2 = Math.sin(stAng * cToRad);
|
|
var cos2 = Math.cos(stAng * cToRad);
|
|
var _xrad = cos2 / a;
|
|
var _yrad = sin2 / b;
|
|
var l = 1 / Math.sqrt(_xrad * _xrad + _yrad * _yrad);
|
|
var xc = lastX - l * cos2;
|
|
var yc = lastY - l * sin2;
|
|
var sin1 = Math.sin((stAng + swAng) * cToRad);
|
|
var cos1 = Math.cos((stAng + swAng) * cToRad);
|
|
var _xrad1 = cos1 / a;
|
|
var _yrad1 = sin1 / b;
|
|
var l1 = 1 / Math.sqrt(_xrad1 * _xrad1 + _yrad1 * _yrad1);
|
|
this.ArrPathCommand[i] = {
|
|
id: arcTo,
|
|
stX: lastX,
|
|
stY: lastY,
|
|
wR: wR * cw,
|
|
hR: hR * ch,
|
|
stAng: stAng * cToRad,
|
|
swAng: swAng * cToRad
|
|
};
|
|
lastX = xc + l1 * cos1;
|
|
lastY = yc + l1 * sin1;
|
|
break;
|
|
case close:
|
|
this.ArrPathCommand[i] = {
|
|
id: close
|
|
};
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
draw: function (shape_drawer) {
|
|
if (shape_drawer.bIsCheckBounds === true && this.fill == "none") {
|
|
return;
|
|
}
|
|
var bIsDrawLast = false;
|
|
var path = this.ArrPathCommand;
|
|
shape_drawer._s();
|
|
for (var j = 0, l = path.length; j < l; ++j) {
|
|
var cmd = path[j];
|
|
switch (cmd.id) {
|
|
case moveTo:
|
|
bIsDrawLast = true;
|
|
shape_drawer._m(cmd.X, cmd.Y);
|
|
break;
|
|
case lineTo:
|
|
bIsDrawLast = true;
|
|
shape_drawer._l(cmd.X, cmd.Y);
|
|
break;
|
|
case bezier3:
|
|
bIsDrawLast = true;
|
|
shape_drawer._c2(cmd.X0, cmd.Y0, cmd.X1, cmd.Y1);
|
|
break;
|
|
case bezier4:
|
|
bIsDrawLast = true;
|
|
shape_drawer._c(cmd.X0, cmd.Y0, cmd.X1, cmd.Y1, cmd.X2, cmd.Y2);
|
|
break;
|
|
case arcTo:
|
|
bIsDrawLast = true;
|
|
ArcToCurvers(shape_drawer, cmd.stX, cmd.stY, cmd.wR, cmd.hR, cmd.stAng, cmd.swAng);
|
|
break;
|
|
case close:
|
|
shape_drawer._z();
|
|
break;
|
|
}
|
|
}
|
|
if (bIsDrawLast) {
|
|
shape_drawer.drawFillStroke(true, this.fill, this.stroke && !shape_drawer.bIsNoStrokeAttack);
|
|
}
|
|
shape_drawer._e();
|
|
},
|
|
check_bounds: function (checker) {
|
|
var path = this.ArrPathCommand;
|
|
for (var j = 0, l = path.length; j < l; ++j) {
|
|
var cmd = path[j];
|
|
switch (cmd.id) {
|
|
case moveTo:
|
|
checker._m(cmd.X, cmd.Y);
|
|
break;
|
|
case lineTo:
|
|
checker._l(cmd.X, cmd.Y);
|
|
break;
|
|
case bezier3:
|
|
checker._c2(cmd.X0, cmd.Y0, cmd.X1, cmd.Y1);
|
|
break;
|
|
case bezier4:
|
|
checker._c(cmd.X0, cmd.Y0, cmd.X1, cmd.Y1, cmd.X2, cmd.Y2);
|
|
break;
|
|
case arcTo:
|
|
ArcToCurvers(checker, cmd.stX, cmd.stY, cmd.wR, cmd.hR, cmd.stAng, cmd.swAng);
|
|
break;
|
|
case close:
|
|
checker._z();
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
hitInInnerArea: function (canvasContext, x, y) {
|
|
if (this.fill === "none") {
|
|
return false;
|
|
}
|
|
var _arr_commands = this.ArrPathCommand;
|
|
var _commands_count = _arr_commands.length;
|
|
var _command_index;
|
|
var _command;
|
|
canvasContext.beginPath();
|
|
for (_command_index = 0; _command_index < _commands_count; ++_command_index) {
|
|
_command = _arr_commands[_command_index];
|
|
switch (_command.id) {
|
|
case moveTo:
|
|
canvasContext.moveTo(_command.X, _command.Y);
|
|
break;
|
|
case lineTo:
|
|
canvasContext.lineTo(_command.X, _command.Y);
|
|
break;
|
|
case arcTo:
|
|
ArcToOnCanvas(canvasContext, _command.stX, _command.stY, _command.wR, _command.hR, _command.stAng, _command.swAng);
|
|
break;
|
|
case bezier3:
|
|
canvasContext.quadraticCurveTo(_command.X0, _command.Y0, _command.X1, _command.Y1);
|
|
break;
|
|
case bezier4:
|
|
canvasContext.bezierCurveTo(_command.X0, _command.Y0, _command.X1, _command.Y1, _command.X2, _command.Y2);
|
|
break;
|
|
case close:
|
|
canvasContext.closePath();
|
|
if (canvasContext.isPointInPath(x, y)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
hitInPath: function (canvasContext, x, y) {
|
|
var _arr_commands = this.ArrPathCommand;
|
|
var _commands_count = _arr_commands.length;
|
|
var _command_index;
|
|
var _command;
|
|
var _last_x, _last_y;
|
|
var _begin_x, _begin_y;
|
|
for (_command_index = 0; _command_index < _commands_count; ++_command_index) {
|
|
_command = _arr_commands[_command_index];
|
|
switch (_command.id) {
|
|
case moveTo:
|
|
_last_x = _command.X;
|
|
_last_y = _command.Y;
|
|
_begin_x = _command.X;
|
|
_begin_y = _command.Y;
|
|
break;
|
|
case lineTo:
|
|
if (HitInLine(canvasContext, x, y, _last_x, _last_y, _command.X, _command.Y)) {
|
|
return true;
|
|
}
|
|
_last_x = _command.X;
|
|
_last_y = _command.Y;
|
|
break;
|
|
case arcTo:
|
|
if (HitToArc(canvasContext, x, y, _command.stX, _command.stY, _command.wR, _command.hR, _command.stAng, _command.swAng)) {
|
|
return true;
|
|
}
|
|
_last_x = (_command.stX - _command.wR * Math.cos(_command.stAng) + _command.wR * Math.cos(_command.swAng));
|
|
_last_y = (_command.stY - _command.hR * Math.sin(_command.stAng) + _command.hR * Math.sin(_command.swAng));
|
|
break;
|
|
case bezier3:
|
|
if (HitInBezier3(canvasContext, x, y, _last_x, _last_y, _command.X0, _command.Y0, _command.X1, _command.Y1)) {
|
|
return true;
|
|
}
|
|
_last_x = _command.X1;
|
|
_last_y = _command.Y1;
|
|
break;
|
|
case bezier4:
|
|
if (HitInBezier4(canvasContext, x, y, _last_x, _last_y, _command.X0, _command.Y0, _command.X1, _command.Y1, _command.X2, _command.Y2)) {
|
|
return true;
|
|
}
|
|
_last_x = _command.X2;
|
|
_last_y = _command.Y2;
|
|
break;
|
|
case close:
|
|
if (HitInLine(canvasContext, x, y, _last_x, _last_y, _begin_x, _begin_y)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
calculateWrapPolygon: function (epsilon, graphics) {
|
|
var arr_polygons = [];
|
|
var cur_polygon = [];
|
|
var path_commands = this.ArrPathCommand;
|
|
var path_commands_count = path_commands.length;
|
|
var last_x, last_y;
|
|
for (var index = 0; index < path_commands_count; ++index) {
|
|
var cur_command = path_commands[index];
|
|
switch (cur_command.id) {
|
|
case moveTo:
|
|
case lineTo:
|
|
cur_polygon.push({
|
|
x: cur_command.X,
|
|
y: cur_command.Y
|
|
});
|
|
last_x = cur_command.X;
|
|
last_y = cur_command.Y;
|
|
break;
|
|
case bezier3:
|
|
cur_polygon = cur_polygon.concat(partition_bezier3(last_x, last_y, cur_command.X0, cur_command.Y0, cur_command.X1, cur_command.Y1, epsilon));
|
|
last_x = cur_command.X1;
|
|
last_y = cur_command.Y1;
|
|
break;
|
|
case bezier4:
|
|
cur_polygon = cur_polygon.concat(partition_bezier4(last_x, last_y, cur_command.X0, cur_command.Y0, cur_command.X1, cur_command.Y1, cur_command.X2, cur_command.Y2, epsilon));
|
|
last_x = cur_command.X2;
|
|
last_y = cur_command.Y2;
|
|
break;
|
|
case arcTo:
|
|
var arr_curve_bezier = getArrayPointsCurveBezierAtArcTo(last_x, last_y, cur_command.stX, cur_command.stY, cur_command.wR, cur_command.hR, cur_command.stAng, cur_command.swAng);
|
|
if (arr_curve_bezier.length > 0) {
|
|
last_x = arr_curve_bezier[arr_curve_bezier.length - 1].x4;
|
|
last_y = arr_curve_bezier[arr_curve_bezier.length - 1].y4;
|
|
for (var i = 0; i < arr_curve_bezier.length; ++i) {
|
|
var cur_curve_bezier = arr_curve_bezier[i];
|
|
cur_polygon = cur_polygon.concat(partition_bezier4(cur_curve_bezier.x0, cur_curve_bezier.y0, cur_curve_bezier.x1, cur_curve_bezier.y1, cur_curve_bezier.x2, cur_curve_bezier.y2, cur_curve_bezier.x3, cur_curve_bezier.y3, epsilon));
|
|
}
|
|
}
|
|
break;
|
|
case close:
|
|
arr_polygons.push(cur_polygon);
|
|
cur_polygon = [];
|
|
}
|
|
}
|
|
for (i = 0; i < arr_polygons.length; ++i) {
|
|
var cur_polygon = arr_polygons[i];
|
|
graphics._m(cur_polygon[0].x, cur_polygon[0].y);
|
|
for (var j = 0; j < cur_polygon.length; ++j) {
|
|
graphics._l(cur_polygon[j].x, cur_polygon[j].y);
|
|
}
|
|
graphics._z();
|
|
graphics.ds();
|
|
}
|
|
},
|
|
isSmartLine: function () {
|
|
if (this.ArrPathCommandInfo.length != 2) {
|
|
return false;
|
|
}
|
|
if (this.ArrPathCommandInfo[0].id == moveTo && this.ArrPathCommandInfo[1].id == lineTo) {
|
|
if (Math.abs(this.ArrPathCommandInfo[0].X - this.ArrPathCommandInfo[1].X) < 0.0001) {
|
|
return true;
|
|
}
|
|
if (Math.abs(this.ArrPathCommandInfo[0].Y - this.ArrPathCommandInfo[1].Y) < 0.0001) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
isSmartRect: function () {
|
|
if (this.ArrPathCommandInfo.length != 5) {
|
|
return false;
|
|
}
|
|
if (this.ArrPathCommandInfo[0].id != moveTo || this.ArrPathCommandInfo[1].id != lineTo || this.ArrPathCommandInfo[2].id != lineTo || this.ArrPathCommandInfo[3].id != lineTo || (this.ArrPathCommandInfo[4].id != lineTo && this.ArrPathCommandInfo[4].id != close)) {
|
|
return false;
|
|
}
|
|
var _float_eps = 0.0001;
|
|
if (Math.abs(this.ArrPathCommandInfo[0].X - this.ArrPathCommandInfo[1].X) < _float_eps) {
|
|
if (Math.abs(this.ArrPathCommandInfo[1].Y - this.ArrPathCommandInfo[2].Y) < _float_eps) {
|
|
if (Math.abs(this.ArrPathCommandInfo[2].X - this.ArrPathCommandInfo[3].X) < _float_eps && Math.abs(this.ArrPathCommandInfo[3].Y - this.ArrPathCommandInfo[0].Y) < _float_eps) {
|
|
if (this.ArrPathCommandInfo[4].id == close) {
|
|
return true;
|
|
}
|
|
if (Math.abs(this.ArrPathCommandInfo[0].X - this.ArrPathCommandInfo[4].X) < _float_eps && Math.abs(this.ArrPathCommandInfo[0].Y - this.ArrPathCommandInfo[4].Y) < _float_eps) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (Math.abs(this.ArrPathCommandInfo[0].Y - this.ArrPathCommandInfo[1].Y) < _float_eps) {
|
|
if (Math.abs(this.ArrPathCommandInfo[1].X - this.ArrPathCommandInfo[2].X) < _float_eps) {
|
|
if (Math.abs(this.ArrPathCommandInfo[2].Y - this.ArrPathCommandInfo[3].Y) < _float_eps && Math.abs(this.ArrPathCommandInfo[3].X - this.ArrPathCommandInfo[0].X) < _float_eps) {
|
|
if (this.ArrPathCommandInfo[4].id == close) {
|
|
return true;
|
|
}
|
|
if (Math.abs(this.ArrPathCommandInfo[0].X - this.ArrPathCommandInfo[4].X) < _float_eps && Math.abs(this.ArrPathCommandInfo[0].Y - this.ArrPathCommandInfo[4].Y) < _float_eps) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
drawSmart: function (shape_drawer) {
|
|
var _graphics = shape_drawer.Graphics;
|
|
var _full_trans = _graphics.m_oFullTransform;
|
|
if (!_graphics || !_full_trans || undefined == _graphics.m_bIntegerGrid) {
|
|
return this.draw(shape_drawer);
|
|
}
|
|
var bIsTransformed = (_full_trans.shx == 0 && _full_trans.shy == 0) ? false : true;
|
|
if (bIsTransformed) {
|
|
return this.draw(shape_drawer);
|
|
}
|
|
var isLine = this.isSmartLine();
|
|
var isRect = false;
|
|
if (!isLine) {
|
|
isRect = this.isSmartRect();
|
|
}
|
|
if (!isLine && !isRect) {
|
|
return this.draw(shape_drawer);
|
|
}
|
|
var _old_int = _graphics.m_bIntegerGrid;
|
|
if (false == _old_int) {
|
|
_graphics.SetIntegerGrid(true);
|
|
}
|
|
var dKoefMMToPx = Math.max(_graphics.m_oCoordTransform.sx, 0.001);
|
|
var _ctx = _graphics.m_oContext;
|
|
var bIsStroke = (shape_drawer.bIsNoStrokeAttack || (this.stroke !== true)) ? false : true;
|
|
var bIsEven = false;
|
|
if (bIsStroke) {
|
|
var _lineWidth = Math.max((shape_drawer.StrokeWidth * dKoefMMToPx + 0.5) >> 0, 1);
|
|
_ctx.lineWidth = _lineWidth;
|
|
if (_lineWidth & 1 == 1) {
|
|
bIsEven = true;
|
|
}
|
|
}
|
|
var bIsDrawLast = false;
|
|
var path = this.ArrPathCommand;
|
|
shape_drawer._s();
|
|
if (!isRect) {
|
|
for (var j = 0, l = path.length; j < l; ++j) {
|
|
var cmd = path[j];
|
|
switch (cmd.id) {
|
|
case moveTo:
|
|
bIsDrawLast = true;
|
|
var _x = (_full_trans.TransformPointX(cmd.X, cmd.Y)) >> 0;
|
|
var _y = (_full_trans.TransformPointY(cmd.X, cmd.Y)) >> 0;
|
|
if (bIsEven) {
|
|
_x -= 0.5;
|
|
_y -= 0.5;
|
|
}
|
|
_ctx.moveTo(_x, _y);
|
|
break;
|
|
case lineTo:
|
|
bIsDrawLast = true;
|
|
var _x = (_full_trans.TransformPointX(cmd.X, cmd.Y)) >> 0;
|
|
var _y = (_full_trans.TransformPointY(cmd.X, cmd.Y)) >> 0;
|
|
if (bIsEven) {
|
|
_x -= 0.5;
|
|
_y -= 0.5;
|
|
}
|
|
_ctx.lineTo(_x, _y);
|
|
break;
|
|
case close:
|
|
_ctx.closePath();
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
var minX = 100000;
|
|
var minY = 100000;
|
|
var maxX = -100000;
|
|
var maxY = -100000;
|
|
bIsDrawLast = true;
|
|
for (var j = 0, l = path.length; j < l; ++j) {
|
|
var cmd = path[j];
|
|
switch (cmd.id) {
|
|
case moveTo:
|
|
case lineTo:
|
|
if (minX > cmd.X) {
|
|
minX = cmd.X;
|
|
}
|
|
if (minY > cmd.Y) {
|
|
minY = cmd.Y;
|
|
}
|
|
if (maxX < cmd.X) {
|
|
maxX = cmd.X;
|
|
}
|
|
if (maxY < cmd.Y) {
|
|
maxY = cmd.Y;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
var _x1 = (_full_trans.TransformPointX(minX, minY)) >> 0;
|
|
var _y1 = (_full_trans.TransformPointY(minX, minY)) >> 0;
|
|
var _x2 = (_full_trans.TransformPointX(maxX, maxY)) >> 0;
|
|
var _y2 = (_full_trans.TransformPointY(maxX, maxY)) >> 0;
|
|
if (bIsEven) {
|
|
_ctx.rect(_x1 + 0.5, _y1 + 0.5, _x2 - _x1, _y2 - _y1);
|
|
} else {
|
|
_ctx.rect(_x1, _y1, _x2 - _x1, _y2 - _y1);
|
|
}
|
|
}
|
|
if (bIsDrawLast) {
|
|
shape_drawer.drawFillStroke(true, this.fill, bIsStroke);
|
|
}
|
|
shape_drawer._e();
|
|
if (false == _old_int) {
|
|
_graphics.SetIntegerGrid(false);
|
|
}
|
|
}
|
|
};
|
|
function partition_bezier3(x0, y0, x1, y1, x2, y2, epsilon) {
|
|
var dx01 = x1 - x0;
|
|
var dy01 = y1 - y0;
|
|
var dx12 = x2 - x1;
|
|
var dy12 = y2 - y1;
|
|
var r01 = Math.sqrt(dx01 * dx01 + dy01 * dy01);
|
|
var r12 = Math.sqrt(dx12 * dx12 + dy12 * dy12);
|
|
if (Math.max(r01, r12) < epsilon) {
|
|
return [{
|
|
x: x0,
|
|
y: y0
|
|
},
|
|
{
|
|
x: x1,
|
|
y: y1
|
|
},
|
|
{
|
|
x: x2,
|
|
y: y2
|
|
}];
|
|
}
|
|
var x01 = (x0 + x1) * 0.5;
|
|
var y01 = (y0 + y1) * 0.5;
|
|
var x12 = (x1 + x2) * 0.5;
|
|
var y12 = (y1 + y2) * 0.5;
|
|
var x012 = (x01 + x12) * 0.5;
|
|
var y012 = (y01 + y12) * 0.5;
|
|
return partition_bezier3(x0, y0, x01, y01, x012, y012, epsilon).concat(partition_bezier3(x012, y012, x12, y12, x2, y2, epsilon));
|
|
}
|
|
function partition_bezier4(x0, y0, x1, y1, x2, y2, x3, y3, epsilon) {
|
|
var dx01 = x1 - x0;
|
|
var dy01 = y1 - y0;
|
|
var dx12 = x2 - x1;
|
|
var dy12 = y2 - y1;
|
|
var dx23 = x3 - x2;
|
|
var dy23 = y3 - y2;
|
|
var r01 = Math.sqrt(dx01 * dx01 + dy01 * dy01);
|
|
var r12 = Math.sqrt(dx12 * dx12 + dy12 * dy12);
|
|
var r23 = Math.sqrt(dx23 * dx23 + dy23 * dy23);
|
|
if (Math.max(r01, r12, r23) < epsilon) {
|
|
return [{
|
|
x: x0,
|
|
y: y0
|
|
},
|
|
{
|
|
x: x1,
|
|
y: y1
|
|
},
|
|
{
|
|
x: x2,
|
|
y: y2
|
|
},
|
|
{
|
|
x: x3,
|
|
y: y3
|
|
}];
|
|
}
|
|
var x01 = (x0 + x1) * 0.5;
|
|
var y01 = (y0 + y1) * 0.5;
|
|
var x12 = (x1 + x2) * 0.5;
|
|
var y12 = (y1 + y2) * 0.5;
|
|
var x23 = (x2 + x3) * 0.5;
|
|
var y23 = (y2 + y3) * 0.5;
|
|
var x012 = (x01 + x12) * 0.5;
|
|
var y012 = (y01 + y12) * 0.5;
|
|
var x123 = (x12 + x23) * 0.5;
|
|
var y123 = (y12 + y23) * 0.5;
|
|
var x0123 = (x012 + x123) * 0.5;
|
|
var y0123 = (y012 + y123) * 0.5;
|
|
return partition_bezier4(x0, y0, x01, y01, x012, y012, x0123, y0123, epsilon).concat(partition_bezier4(x0123, y0123, x123, y123, x23, y23, x3, y3, epsilon));
|
|
} |