421 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			421 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { Individual } from "@Model/Individual";
 | |
| import { CtrlObject } from "@Model/CtrlObject";
 | |
| import type { Behavior, IAnyBehavior } from "@Model/Behavior"; 
 | |
| import { Label } from "@Model/Label";
 | |
| import { Range } from "@Model/Range";
 | |
| import { Model, ObjectID } from "@Model/Model";
 | |
| import { getDefaultValue } from "@Model/Parameter";
 | |
| 
 | |
| enum GenMod {
 | |
|     Point = "p",
 | |
|     Range = "R"
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * 群体类型
 | |
|  */
 | |
| class Group extends CtrlObject {
 | |
| 
 | |
| 	/**
 | |
| 	 * 所有个体
 | |
| 	 */
 | |
| 	public individuals: Set<Individual> = new Set();
 | |
| 
 | |
|     /**
 | |
|      * 个体生成方式
 | |
|      */
 | |
|     public genMethod: GenMod = GenMod.Range;
 | |
| 
 | |
|     /**
 | |
|      * 生成位置坐标
 | |
|      */
 | |
|     public genPoint: number[] = [0, 0, 0];
 | |
| 
 | |
|     /**
 | |
|      * 生成范围
 | |
|      */
 | |
|     public genRange?: CtrlObject | Label;
 | |
| 
 | |
|     /**
 | |
|      * 生成个数
 | |
|      */
 | |
|     public genCount: number = 100;
 | |
| 
 | |
|     /**
 | |
|      * 生成错误信息
 | |
|      */
 | |
|     public genErrorMessage?: string;
 | |
| 
 | |
|     /**
 | |
|      * 生成错误信息
 | |
|      */
 | |
|     public genErrorMessageShowCount: number = 0;
 | |
| 
 | |
|     /**
 | |
|      * 删除个数
 | |
|      */
 | |
|     public killCount: number = 100;
 | |
| 
 | |
|     private genInSingleRange(count: number, range: Range) {
 | |
|         for (let i = 0; i < count; i++) {
 | |
|             let individual = new Individual(this);
 | |
|             individual.position[0] = range.position[0] + (Math.random() - .5) * 2 * range.radius[0];
 | |
|             individual.position[1] = range.position[1] + (Math.random() - .5) * 2 * range.radius[1];
 | |
|             individual.position[2] = range.position[2] + (Math.random() - .5) * 2 * range.radius[2];
 | |
|             this.add(individual);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private genWithPointMod(): boolean {
 | |
|         for (let i = 0; i < this.genCount; i++) {
 | |
|             let individual = new Individual(this);
 | |
|             individual.position[0] = this.genPoint[0];
 | |
|             individual.position[1] = this.genPoint[1];
 | |
|             individual.position[2] = this.genPoint[2];
 | |
|             this.add(individual);
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     private genWithRangeMod(): boolean {
 | |
|         let rangeList: Range[] = [];
 | |
| 
 | |
|         // 单一范围对象
 | |
|         if (this.genRange instanceof Range) {
 | |
| 
 | |
|             // 无效的对象
 | |
|             if (this.genRange.isDeleted()) {
 | |
|                 this.genErrorMessage = "Common.Attr.Key.Generation.Error.Invalid.Range";
 | |
|                 return false;
 | |
|             }
 | |
|             
 | |
|             else {
 | |
|                 rangeList = [this.genRange];
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // 多重范围对象
 | |
|         else if (this.genRange instanceof Label) {
 | |
| 
 | |
|             // 无效的标签
 | |
|             if (this.genRange.isDeleted()) {
 | |
|                 this.genErrorMessage = "Common.Attr.Key.Generation.Error.Invalid.Label";
 | |
|                 return false;
 | |
|             }
 | |
|             
 | |
|             else {
 | |
|                 let objList: CtrlObject[] = this.model.getObjectByLabel(this.genRange);
 | |
|                 rangeList = objList.filter((obj) => {
 | |
|                     return obj instanceof Range
 | |
|                 }) as Range[];
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // 空对象
 | |
|         else {
 | |
|             this.genErrorMessage = "Common.Attr.Key.Generation.Error.Empty.Object";
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         // 单一范围生成
 | |
|         if (rangeList.length === 1) {
 | |
|             this.genInSingleRange(this.genCount, rangeList[0]);
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         // 多重范围
 | |
|         else if (rangeList.length > 1) {
 | |
|             let allVolume: number = 0;
 | |
|             let allGenCount: number = 0;
 | |
|             let genData: number[] = [];
 | |
| 
 | |
|             // 计算体积
 | |
|             for (let i = 0; i < rangeList.length; i++) {
 | |
|                 let volume =
 | |
|                     rangeList[i].radius[0] *
 | |
|                     rangeList[i].radius[1] *
 | |
|                     rangeList[i].radius[2];
 | |
|                 allVolume += volume;
 | |
|                 genData.push(volume);
 | |
|             }
 | |
| 
 | |
|             // 按权重分配生成个数
 | |
|             for (let i = 0; i < genData.length; i++) {
 | |
|                 const count = Math.floor((genData[i] / allVolume) * this.genCount) + 1;
 | |
|                 allGenCount += count;
 | |
|                 genData[i] = count;
 | |
|             }
 | |
| 
 | |
|             // 按照溢出个数删除冗余个数
 | |
|             let morCount = allGenCount - this.genCount;
 | |
|             let safeCount = 0;
 | |
|             while (morCount > 0 && safeCount < 1000) {
 | |
|                 safeCount ++;
 | |
|                 let randomIndex = Math.floor(Math.random() * genData.length);
 | |
|                 if (genData[randomIndex] > 0) {
 | |
|                     genData[randomIndex] --;
 | |
|                     morCount --;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // 数据生成
 | |
|             for (let i = 0; i < rangeList.length; i++) {
 | |
|                 this.genInSingleRange(genData[i], rangeList[i]);
 | |
|             }
 | |
|             
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         // 空数据
 | |
|         else {
 | |
|             this.genErrorMessage = "Common.Attr.Key.Generation.Error.Empty.Range.List";
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         this.genErrorMessage = "Common.No.Unknown.Error";
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 生成个体
 | |
|      */
 | |
|     public genIndividuals(): boolean {
 | |
|         let success = false;
 | |
|         switch (this.genMethod) {
 | |
|             case GenMod.Point:
 | |
|                 success = this.genWithPointMod();
 | |
|                 break;
 | |
|             case GenMod.Range:
 | |
|                 success = this.genWithRangeMod();
 | |
|                 break;  
 | |
|         }
 | |
|         if (success) {
 | |
|             this.model.emit("individualChange", this);
 | |
|         }
 | |
|         return success;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 随机杀死个体
 | |
|      */
 | |
|     public killIndividuals(): boolean {
 | |
|         let success = false;
 | |
|         let killCount = this.killCount;
 | |
|         if (killCount > this.individuals.size) {
 | |
|             killCount = this.individuals.size;
 | |
|         }
 | |
| 
 | |
|         // 生成索引数组
 | |
|         const allIndex = new Array(this.individuals.size).fill(0).map((_, i) => i);
 | |
|         const deleteIndex: Set<number> = new Set();
 | |
| 
 | |
|         for (let i = 0; i < killCount; i++) {
 | |
|             let randomIndex = Math.floor(Math.random() * allIndex.length);
 | |
|             deleteIndex.add(allIndex[randomIndex]);
 | |
|             allIndex.splice(randomIndex, 1);
 | |
|         }
 | |
| 
 | |
|         let j = 0;
 | |
|         this.individuals.forEach((individual) => {
 | |
|             if (deleteIndex.has(j)) {
 | |
|                 this.remove(individual);
 | |
|                 success = true;
 | |
|             }
 | |
|             j++;
 | |
|         });
 | |
| 
 | |
|         if (success) {
 | |
|             this.model.emit("individualChange", this);
 | |
|         }
 | |
| 
 | |
|         return success
 | |
|     }
 | |
| 
 | |
| 	/**
 | |
| 	 * 创建个体
 | |
|      * @param count 创建数量
 | |
| 	 */
 | |
| 	public new(count: number = 1): Individual {
 | |
|         let newIndividual: Individual | undefined;
 | |
| 		for (let i = 0; i < count; i++) {
 | |
|             newIndividual = new Individual(this);
 | |
| 			this.individuals.add(newIndividual);
 | |
| 		}
 | |
|         if (newIndividual) {
 | |
|             return newIndividual;
 | |
|         } else {
 | |
|             return new Individual(this);
 | |
|         }
 | |
| 	}
 | |
| 
 | |
|     /**
 | |
|      * 添加个体
 | |
|      * @param individual 个体
 | |
|      */
 | |
|     public add(individual: Individual[] | Individual): this {
 | |
|         if (Array.isArray(individual)) {
 | |
|             for (let i = 0; i < individual.length; i++) {
 | |
|                 individual[i].group = this;
 | |
|                 this.individuals.add(individual[i]);
 | |
|             }
 | |
|         } else {
 | |
|             individual.group = this;
 | |
|             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++) {
 | |
|                 individual[i].group = undefined;
 | |
|                 this.individuals.delete(individual[i]);
 | |
|             }
 | |
|         } else {
 | |
|             individual.group = undefined;
 | |
|             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;
 | |
|     }
 | |
| 
 | |
| 	/**
 | |
| 	 * 行为列表
 | |
| 	 */
 | |
| 	public behaviors: IAnyBehavior[] = [];
 | |
| 
 | |
| 	/**
 | |
| 	 * 添加行为
 | |
| 	 * @param behavior 添加行为 
 | |
| 	 */
 | |
| 	public addBehavior(behavior: Behavior | Behavior[]): this {
 | |
| 		if (Array.isArray(behavior)) {
 | |
| 			this.behaviors = this.behaviors.concat(behavior);
 | |
|             for (let i = 0; i < behavior.length; i++) {
 | |
|                 behavior[i].mount(this, this.model);
 | |
|             }
 | |
| 		} else {
 | |
| 			this.behaviors.push(behavior);
 | |
|             behavior.mount(this, this.model);
 | |
| 		}
 | |
| 
 | |
| 		// 按照优先级
 | |
| 		this.behaviors = this.behaviors.sort((b1, b2) => {
 | |
| 			return (b1.priority ?? 0) - (b2.priority ?? 0)
 | |
| 		});
 | |
| 		return this;
 | |
| 	}
 | |
| 
 | |
|     /**
 | |
| 	 * 删除行为
 | |
| 	 * @param behavior 添加行为 
 | |
| 	 */
 | |
| 	public deleteBehavior(behavior: Behavior): this {
 | |
|         
 | |
|         let deleteIndex = -1;
 | |
|         for (let i = 0; i < this.behaviors.length; i++) {
 | |
|             if (this.behaviors[i].id === behavior.id) {
 | |
|                 deleteIndex = i;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (deleteIndex >= 0) {
 | |
|             this.behaviors[deleteIndex].unmount(this, this.model);
 | |
|             this.behaviors.splice(deleteIndex, 1);
 | |
|         }
 | |
| 
 | |
| 		return this;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
|      * 执行行为影响
 | |
| 	 * @param
 | |
|      */
 | |
| 	public runner(t: number, effectType: "finalEffect" | "effect" | "afterEffect" ): void {
 | |
| 
 | |
|         for(let j = 0; j < this.behaviors.length; j++) {
 | |
| 
 | |
|             const behavior = this.behaviors[j];
 | |
|             if (behavior.isDeleted()) {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             const runnerFunction = behavior[effectType];
 | |
|             if (!runnerFunction) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             for (let k = 0; k < behavior.currentGroupKey.length; k++) {
 | |
| 
 | |
|                 let parameterCache = behavior.parameter[
 | |
|                     behavior.currentGroupKey[k] as string
 | |
|                 ];
 | |
| 
 | |
|                 if (Array.isArray(parameterCache?.objects)) {
 | |
|                     parameterCache.objects = [this];
 | |
| 
 | |
|                 } else {
 | |
|                     parameterCache.objects = this;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             this.individuals.forEach((individual) => {
 | |
|                 runnerFunction(individual, this, this.model, t);
 | |
|             });
 | |
|         }
 | |
| 	}
 | |
| 
 | |
|     /**
 | |
|      * 导出坐标数据
 | |
|      */
 | |
|     public exportPositionData(): Float32Array {
 | |
|         let index = 0;
 | |
|         let dataBuffer = new Float32Array(this.individuals.size * 3);
 | |
|         this.individuals.forEach((individual) => {
 | |
|             dataBuffer[index ++] = individual.position[0];
 | |
|             dataBuffer[index ++] = individual.position[1];
 | |
|             dataBuffer[index ++] = individual.position[2];
 | |
|         });
 | |
|         return dataBuffer;
 | |
|     }
 | |
|     
 | |
|     public constructor(model: Model) {
 | |
| 
 | |
|         super(model);
 | |
|         
 | |
|         if (model.renderer) {
 | |
|             this.renderParameter = getDefaultValue(model.renderer.pointsParameterOption);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| export default Group;
 | |
| export { Group, GenMod }; |