Compare commits
	
		
			No commits in common. "0f7657ad8381dfc4560b8d5d96034d0c9a2c44eb" and "6b3a10070ffdb66da87123d303ebfd899d70aa57" have entirely different histories.
		
	
	
		
			0f7657ad83
			...
			6b3a10070f
		
	
		
| @ -1,14 +1,13 @@ | ||||
| import { BehaviorRecorder, IAnyBehaviorRecorder } from "@Model/Behavior"; | ||||
| import { Template } from "./Template"; | ||||
| import { Dynamics } from "./Dynamics"; | ||||
| import { Brownian } from "./Brownian"; | ||||
| import { BoundaryConstraint } from "./BoundaryConstraint";  | ||||
| 
 | ||||
| const AllBehaviors: IAnyBehaviorRecorder[] = [ | ||||
|     new BehaviorRecorder(Dynamics), | ||||
|     new BehaviorRecorder(Brownian), | ||||
|     new BehaviorRecorder(BoundaryConstraint) | ||||
| ] | ||||
| const AllBehaviors: IAnyBehaviorRecorder[] = new Array(4).fill(0).map((_, i) => { | ||||
|     let behavior = new BehaviorRecorder(Template); | ||||
|     behavior.behaviorId = behavior.behaviorId + i; | ||||
|     behavior.behaviorName = behavior.behaviorName + Math.random().toString(36).slice(-6); | ||||
|     behavior.category = "Category" + Math.floor(Math.random() * 3).toString(); | ||||
|     return behavior; | ||||
| }); | ||||
| 
 | ||||
