Skip to content

Commit

Permalink
Merge branch 'master' into console.log
Browse files Browse the repository at this point in the history
  • Loading branch information
asturur authored Sep 24, 2023
2 parents 8b291bd + 3963089 commit 48acca2
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 135 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## [next]

- chore(): cleanup logs and error messages [#9369](https://github.com/fabricjs/fabric.js/pull/9369)
- feature(Object) BREAKING: Remove lines parameter from object.containsPoint [#9375](https://github.com/fabricjs/fabric.js/pull/9375)
- patch(Control): move hit detection to shouldActivate [#9374](https://github.com/fabricjs/fabric.js/pull/9374)
- fix(StaticCanvas): disposing animations [#9361](https://github.com/fabricjs/fabric.js/pull/9361)
- fix(IText): cursor width under group [#9341](https://github.com/fabricjs/fabric.js/pull/9341)
- TS(Canvas): constructor optional el [#9348](https://github.com/fabricjs/fabric.js/pull/9348)
Expand Down
5 changes: 2 additions & 3 deletions src/Collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,8 @@ export function createCollectionMixin<TBase extends Constructor>(Base: TBase) {
object.visible &&
((includeIntersecting && object.intersectsWithRect(tl, br, true)) ||
object.isContainedWithinRect(tl, br, true) ||
(includeIntersecting &&
object.containsPoint(tl, undefined, true)) ||
(includeIntersecting && object.containsPoint(br, undefined, true)))
(includeIntersecting && object.containsPoint(tl, true)) ||
(includeIntersecting && object.containsPoint(br, true)))
) {
objects.push(object);
}
Expand Down
13 changes: 10 additions & 3 deletions src/controls/Control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type {
} from '../EventTypeDefs';
import { Point } from '../Point';
import type { InteractiveFabricObject } from '../shapes/Object/InteractiveObject';
import type { TDegree, TMat2D } from '../typedefs';
import type { TCornerPoint, TDegree, TMat2D } from '../typedefs';
import { cornerPointContainsPoint } from '../util/intersection/findCrossPoint';
import { cos } from '../util/misc/cos';
import { degreesToRadians } from '../util/misc/radiansDegreesConversion';
import { sin } from '../util/misc/sin';
Expand Down Expand Up @@ -169,11 +170,17 @@ export class Control {
*/
declare mouseUpHandler?: ControlActionHandler;

shouldActivate(controlKey: string, fabricObject: InteractiveFabricObject) {
shouldActivate(
controlKey: string,
fabricObject: InteractiveFabricObject,
pointer: Point,
cornerPoint: TCornerPoint
) {
// TODO: locking logic can be handled here instead of in the control handler logic
return (
fabricObject.canvas?.getActiveObject() === fabricObject &&
fabricObject.isControlVisible(controlKey)
fabricObject.isControlVisible(controlKey) &&
cornerPointContainsPoint(pointer, cornerPoint)
);
}

Expand Down
19 changes: 10 additions & 9 deletions src/shapes/Object/InteractiveObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,18 +198,19 @@ export class InteractiveFabricObject<
const cornerEntries = Object.entries(this.oCoords);
for (let i = cornerEntries.length - 1; i >= 0; i--) {
const [key, corner] = cornerEntries[i];
if (this.controls[key].shouldActivate(key, this)) {
const lines = this._getImageLines(
if (
this.controls[key].shouldActivate(
key,
this,
pointer,
forTouch ? corner.touchCorner : corner.corner
);
const xPoints = this._findCrossPoints(pointer, lines);
if (xPoints !== 0 && xPoints % 2 === 1) {
this.__corner = key;
return key;
}
)
) {
// this.canvas.contextTop.fillRect(pointer.x - 1, pointer.y - 1, 2, 2);
return (this.__corner = key);
}

// // debugging
// // debugging needs rework
//
// this.canvas.contextTop.fillRect(lines.bottomline.d.x, lines.bottomline.d.y, 2, 2);
// this.canvas.contextTop.fillRect(lines.bottomline.o.x, lines.bottomline.o.y, 2, 2);
Expand Down
132 changes: 12 additions & 120 deletions src/shapes/Object/ObjectGeometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,7 @@ import type { StaticCanvas } from '../../canvas/StaticCanvas';
import { ObjectOrigin } from './ObjectOrigin';
import type { ObjectEvents } from '../../EventTypeDefs';
import type { ControlProps } from './types/ControlProps';

type TLineDescriptor = {
o: Point;
d: Point;
};

type TBBoxLines = {
topline: TLineDescriptor;
leftline: TLineDescriptor;
bottomline: TLineDescriptor;
rightline: TLineDescriptor;
};
import { cornerPointContainsPoint } from '../../util/intersection/findCrossPoint';

type TMatrixCache = {
key: string;
Expand Down Expand Up @@ -296,19 +285,19 @@ export class ObjectGeometry<EventSpec extends ObjectEvents = ObjectEvents>
* Checks if object is fully contained within area of another object
* @param {Object} other Object to test
* @param {Boolean} [absolute] use coordinates without viewportTransform
* @param {Boolean} [calculate] use coordinates of current position instead of store ones
* @param {Boolean} [calculate] use coordinates of current position instead of stored ones
* @return {Boolean} true if object is fully contained within area of another object
*/
isContainedWithinObject(
other: ObjectGeometry,
absolute = false,
calculate = false
): boolean {
const points = this.getCoords(absolute, calculate),
otherCoords = absolute ? other.aCoords : other.lineCoords,
lines = other._getImageLines(otherCoords);
const points = this.getCoords(absolute, calculate);
for (let i = 0; i < 4; i++) {
if (!other.containsPoint(points[i], lines)) {
// bug/confusing: this containsPoint should receive 'calculate' as well.
// will come later because it needs to come with tests
if (!other.containsPoint(points[i], absolute)) {
return false;
}
}
Expand Down Expand Up @@ -349,22 +338,15 @@ export class ObjectGeometry<EventSpec extends ObjectEvents = ObjectEvents>
/**
* Checks if point is inside the object
* @param {Point} point Point to check against
* @param {Object} [lines] object returned from @method _getImageLines
* @param {Boolean} [absolute] use coordinates without viewportTransform
* @param {Boolean} [calculate] use coordinates of current position instead of stored ones
* @return {Boolean} true if point is inside the object
*/
containsPoint(
point: Point,
lines?: TBBoxLines,
absolute = false,
calculate = false
): boolean {
const coords = this._getCoords(absolute, calculate),
imageLines = lines || this._getImageLines(coords),
xPoints = this._findCrossPoints(point, imageLines);
// if xPoints is odd then point is inside the object
return xPoints !== 0 && xPoints % 2 === 1;
containsPoint(point: Point, absolute = false, calculate = false): boolean {
return cornerPointContainsPoint(
point,
this._getCoords(absolute, calculate)
);
}

/**
Expand Down Expand Up @@ -414,7 +396,7 @@ export class ObjectGeometry<EventSpec extends ObjectEvents = ObjectEvents>
): boolean {
// worst case scenario the object is so big that contains the screen
const centerPoint = pointTL.midPointFrom(pointBR);
return this.containsPoint(centerPoint, undefined, true, calculate);
return this.containsPoint(centerPoint, true, calculate);
}

/**
Expand All @@ -440,96 +422,6 @@ export class ObjectGeometry<EventSpec extends ObjectEvents = ObjectEvents>
);
}

/**
* Method that returns an object with the object edges in it, given the coordinates of the corners
* @private
* @param {Object} lineCoords or aCoords Coordinates of the object corners
*/
_getImageLines({ tl, tr, bl, br }: TCornerPoint): TBBoxLines {
const lines = {
topline: {
o: tl,
d: tr,
},
rightline: {
o: tr,
d: br,
},
bottomline: {
o: br,
d: bl,
},
leftline: {
o: bl,
d: tl,
},
};

// // debugging
// if (this.canvas.contextTop) {
// this.canvas.contextTop.fillRect(lines.bottomline.d.x, lines.bottomline.d.y, 2, 2);
// this.canvas.contextTop.fillRect(lines.bottomline.o.x, lines.bottomline.o.y, 2, 2);
//
// this.canvas.contextTop.fillRect(lines.leftline.d.x, lines.leftline.d.y, 2, 2);
// this.canvas.contextTop.fillRect(lines.leftline.o.x, lines.leftline.o.y, 2, 2);
//
// this.canvas.contextTop.fillRect(lines.topline.d.x, lines.topline.d.y, 2, 2);
// this.canvas.contextTop.fillRect(lines.topline.o.x, lines.topline.o.y, 2, 2);
//
// this.canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2);
// this.canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2);
// }

return lines;
}

/**
* Helper method to determine how many cross points are between the 4 object edges
* and the horizontal line determined by a point on canvas
* @private
* @param {Point} point Point to check
* @param {Object} lines Coordinates of the object being evaluated
* @return {number} number of crossPoint
*/
_findCrossPoints(point: Point, lines: TBBoxLines): number {
let xcount = 0;

for (const lineKey in lines) {
let xi;
const iLine = lines[lineKey as keyof TBBoxLines];
// optimization 1: line below point. no cross
if (iLine.o.y < point.y && iLine.d.y < point.y) {
continue;
}
// optimization 2: line above point. no cross
if (iLine.o.y >= point.y && iLine.d.y >= point.y) {
continue;
}
// optimization 3: vertical line case
if (iLine.o.x === iLine.d.x && iLine.o.x >= point.x) {
xi = iLine.o.x;
}
// calculate the intersection point
else {
const b1 = 0;
const b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
const a1 = point.y - b1 * point.x;
const a2 = iLine.o.y - b2 * iLine.o.x;

xi = -(a1 - a2) / (b1 - b2);
}
// don't count xi < point.x cases
if (xi >= point.x) {
xcount += 1;
}
// optimization 4: specific for square images
if (xcount === 2) {
break;
}
}
return xcount;
}

/**
* Returns coordinates of object's bounding rectangle (left, top, width, height)
* the box is intended as aligned to axis of canvas.
Expand Down
114 changes: 114 additions & 0 deletions src/util/intersection/findCrossPoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import type { XY } from '../../Point';
import type { TCornerPoint } from '../../typedefs';

type TLineDescriptor = {
o: XY;
d: XY;
};

export type TBBoxLines = {
topline: TLineDescriptor;
leftline: TLineDescriptor;
bottomline: TLineDescriptor;
rightline: TLineDescriptor;
};

/**
* Helper method to determine how many cross points are between the 4 object edges
* and the horizontal line determined by a point on canvas
* @private
* @param {Point} point Point to check
* @param {Object} lines Coordinates of the object being evaluated
* @return {number} number of crossPoint
*/
const findCrossPoints = (point: XY, lines: TBBoxLines): number => {
let xcount = 0;

for (const lineKey in lines) {
let xi;
const iLine = lines[lineKey as keyof TBBoxLines];
// optimization 1: line below point. no cross
if (iLine.o.y < point.y && iLine.d.y < point.y) {
continue;
}
// optimization 2: line above point. no cross
if (iLine.o.y >= point.y && iLine.d.y >= point.y) {
continue;
}
// optimization 3: vertical line case
if (iLine.o.x === iLine.d.x && iLine.o.x >= point.x) {
xi = iLine.o.x;
}
// calculate the intersection point
else {
const b1 = 0;
const b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
const a1 = point.y - b1 * point.x;
const a2 = iLine.o.y - b2 * iLine.o.x;

xi = -(a1 - a2) / (b1 - b2);
}
// don't count xi < point.x cases
if (xi >= point.x) {
xcount += 1;
}
// optimization 4: specific for square images (square or rects?)
// todo remove this optimazion for
if (xcount === 2) {
break;
}
}
return xcount;
};

/**
* Method that returns an object with the object edges in it, given the coordinates of the corners
* @private
* @param {Object} lineCoords or aCoords Coordinates of the object corners
*/
const getImageLines = ({ tl, tr, bl, br }: TCornerPoint): TBBoxLines => {
const lines = {
topline: {
o: tl,
d: tr,
},
rightline: {
o: tr,
d: br,
},
bottomline: {
o: br,
d: bl,
},
leftline: {
o: bl,
d: tl,
},
};

// // debugging
// if (this.canvas.contextTop) {
// this.canvas.contextTop.fillRect(lines.bottomline.d.x, lines.bottomline.d.y, 2, 2);
// this.canvas.contextTop.fillRect(lines.bottomline.o.x, lines.bottomline.o.y, 2, 2);
//
// this.canvas.contextTop.fillRect(lines.leftline.d.x, lines.leftline.d.y, 2, 2);
// this.canvas.contextTop.fillRect(lines.leftline.o.x, lines.leftline.o.y, 2, 2);
//
// this.canvas.contextTop.fillRect(lines.topline.d.x, lines.topline.d.y, 2, 2);
// this.canvas.contextTop.fillRect(lines.topline.o.x, lines.topline.o.y, 2, 2);
//
// this.canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2);
// this.canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2);
// }

return lines;
};

export const cornerPointContainsPoint = (
point: XY,
cornerPoint: TCornerPoint
): boolean => {
const xPoints = findCrossPoints(point, getImageLines(cornerPoint));
// if xPoints is odd then point is inside the object
return xPoints !== 0 && xPoints % 2 === 1;
};

0 comments on commit 48acca2

Please sign in to comment.