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 { BehaviorRecorder, IAnyBehaviorRecorder } from "@Model/Behavior"; | ||||||
| import { Template } from "./Template"; | import { Template } from "./Template"; | ||||||
| import { Dynamics } from "./Dynamics"; |  | ||||||
| import { Brownian } from "./Brownian"; |  | ||||||
| import { BoundaryConstraint } from "./BoundaryConstraint";  |  | ||||||
| 
 | 
 | ||||||
| const AllBehaviors: IAnyBehaviorRecorder[] = [ | const AllBehaviors: IAnyBehaviorRecorder[] = new Array(4).fill(0).map((_, i) => { | ||||||
|     new BehaviorRecorder(Dynamics), |     let behavior = new BehaviorRecorder(Template); | ||||||
|     new BehaviorRecorder(Brownian), |     behavior.behaviorId = behavior.behaviorId + i; | ||||||
|     new BehaviorRecorder(BoundaryConstraint) |     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 { Behavior } from "@Model/Behavior"; | ||||||
| import { Group } from "@Model/Group"; |  | ||||||
| import { Individual } from "@Model/Individual"; |  | ||||||
| import { Model } from "@Model/Model"; |  | ||||||
| 
 | 
 | ||||||
| type ITemplateBehaviorParameter = { | type ITemplateBehaviorParameter = { | ||||||
|      |      | ||||||
| @ -19,9 +16,7 @@ class Template extends Behavior<ITemplateBehaviorParameter, ITemplateBehaviorEve | |||||||
| 
 | 
 | ||||||
|     public override describe: string = "$Intro"; |     public override describe: string = "$Intro"; | ||||||
| 
 | 
 | ||||||
|     public override category: string = "$Category"; |     terms: Record<string, Record<string, string>> = { | ||||||
| 
 |  | ||||||
|     public override terms: Record<string, Record<string, string>> = { |  | ||||||
|         "$Title": { |         "$Title": { | ||||||
|             "ZH_CN": "行为", |             "ZH_CN": "行为", | ||||||
|             "EN_US": "Behavior" |             "EN_US": "Behavior" | ||||||
| @ -31,10 +26,6 @@ class Template extends Behavior<ITemplateBehaviorParameter, ITemplateBehaviorEve | |||||||
|             "EN_US": "This is a template behavior" |             "EN_US": "This is a template behavior" | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 |  | ||||||
|     public effect(individual: Individual, group: Group, model: Model, t: number): void { |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export { Template }; | export { Template }; | ||||||
| @ -120,7 +120,7 @@ class BehaviorList extends Component<IBehaviorListProps & IMixinSettingProps> { | |||||||
| 			</div> | 			</div> | ||||||
| 			<div className="behavior-content-view"> | 			<div className="behavior-content-view"> | ||||||
| 				{this.renderTerm(behavior, name, "title-view", needLocal)} | 				{this.renderTerm(behavior, name, "title-view", needLocal)} | ||||||
| 				{this.renderTerm(behavior, info, "info-view", true)} | 				{this.renderTerm(behavior, info, "info-view", needLocal)} | ||||||
| 			</div> | 			</div> | ||||||
| 			<div className="behavior-action-view"> | 			<div className="behavior-action-view"> | ||||||
| 				{this.renderActionButton(behavior)} | 				{this.renderActionButton(behavior)} | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ interface ICommandBarProps { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @useSetting | @useSetting | ||||||
| @useStatusWithEvent("mouseModChange", "actuatorStartChange") | @useStatusWithEvent("mouseModChange") | ||||||
| class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixinStatusProps> { | class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixinStatusProps> { | ||||||
| 
 | 
 | ||||||
|     render(): ReactNode { |     render(): ReactNode { | ||||||
| @ -34,13 +34,7 @@ class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixi | |||||||
|         > |         > | ||||||
|             <div> |             <div> | ||||||
|                 {this.getRenderButton({ iconName: "Save", i18NKey: "Command.Bar.Save.Info" })} |                 {this.getRenderButton({ iconName: "Save", i18NKey: "Command.Bar.Save.Info" })} | ||||||
|                 {this.getRenderButton({ |                 {this.getRenderButton({ iconName: "Play", i18NKey: "Command.Bar.Play.Info" })} | ||||||
|                     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({ |                 {this.getRenderButton({ | ||||||
|                     iconName: "HandsFree", i18NKey: "Command.Bar.Drag.Info",  |                     iconName: "HandsFree", i18NKey: "Command.Bar.Drag.Info",  | ||||||
|                     active: mouseMod === MouseMod.Drag, |                     active: mouseMod === MouseMod.Drag, | ||||||
|  | |||||||
| @ -42,7 +42,7 @@ class HeaderBar extends Component< | |||||||
|         return (t: number) => { |         return (t: number) => { | ||||||
|             let newState: HeaderBarState = {} as any; |             let newState: HeaderBarState = {} as any; | ||||||
|             newState[type] = 1 / t; |             newState[type] = 1 / t; | ||||||
|             if (this.updateTime > 20) { |             if (this.updateTime > 60) { | ||||||
|                 this.updateTime = 0; |                 this.updateTime = 0; | ||||||
|                 this.setState(newState); |                 this.setState(newState); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -12,7 +12,6 @@ import { I18N } from "@Component/Localization/Localization"; | |||||||
| import { superConnectWithEvent, superConnect } from "./Context"; | import { superConnectWithEvent, superConnect } from "./Context"; | ||||||
| import { PopupController } from "./Popups"; | import { PopupController } from "./Popups"; | ||||||
| import { Behavior } from "@Model/Behavior"; | import { Behavior } from "@Model/Behavior"; | ||||||
| import { Actuator } from "@Model/Actuator"; |  | ||||||
| 
 | 
 | ||||||
| function randomColor(unNormal: boolean = false) { | function randomColor(unNormal: boolean = false) { | ||||||
|     const color = [ |     const color = [ | ||||||
| @ -45,7 +44,6 @@ interface IStatusEvent { | |||||||
|     individualChange: void; |     individualChange: void; | ||||||
|     behaviorChange: void; |     behaviorChange: void; | ||||||
|     popupChange: void; |     popupChange: void; | ||||||
|     actuatorStartChange: void; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class Status extends Emitter<IStatusEvent> { | class Status extends Emitter<IStatusEvent> { | ||||||
| @ -73,11 +71,6 @@ class Status extends Emitter<IStatusEvent> { | |||||||
|      */ |      */ | ||||||
|     public model: Model = new Model(); |     public model: Model = new Model(); | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * 执行器 |  | ||||||
|      */ |  | ||||||
|     public actuator: Actuator; |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * 弹窗 |      * 弹窗 | ||||||
|      */ |      */ | ||||||
| @ -111,14 +104,8 @@ class Status extends Emitter<IStatusEvent> { | |||||||
|     public constructor() { |     public constructor() { | ||||||
|         super(); |         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")); |         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 |      * 获取下一个 ID | ||||||
|      */ |      */ | ||||||
|     public getNextId() { |     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.behaviorId = this.behaviorInstance.behaviorId; | ||||||
|         this.behaviorName = this.behaviorInstance.behaviorName; |         this.behaviorName = this.behaviorInstance.behaviorName; | ||||||
|         this.describe = this.behaviorInstance.describe; |         this.describe = this.behaviorInstance.describe; | ||||||
|         this.category = this.behaviorInstance.category; |  | ||||||
|         this.terms = this.behaviorInstance.terms; |         this.terms = this.behaviorInstance.terms; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -372,7 +371,7 @@ class Behavior< | |||||||
|      * @param model 模型 |      * @param model 模型 | ||||||
|      * @param t 经过时间 |      * @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 model 模型 | ||||||
|      * @param t 经过时间 |      * @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 model 模型 | ||||||
|      * @param t 经过时间 |      * @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 | 	 * @param | ||||||
|      */ |      */ | ||||||
| 	public runner(t: number, effectType: "finalEffect" | "effect" | "afterEffect" ): void { | 	public runner(t: number, effectType: "beforeEffect" | "effect" | "afterEffect" ): void { | ||||||
| 		this.individuals.forEach((individual) => { | 		this.individuals.forEach((individual) => { | ||||||
| 			for(let j = 0; j < this.behaviors.length; j++) { | 			for(let j = 0; j < this.behaviors.length; j++) { | ||||||
| 				this.behaviors[j][effectType](individual, this, this.model, t); | 				this.behaviors[j][effectType](individual, this, this.model, t); | ||||||
|  | |||||||
| @ -12,9 +12,9 @@ class Individual { | |||||||
|      * @param y y 坐标 |      * @param y y 坐标 | ||||||
|      * @param z z 坐标 |      * @param z z 坐标 | ||||||
|      */ |      */ | ||||||
|     public vectorLength(x: number[]): number; |     public static vectorLength(x: number[]): number; | ||||||
|     public vectorLength(x: number, y: number, z: number): number; |     public static vectorLength(x: number, y: number, z: number): number; | ||||||
|     public vectorLength(x: number | number[], y?: number, z?: number): number { |     public static vectorLength(x: number | number[], y?: number, z?: number): number { | ||||||
|         if (Array.isArray(x)) { |         if (Array.isArray(x)) { | ||||||
|             return ((x[0] ?? 0)**2 + (x[1] ?? 0)**2 + (x[2] ?? 0)**2)**.5; |             return ((x[0] ?? 0)**2 + (x[1] ?? 0)**2 + (x[2] ?? 0)**2)**.5; | ||||||
|         } else { |         } else { | ||||||
| @ -28,10 +28,10 @@ class Individual { | |||||||
|      * @param y y 坐标 |      * @param y y 坐标 | ||||||
|      * @param z z 坐标 |      * @param z z 坐标 | ||||||
|      */ |      */ | ||||||
|     public vectorNormalize(x: number[]): [number, number, number]; |     public static vectorNormalize(x: number[]): [number, number, number]; | ||||||
|     public vectorNormalize(x: number, y: number, z: number): [number, number, number]; |     public static vectorNormalize(x: number, y: number, z: number): [number, number, number]; | ||||||
|     public vectorNormalize(x: number | number[], y?: number, z?: number): [number, number, number] { |     public static vectorNormalize(x: number | number[], y?: number, z?: number): [number, number, number] { | ||||||
|         let length = this.vectorLength(x as number, y as number, z as number); |         let length = Individual.vectorLength(x as number, y as number, z as number); | ||||||
|         if (Array.isArray(x)) { |         if (Array.isArray(x)) { | ||||||
|             return [ |             return [ | ||||||
|                 (x[0] ?? 0) / length, |                 (x[0] ?? 0) / length, | ||||||
| @ -52,39 +52,6 @@ class Individual { | |||||||
| 	 */ | 	 */ | ||||||
| 	public position: number[] = [0, 0, 0]; | 	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 目标位置 |      * @param position 目标位置 | ||||||
|      */ |      */ | ||||||
|     public distanceTo(position: Individual | number[]): number { |     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"; | import { Behavior, IAnyBehavior, IAnyBehaviorRecorder } from "./Behavior"; | ||||||
| 
 | 
 | ||||||
| type ModelEvent = { | type ModelEvent = { | ||||||
|  |     loop: number; | ||||||
|     labelChange: Label[]; |     labelChange: Label[]; | ||||||
|     objectChange: CtrlObject[]; |     objectChange: CtrlObject[]; | ||||||
|     individualChange: Group; |     individualChange: Group; | ||||||
| @ -258,7 +259,7 @@ class Model extends Emitter<ModelEvent> { | |||||||
|         for (let i = 0; i < this.objectPool.length; i++) { |         for (let i = 0; i < this.objectPool.length; i++) { | ||||||
|             let object = this.objectPool[i]; |             let object = this.objectPool[i]; | ||||||
|             if (object instanceof Group && object.update) { |             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++) { |         for (let i = 0; i < this.objectPool.length; i++) { | ||||||
|             let object = this.objectPool[i]; |             let object = this.objectPool[i]; | ||||||
|             if (object instanceof Group && object.update) { |             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++) { |         for (let i = 0; i < this.objectPool.length; i++) { | ||||||
|             let object = this.objectPool[i]; |             let object = this.objectPool[i]; | ||||||
|             if (object instanceof Group && object.update) { |             if (object instanceof Group && object.update) { | ||||||
|                 object.runner(t, "finalEffect"); |                 object.runner(t, "afterEffect"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.draw(); |         this.draw(); | ||||||
|  | 
 | ||||||
|  |         this.emit("loop", t); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public draw() { |     public draw() { | ||||||
|  | |||||||
| @ -10,7 +10,6 @@ import { RootContainer } from "@Component/Container/RootContainer"; | |||||||
| import { LayoutDirection } from "@Context/Layout"; | import { LayoutDirection } from "@Context/Layout"; | ||||||
| import { CommandBar } from "@Component/CommandBar/CommandBar"; | import { CommandBar } from "@Component/CommandBar/CommandBar"; | ||||||
| import { Popup } from "@Component/Popup/Popup"; | import { Popup } from "@Component/Popup/Popup"; | ||||||
| import { AllBehaviors } from "@Behavior/Behavior"; |  | ||||||
| import "./SimulatorWeb.scss"; | import "./SimulatorWeb.scss"; | ||||||
| 
 | 
 | ||||||
| initializeIcons("https://img.mrkbear.com/fabric-cdn-prod_20210407.001/"); | 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.model.update(0); | ||||||
|             this.status.newLabel().name = "New Label"; |             this.status.newLabel().name = "New Label"; | ||||||
|             this.status.newLabel().name = "Test Label 01"; |             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; |         (window as any).s = this; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user