| /** | ||||
|  * 分类词条 | ||||
|  | ||||
| @ -1,74 +0,0 @@ | ||||
| import { Behavior } from "@Model/Behavior"; | ||||
| import { Group } from "@Model/Group"; | ||||
| import { Individual } from "@Model/Individual"; | ||||
| import { Label } from "@Model/Label"; | ||||
| import { Model } from "@Model/Model"; | ||||
| import { Range } from "@Model/Range"; | ||||
| 
 | ||||
| type IBoundaryConstraintBehaviorParameter = { | ||||
|     range: "LR" | ||||
| } | ||||
| 
 | ||||
| type IBoundaryConstraintBehaviorEvent = {} | ||||
| 
 | ||||
| class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter, IBoundaryConstraintBehaviorEvent> { | ||||
| 
 | ||||
|     public override behaviorId: string = "BoundaryConstraint"; | ||||
| 
 | ||||
|     public override behaviorName: string = "$Title"; | ||||
| 
 | ||||
|     public override iconName: string = "Running"; | ||||
| 
 | ||||
|     public override describe: string = "$Intro"; | ||||
| 
 | ||||
|     public override category: string = "$Physics"; | ||||
| 
 | ||||
| 	public override parameterOption = { | ||||
| 		range: { | ||||
| 			type: "LR", | ||||
| 			name: "$range", | ||||
| 			defaultValue: undefined | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
|     public override terms: Record<string, Record<string, string>> = { | ||||
|         "$Title": { | ||||
|             "ZH_CN": "边界约束", | ||||
|             "EN_US": "Boundary constraint" | ||||
|         }, | ||||
|         "$Intro": { | ||||
|             "ZH_CN": "个体越出边界后将主动返回", | ||||
|             "EN_US": "Individuals will return actively after crossing the border" | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     public effect(individual: Individual, group: Group, model: Model, t: number): void { | ||||
|         let rangeList: Range[] = []; | ||||
| 		if (this.parameter.range instanceof Range) { | ||||
| 			rangeList.push(this.parameter.range); | ||||
| 		} | ||||
| 		if (this.parameter.range instanceof Label) { | ||||
| 			rangeList = model.getObjectByLabel(this.parameter.range).filter((obj) => { | ||||
| 				return obj instanceof Range | ||||
| 			}) as any; | ||||
| 		} | ||||
| 		for (let i = 0; i < rangeList.length; i++) { | ||||
| 
 | ||||
| 			let rx = rangeList[i].position[0] - individual.position[0]; | ||||
| 			let ry = rangeList[i].position[1] - individual.position[1]; | ||||
| 			let rz = rangeList[i].position[2] - individual.position[2]; | ||||
| 
 | ||||
| 			let ox = Math.abs(rx) > rangeList[i].radius[0]; | ||||
| 			let oy = Math.abs(ry) > rangeList[i].radius[1]; | ||||
| 			let oz = Math.abs(rz) > rangeList[i].radius[2]; | ||||
| 
 | ||||
| 			individual.applyForce( | ||||
| 				ox ? rx : 0, | ||||
| 				oy ? ry : 0, | ||||
| 				oz ? rz : 0 | ||||
| 			) | ||||
| 		} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export { BoundaryConstraint }; | ||||
| @ -1,109 +0,0 @@ | ||||
| import { Behavior } from "@Model/Behavior"; | ||||
| import { Group } from "@Model/Group"; | ||||
| import { Individual } from "@Model/Individual"; | ||||
| import { Model } from "@Model/Model"; | ||||
| 
 | ||||
| type IBrownianBehaviorParameter = { | ||||
|     maxFrequency: "number", | ||||
| 	minFrequency: "number", | ||||
| 	maxStrength: "number", | ||||
| 	minStrength: "number" | ||||
| } | ||||
| 
 | ||||
| type IBrownianBehaviorEvent = {} | ||||
| 
 | ||||
| class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEvent> { | ||||
| 
 | ||||
|     public override behaviorId: string = "Brownian"; | ||||
| 
 | ||||
|     public override behaviorName: string = "$Title"; | ||||
| 
 | ||||
|     public override iconName: string = "Running"; | ||||
| 
 | ||||
|     public override describe: string = "$Intro"; | ||||
| 
 | ||||
| 	public override category: string = "$Physics"; | ||||
| 
 | ||||
| 	public override parameterOption = { | ||||
| 		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 | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
|     public override terms: Record<string, Record<string, string>> = { | ||||
|         "$Title": { | ||||
|             "ZH_CN": "布朗运动", | ||||
|             "EN_US": "Brownian motion" | ||||
|         }, | ||||
|         "$Intro": { | ||||
|             "ZH_CN": "一种无规则的随机运动", | ||||
|             "EN_US": "An irregular random motion" | ||||
|         }, | ||||
| 		"$Max.Frequency": { | ||||
|             "ZH_CN": "最大频率", | ||||
|             "EN_US": "Maximum frequency" | ||||
|         }, | ||||
| 		"$Min.Frequency": { | ||||
|             "ZH_CN": "最小频率", | ||||
|             "EN_US": "Minimum frequency" | ||||
|         }, | ||||
| 		"$Max.Strength": { | ||||
|             "ZH_CN": "最大强度", | ||||
|             "EN_US": "Maximum strength" | ||||
|         }, | ||||
| 		"$Min.Strength": { | ||||
|             "ZH_CN": "最小强度", | ||||
|             "EN_US": "Minimum strength" | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| 	public effect(individual: Individual, group: Group, model: Model, t: number): void { | ||||
| 
 | ||||
| 		const {maxFrequency, minFrequency, maxStrength, minStrength} = this.parameter; | ||||
| 
 | ||||
| 		let nextTime = individual.getData("Brownian.nextTime") ??  | ||||
| 		minFrequency +  Math.random() * (maxFrequency - minFrequency); | ||||
| 		let currentTime = individual.getData("Brownian.currentTime") ?? 0; | ||||
| 		 | ||||
| 		currentTime += t; | ||||
| 		if (currentTime > nextTime) { | ||||
| 			individual.applyForce( | ||||
| 				minStrength + (Math.random() * 2 - 1) * (maxStrength - minStrength), | ||||
| 				minStrength + (Math.random() * 2 - 1) * (maxStrength - minStrength), | ||||
| 				minStrength + (Math.random() * 2 - 1) * (maxStrength - minStrength) | ||||
| 			); | ||||
| 			nextTime = minFrequency +  Math.random() * (maxFrequency - minFrequency); | ||||
| 			currentTime = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		individual.setData("Brownian.nextTime", nextTime); | ||||
| 		individual.setData("Brownian.currentTime", currentTime); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| export { Brownian }; | ||||
| @ -1,140 +0,0 @@ | ||||
| import { Behavior } from "@Model/Behavior"; | ||||
| import Group from "@Model/Group"; | ||||
| import Individual from "@Model/Individual"; | ||||
| import { Model } from "@Model/Model"; | ||||
| 
 | ||||
| type IDynamicsBehaviorParameter = { | ||||
|     mass: "number", | ||||
| 	maxAcceleration: "number", | ||||
| 	maxVelocity: "number", | ||||
| 	resistance: "number" | ||||
| } | ||||
| 
 | ||||
| type IDynamicsBehaviorEvent = {} | ||||
| 
 | ||||
| class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEvent> { | ||||
| 
 | ||||
|     public override behaviorId: string = "Dynamics"; | ||||
| 
 | ||||
|     public override behaviorName: string = "$Title"; | ||||
| 
 | ||||
|     public override iconName: string = "Running"; | ||||
| 
 | ||||
|     public override describe: string = "$Intro"; | ||||
| 
 | ||||
| 	public override category: string = "$Physics"; | ||||
| 
 | ||||
| 	public override parameterOption = { | ||||
| 		mass: { | ||||
| 			name: "$Mass", | ||||
| 			type: "number", | ||||
| 			defaultValue: 1, | ||||
| 			numberStep: .01, | ||||
| 			numberMin: .001 | ||||
| 		}, | ||||
| 		maxAcceleration: { | ||||
| 			name: "$Max.Acceleration", | ||||
| 			type: "number", | ||||
| 			defaultValue: 5, | ||||
| 			numberStep: .1, | ||||
| 			numberMin: 0 | ||||
| 		}, | ||||
| 		maxVelocity: { | ||||
| 			name: "$Max.Velocity", | ||||
| 			type: "number", | ||||
| 			defaultValue: 10, | ||||
| 			numberStep: .1, | ||||
| 			numberMin: 0 | ||||
| 		}, | ||||
| 		resistance: { | ||||
| 			name: "$Resistance", | ||||
| 			type: "number", | ||||
| 			defaultValue: 0.1, | ||||
| 			numberStep: .1, | ||||
| 			numberMin: 0 | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
|     public override terms: Record<string, Record<string, string>> = { | ||||
|         "$Title": { | ||||
|             "ZH_CN": "动力学", | ||||
|             "EN_US": "Dynamics" | ||||
|         }, | ||||
|         "$Intro": { | ||||
|             "ZH_CN": "一切可以运动物体的必要行为,执行物理法则。", | ||||
|             "EN_US": "All necessary behaviors that can move objects and implement the laws of physics." | ||||
|         }, | ||||
| 		"$Mass": { | ||||
| 			"ZH_CN": "质量 (Kg)", | ||||
|             "EN_US": "Mass (Kg)" | ||||
| 		}, | ||||
| 		"$Max.Acceleration": { | ||||
| 			"ZH_CN": "最大加速度 (m/s²)", | ||||
|             "EN_US": "Maximum acceleration (m/s²)" | ||||
| 		}, | ||||
| 		"$Max.Velocity": { | ||||
| 			"ZH_CN": "最大速度 (m/s)", | ||||
|             "EN_US": "Maximum velocity (m/s)" | ||||
| 		}, | ||||
| 		"$Physics": { | ||||
| 			"ZH_CN": "物理", | ||||
|             "EN_US": "Physics" | ||||
| 		} | ||||
|     }; | ||||
| 
 | ||||
| 	public override finalEffect(individual: Individual, group: Group, model: Model, t: number): void { | ||||
| 
 | ||||
| 		// 计算当前速度
 | ||||
| 		const currentV = individual.vectorLength(individual.velocity); | ||||
| 
 | ||||
| 		// 计算阻力
 | ||||
| 		const resistance = currentV * currentV * this.parameter.resistance; | ||||
| 
 | ||||
| 		// 应用阻力
 | ||||
| 		if (currentV) { | ||||
| 			individual.applyForce( | ||||
| 				(- individual.velocity[0] / currentV) * resistance, | ||||
| 				(- individual.velocity[1] / currentV) * resistance, | ||||
| 				(- individual.velocity[2] / currentV) * resistance | ||||
| 			); | ||||
| 		} | ||||
| 		 | ||||
| 		// 计算加速度
 | ||||
| 		individual.acceleration[0] = individual.force[0] / this.parameter.mass; | ||||
| 		individual.acceleration[1] = individual.force[1] / this.parameter.mass; | ||||
| 		individual.acceleration[2] = individual.force[2] / this.parameter.mass; | ||||
| 
 | ||||
| 		// 加速度约束
 | ||||
| 		const lengthA = individual.vectorLength(individual.acceleration); | ||||
| 		if (lengthA > this.parameter.maxAcceleration) { | ||||
| 			individual.acceleration[0] = individual.acceleration[0] * this.parameter.maxAcceleration / lengthA; | ||||
| 			individual.acceleration[1] = individual.acceleration[1] * this.parameter.maxAcceleration / lengthA; | ||||
| 			individual.acceleration[2] = individual.acceleration[2] * this.parameter.maxAcceleration / lengthA; | ||||
| 		} | ||||
| 
 | ||||
| 		// 计算速度
 | ||||
| 		individual.velocity[0] = individual.velocity[0] + individual.acceleration[0] * t; | ||||
| 		individual.velocity[1] = individual.velocity[1] + individual.acceleration[1] * t; | ||||
| 		individual.velocity[2] = individual.velocity[2] + individual.acceleration[2] * t; | ||||
| 
 | ||||
| 		// 速度约束
 | ||||
| 		const lengthV = individual.vectorLength(individual.velocity); | ||||
| 		if (lengthV > this.parameter.maxVelocity) { | ||||
| 			individual.velocity[0] = individual.velocity[0] * this.parameter.maxVelocity / lengthV; | ||||
| 			individual.velocity[1] = individual.velocity[1] * this.parameter.maxVelocity / lengthV; | ||||
| 			individual.velocity[2] = individual.velocity[2] * this.parameter.maxVelocity / lengthV; | ||||
| 		} | ||||
| 
 | ||||
| 		// 应用速度
 | ||||
| 		individual.position[0] = individual.position[0] + individual.velocity[0] * t; | ||||
| 		individual.position[1] = individual.position[1] + individual.velocity[1] * t; | ||||
| 		individual.position[2] = individual.position[2] + individual.velocity[2] * t; | ||||
| 
 | ||||
| 		// 清除受力
 | ||||
| 		individual.force[0] = 0; | ||||
| 		individual.force[1] = 0; | ||||
| 		individual.force[2] = 0; | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| export { Dynamics }; | ||||
| @ -1,7 +1,4 @@ | ||||
| import { Behavior } from "@Model/Behavior"; | ||||
| import { Group } from "@Model/Group"; | ||||
| import { Individual } from "@Model/Individual"; | ||||
| import { Model } from "@Model/Model"; | ||||
| 
 | ||||
| type ITemplateBehaviorParameter = { | ||||
|      | ||||
| @ -19,9 +16,7 @@ class Template extends Behavior<ITemplateBehaviorParameter, ITemplateBehaviorEve | ||||
| 
 | ||||
|     public override describe: string = "$Intro"; | ||||
| 
 | ||||
|     public override category: string = "$Category"; | ||||
| 
 | ||||
|     public override terms: Record<string, Record<string, string>> = { | ||||
|     terms: Record<string, Record<string, string>> = { | ||||
|         "$Title": { | ||||
|             "ZH_CN": "行为", | ||||
|             "EN_US": "Behavior" | ||||
| @ -31,10 +26,6 @@ class Template extends Behavior<ITemplateBehaviorParameter, ITemplateBehaviorEve | ||||
|             "EN_US": "This is a template behavior" | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     public effect(individual: Individual, group: Group, model: Model, t: number): void { | ||||
|          | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export { Template }; | ||||
| @ -120,7 +120,7 @@ class BehaviorList extends Component<IBehaviorListProps & IMixinSettingProps> { | ||||
| 			</div> | ||||
| 			<div className="behavior-content-view"> | ||||
| 				{this.renderTerm(behavior, name, "title-view", needLocal)} | ||||
| 				{this.renderTerm(behavior, info, "info-view", true)} | ||||
| 				{this.renderTerm(behavior, info, "info-view", needLocal)} | ||||
| 			</div> | ||||
| 			<div className="behavior-action-view"> | ||||
| 				{this.renderActionButton(behavior)} | ||||
|  | ||||
| @ -15,7 +15,7 @@ interface ICommandBarProps { | ||||
| } | ||||
| 
 | ||||
| @useSetting | ||||
| @useStatusWithEvent("mouseModChange", "actuatorStartChange") | ||||
| @useStatusWithEvent("mouseModChange") | ||||
| class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixinStatusProps> { | ||||
| 
 | ||||
|     render(): ReactNode { | ||||
| @ -34,13 +34,7 @@ class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixi | ||||
|         > | ||||
|             <div> | ||||
|                 {this.getRenderButton({ iconName: "Save", i18NKey: "Command.Bar.Save.Info" })} | ||||
|                 {this.getRenderButton({ | ||||
|                     iconName: this.props.status?.actuator.start() ? "Pause" : "Play", | ||||
|                     i18NKey: "Command.Bar.Play.Info", | ||||
|                     click: () => this.props.status ? this.props.status.actuator.start( | ||||
|                         !this.props.status.actuator.start() | ||||
|                     ) : undefined | ||||
|                 })} | ||||
|                 {this.getRenderButton({ iconName: "Play", i18NKey: "Command.Bar.Play.Info" })} | ||||
|                 {this.getRenderButton({ | ||||
|                     iconName: "HandsFree", i18NKey: "Command.Bar.Drag.Info",  | ||||
|                     active: mouseMod === MouseMod.Drag, | ||||
|  | ||||
| @ -42,7 +42,7 @@ class HeaderBar extends Component< | ||||
|         return (t: number) => { | ||||
|             let newState: HeaderBarState = {} as any; | ||||
|             newState[type] = 1 / t; | ||||
|             if (this.updateTime > 20) { | ||||
|             if (this.updateTime > 60) { | ||||
|                 this.updateTime = 0; | ||||
|                 this.setState(newState); | ||||
|             } | ||||
|  | ||||
| @ -12,7 +12,6 @@ import { I18N } from "@Component/Localization/Localization"; | ||||
| import { superConnectWithEvent, superConnect } from "./Context"; | ||||
| import { PopupController } from "./Popups"; | ||||
| import { Behavior } from "@Model/Behavior"; | ||||
| import { Actuator } from "@Model/Actuator"; | ||||
| 
 | ||||
| function randomColor(unNormal: boolean = false) { | ||||
|     const color = [ | ||||
| @ -45,7 +44,6 @@ interface IStatusEvent { | ||||
|     individualChange: void; | ||||
|     behaviorChange: void; | ||||
|     popupChange: void; | ||||
|     actuatorStartChange: void; | ||||
| } | ||||
| 
 | ||||
| class Status extends Emitter<IStatusEvent> { | ||||
| @ -73,11 +71,6 @@ class Status extends Emitter<IStatusEvent> { | ||||
|      */ | ||||
|     public model: Model = new Model(); | ||||
| 
 | ||||
