gpt4 book ai didi

javascript - 约束矩形的受力导向布局

转载 作者:行者123 更新时间:2023-12-03 05:24:57 25 4
gpt4 key购买 nike

我需要均匀分布一堆与轴对齐的滑动矩形,这些矩形受最大宽度/高度和一些水平/垂直坐标的约束,具体取决于滑动形状本身的位置。矩形限制在一个方向上,可以沿另一个轴滑动,也可以不重叠且也不能跨步。

这个问题基于:How to implement a constraint solver for 2-D geometry?和Spektre关于力驱动约束求解器的公认建议。

整个结构像通常的图形一样构建,其中的矩形代表节点。

现在,我需要检查每个矩形的大小以进行正确的力计算并避免重叠,但是我很难理解如何将力场应用于二维形状以及两个矩形之间的距离应计算。也许是顶点还是边?

相关代码在下面的功能Solver.solve()中,其中s.Z分别代表水平形状的高度和垂直形状的宽度:

for(var i=0, l=sliders.length; i<l; i++) {
var si = sliders[i];
for(var j=i+1, k=sliders.length; j<k; j++) {
var sj = sliders[j];
if(si._horizontal == sj._horizontal) {
// longer side interaction
if(si._horizontal == 1) {
a0 = si.X + si.a; a1 = sj.X + sj.a;
b0 = si.X + si.b; b1 = sj.X + sj.b;
x0 = si.Y; x1 = sj.Y;
} else {
a0 = si.Y + si.a; a1 = sj.Y + sj.a;
b0 = si.Y + si.b; b1 = sj.Y + sj.b;
x0 = si.X; x1 = sj.X;
}
if(((a0 <= b1) && (b0 >= a1)) || ((a1 <= b0) && (b1 >= a0))) {
x0 = x1 - x0;
if((si.ia >= 0) && (x0 < 0.0) && ((fabs(si.x0) < si.Z) || (fabs(si.x0) > fabs(x0)))) si.x0 = -x0;
if((si.ia >= 0) && (x0 > 0.0) && ((fabs(si.x1) < si.Z) || (fabs(si.x1) > fabs(x0)))) si.x1 = -x0;
if((sj.ia >= 0) && (x0 < 0.0) && ((fabs(sj.x0) < sj.Z) || (fabs(sj.x0) > fabs(x0)))) sj.x0 = +x0;
if((sj.ia >= 0) && (x0 > 0.0) && ((fabs(sj.x1) < sj.Z) || (fabs(sj.x1) > fabs(x0)))) sj.x1 = +x0;
}
// shorter side interaction
if(si._horizontal == 1) {
a0 = si.Y - si.Z; a1 = sj.Y + sj.Z;
b0 = si.Y + si.Z; b1 = sj.Y + sj.Z;
x0 = si.X; x1 = sj.X;
} else {
a0 = si.X - si.Z; a1 = sj.X + sj.Z;
b0 = si.X + si.Z; b1 = sj.X + sj.Z;
x0 = si.Y; x1 = sj.Y;
}
if(((a0 <= b1) && (b0 >= a1)) || ((a1 <= b0) && (b1 >= a0))) {
if(x0 < x1) {
x0 += si.b; x1 += sj.a;
} else{
x0 += si.a; x1 += sj.b;
}
x0 = x1 - x0;
if(si.ia >= 0) {
var sa = this.sliders[si.ia];
if((sa.ia >= 0) && (x0 < 0.0) && ((fabs(sa.x0) < sa.Z) || (fabs(sa.x0) > fabs(x0)))) sa.x0 = -x0;
if((sa.ia >= 0) && (x0 > 0.0) && ((fabs(sa.x1) < sa.Z) || (fabs(sa.x1) > fabs(x0)))) sa.x1 = -x0;
}
if(sj.ia >= 0) {
var sa = sliders[sj.ia];
if((sa.ia >= 0) && (x0 < 0.0) && ((fabs(sa.x0) < sa.Z) || (fabs(sa.x0) > fabs(x0)))) sa.x0 = +x0;
if((sa.ia >= 0) && (x0 > 0.0) && ((fabs(sa.x1) < sa.Z) || (fabs(sa.x1) > fabs(x0)))) sa.x1 = +x0;
}
}
}
}
}
// set x0 as 1D vector to closest perpendicular neighbour before and x1 after
for(var i=0, l=sliders.length; i<l; i++) {
var si = sliders[i];
for(var j=i+1, k=sliders.length; j<k; j++) {
var sj = sliders[j];
if(si._horizontal != sj._horizontal) {
// skip ignored sliders for this
var ignore = false;
for(var n=0, m=si.ic.length; n<m; n++) {
if(si.ic[n] == j) {
ignore = true;
break;
}
}
if(ignore === true) continue;
if(si._horizontal == 1) {
a0 = si.X + si.a; a1 = sj.X - sj.Z;
b0 = si.X + si.b; b1 = sj.X + sj.Z;
x0 = si.Y;
} else {
a0 = si.Y + si.a; a1 = sj.Y - sj.Z;
b0 = si.Y + si.b; b1 = sj.Y + sj.Z;
x0 = si.X;
}
if(((a0 <= b1) && (b0 >= a1)) || ((a1 <= b0) && (b1 >= a0))){
if(si._horizontal == 1) {
a1 = sj.Y + sj.a;
b1 = sj.Y + sj.b;
} else {
a1 = sj.X + sj.a;
b1 = sj.X + sj.b;
}
a1 -= x0; b1 -= x0;
if(fabs(a1) < fabs(b1)) x0 = -a1; else x0 = -b1;
if((si.ia >= 0) && (x0 < 0.0) && ((fabs(si.x0) < si.Z) || (fabs(si.x0) > fabs(x0)))) si.x0 = +x0;
if((si.ia >= 0) && (x0 > 0.0) && ((fabs(si.x1) < si.Z) || (fabs(si.x1) > fabs(x0)))) si.x1 = +x0;
if(sj.ia < 0) continue;
var sa = sliders[sj.ia];
if((sa.ia >= 0) && (x0 < 0.0) && ((fabs(sa.x0) < sa.Z) || (fabs(sa.x0) > fabs(x0)))) sa.x0 = -x0;
if((sa.ia >= 0) && (x0 > 0.0) && ((fabs(sa.x1) < sa.Z) || (fabs(sa.x1) > fabs(x0)))) sa.x1 = -x0;
}
}
}
}


