fix(editor): connector target position NaN (#11606)

Close [BS-3086](https://linear.app/affine-design/issue/BS-3086/frame里套frame,连一下connector,拖两下,白板损坏)

### What Changes
- Fixed `bound.toRelative` may be return `NaN` when `bound.w === 0 || bound.h ===0`
- Remove type assertions from `connector-manager.ts` for more type safety
This commit is contained in:
L-Sun
2025-04-10 12:33:24 +00:00
parent d5aebc1421
commit 588659ef67
5 changed files with 109 additions and 59 deletions

View File

@@ -11,6 +11,24 @@ export function randomSeed(): number {
return Math.floor(Math.random() * 2 ** 31);
}
/**
* Calculates the intersection point of two line segments.
*
* @param sp - Start point of the first line segment [x, y]
* @param ep - End point of the first line segment [x, y]
* @param sp2 - Start point of the second line segment [x, y]
* @param ep2 - End point of the second line segment [x, y]
* @param infinite - If true, treats the lines as infinite lines rather than line segments
* @returns The intersection point [x, y] if the lines intersect, null if they are parallel or coincident
*
* @example
* const intersection = lineIntersects([0, 0], [2, 2], [0, 2], [2, 0]);
* // Returns [1, 1] - the intersection point of the two line segments
*
* @example
* const parallel = lineIntersects([0, 0], [2, 2], [0, 1], [2, 3], true);
* // Returns null - the lines are parallel
*/
export function lineIntersects(
sp: IVec,
ep: IVec,
@@ -45,10 +63,23 @@ export function lineIntersects(
return null;
}
/**
* Finds the nearest point on a polygon to a given point.
*
* @param points - Array of points defining the polygon vertices [x, y][]
* @param point - The point to find the nearest point to [x, y]
* @returns The nearest point on the polygon to the given point
* @throws Error if points array is empty or has less than 2 points
*/
export function polygonNearestPoint(points: IVec[], point: IVec) {
const len = points.length;
let rst: IVec;
let dis = Infinity;
if (len < 2) {
throw new Error('Polygon must have at least 2 points');
}
let rst: IVec = points[0]; // Initialize with first point as fallback
let dis = Vec.dist(points[0], point);
for (let i = 0; i < len; i++) {
const p = points[i];
const p2 = points[(i + 1) % len];
@@ -59,7 +90,7 @@ export function polygonNearestPoint(points: IVec[], point: IVec) {
rst = temp;
}
}
return rst!;
return rst;
}
export function polygonPointDistance(points: IVec[], point: IVec) {

View File

@@ -341,8 +341,15 @@ export class Bound implements IBound {
return serializeXYWH(this.x, this.y, this.w, this.h);
}
/**
* Convert a point to relative coordinates.
* @param point - The point to convert.
* @returns The normalized relative coordinates of the point.
*/
toRelative([x, y]: IVec): IVec {
return [(x - this.x) / this.w, (y - this.y) / this.h];
const normalizedX = this.w === 0 ? 0 : (x - this.x) / this.w;
const normalizedY = this.h === 0 ? 0 : (y - this.y) / this.h;
return [normalizedX, normalizedY];
}
toXYWH(): XYWH {

View File

@@ -565,6 +565,8 @@ export class Vec {
* @param n
* @param min
*/
static clampV(A: IVec, min: number, max?: number): IVec;
static clampV(A: number[], min: number): number[];
// eslint-disable-next-line @typescript-eslint/unified-signatures

View File

@@ -214,7 +214,12 @@ export class Viewport {
* This property is used to calculate the scale of the editor.
*/
get viewScale() {
if (!this._shell || this._cachedOffsetWidth === null) return 1;
if (
!this._shell ||
this._cachedOffsetWidth === null ||
this._cachedOffsetWidth === 0
)
return 1;
return this.boundingClientRect.width / this._cachedOffsetWidth;
}