Brownian behavior add limit by range

This commit is contained in:
MrKBear 2022-05-09 21:24:28 +08:00
parent 4ade5d4bc3
commit 904de02140

View File

@ -7,7 +7,9 @@ type IBrownianBehaviorParameter = {
maxFrequency: "number",
minFrequency: "number",
maxStrength: "number",
minStrength: "number"
minStrength: "number",
dirLimit: "boolean",
angle: "number"
}
type IBrownianBehaviorEvent = {}
@ -28,9 +30,88 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
maxFrequency: { type: "number", name: "$Max.Frequency", defaultValue: 5, numberStep: .1, numberMin: 0 },
minFrequency: { type: "number", name: "$Min.Frequency", defaultValue: 0, numberStep: .1, numberMin: 0 },
maxStrength: { type: "number", name: "$Max.Strength", defaultValue: 10, numberStep: .01, numberMin: 0 },
minStrength: { type: "number", name: "$Min.Strength", defaultValue: 0, numberStep: .01, numberMin: 0 }
minStrength: { type: "number", name: "$Min.Strength", defaultValue: 0, numberStep: .01, numberMin: 0 },
dirLimit: { type: "boolean", name: "$Direction.Limit", defaultValue: false },
angle: {
type: "number", name: "$Angle", defaultValue: 180, numberStep: 5,
numberMin: 0, numberMax: 360, condition: { key: "dirLimit", value: true }
}
};
private randomFocus360(): number[] {
let randomVec = [
Math.random() * 2 - 1,
Math.random() * 2 - 1,
Math.random() * 2 - 1
];
let randomVecLen = (randomVec[0] ** 2 + randomVec[1] ** 2 + randomVec[2] ** 2) ** 0.5;
return [randomVec[0] / randomVecLen, randomVec[1] / randomVecLen, randomVec[2] / randomVecLen];
}
private rotateWithVec(vec: number[], r: number[], ang: number) {
const cos = Math.cos(ang); const sin = Math.sin(ang);
const a1 = r[0] ?? 0; const a2 = r[1] ?? 0; const a3 = r[2] ?? 0;
return [
(cos + (1 - cos) * a1 * a1) * (vec[0] ?? 0) +
((1 - cos) * a1 * a2 - sin * a3) * (vec[1] ?? 0) +
((1 - cos) * a1 * a3 + sin * a2) * (vec[2] ?? 0),
((1 - cos) * a1 * a2 + sin * a3) * (vec[0] ?? 0) +
(cos + (1 - cos) * a2 * a2) * (vec[1] ?? 0) +
((1 - cos) * a2 * a3 - sin * a1) * (vec[2] ?? 0),
((1 - cos) * a1 * a3 - sin * a2) * (vec[0] ?? 0) +
((1 - cos) * a2 * a3 + sin * a1) * (vec[1] ?? 0) +
(cos + (1 - cos) * a3 * a3) * (vec[2] ?? 0)
]
}
private angle2Vector(v1: number[], v2: number[]): number {
return Math.acos(
(v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) /
(
(v1[0] ** 2 + v1[1] ** 2 + v1[2] ** 2) ** 0.5 *
(v2[0] ** 2 + v2[1] ** 2 + v2[2] ** 2) ** 0.5
)
) * 180 / Math.PI;
}
private randomFocusRange(dir: number[], angle: number): number[] {
// 计算 X-Z 投影
let pxz = [dir[0] ?? 0, 0, dir[2] ?? 0];
// 通过叉乘计算垂直向量
let dxz: number[];
// 如果投影向量没有长度,使用单位向量
if (pxz[0] ** 2 + pxz[2] ** 2 === 0) {
dxz = [0, 0, 1];
}
// 通过叉乘计算垂直轴线
else {
dxz = [
dir[1] * pxz[2] - pxz[1] * dir[2],
dir[2] * pxz[0] - pxz[2] * dir[0],
dir[0] * pxz[1] - pxz[0] * dir[1]
];
let lenDxz = (dxz[0] ** 2 + dxz[1] ** 2 + dxz[2] ** 2) ** 0.5;
dxz = [dxz[0] / lenDxz, dxz[1] / lenDxz, dxz[2] / lenDxz];
}
// 航偏角 360 随机旋转
let randomH = this.rotateWithVec(dxz, dir, Math.random() * Math.PI * 2);
// 俯仰角 180 * R 随机旋转
let randomP = this.rotateWithVec(dir, randomH, (Math.random() - 0.5) * 2 * angle * Math.PI / 180);
return randomP;
}
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
const {maxFrequency, minFrequency, maxStrength, minStrength} = this.parameter;
@ -41,11 +122,43 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
currentTime += t;
if (currentTime > nextTime) {
let randomDir: number[];
// 开启角度限制
if (this.parameter.dirLimit) {
// 计算当前速度大小
const vLen = individual.vectorLength(individual.velocity);
// 随机旋转算法
if (vLen !== 0) {
randomDir = this.randomFocusRange(
[
individual.velocity[0] / vLen,
individual.velocity[1] / vLen,
individual.velocity[2] / vLen
],
this.parameter.angle / 2
);
}
else {
randomDir = this.randomFocus360()
}
}
// 随机生成算法
else {
randomDir = this.randomFocus360()
}
individual.applyForce(
minStrength + (Math.random() * 2 - 1) * (maxStrength - minStrength),
minStrength + (Math.random() * 2 - 1) * (maxStrength - minStrength),
minStrength + (Math.random() * 2 - 1) * (maxStrength - minStrength)
minStrength + randomDir[0] * (maxStrength - minStrength),
minStrength + randomDir[1] * (maxStrength - minStrength),
minStrength + randomDir[2] * (maxStrength - minStrength)
);
nextTime = minFrequency + Math.random() * (maxFrequency - minFrequency);
currentTime = 0;
}
@ -78,7 +191,15 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
"$Min.Strength": {
"ZH_CN": "最小强度",
"EN_US": "Minimum strength"
}
},
"$Direction.Limit": {
"ZH_CN": "开启角度限制",
"EN_US": "Enable limit angle"
},
"$Angle": {
"ZH_CN": "限制立体角 (deg)",
"EN_US": "Restricted solid angle (deg)"
}
};
}