如何计算矩形的力,以从力场中获得均匀的分布,即矩形之间的距离将是最大可能的?考虑到矩形确实很热,并且就其自定义x / y约束而言,必须最多隔开一定距离。

任何帮助将不胜感激。

编辑:

样本: https://plnkr.co/edit/3xGmAKsly2qCGMp3fPrJ?p=preview

最佳答案

如果我正确地回答了这个问题,OP会要求您提供一组驱动滑块的规则,以便最终的模拟状态可以得出有效的解决方案。

所以这是我的方法,它实际上是从OP中的链接答案中概括出我的求解器代码,但由于我已经达到30 KB的限制,因此它不适合在那里,我觉得需要更多的解释,然后才是注释的代码,所以这里是:


势力

为了确保尽可能相等的间距,您需要稍微更改规则(除了实际物理学之外),因此您只考虑驱动力的最近障碍,而不是现实世界中的所有障碍。而且,该力仅受距离影响,而不像大多数物理力一样受接触/重叠区域的权重(包括静电)。

因此,在i-th滑块(黄色)的迭代过程中,找到所有四个方向(红色)上最接近障碍物的距离:

closest neighbors

并计算必须随距离缩放的驱动力。线性与否无关紧要,但对于从左/右或上/下均匀间隔的障碍物,合力应为零。缩放比例主要改变动力学行为。最终结果仅在约束限制运动以实现均匀间距的状态下受其影响。因此,您可以使用以下任何一种示例:

F = c * (d1-d0)
F = c * (d1^2 - d0^2)
F = c * (1/d1^2 - 1/d0^2)


其中, c是某个量值系数, d0,d1是同一轴上的两个最接近的障碍物距离。

现在,您将获得2个力(每个轴一个)


Fx-水平轴力
Fy-垂直轴力


简而言之。但是约束存在问题。例如,选定的滑块(黄色)是垂直的。这意味着它只能在x轴上移动。因此,您施加了 Fx驱动力。 Fy力应驱动其水平的且可以在y轴上移动的父滑块(蓝色)(如果未固定为粗略值)。

这就引入了一个问题,因为滑块也可以有自己的 Fy,因此您应该始终仅从两侧选择最强的力。这意味着您需要记住两侧的两个距离/力,并始终选择最小距离或|最高|。双方的力量。

这就是 x0,x1变量的来源,它们在可移动轴上保持到最近障碍物(包括子对象)的最小距离,并且仅在将所有滑块的计算值转换为 Fx,Fy驱动力之后。

为了检测邻居和碰撞,我识别出三种交互类型


在水平和垂直滑块之间的垂直(图像上的底部交互)
宽平行线,位于两个相同类型的滑块之间,并与其较长的侧面接触(图像上的左右相互作用)
较短的平行线,位于两个相同类型的滑块之间,并与较短的侧面接触(图像上的顶部相互作用)

极限和系数

我还介绍了一些速度限制和系数(不仅仅是为了避免振荡而进行倾卸)。速度和加速度限制会影响求解时间和稳定性。还有另一个原因要使用它们(我不这样做),这是为了保留滑块的顺序,以使它们不会彼此跳过。这可以通过简单地限制最高速度来完成,因此每次迭代中任何滑块的移动量都不会超过滑块厚度的一半。因此,在发生碰撞的情况下,碰撞例程将启动。

我跳过了我的代码,因为设置了我的配置,所以滑块的顺序与其索引号一致。因此,如果我检测到某个滑块在索引较高的位置时位于某个经过测试的滑块的左侧,则表示它已跳过,而我将通过恢复最后一个位置来处理它……这是便宜的技巧,在包含链中有许多子滑块,而不仅仅是一个。
反馈

由于种种限制,有时您的驱动力可能会丢失(移动到固定的或卡住的父滑块),以防这种行为破坏了结果,您应该以相反的方式将相反的力传播到引起它的邻居。对于简单的结构,由于它已经包含了驱动力,因此没有必要,但是对于更复杂的儿童链,则可能构成威胁(但这是我的野蛮想法,对此我可能是错误的)。
从本质上讲,这是通过整合来自所有邻居物体而不只是最接近物体的力来提供的,但是我认为这将导致间距不相等。

关于javascript - 约束矩形的受力导向布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41190656/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com