|     /** | ||||
|      * 执行器 | ||||
|      */ | ||||
|     public actuator: Actuator; | ||||
| 
 | ||||
|     /** | ||||
|      * 弹窗 | ||||
|      */ | ||||
| @ -111,14 +104,8 @@ class Status extends Emitter<IStatusEvent> { | ||||
|     public constructor() { | ||||
|         super(); | ||||
| 
 | ||||
|         // 初始化执行器
 | ||||
|         this.actuator = new Actuator(this.model); | ||||
| 
 | ||||
|         // 执行器开启事件
 | ||||
|         this.actuator.on("startChange", () => { this.emit("actuatorStartChange") }); | ||||
| 
 | ||||
|         // 循环事件
 | ||||
|         this.actuator.on("loop", (t) => { this.emit("physicsLoop", t) }); | ||||
|         this.model.on("loop", (t) => { this.emit("physicsLoop", t) }); | ||||
| 
 | ||||
|         // 对象变化事件
 | ||||
|         this.model.on("objectChange", () => this.emit("objectChange")); | ||||
|  | ||||
| @ -1,125 +0,0 @@ | ||||
| import { Model } from "./Model"; | ||||
| import { Emitter } from "./Emitter";  | ||||
| 
 | ||||
| interface IActuatorEvent { | ||||
| 	startChange: boolean; | ||||
| 	loop: number; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 模型执行器 | ||||
|  */ | ||||
| class Actuator extends Emitter<IActuatorEvent> { | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 速度系数 | ||||
| 	 */ | ||||
| 	public speed: number = 1; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 模拟帧率 | ||||
| 	 */ | ||||
| 	public fps: number = 36; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 仿真是否进行 | ||||
| 	 */ | ||||
| 	private startFlag: boolean = false; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 主时钟状态控制 | ||||
| 	 */ | ||||
| 	public start(start?: boolean): boolean { | ||||
| 		if (start === undefined) { | ||||
| 			return this.startFlag; | ||||
| 		} else { | ||||
| 			this.startFlag = start; | ||||
| 			this.lastTime = 0; | ||||
| 			this.alignTimer = 0; | ||||
| 			this.emit("startChange", start); | ||||
| 			return start; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 绑定模型 | ||||
| 	 */ | ||||
| 	public model: Model; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 上一帧的时间 | ||||
| 	 */ | ||||
| 	private lastTime: number = 0; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 对其计时器 | ||||
| 	 */ | ||||
| 	private alignTimer: number = 0; | ||||
| 
 | ||||
| 	public tickerType: 1 | 2 = 2; | ||||
| 
 | ||||
| 	private ticker(t: number) { | ||||
| 		if (this.startFlag && t !== 0) { | ||||
| 			if (this.lastTime === 0) { | ||||
| 				this.lastTime = t; | ||||
| 			} else { | ||||
| 				let durTime = (t - this.lastTime) / 1000; | ||||
| 				this.lastTime = t; | ||||
| 
 | ||||
| 				// 丢帧判定
 | ||||
| 				if (durTime > 0.1) { | ||||
| 					console.log("Actuator: Ticker dur time error. dropping...") | ||||
| 				} | ||||
| 
 | ||||
| 				this.alignTimer += durTime; | ||||
| 				if (this.alignTimer > (1 / this.fps)) { | ||||
| 					this.model.update(this.alignTimer * this.speed); | ||||
| 					this.emit("loop", this.alignTimer); | ||||
| 					this.alignTimer = 0; | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			this.emit("loop", Infinity); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 帧率对其时钟 | ||||
| 	 * 1、使用 requestAnimationFrame 保证最高的性能 | ||||
| 	 * 2、最大模拟帧率只能小于 60 | ||||
| 	 * 3、可能出现帧率对其问题 | ||||
| 	 */ | ||||
| 	private tickerAlign = (t: number) => { | ||||
| 		this.ticker(t); | ||||
| 		requestAnimationFrame(this.tickerAlign); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 精确时钟 | ||||
| 	 */ | ||||
| 	private tickerExp = () => { | ||||
| 		this.ticker(window.performance.now()); | ||||
| 		setTimeout(this.tickerExp, (1 / this.fps) * 1000); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 执行器 | ||||
| 	 */ | ||||
| 	private runTicker = (t: number) => { | ||||
| 		if (this.tickerType === 1) { | ||||
| 			this.ticker(t); | ||||
| 			requestAnimationFrame(this.runTicker); | ||||
| 		} else { | ||||
| 			this.ticker(window.performance.now()); | ||||
| 			setTimeout(this.runTicker, (1 / this.fps) * 1000); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public constructor(model: Model) { | ||||
| 		super(); | ||||
| 		this.model = model; | ||||
| 		this.runTicker(0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| export { Actuator } | ||||
| @ -64,7 +64,7 @@ interface IBehaviorParameterOptionItem<T extends IParamType = IParamType> { | ||||
|     /** | ||||
|      * 参数类型 | ||||
|      */ | ||||
|     type: T | string; | ||||
|     type: T; | ||||
| 
 | ||||
|     /** | ||||
|      * 参数默认值 | ||||
| @ -202,7 +202,7 @@ class BehaviorRecorder< | ||||
|      * 获取下一个 ID | ||||
|      */ | ||||
|     public getNextId() { | ||||
|         return `B-${this.behaviorId}-${this.nameIndex ++}`; | ||||
|         return `B-${this.behaviorName}-${this.nameIndex ++}`; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -269,7 +269,6 @@ class BehaviorRecorder< | ||||
|         this.behaviorId = this.behaviorInstance.behaviorId; | ||||
|         this.behaviorName = this.behaviorInstance.behaviorName; | ||||
|         this.describe = this.behaviorInstance.describe; | ||||
|         this.category = this.behaviorInstance.category; | ||||
|         this.terms = this.behaviorInstance.terms; | ||||
|     } | ||||
| } | ||||
| @ -372,7 +371,7 @@ class Behavior< | ||||
|      * @param model 模型 | ||||
|      * @param t 经过时间 | ||||
|      */ | ||||
|     public effect(individual: Individual, group: Group, model: Model, t: number): void {}; | ||||
|     public beforeEffect(individual: Individual, group: Group, model: Model, t: number): void {}; | ||||
| 
 | ||||
|     /** | ||||
|      * 作用影响于个体 | ||||
| @ -381,7 +380,7 @@ class Behavior< | ||||
|      * @param model 模型 | ||||
|      * @param t 经过时间 | ||||
|      */ | ||||
|     public afterEffect(individual: Individual, group: Group, model: Model, t: number): void {}; | ||||
|     public effect(individual: Individual, group: Group, model: Model, t: number): void {}; | ||||
| 
 | ||||
|     /** | ||||
|      * 全部影响作用后 | ||||
| @ -390,7 +389,7 @@ class Behavior< | ||||
|      * @param model 模型 | ||||
|      * @param t 经过时间 | ||||
|      */ | ||||
|     public finalEffect(individual: Individual, group: Group, model: Model, t: number): void {}; | ||||
|     public afterEffect(individual: Individual, group: Group, model: Model, t: number): void {}; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -330,7 +330,7 @@ class Group extends CtrlObject { | ||||
|      * 执行行为影响 | ||||
| 	 * @param | ||||
|      */ | ||||
| 	public runner(t: number, effectType: "finalEffect" | "effect" | "afterEffect" ): void { | ||||
| 	public runner(t: number, effectType: "beforeEffect" | "effect" | "afterEffect" ): void { | ||||
| 		this.individuals.forEach((individual) => { | ||||
| 			for(let j = 0; j < this.behaviors.length; j++) { | ||||
| 				this.behaviors[j][effectType](individual, this, this.model, t); | ||||
|  | ||||
| @ -12,9 +12,9 @@ class Individual { | ||||
|      * @param y y 坐标 | ||||
|      * @param z z 坐标 | ||||
|      */ | ||||
|     public vectorLength(x: number[]): number; | ||||
|     public vectorLength(x: number, y: number, z: number): number; | ||||
|     public vectorLength(x: number | number[], y?: number, z?: number): number { | ||||
|     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 { | ||||
| @ -28,10 +28,10 @@ class Individual { | ||||
|      * @param y y 坐标 | ||||
|      * @param z z 坐标 | ||||
|      */ | ||||
|     public vectorNormalize(x: number[]): [number, number, number]; | ||||
|     public vectorNormalize(x: number, y: number, z: number): [number, number, number]; | ||||
|     public vectorNormalize(x: number | number[], y?: number, z?: number): [number, number, number] { | ||||
|         let length = this.vectorLength(x as number, y as number, z as number); | ||||
|     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, | ||||
| @ -52,39 +52,6 @@ class Individual { | ||||
| 	 */ | ||||
| 	public position: number[] = [0, 0, 0]; | ||||
| 
 | ||||
|     /** | ||||
|      * 速度 | ||||
|      */ | ||||
|     public velocity: number[] = [0, 0, 0]; | ||||
| 
 | ||||
|     /** | ||||
|      * 加速度 | ||||
|      */ | ||||
|     public acceleration: number[] = [0, 0, 0]; | ||||
| 
 | ||||
|     /** | ||||
|      * 作用力 | ||||
|      */ | ||||
|     public force: number[] = [0, 0, 0]; | ||||
| 
 | ||||
|     /** | ||||
|      * 施加力 | ||||
|      */ | ||||
|     public applyForce(x: number[]): [number, number, number]; | ||||
|     public applyForce(x: number, y: number, z: number): [number, number, number]; | ||||
|     public applyForce(x: number | number[], y?: number, z?: number): [number, number, number] { | ||||
|         if (Array.isArray(x)) { | ||||
|             this.force[0] += x[0] ?? 0; | ||||
|             this.force[1] += x[1] ?? 0; | ||||
|             this.force[2] += x[2] ?? 0; | ||||
|         } else { | ||||
|             this.force[0] += x ?? 0; | ||||
|             this.force[1] += y ?? 0; | ||||
|             this.force[2] += z ?? 0; | ||||
|         } | ||||
|         return this.force as [number, number, number]; | ||||
|     } | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 所属群组 | ||||
| 	 */ | ||||
| @ -140,7 +107,7 @@ class Individual { | ||||
|      * @param position 目标位置 | ||||
|      */ | ||||
|     public distanceTo(position: Individual | number[]): number { | ||||
|         return this.vectorLength(this.vectorTo(position)); | ||||
|         return Individual.vectorLength(this.vectorTo(position)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -8,6 +8,7 @@ import { Label } from "./Label"; | ||||
| import { Behavior, IAnyBehavior, IAnyBehaviorRecorder } from "./Behavior"; | ||||
| 
 | ||||
| type ModelEvent = { | ||||
|     loop: number; | ||||
|     labelChange: Label[]; | ||||
|     objectChange: CtrlObject[]; | ||||
|     individualChange: Group; | ||||
| @ -258,7 +259,7 @@ class Model extends Emitter<ModelEvent> { | ||||
|         for (let i = 0; i < this.objectPool.length; i++) { | ||||
|             let object = this.objectPool[i]; | ||||
|             if (object instanceof Group && object.update) { | ||||
|                 object.runner(t, "effect"); | ||||
|                 object.runner(t, "beforeEffect"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -266,7 +267,7 @@ class Model extends Emitter<ModelEvent> { | ||||
|         for (let i = 0; i < this.objectPool.length; i++) { | ||||
|             let object = this.objectPool[i]; | ||||
|             if (object instanceof Group && object.update) { | ||||
|                 object.runner(t, "afterEffect"); | ||||
|                 object.runner(t, "effect"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -274,11 +275,13 @@ class Model extends Emitter<ModelEvent> { | ||||
|         for (let i = 0; i < this.objectPool.length; i++) { | ||||
|             let object = this.objectPool[i]; | ||||
|             if (object instanceof Group && object.update) { | ||||
|                 object.runner(t, "finalEffect"); | ||||
|                 object.runner(t, "afterEffect"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         this.draw(); | ||||
| 
 | ||||
|         this.emit("loop", t); | ||||
|     } | ||||
| 
 | ||||
|     public draw() { | ||||
|  | ||||
| @ -10,7 +10,6 @@ import { RootContainer } from "@Component/Container/RootContainer"; | ||||
| import { LayoutDirection } from "@Context/Layout"; | ||||
| import { CommandBar } from "@Component/CommandBar/CommandBar"; | ||||
| import { Popup } from "@Component/Popup/Popup"; | ||||
| import { AllBehaviors } from "@Behavior/Behavior"; | ||||
| import "./SimulatorWeb.scss"; | ||||
| 
 | ||||
| initializeIcons("https://img.mrkbear.com/fabric-cdn-prod_20210407.001/"); | ||||
| @ -55,16 +54,6 @@ class SimulatorWeb extends Component { | ||||
|             this.status.model.update(0); | ||||
|             this.status.newLabel().name = "New Label"; | ||||
|             this.status.newLabel().name = "Test Label 01"; | ||||
|             let dynamic = this.status.model.addBehavior(AllBehaviors[0]); | ||||
|             dynamic.name = "dynamic"; | ||||
|             let brownian = this.status.model.addBehavior(AllBehaviors[1]); | ||||
|             brownian.name = "brownian"; | ||||
|             let boundary = this.status.model.addBehavior(AllBehaviors[2]); | ||||
|             boundary.name = "boundary"; | ||||
|             boundary.parameter.range = this.status.model.allRangeLabel; | ||||
|             group.addBehavior(dynamic); | ||||
|             group.addBehavior(brownian); | ||||
|             group.addBehavior(boundary); | ||||
|         } | ||||
| 
 | ||||
|         (window as any).s = this; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user