Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
bc93beb
fix(util.calcAngleBetweenVectors): ensure cos value is in the range -…
Jul 26, 2022
41271bf
fix(getBisector): check if ro is near zero instead of beign strictly …
Jul 26, 2022
7f01868
fix(): projections on projectStrokeOnPoints function. Width, height, …
Jul 26, 2022
37e4fac
fix(calcAngleBetweenVectors): use atan2 instead of dot product
Jul 27, 2022
a9c8f35
fix(): remove `Math.hypot`
ShaMan123 Jul 27, 2022
6cb36dc
Update misc.js
ShaMan123 Jul 27, 2022
d3773ed
Update misc.js
ShaMan123 Jul 27, 2022
6befc8c
Update src/util/misc.js
luizzappa Jul 29, 2022
afdec06
Update src/util/misc.js
luizzappa Jul 29, 2022
c05e719
refactor(getBisector): removed phi variable
Jul 29, 2022
6790b72
refactor(polyline._setPositionDimensions): removed exactBoundingBox
Jul 29, 2022
b49b469
refactor(projectStrokeOnPoints): repeated code reduction and style ch…
Jul 29, 2022
b554b6d
refactor(getOrthogonalUnitVector): removed optional parameter
Jul 29, 2022
cb45499
dep(): `strokeBoundingBox`
ShaMan123 Jul 29, 2022
52a2e02
lint
ShaMan123 Jul 29, 2022
f329270
refactor(): lint + reorder if block
ShaMan123 Jul 29, 2022
f8b0dcf
Revert "refactor(getOrthogonalUnitVector): removed optional parameter"
ShaMan123 Jul 29, 2022
7b12924
lint `getOrthogonalUnitVector`
ShaMan123 Jul 29, 2022
9017242
fix(): `hypot`
ShaMan123 Jul 29, 2022
5e2cd10
test(): add `hypot` test
ShaMan123 Jul 29, 2022
e9ee038
docs
ShaMan123 Jul 29, 2022
3faf8f0
Update util.js
ShaMan123 Jul 29, 2022
139f4e8
safeguard
ShaMan123 Jul 29, 2022
ce7be83
Merge branch 'master' into pr/8083
ShaMan123 Jul 29, 2022
0faf3dc
revert(): dep `exactBoundingBox`
ShaMan123 Jul 29, 2022
3925115
Update polygon.js
ShaMan123 Jul 29, 2022
83af206
Merge branch 'master' into pr/8083
ShaMan123 Aug 2, 2022
2a25b00
fix(projectStrokeOnPoints): fixed bug in bevel and miter stroke. Redu…
Aug 3, 2022
a960b45
feat(projectStrokeOnPoints): possibility to also returns the point an…
Aug 3, 2022
813358e
hypot module
ShaMan123 Aug 3, 2022
244fc69
Update misc.ts
ShaMan123 Aug 3, 2022
d12b1a1
Merge branch 'master' into pr/8083
ShaMan123 Aug 5, 2022
e92297e
Revert "Merge branch 'master' into pr/8083" misc
ShaMan123 Aug 5, 2022
de55c38
Revert "fix conflict"
ShaMan123 Aug 5, 2022
e4d5a74
fix circular deps
ShaMan123 Aug 8, 2022
e2debbf
refactor(projectStrokeOnPoints): returning context instead of tracePr…
Aug 8, 2022
b9daa0f
fix(): extended _getTransformedDimensions in polyline
Aug 9, 2022
e54910b
Revert "fix(): extended _getTransformedDimensions in polyline"
Aug 29, 2022
a7138dc
fix(_setPositionDimensions): use the actual position of the object in…
Aug 29, 2022
6a5e615
fix(): extended _set in polyline
Aug 29, 2022
85d2eae
checkout
ShaMan123 Sep 2, 2022
3a82295
Revert "checkout"
ShaMan123 Sep 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions src/shapes/polyline.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,24 @@
_setPositionDimensions: function(options) {
options || (options = {});
var calcDim = this._calcDimensions(options), correctLeftTop,
correctSize = this.exactBoundingBox ? this.strokeWidth : 0;
this.width = calcDim.width - correctSize;
this.height = calcDim.height - correctSize;
correctSizeX = this.exactBoundingBox
? this.strokeUniform
? this.strokeWidth/this.scaleX
: this.strokeWidth
: 0,
correctSizeY = this.exactBoundingBox
? this.strokeUniform
? this.strokeWidth/this.scaleY
: this.strokeWidth
: 0;
this.width = calcDim.width - correctSizeX;
this.height = calcDim.height - correctSizeY;
if (!options.fromSVG) {
correctLeftTop = this.translateToGivenOrigin(
{
// this looks bad, but is one way to keep it optional for now.
x: calcDim.left - this.strokeWidth / 2 + correctSize / 2,
y: calcDim.top - this.strokeWidth / 2 + correctSize / 2
x: calcDim.left - this.strokeWidth / 2 + correctSizeX / 2,
y: calcDim.top - this.strokeWidth / 2 + correctSizeY / 2
},
'left',
'top',
Expand All @@ -108,8 +117,8 @@
this.top = options.fromSVG ? calcDim.top : correctLeftTop.y;
}
this.pathOffset = {
x: calcDim.left + this.width / 2 + correctSize / 2,
y: calcDim.top + this.height / 2 + correctSize / 2
x: calcDim.left + this.width / 2 + correctSizeX / 2,
y: calcDim.top + this.height / 2 + correctSizeY / 2
};
},

Expand Down
206 changes: 179 additions & 27 deletions src/util/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* @namespace fabric.util
*/
fabric.util = {

/**
* Calculate the cos of an angle, avoiding returning floats for known results
* @static
Expand Down Expand Up @@ -158,15 +157,18 @@
},

/**
* Calculates angle between 2 vectors using dot product
* Calculates angle between 2 vectors using atan2
* @static
* @memberOf fabric.util
* @param {Point} a
* @param {Point} b
* @returns the angle in radian between the vectors
*/
calcAngleBetweenVectors: function (a, b) {
return Math.acos((a.x * b.x + a.y * b.y) / (Math.hypot(a.x, a.y) * Math.hypot(b.x, b.y)));
var dot = a.x * b.x + a.y * b.y,
det = a.x * b.y - a.y * b.x;

return Math.atan2(det, dot);
},

/**
Expand All @@ -176,7 +178,8 @@
* @returns {Point} vector representing the unit vector of pointing to the direction of `v`
*/
getHatVector: function (v) {
return new fabric.Point(v.x, v.y).scalarMultiply(1 / Math.hypot(v.x, v.y));
var hypot = Math.sqrt(v.x * v.x + v.y * v.y);
return new fabric.Point(v.x, v.y).scalarMultiply(1 / hypot);
},

/**
Expand All @@ -190,19 +193,33 @@
getBisector: function (A, B, C) {
var AB = fabric.util.createVector(A, B), AC = fabric.util.createVector(A, C);
var alpha = fabric.util.calcAngleBetweenVectors(AB, AC);
// check if alpha is relative to AB->BC
var ro = fabric.util.calcAngleBetweenVectors(fabric.util.rotateVector(AB, alpha), AC);
var phi = alpha * (ro === 0 ? 1 : -1) / 2;
var phi = alpha / 2;
return {
vector: fabric.util.getHatVector(fabric.util.rotateVector(AB, phi)),
angle: alpha
angle: Math.abs(alpha)
};
},

/**
* Project stroke width on points returning 2 projections for each point as follows:
* @static
* @memberOf fabric.util
* @param {Point} vector
* @param {Boolean} counterClockwise the direction of the orthogonal vector
* @returns {Point} the unit orthogonal vector
*/
getOrthogonalUnitVector: function(vector, counterClockwise = true) {
return fabric.util.getHatVector(
new fabric.Point(
counterClockwise ? -vector.y : vector.y,
counterClockwise ? vector.x : -vector.x
)
)
},

/**
* Project stroke width on points returning projections for each point as follows:
* - `miter`: 2 points corresponding to the outer boundary and the inner boundary of stroke.
* - `bevel`: 2 points corresponding to the bevel boundaries, tangent to the bisector.
* - `bevel`: 4 points corresponding to the bevel possible boundaries, orthogonal to the stroke.
* - `round`: same as `bevel`
* Used to calculate object's bounding box
* @static
Expand All @@ -221,50 +238,185 @@
projectStrokeOnPoints: function (points, options, openPath) {
var coords = [], s = options.strokeWidth / 2,
strokeUniformScalar = options.strokeUniform ?
new fabric.Point(1 / options.scaleX, 1 / options.scaleY) : new fabric.Point(1, 1),
getStrokeHatVector = function (v) {
var scalar = s / (Math.hypot(v.x, v.y));
return new fabric.Point(v.x * scalar * strokeUniformScalar.x, v.y * scalar * strokeUniformScalar.y);
};
new fabric.Point(1 / options.scaleX, 1 / options.scaleY) : new fabric.Point(1, 1);

if (points.length <= 1) {return coords;}

points.forEach(function (p, index) {
var A = new fabric.Point(p.x, p.y), B, C;
if (index === 0) {
C = points[index + 1];
B = openPath ? getStrokeHatVector(fabric.util.createVector(C, A)).addEquals(A) : points[points.length - 1];
B = openPath ? A : points[points.length - 1];
}
else if (index === points.length - 1) {
B = points[index - 1];
C = openPath ? getStrokeHatVector(fabric.util.createVector(B, A)).addEquals(A) : points[0];
C = openPath ? A : points[0];
}
else {
B = points[index - 1];
C = points[index + 1];
}
var bisector = fabric.util.getBisector(A, B, C),
bisectorVector = bisector.vector,

if (openPath && index === 0) {
var scaledA = new fabric.Point(A.x * options.scaleX, A.y * options.scaleY),
scaledC = new fabric.Point(C.x * options.scaleX, C.y * options.scaleY);

var vector = fabric.util.createVector(
options.strokeUniform ? scaledA : A,
options.strokeUniform ? scaledC : C
),
hatOrthogonalVector = fabric.util.getOrthogonalUnitVector(vector),
orthogonalVector = new fabric.Point(
hatOrthogonalVector.x * s * strokeUniformScalar.x,
hatOrthogonalVector.y * s * strokeUniformScalar.y
);

coords.push(A.add(orthogonalVector));
coords.push(A.subtract(orthogonalVector));
return;
}

if (openPath && index === points.length - 1) {
var scaledA = new fabric.Point(A.x * options.scaleX, A.y * options.scaleY),
scaledB = new fabric.Point(B.x * options.scaleX, B.y * options.scaleY);

var vector = fabric.util.createVector(
options.strokeUniform ? scaledA : A,
options.strokeUniform ? scaledB : B
),
hatOrthogonalVector = fabric.util.getOrthogonalUnitVector(vector),
orthogonalVector = new fabric.Point(
hatOrthogonalVector.x * s * strokeUniformScalar.x,
hatOrthogonalVector.y * s * strokeUniformScalar.y
);

coords.push(A.add(orthogonalVector));
coords.push(A.subtract(orthogonalVector));
return;
}

var bisector,
scaledA,
scaledB,
scaledC;
if (options.strokeUniform) {
scaledA = new fabric.Point(A.x * options.scaleX, A.y * options.scaleY);
scaledB = new fabric.Point(B.x * options.scaleX, B.y * options.scaleY);
scaledC = new fabric.Point(C.x * options.scaleX, C.y * options.scaleY);

bisector = fabric.util.getBisector(scaledA, scaledB, scaledC);
} else {
bisector = fabric.util.getBisector(A, B, C);
}

var bisectorVector = bisector.vector,
alpha = bisector.angle,
scalar,
miterVector;

if (options.strokeLineJoin === 'miter') {
scalar = -s / Math.sin(alpha / 2);

miterVector = new fabric.Point(
bisectorVector.x * scalar * strokeUniformScalar.x,
bisectorVector.y * scalar * strokeUniformScalar.y
);
if (Math.hypot(miterVector.x, miterVector.y) / s <= options.strokeMiterLimit) {

var strokeMiterLimit;
if (options.strokeUniform) {
var miterLimitLenght = options.strokeMiterLimit * s,
miterLimitVector = new fabric.Point(
bisectorVector.x * miterLimitLenght * strokeUniformScalar.x,
bisectorVector.y * miterLimitLenght * strokeUniformScalar.y
);
strokeMiterLimit = Math.hypot(miterLimitVector.x, miterLimitVector.y) / s;
strokeMiterLimit = Math.sqrt(miterLimitVector.x * miterLimitVector.x + miterLimitVector.y * miterLimitVector.y) / s;
} else {
strokeMiterLimit = options.strokeMiterLimit;
}

if (Math.sqrt(miterVector.x * miterVector.x + miterVector.y * miterVector.y) / s <= strokeMiterLimit) {
coords.push(A.add(miterVector));
coords.push(A.subtract(miterVector));
return;
}
}

if (options.strokeLineJoin === 'bevel' || options.strokeLineJoin === 'miter') { // miter greater than stroke miter limit

var AB = fabric.util.createVector(
options.strokeUniform ? scaledA : A,
options.strokeUniform ? scaledB : B
),
hatOrthogonalAB = fabric.util.getOrthogonalUnitVector(AB),
orthogonalAB = new fabric.Point(
hatOrthogonalAB.x * s * strokeUniformScalar.x,
hatOrthogonalAB.y * s * strokeUniformScalar.y
);

var AC = fabric.util.createVector(
options.strokeUniform ? scaledA : A,
options.strokeUniform ? scaledC : C
),
hatOrthogonalAC = fabric.util.getOrthogonalUnitVector(AC),
orthogonalAC = new fabric.Point(
hatOrthogonalAC.x * s * strokeUniformScalar.x,
hatOrthogonalAC.y * s * strokeUniformScalar.y
);


coords.push(A.add(orthogonalAB));
coords.push(A.subtract(orthogonalAB));

coords.push(A.add(orthogonalAC));
coords.push(A.subtract(orthogonalAC));

return;
}

if (options.strokeLineJoin === 'round') {

if (alpha > PiBy2) {
var AB = fabric.util.createVector(
options.strokeUniform ? scaledA : A,
options.strokeUniform ? scaledB : B
),
hatOrthogonalAB = fabric.util.getOrthogonalUnitVector(AB)
orthogonalAB = new fabric.Point(
hatOrthogonalAB.x * s * strokeUniformScalar.x,
hatOrthogonalAB.y * s * strokeUniformScalar.y
);

var AC = fabric.util.createVector(
options.strokeUniform ? scaledA : A,
options.strokeUniform ? scaledC : C
),
hatOrthogonalAC = fabric.util.getOrthogonalUnitVector(AC),
orthogonalAC = new fabric.Point(
hatOrthogonalAC.x * s * strokeUniformScalar.x,
hatOrthogonalAC.y * s * strokeUniformScalar.y
)

coords.push(A.add(orthogonalAB));
coords.push(A.subtract(orthogonalAB));

coords.push(A.add(orthogonalAC));
coords.push(A.subtract(orthogonalAC));

return;
}

var radiusOnAxisX = new fabric.Point(s * strokeUniformScalar.x, 0),
radiusOnAxisY = new fabric.Point(0, s * strokeUniformScalar.y);

coords.push(A.add(radiusOnAxisX));
coords.push(A.subtract(radiusOnAxisX));

coords.push(A.add(radiusOnAxisY));
coords.push(A.subtract(radiusOnAxisY));

return;
}
scalar = -s * Math.SQRT2;
miterVector = new fabric.Point(
bisectorVector.x * scalar * strokeUniformScalar.x,
bisectorVector.y * scalar * strokeUniformScalar.y
);
coords.push(A.add(miterVector));
coords.push(A.subtract(miterVector));
});
return coords;
},
Expand Down