-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(polyline): stroke bounding rect calculation typescript #8344
fix(polyline): stroke bounding rect calculation typescript #8344
Conversation
…ave the need to check the direction of ro
… adjust returned points
…ht and pathOffset
I had resolved the need to recalculate BBOX to only when |
That is how fabric behaves. |
For example text and group already depend on using set |
Regarding polygon controls. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about splitting up the code and creating a function for each case?
If you don't like that I think we should define A, B, C, AB, AC at the top if possible.
- I saw some
var
. They should all becomeconst
orlet
. VSCODE has an option to quick fix all occurrences if you hover over one. - export all types
The code will be cleaner, I will refactor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://codesandbox.io/s/project-stroke-points-with-skew-correction-forked-xe09oo
can't wait for codesandbox action to work! Then no more of this copy paste bullshit
@luizzappa indicate all is good so I can merge with peace |
@asturur surfacing this comment so you won't get surprised.
|
@asturur any objections we make polygon controls part of core using the adjusted logic from the demo @luizzappa created? |
Everything seems to be correct. Thanks for all the support @ShaMan123 ! Regarding the tests, is it better to open a new PR (or are we still in #8087)? |
My pleasure. |
It is ok to disagree. Solutions and costs needs to be balanced.
I just asked you to expose a leaner version of it since here there are no cases of 3+ dimensional distances. Right? Said so the bounding box calculation in this way breaks ( obviously ) the skewing bounding box from controls that at least in the sandbox jump left and right often. For the example of the skew i made before, it should also be taken in account that for roundy corner, and overall different solution could be to trace the path that the strokes create, approximate (basically is an exact match) the roundy part with a beizer curve, and the split it in segments with the measurement code, and then apply the transformations to those points, that would give you more points in total, but would solve the issue under skew or at least it would approximate good enough the skewed rounded corner. It is already possible to transform a path with a canvas transform and obtain a new path that absorbs the transformation and move all its points and control points to look like identical to the transformed path ( at leat from my tests, i can't back this up with a theorem ) |
I was being greedy. I suggested other options to calculate the bbox such as what you've suggested. We could explore them later on. |
setDimensions: function () { | ||
const { left, top, width, height, pathOffset, strokeOffset } = | ||
this._calcDimensions(); | ||
this.set({ width, height, pathOffset, strokeOffset }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i kind of get that strokeOffset
is the value that tells us in which direction the bounding box move most of its new space, becuase that was always one of the things that this kind of new calculation was adding, the center of the shape with and without stroke is no more the same and this strokeOffset kind of represent that.
But where are we using it? I can't see use it in the PR nor in the codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trash my comments entirely in favor of
f158267
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
github mixes comments sometimes ( it does it to me ).
Indeed this answer is for the other thread.
strokeOffset still a mistery? or your answer was related and i don't see how?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to the other thread indeed.
Can't find it anymore. Too much chat.
Don't understand your questions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i ll try easier, at the cost of looking unpolite.
Show me where strokeOffset is used.
i see it calculated, not used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I wrote in the thread it is meant for the dev.
Think of using originX/Y in a case of a large miter for example. The poly might shift a lot because of this miter. So a dev might want to adjust the position so the origin is in respect to the points instead of the entire stoked miter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok so i guessed right what is it, and is unused.
I'll think of it.
We should definitely add polygon/path controls after 6.0 is released. |
i need to craft a polygon with a translate transform, that i think gets absorbed with top and left, let me do that. |
Added the test in master, if it pass you can merge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is good.
There are some things i don't like on the generic changes in polygon, and i will come back to them:
- new special _set: for the polygon and the bounding box
- setDimension is a new function, but has 2 task, setDimensions and return me the top,left and that is weird. Not dangerous, but at least uncommon. Would be probably less weird if it would return all it calculated, and then we extract what we need from it four our edge cases
- boundingBox early return should go away if is only a compiler issue and not a real code issue that fabricJS could trigger ( seems also double protected in the polyline class too. ). In general if a function requires an array of things, it either handles the empty array or pretends a non empty array.
- handle strokeOffset where is needed, to keep a box steady
…abricjs#8344) see PR description BREAKING: `_setPositionDimensions` => `setDimensions` Co-authored-by: Luiz Zappa <[email protected]> Co-authored-by: ShaMan123 <[email protected]> Co-authored-by: Andrea Bogazzi <[email protected]>
* test(stroke projection): separating the goldens from this commit * goldens * refactor(stroke_projection): fewer test cases, but more focused on boundary conditions * goldens after refactor * reset freedrawing * es6 * fix(): stroke line cap and size of goldens * goldens * fix skewing progress progress(): position major progress fix(): flip cleanup no multiply good state! fix(): successive skewing cleaup * Update controls_handlers.js * Update CHANGELOG.md * fix(): use set * Update object.class.ts * Update controls.actions.ts * ws * fix(): flip assignment * goldens * revert(): svg stuff * cleanup * Update object.class.ts * getAcuteAngleFactor => StrokeLineJoinProjections.getOrthogonalRotationFactor * fix(): restore `compensateScaleForSkew` * cleanup * cleanup compensate * rename * WIP compensation * progress! * revert * refactor * cleanup * refactor(): getBisector to class @luizzappa I broke the return type * test(): group * refactor(): axis keys * refactor skew handlers * minor fix + rename * `compensationFactor` only for `skewY` commited @luizzappa suggestion * test(): two equal points * fix(): anchoring origin * fix(): zero unit vector * Update polygon.js * reduce points * fix(): polygon last points eq first * fix(): 1 point case * fix(): line cap 1 point * fix(): reduce polygon end points eq start * Update stroke_projection.js * Update stroke_projection.js * comment * rename for clarity * checkout unrelated changes * checkout svg visual tests/assets @asturur I changed these since the test wasn't sensitive enough * Update controls.actions.ts * test(): add more cases * fix(projectBevel) * cleanup findRight * revert(): square projection of zero vector handled by reducing points * fix(projectBevel) * test(): add singlePoint and skewX=0, skewY=0 * Revert "revert(): square projection of zero vector" This reverts commit a03e124. * fix(): 1 point round * comments * fix() strokeMiterLimit * fix() strokeMiterLimit remove Math.round * fix() strokeMiterLimit leq * feat(): add isBetweenVectors, crossProduct and dotProduct * fix(StrokeLineJoinProjections): orthogonal projection * Update polyline.class.ts * fix(): `_getTransformedDimensions` `_getTransformedDimensions` is used by the skewing control logic so it needs to calc size properly * refactor(StrokeLineJoinProjections): check alpha equals 0 or 2PI * tests(polygon#_calcDimensions) * test(): stroke projections * es6 syntax * test(polygon): _calcDimensions with custom options * refactor visual test results * better * rename * Update CHANGELOG.md * fix(polyline): strokeDiff and strokeOffset * test(polygon#_calcDimensions): fix strokeDiff and strokeOffset * fix(polyline#_calcDimensions): remove options mutate * test(): reduce the number of tests * test(): remove testing group * Update stroke_projection.js * no caching * prettier * fix * fix(strokeLineCapProjections): project square and round with 1 point * test(): fix blur and setDimensions order * refactor(strokeLineCapProjections): projectRound and projectSquare for 1 point case * prettier * fix(strokeLineJoinProjections): correct calc for round cases with stroke uniform and skew * refactor(strokeLineJoinProjections): simplify calculation of which projection is the beginning of the circle segment * refactor(strokeLineJoinProjections): safe guard * fix(StrokeLineCapProjections): line-cap square with uniform stroke * refactor(StrokeLineCapProjections): safe guard with Math.max * test(): remove skew when testing miter-limit * test(): remove a case that does not exist more * fix(StrokeLineJoinProjections): round with skewX and skewY * test(): add round case with skewX and skewY * goldens * typo * fix merge conflicts * Update polyline.class.ts * fix(): `_getTransformedDimensions` * Update controls_handlers.js * Update polyline.class.ts * Revert "Merge commit 'refs/pull/8311/head' of github.com:fabricjs/fabric.js into test-stroke-bbox" This reverts commit 477f98b, reversing changes made to 5214ea8. * test(): disabled node test for stroke projection * fix(polyline#_set): recalc projections for round stroke * imports * comment * checkout * checkout * fix(): restore `strokeOffset` * rm goldens for diff readability * refactor(polyline#_getTransformedDimensions): remove strokeProjectionOptions * comments on _getTransformedDimensions() * fix merge hopefully * Update stroke_projection.js * disable visual test * another merge fix https://github.com/fabricjs/fabric.js/blob/0b0a09a9c562ba848f8d6d0b9fbe461c13b93d29/src/shapes/polyline.class.ts#L221-L224 * todo * Update CHANGELOG.md --------- Co-authored-by: ShaMan123 <[email protected]> Co-authored-by: Luiz Zappa <[email protected]>
I migrated the PR #8083 code to typescript and corrected the calculation when there is skew.
closes #8083
Related Issues
Proposed changes
calcAngleBetweenVectors
function, calculate angle between two vector using atan2 instead of dot product. So eliminated the need to check the angle direction ingetBisector
function,_setPositionDimensions
function in the polyline class to adjust the width/height by the scale/skew.projectStrokeOnPoints
function to cover all stroke line join cases and also to uniform stroke.Working example
The red dotted vector is the bisector returned by the getBisector function. The green dotted vector is in the opposite direction and is what is used to determine which side to plot the projections. The projections are the colored vectors, one color for each point.
To make debugging easier, you can configure the parameters (stroke line join type, strokeUniform, miterLimit, etc..)
Documentation
This section details the calculation of projections for each type of stroke line join.
Summary
Links:
1. Open path
These cases differ in relation to the line-cap property (MDN reference)
1.1 Butt
The projections are calculated by two vectors orthogonal to the stroke.
Scaling effect: only when the
stroke
isuniform
that scaling affects the calculation of projections. It changes the position of the points (see more details here), so we must take it into account to have the new arrangement of the points.Skew effect: just apply distortion after projection calculation.
1.2 Round
Same as 2.3 Round
1.3 Square
Project a rectangle of points on the stroke in the opposite direction of the vector
AT
. The projections are the two vectors starting from the vertex pointing to the sides of the square.Scaling effect: only when the
stroke
isuniform
that scaling affects the calculation of projections. It changes the position of the points (see more details here), so we must take it into account to have the new arrangement of the points.Skew effect: just apply distortion after projection calculation.
2. Closed path
There are three types of stroke line join (MDN reference)
2.1 Miter
The corner is formed by extending the outer edges of the stroke at the tangents of the path segments until they intersect.
Stroke miter limit: when two line segments meet at a sharp angle, it is possible for the join to extend, far beyond the thickness of the line stroking the path. The stroke-miterlimit imposes a limit on the extent of the line join, if so the join is converted from a miter to a bevel. (MDN reference)
The ratio of
miter length
(distance between the outer tip and the inner corner of the miter) to stroke-width is directly related to the angle (alpha
) of thebisector
:Note: when the
stroke
isuniform
, the arrangement of the points is changed (see explanation below), consequently the strokeMiterLimit. In this case we must recalculate this limit.Scaling effect : scaling changes the arrangement of points, hence the bisector calculation. Take a look:
Plotting a triangle with the points:
The angle of the bisector is
alpha
.But when I apply a scaling of 2 only on the X axis, the angle of the bisector changes (
beta
<alpha
), hence the miter vector too. To calculate this new angle we must apply the scaling on the points to find the new vectors that formA
,B
andC
.With these vectors we were able to correctly calculate the miter vector.
Skew effect: just apply distortion after projection calculation.
2.2 Bevel
The projections are both orthogonal to the vertex:
Scaling effect: only when the
stroke
isuniform
that scaling affects the calculation of projections. It changes the position of the points (see more details here), so we must take it into account to have the new arrangement of the points.Skew effect: just apply distortion after projection calculation.
2.3 Round
This case has calculation of projections differently if there is or not the presence of skew.
2.3.1 Round without skew
The projections are the two vectors parallel to the X and Y axes.
Scaling effect: does not directly affect projections (only stroke size).
2.3.2 Round with skew
Projections are the points furthest from the vertex in relation to the direction of the X and Y axis respectively. See the example below.
Consider this case:
There is a point$(X_i, Y_i)$ that will be the furthest Y (max Y) distance from the origin after applying
skewY
. This is our point of interest.We have:
[eq.1]$Y_{afterSkewY} = Y_i + X_i * Tan(skewY)$
And we know that$X_i$ is a function of $Y_i$ :
[eq.2]
Substituting eq.2 in eq.1 we have:
So we have an equation in terms of just Y:
The plus/minus sign represent both sides of the circle after skewY:
The furthest$f(Y_i)$ :
Y after skewY
(max Y) distance from the origin are the maximum/minimun ofComputing$f'(Y_i)=0$ for both cases, we find:
Substituting$Y_i$ into the circle equation, we find $X_i$ :
That's our solution:
We found the furthest point on the Y axis, now we need to calculate the furthest point on the X axis:
In this case we have to take into account that if there is$(Xi, Yi)$ after applying
skewY
it will affectskewX
. The canvas first appliesskewY
and then appliesskewX
, so the pointskewX
andskewY
will look like this:skewY
:skewX
:That is,
skewX
is dependent on the result ofskewY
.So the equations we have to solve are:
[eq.1]$X_{afterSkewX} = X_i + Y_i * Tan(skewX)$
[eq.2]$Y_i = \sqrt{r^2 - X_i^2} \mp X_i * Tan(skewY)$
Substituting eq.2 into eq.1:
Computing$f'(X_i)=0$ for both cases, we find:
Scaling effect: in the case of stroke uniform, we must remove the effect of the
scale
in the calculation of the farthests points,. Because of this, the equations are slightly modified.Calculating the furthest point on the Y axis:
[eq.1]$Y_{afterSkewY} = Y_i * (1/scaleY) + X_i * (1/scaleX) * Tan(skewY)$
[eq.2]$X_i = \pm \sqrt{r^2 - Y_i^2}$
Substituting eq.2 into eq.1:
Computing$f'(Y_i)=0$ for both cases, we find:
Calculating the furthest point on the X axis:
[eq.1]$X_{afterSkewX} = X_i / scaleX + Y_i * Tan(skewX)$
[eq.2]$Y_i = \sqrt{r^2 - X_i^2} / scaleY \mp X_i * Tan(skewY) / scaleX$
Substituting eq.2 into eq.1:
Computing$f'(X_i)=0$ for both cases, we find:
TO DO
skewX
andskewY
are applied at the same time when stroke line join isround
(still buggy)strokeLinecap
equalsround
andsquare
_set
function whenskew
is applied, I need to invoke dimensioning for all line type joinsround
projection?