diff --git a/source/Model/Group.ts b/source/Model/Group.ts index eda8573..bc68c48 100644 --- a/source/Model/Group.ts +++ b/source/Model/Group.ts @@ -6,18 +6,75 @@ import { Individual } from "./Individual"; class Group { /** - * 所有成员 + * 所有个体 */ - public individuals: Individual[] = []; + public individuals: Set = new Set(); /** - * 添加一个成员 + * 创建个体 + * @param count 创建数量 */ - public add(count: number = 1) { + public new(count: number = 1): this { for (let i = 0; i < count; i++) { - this.individuals.push(new Individual(this)); + this.individuals.add(new Individual(this)); } + return this; } + + /** + * 添加个体 + * @param individual 个体 + */ + public add(individual: Individual[] | Individual): this { + if (Array.isArray(individual)) { + for (let i = 0; i < individual.length; i++) { + this.individuals.add(individual[i]); + } + } else { + this.individuals.add(individual); + } + return this; + } + + /** + * 移除成员 + * @param individual 需要移除的个体 + */ + public remove(individual: Individual[] | Individual): this { + if (Array.isArray(individual)) { + for (let i = 0; i < individual.length; i++) { + this.individuals.delete(individual[i]); + } + } else { + this.individuals.delete(individual); + } + return this; + } + + /** + * 通过距离获取个体 + * 此函数将排除传入对象 + * @param position 观测位置 + * @param distance 距离 + * @param excludeSelf 是否排除自身 + */ + public getIndividualsByDistance( + position: Individual | number[], distance: number, excludeSelf?: boolean + ): Individual[] { + const result: Individual[] = []; + this.individuals.forEach(((individual) => { + + // 排除自身 + if (individual === position && excludeSelf) { + return; + } + + if (individual.distanceTo(position) < distance) { + result.push(individual); + } + })); + return result; + } } export default Group; diff --git a/source/Model/Individual.ts b/source/Model/Individual.ts index 607677a..1803fcb 100644 --- a/source/Model/Individual.ts +++ b/source/Model/Individual.ts @@ -5,6 +5,47 @@ import type { Group } from "./Group"; */ class Individual { + /** + * 计算向量长度 + * @param x x 坐标 | 向量 + * @param y y 坐标 + * @param z z 坐标 + */ + public static vectorLength(x: number[]): number; + public static vectorLength(x: number, y: number, z: number): number; + public static vectorLength(x: number | number[], y?: number, z?: number): number { + if (Array.isArray(x)) { + return ((x[0] ?? 0)**2 + (x[1] ?? 0)**2 + (x[2] ?? 0)**2)**.5; + } else { + return ((x ?? 0)**2 + (y ?? 0)**2 + (z ?? 0)**2)**.5; + } + } + + /** + * 向量归一化 + * @param x x 坐标 | 向量 + * @param y y 坐标 + * @param z z 坐标 + */ + public static vectorNormalize(x: number[]): [number, number, number]; + public static vectorNormalize(x: number, y: number, z: number): [number, number, number]; + public static vectorNormalize(x: number | number[], y?: number, z?: number): [number, number, number] { + let length = Individual.vectorLength(x as number, y as number, z as number); + if (Array.isArray(x)) { + return [ + (x[0] ?? 0) / length, + (x[1] ?? 0) / length, + (x[2] ?? 0) / length + ]; + } else { + return [ + (x ?? 0) / length, + (y ?? 0) / length, + (z ?? 0) / length + ]; + } + } + /** * 坐标 */ @@ -21,6 +62,52 @@ class Individual { public constructor(group: Group) { this.group = group; } + + /** + * 死亡 + */ + public die(): this { + this.group.remove(this); + return this; + } + + /** + * 转移到新群体 + * @param newGroup 新群体 + */ + public transfer(newGroup: Group): this { + this.group.remove(this); + newGroup.add(this); + this.group = newGroup; + return this; + } + + /** + * 计算从此个体到目标位置的向量 + */ + public vectorTo(position: Individual | number[]): [number, number, number] { + if (position instanceof Individual) { + return [ + position.position[0] - this.position[0], + position.position[1] - this.position[1], + position.position[2] - this.position[2] + ]; + } else { + return [ + (position[0] ?? 0) - this.position[0], + (position[1] ?? 0) - this.position[1], + (position[2] ?? 0) - this.position[2] + ]; + } + } + + /** + * 计算与目标位置距离 + * @param position 目标位置 + */ + public distanceTo(position: Individual | number[]): number { + return Individual.vectorLength(this.vectorTo(position)); + } } export default Individual;