javascript - 推广寻找矩形四边最小尺寸解的方法
在一个应用程序中,用户可以点击矩形框的边缘,将其在该方向上调整到“最小有效尺寸”。例如,如果他们点击右边缘,框将调整到最小的有效宽度,同时保持左边缘不动。如果他们点击底部,底边会尽可能向上收缩而不失效。“有效”的标准无关紧要,但计算起来需要一些工作。
现在我已经写了一些代码,使其可以用于盒子的右边缘,使用二分查找来缩小最小有效尺寸。但我需要一个通用的解决方案,不涉及在四个不同的变体中重复我的代码,而我又想不出如何做到这一点。
现在的代码(仅支持右边缘):
interface Zone {
x: number;
y: number;
width: number;
height: number;
}
enum Side {
'top',
'left',
'bottom',
'right',
}
enum ResizeDirection {
NEGATIVE = -1,
POSITIVE = 1,
}
async function findMinValidZone(
zone: Zone,
minWidth: number,
maxWidth: number,
side: Side,
validator: (z: Zone) => Promise<boolean>
): Promise<Zone> {
const area = {...zone};
let d = maxWidth;
let step = maxWidth - minWidth;
let currentDirection = ResizeDirection.NEGATIVE; // positive means widen the area, negative means narrow it
let lastGood: number = d;
do {
d += step * currentDirection;
if (d < minWidth || d > maxWidth) {
break;
}
area.width = d;
const ok = await validator(area);
if (ok) {
currentDirection = ResizeDirection.NEGATIVE;
lastGood = d;
} else {
currentDirection = ResizeDirection.POSITIVE;
}
step = step / 2;
} while (step >= 1);
area.width = lastGood;
return area;
}
答案#1
我能想到的一种方法是将矩形旋转所需的 90 度倍数,以便将矩形转换,使你总是处理右边缘,然后在验证和返回结果时将其旋转回去。实际旋转可以使用矩阵变换来完成,但现在我会把实际的旋转函数留给你来编写。然后你的通用代码看起来会像这样:
function findMinValidZone(
zone: Zone,
minwidth: number,
maxWidth: number,
side: Side,
validator: (z: Zone) => Promise<boolean>
): Promise<Zone> {
let rotationAngle: number;
switch (side) {
case Side.bottom:
rotationAngle = -Math.PI / 2;
break;
case Side.left:
rotationAngle = Math.PI;
break;
case Side.right:
rotationAngle = 0;
break;
case Side.top:
rotationAngle = Math.PI / 2;
}
let d = maxWidth;
let step = maxWidth - minwidth;
// rotate the area so that you are always working on the width dimension
// while keeping other dimensions fixed. Then rotate area back again
// when validating and when returning the results
const area = rotateZone({...zone}, rotationAngle);
let currentDirection = ResizeDirection.NEGATIVE; // positive means widen the area, negative means narrow it
let lastGood: number = d;
do {
d += step * currentDirection;
if (d < minwidth || d > maxWidth) {
break;
}
area.width = d;
const ok = await validator(rotateZone(area, -rotationAngle));
if (ok) {
currentDirection = ResizeDirection.NEGATIVE;
lastGood = d;
} else {
currentDirection = ResizeDirection.POSITIVE;
}
step = step / 2;
} while (step >= 1);
area.width = lastGood;
return rotateZone(area, -rotationAngle);
}
我会考虑 rotateZone 函数本身,但原则上使用像 https://www.npmjs.com/package/transformation-matrix 这样的库应该足够简单
