Compare commits
	
		
			7 Commits
		
	
	
		
			d1ffc85df4
			...
			e8935403ea
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e8935403ea | |||
| 851ca149b9 | |||
| 63f13183d0 | |||
| 1b9c47f688 | |||
| a91143f6c0 | |||
| d7d6ac4058 | |||
| c44c10ad42 | 
| @ -4,7 +4,7 @@ div.color-input { | ||||
| 
 | ||||
|     div.color-view { | ||||
|         width: $line-min-height; | ||||
|         max-width: $line-min-height; | ||||
|         min-width: $line-min-height; | ||||
|         display: flex; | ||||
|         justify-content: center; | ||||
|         align-items: center; | ||||
| @ -24,7 +24,12 @@ div.color-input { | ||||
|         align-items: center; | ||||
| 
 | ||||
|         div.text-box { | ||||
|             max-width: calc( 100% - 24px); | ||||
|             text-overflow: ellipsis; | ||||
|             overflow: hidden; | ||||
|             padding-left: 1px; | ||||
|             white-space: nowrap; | ||||
|             word-break: keep-all; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -11,7 +11,7 @@ div.details-list { | ||||
|         min-height: 30px; | ||||
| 
 | ||||
|         div.details-list-value { | ||||
|             padding: 5px 5px; | ||||
|             padding: 5px 0; | ||||
|             display: flex; | ||||
|             justify-content: center; | ||||
|             align-items: center; | ||||
|  | ||||
| @ -23,10 +23,13 @@ interface IDetailsListProps { | ||||
|     click?: () => void; | ||||
|     clickLine?: (item: IItems) => any; | ||||
|     checkBox?: (data: IItems) => any; | ||||
|     renderCheckbox?: (data: IItems, click: () => void) => ReactNode; | ||||
| } | ||||
| 
 | ||||
| class DetailsList extends Component<IDetailsListProps> { | ||||
| 
 | ||||
|     private propagationCount = 0; | ||||
| 
 | ||||
|     private renderValue<D extends IItems, K extends keyof D>(item: IItems, column: IColumns<D, K>) { | ||||
|         const classList: string[] = []; | ||||
|         if (!column.noDefaultStyle) { | ||||
| @ -43,15 +46,36 @@ class DetailsList extends Component<IDetailsListProps> { | ||||
|         </div> | ||||
|     } | ||||
| 
 | ||||
|     private handelClickCheckbox(item: IItems) { | ||||
|         if (this.propagationCount <= 2 && this.props.checkBox) { | ||||
|             this.props.checkBox(item); | ||||
|             this.propagationCount = 2; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private renderCheckbox(item: IItems) { | ||||
|         const { checkboxClassName } = this.props; | ||||
|         return <div  | ||||
|             className={"details-list-checkbox" + (checkboxClassName ? ` ${checkboxClassName}` : "")} | ||||
|             onClick={() => this.handelClickCheckbox(item)} | ||||
|         > | ||||
|             <Icon iconName="CheckMark"></Icon> | ||||
|         </div>; | ||||
|     } | ||||
| 
 | ||||
|     public render(): ReactNode { | ||||
|         return <Theme | ||||
|             className={"details-list" + (this.props.className ? ` ${this.props.className}` : "")} | ||||
|             onClick={this.props.click} | ||||
|             onClick={() => { | ||||
|                 if (this.propagationCount <= 0 && this.props.click) { | ||||
|                     this.props.click(); | ||||
|                 } | ||||
|                 this.propagationCount = 0; | ||||
|             }} | ||||
|             backgroundLevel={BackgroundLevel.Level4} | ||||
|             fontLevel={FontLevel.normal} | ||||
|         >{ | ||||
|             this.props.items.map((item) => { | ||||
|                 const { checkboxClassName } = this.props; | ||||
|                 const classList: string[] = ["details-list-item"]; | ||||
|                 if (item.select) { | ||||
|                     classList.push("active"); | ||||
| @ -59,10 +83,10 @@ class DetailsList extends Component<IDetailsListProps> { | ||||
|                 return <div | ||||
|                     className={classList.join(" ")} | ||||
|                     key={item.key} | ||||
|                     onClick={(e) => { | ||||
|                         if (this.props.clickLine) { | ||||
|                             e.stopPropagation(); | ||||
|                     onClick={() => { | ||||
|                         if (this.propagationCount <= 1 && this.props.clickLine) { | ||||
|                             this.props.clickLine(item); | ||||
|                             this.propagationCount = 1; | ||||
|                         } | ||||
|                     }} | ||||
|                 > | ||||
| @ -75,17 +99,9 @@ class DetailsList extends Component<IDetailsListProps> { | ||||
|                     } | ||||
|                     { | ||||
|                         this.props.hideCheckBox ? null :  | ||||
|                         <div  | ||||
|                             className={"details-list-checkbox" + (checkboxClassName ? ` ${checkboxClassName}` : "")} | ||||
|                             onClick={(e) => { | ||||
|                                 if (this.props.checkBox) { | ||||
|                                     e.stopPropagation(); | ||||
|                                     this.props.checkBox(item); | ||||
|                                 } | ||||
|                             }} | ||||
|                         > | ||||
|                             <Icon iconName="CheckMark"></Icon> | ||||
|                         </div> | ||||
|                             this.props.renderCheckbox ?  | ||||
|                                 this.props.renderCheckbox(item, () => this.handelClickCheckbox(item)) : | ||||
|                                 this.renderCheckbox(item) | ||||
|                     } | ||||
|                     { | ||||
|                         this.props.columns.map((column) => { | ||||
|  | ||||
| @ -13,7 +13,7 @@ import "./ObjectPicker.scss"; | ||||
| type IObjectType = Label | Group | Range | CtrlObject; | ||||
| 
 | ||||
| interface IObjectPickerProps extends ITextFieldProps { | ||||
|     type: Array<"L" | "G" | "R">; | ||||
|     type: string; | ||||
|     value?: IObjectType; | ||||
|     valueChange?: (value: IObjectType) => any; | ||||
|     cleanValue?: () => any; | ||||
| @ -28,17 +28,23 @@ class ObjectPicker extends Component<IObjectPickerProps & IMixinStatusProps, IOb | ||||
| 
 | ||||
|     private getAllOption() { | ||||
|         let option: Array<IObjectType> = []; | ||||
|         if (this.props.status) { | ||||
|         if (!this.props.status) return option; | ||||
| 
 | ||||
|             for (let i = 0; i < this.props.type.length; i++) { | ||||
| 
 | ||||
|                 if (this.props.type[i] === "L") { | ||||
|         if (this.props.type.includes("L")) { | ||||
|             for (let j = 0; j < this.props.status.model.labelPool.length; j++) { | ||||
|                 option.push(this.props.status.model.labelPool[j]); | ||||
|             } | ||||
| 
 | ||||
|             if (this.props.type.includes("R")) { | ||||
|                 option.push(this.props.status.model.allRangeLabel); | ||||
|             } | ||||
| 
 | ||||
|                 if (this.props.type[i] === "R") { | ||||
|             if (this.props.type.includes("G")) { | ||||
|                 option.push(this.props.status.model.allGroupLabel); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (this.props.type.includes("R")) { | ||||
|             for (let j = 0; j < this.props.status.model.objectPool.length; j++) { | ||||
|                 if (this.props.status.model.objectPool[j] instanceof Range) { | ||||
|                     option.push(this.props.status.model.objectPool[j]); | ||||
| @ -46,15 +52,14 @@ class ObjectPicker extends Component<IObjectPickerProps & IMixinStatusProps, IOb | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|                 if (this.props.type[i] === "G") { | ||||
|         if (this.props.type.includes("G")) { | ||||
|             for (let j = 0; j < this.props.status.model.objectPool.length; j++) { | ||||
|                 if (this.props.status.model.objectPool[j] instanceof Group) { | ||||
|                     option.push(this.props.status.model.objectPool[j]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         return option; | ||||
|     } | ||||
| 
 | ||||
| @ -100,9 +105,10 @@ class ObjectPicker extends Component<IObjectPickerProps & IMixinStatusProps, IOb | ||||
|             isDelete = this.props.value.isDeleted(); | ||||
|         } else { | ||||
|             disPlayInfo = { | ||||
|                 name: "", | ||||
|                 name: "Input.Error.Select", | ||||
|                 icon: "Label", | ||||
|                 color: "rgba(0,0,0,0)" | ||||
|                 color: "transparent", | ||||
|                 needI18n: true | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -139,9 +145,9 @@ class ObjectPicker extends Component<IObjectPickerProps & IMixinStatusProps, IOb | ||||
|                     }} | ||||
|                 > | ||||
|                     {    | ||||
|                         disPlayInfo.name ?  | ||||
|                             <span>{disPlayInfo.name}</span> : | ||||
|                             <Localization i18nKey="Input.Error.Select"/> | ||||
|                         disPlayInfo.needI18n ?  | ||||
|                             <Localization i18nKey={disPlayInfo.name as any}/> : | ||||
|                             <span>{disPlayInfo.name}</span> | ||||
|                     } | ||||
|                 </div> | ||||
|                 <div | ||||
|  | ||||
| @ -7,9 +7,15 @@ import { Range } from "@Model/Range"; | ||||
| import { Component, ReactNode, RefObject } from "react"; | ||||
| import "./PickerList.scss"; | ||||
| 
 | ||||
| type IDisplayInfo = Record<"color" | "icon" | "name", string>; | ||||
| type IPickerListItem = CtrlObject | Label | Range | Group; | ||||
| type IDisplayItem = { | ||||
| interface IDisplayInfo { | ||||
|     color: string; | ||||
|     icon: string; | ||||
|     name: string; | ||||
|     needI18n?: boolean; | ||||
| }; | ||||
| 
 | ||||
| interface IDisplayItem { | ||||
|     nameKey: AllI18nKeys; | ||||
|     key: string; | ||||
| 	mark?: boolean; | ||||
| @ -20,6 +26,7 @@ function getObjectDisplayInfo(item: IPickerListItem): IDisplayInfo { | ||||
| 	let color: number[] = []; | ||||
| 	let icon: string = "tag"; | ||||
| 	let name: string = ""; | ||||
|     let needI18n: boolean = false; | ||||
| 
 | ||||
| 	if (item instanceof Range) { | ||||
| 		icon = "CubeShape" | ||||
| @ -34,15 +41,30 @@ function getObjectDisplayInfo(item: IPickerListItem): IDisplayInfo { | ||||
| 		name = item.displayName; | ||||
| 	} | ||||
| 	if (item instanceof Label) { | ||||
| 
 | ||||
| 		if (item.isBuildIn) { | ||||
|             needI18n = true; | ||||
|             if (item.id === "AllRange") { | ||||
|                 icon = "ProductList"; | ||||
|                 name = "Build.In.Label.Name.All.Range"; | ||||
|             } else if (item.id === "AllGroup") { | ||||
|                 icon = "SizeLegacy"; | ||||
|                 name = "Build.In.Label.Name.All.Group"; | ||||
|             } | ||||
|         }  | ||||
|          | ||||
|         else { | ||||
|             icon = "tag"; | ||||
|             color = item.color.concat([]); | ||||
|             name = item.name; | ||||
|         } | ||||
| 	} | ||||
| 
 | ||||
| 	return { | ||||
| 		color: `rgb(${color[0]},${color[1]},${color[2]})`, | ||||
| 		color: needI18n ? "transparent" : `rgb(${color[0]},${color[1]},${color[2]})`, | ||||
| 		icon: icon, | ||||
| 		name: name | ||||
| 		name: name, | ||||
|         needI18n: needI18n | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -79,7 +101,11 @@ class PickerList extends Component<IPickerListProps> { | ||||
| 				<Icon iconName={displayInfo.icon}/> | ||||
| 			</div> | ||||
| 			<div className="list-item-name"> | ||||
| 				{displayInfo.name} | ||||
| 				{ | ||||
|                     displayInfo.needI18n ?  | ||||
|                         <Localization i18nKey={displayInfo.name as any}/> :  | ||||
|                         displayInfo.name | ||||
|                 } | ||||
| 			</div> | ||||
| 		</div>; | ||||
| 	} | ||||
|  | ||||
| @ -37,6 +37,7 @@ interface IStatusEvent { | ||||
|     rangeAttrChange: void; | ||||
|     labelAttrChange: void; | ||||
|     groupAttrChange: void; | ||||
|     individualChange: void; | ||||
| } | ||||
| 
 | ||||
| class Status extends Emitter<IStatusEvent> { | ||||
| @ -74,6 +75,16 @@ class Status extends Emitter<IStatusEvent> { | ||||
|      */ | ||||
|     public focusLabel?: Label; | ||||
| 
 | ||||
|     private drawtimer?: NodeJS.Timeout; | ||||
| 
 | ||||
|     private delayDraw = () => { | ||||
|         this.drawtimer ? clearTimeout(this.drawtimer) : null; | ||||
|         this.drawtimer = setTimeout(() => { | ||||
|             this.model.draw(); | ||||
|             this.drawtimer = undefined; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public constructor() { | ||||
|         super(); | ||||
| 
 | ||||
| @ -85,11 +96,11 @@ class Status extends Emitter<IStatusEvent> { | ||||
|         this.model.on("labelChange", () => this.emit("labelChange")); | ||||
| 
 | ||||
|         // 对象变换时执行渲染,更新渲染器数据
 | ||||
|         this.on("objectChange", () => { | ||||
|             setTimeout(() => { | ||||
|                 this.model.draw(); | ||||
|         this.on("objectChange", this.delayDraw); | ||||
|         this.model.on("individualChange", this.delayDraw); | ||||
|         this.model.on("individualChange", () => { | ||||
|             this.emit("individualChange"); | ||||
|         }); | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     public bindRenderer(renderer: AbstractRenderer) { | ||||
|  | ||||
| @ -44,6 +44,8 @@ const EN_US = { | ||||
|     "Panel.Info.Label.Details.View": "Edit view label attributes", | ||||
|     "Panel.Title.Group.Details.View": "Group", | ||||
|     "Panel.Info.Group.Details.View": "Edit view group attributes", | ||||
|     "Build.In.Label.Name.All.Group": "All group", | ||||
|     "Build.In.Label.Name.All.Range": "All range", | ||||
|     "Common.No.Data": "No Data", | ||||
|     "Common.No.Unknown.Error": "Unknown error", | ||||
|     "Common.Attr.Title.Basic": "Basic properties", | ||||
| @ -73,6 +75,10 @@ const EN_US = { | ||||
|     "Common.Attr.Key.Generation.Point.X": "Generation Point X", | ||||
|     "Common.Attr.Key.Generation.Point.Y": "Generation Point Y", | ||||
|     "Common.Attr.Key.Generation.Point.Z": "Generation Point Z", | ||||
|     "Common.Attr.Key.Generation.Error.Empty.Object": "Please select a range object or label to add to the object", | ||||
|     "Common.Attr.Key.Generation.Error.Empty.Range.List": "The specified label does not contain any scope objects", | ||||
|     "Common.Attr.Key.Generation.Error.Invalid.Range": "The specified scope object is invalid", | ||||
|     "Common.Attr.Key.Generation.Error.Invalid.Label": "The specified label has expired", | ||||
|     "Panel.Info.Range.Details.Attr.Error.Not.Range": "Object is not a Range", | ||||
|     "Panel.Info.Range.Details.Attr.Error.Unspecified": "Unspecified range object", | ||||
|     "Panel.Info.Group.Details.Attr.Error.Not.Group": "Object is not a Group", | ||||
|  | ||||
| @ -44,6 +44,8 @@ const ZH_CN = { | ||||
|     "Panel.Info.Label.Details.View": "编辑查看标签属性", | ||||
|     "Panel.Title.Group.Details.View": "群", | ||||
|     "Panel.Info.Group.Details.View": "编辑查看群属性", | ||||
|     "Build.In.Label.Name.All.Group": "全部群", | ||||
|     "Build.In.Label.Name.All.Range": "全部范围", | ||||
|     "Common.No.Data": "暂无数据", | ||||
|     "Common.No.Unknown.Error": "未知错误", | ||||
|     "Common.Attr.Title.Basic": "基础属性", | ||||
| @ -73,6 +75,10 @@ const ZH_CN = { | ||||
|     "Common.Attr.Key.Generation.Point.X": "生成位置 X 坐标", | ||||
|     "Common.Attr.Key.Generation.Point.Y": "生成位置 Y 坐标", | ||||
|     "Common.Attr.Key.Generation.Point.Z": "生成位置 Z 坐标", | ||||
|     "Common.Attr.Key.Generation.Error.Empty.Object": "请选择一个范围对象或添加至对象的标签", | ||||
|     "Common.Attr.Key.Generation.Error.Empty.Range.List": "指定的标签中没有包含任何范围对象", | ||||
|     "Common.Attr.Key.Generation.Error.Invalid.Range": "指定的范围对象已失效", | ||||
|     "Common.Attr.Key.Generation.Error.Invalid.Label": "指定的标签已失效", | ||||
|     "Panel.Info.Range.Details.Attr.Error.Not.Range": "对象不是一个范围", | ||||
|     "Panel.Info.Range.Details.Attr.Error.Unspecified": "未指定范围对象", | ||||
|     "Panel.Info.Group.Details.Attr.Error.Not.Group": "对象不是一个群", | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| import { Individual } from "./Individual"; | ||||
| import { CtrlObject } from "./CtrlObject"; | ||||
| import type { Behavior } from "./Behavior";  | ||||
| import type { Label } from "./Label"; | ||||
| import { Label } from "./Label"; | ||||
| import { Range } from "./Range"; | ||||
| 
 | ||||
| enum GenMod { | ||||
|     Point = "p", | ||||
| @ -36,7 +37,156 @@ class Group extends CtrlObject { | ||||
|     /** | ||||
|      * 生成个数 | ||||
|      */ | ||||
|     public genCount?: number = 0; | ||||
|     public genCount: number = 1; | ||||
| 
 | ||||
|     /** | ||||
|      * 生成错误信息 | ||||
|      */ | ||||
|     public genErrorMessage?: string; | ||||
| 
 | ||||
|     /** | ||||
|      * 生成错误信息 | ||||
|      */ | ||||
|     public genErrorMessageShowCount: number = 0; | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 创建个体 | ||||
|  | ||||
| @ -6,6 +6,11 @@ import { ObjectID } from "./Renderer"; | ||||
|  */ | ||||
| class Label { | ||||
| 
 | ||||
|     /** | ||||
|      * 是否为内置标签 | ||||
|      */ | ||||
|     public isBuildIn: boolean = false; | ||||
|      | ||||
|     /** | ||||
|      * 唯一标识符 | ||||
|      */ | ||||
| @ -41,6 +46,7 @@ class Label { | ||||
|      * 判断是否为相同标签 | ||||
|      */ | ||||
|     public equal(label: Label): boolean { | ||||
|         if (this.isDeleted() || label.isDeleted()) return false; | ||||
|         return this === label || this.id === label.id; | ||||
|     } | ||||
| 
 | ||||
| @ -50,16 +56,32 @@ class Label { | ||||
|     private deleteFlag: boolean = false; | ||||
| 
 | ||||
|     /** | ||||
|      * 是否被删除 | ||||
|      * 测试是否被删除 | ||||
|      */ | ||||
|     public isDeleted(): boolean { | ||||
|         if (this.deleteFlag) return true; | ||||
|     public testDelete() { | ||||
|         for (let i = 0; i < this.model.labelPool.length; i++) { | ||||
|             if (this.model.labelPool[i].equal(this)) return false; | ||||
|         } | ||||
|         this.deleteFlag = true; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 是否被删除 | ||||
|      */ | ||||
|     public isDeleted(): boolean { | ||||
|         if (this.isBuildIn) return false; | ||||
|         if (this.deleteFlag) return true; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 设置为内置标签 | ||||
|      */ | ||||
|     public setBuildInLabel(): this { | ||||
|         this.isBuildIn = true; | ||||
|         return this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -101,9 +123,10 @@ class LabelObject { | ||||
|      * 是否存在标签 | ||||
|      */ | ||||
|     public hasLabel(label: Label): boolean { | ||||
|         if (label.isDeleted()) return false; | ||||
|         let has = false; | ||||
|         this.labels.forEach((localLabel) => { | ||||
|             if (localLabel.equal(label)) has = true; | ||||
|             if (!localLabel.isDeleted() && localLabel.equal(label)) has = true; | ||||
|         }); | ||||
|         return has; | ||||
|     } | ||||
|  | ||||
| @ -17,6 +17,7 @@ type ModelEvent = { | ||||
|     objectAdd: CtrlObject; | ||||
|     objectDelete: CtrlObject[]; | ||||
|     objectChange: CtrlObject[]; | ||||
|     individualChange: Group; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
| @ -50,6 +51,16 @@ class Model extends Emitter<ModelEvent> { | ||||
|      */ | ||||
|     public labelPool: Label[] = []; | ||||
| 
 | ||||
|     /** | ||||
|      * 内置标签-全部范围标签 | ||||
|      */ | ||||
|     public allRangeLabel = new Label(this, "AllRange").setBuildInLabel(); | ||||
| 
 | ||||
|     /** | ||||
|      * 内置标签-全部群标签 | ||||
|      */ | ||||
|     public allGroupLabel = new Label(this, "AllGroup").setBuildInLabel(); | ||||
| 
 | ||||
|     /** | ||||
|      * 添加标签 | ||||
|      */ | ||||
| @ -84,6 +95,7 @@ class Model extends Emitter<ModelEvent> { | ||||
| 
 | ||||
|         if (deletedLabel) { | ||||
|             this.labelPool.splice(index, 1); | ||||
|             deletedLabel.testDelete(); | ||||
|             console.log(`Model: Delete label ${deletedLabel.name ?? deletedLabel.id}`); | ||||
|             this.emit("labelDelete", deletedLabel); | ||||
|             this.emit("labelChange", this.labelPool); | ||||
| @ -93,23 +105,24 @@ class Model extends Emitter<ModelEvent> { | ||||
|     /** | ||||
|      * 通过标签获取指定类型的对象 | ||||
|      * @param label 标签 | ||||
|      * @param type 筛选类型 | ||||
|      */ | ||||
|     public getObjectByLabel( | ||||
|         label: Label, type?:  | ||||
|             (new (...p: any) => Range) |  | ||||
|             (new (...p: any) => Group) | ||||
|     ): CtrlObject[] { | ||||
|     public getObjectByLabel(label: Label): CtrlObject[] { | ||||
| 
 | ||||
|         if (label.isDeleted()) return []; | ||||
| 
 | ||||
|         const res: CtrlObject[] = []; | ||||
|         for (let i = 0; i < this.objectPool.length; i++) { | ||||
|             if (this.objectPool[i].hasLabel(label)) { | ||||
|                 if (type) { | ||||
|                     if (this.objectPool[i] instanceof type) { | ||||
| 
 | ||||
|             if (label.equal(this.allGroupLabel) && this.objectPool[i] instanceof Group) { | ||||
|                 res.push(this.objectPool[i]); | ||||
|             } | ||||
|                 } else { | ||||
| 
 | ||||
|             else if (label.equal(this.allRangeLabel) && this.objectPool[i] instanceof Range) { | ||||
|                 res.push(this.objectPool[i]); | ||||
|             } | ||||
| 
 | ||||
|             else if (this.objectPool[i].hasLabel(label)) { | ||||
|                 res.push(this.objectPool[i]); | ||||
|             } | ||||
|         } | ||||
|         return res; | ||||
|  | ||||
| @ -116,7 +116,7 @@ class GroupDetails extends Component<IGroupDetailsProps & IMixinStatusProps> { | ||||
| 
 | ||||
|             <AttrInput | ||||
|                 id={group.id} isNumber={true} step={1} keyI18n="Common.Attr.Key.Generation.Count" | ||||
|                 value={group.genCount} min={0} max={1000} | ||||
|                 value={group.genCount} min={1} max={1000} | ||||
|                 valueChange={(val) => { | ||||
|                     this.props.status?.changeGroupAttrib(group.id, "genCount", (val as any) / 1); | ||||
|                 }} | ||||
| @ -130,7 +130,9 @@ class GroupDetails extends Component<IGroupDetailsProps & IMixinStatusProps> { | ||||
| 				keyI18n="Common.Attr.Key.Generation" | ||||
| 				onIconName="BuildDefinition" offIconName="BuildDefinition" | ||||
| 				valueChange={() => { | ||||
| 					console.log("gen"); | ||||
| 					if(!group.genIndividuals()) { | ||||
|                         this.props.status?.changeGroupAttrib(group.id, "genErrorMessageShowCount", 1); | ||||
|                     } | ||||
| 				}} | ||||
| 			/> | ||||
| 
 | ||||
| @ -169,11 +171,23 @@ class GroupDetails extends Component<IGroupDetailsProps & IMixinStatusProps> { | ||||
|     } | ||||
| 
 | ||||
|     private renderRangeGenOption(group: Group) { | ||||
| 
 | ||||
|         let isRenderErrorInfo: boolean = false; | ||||
|         if (group.genErrorMessageShowCount > 0) { | ||||
|             group.genErrorMessageShowCount --; | ||||
|             if (group.genErrorMessage) { | ||||
|                 isRenderErrorInfo = true; | ||||
|             } | ||||
|         } else { | ||||
|             group.genErrorMessage = undefined; | ||||
|         } | ||||
| 
 | ||||
|         return <> | ||||
|             <ObjectPicker | ||||
|                 keyI18n="Common.Attr.Key.Generation.Use.Range" | ||||
|                 type={["L", "R"]} | ||||
|                 type={"LR"} | ||||
|                 value={group.genRange} | ||||
|                 errorI18n={isRenderErrorInfo ? group.genErrorMessage as any : undefined} | ||||
|                 valueChange={(value) => { | ||||
|                     this.props.status?.changeGroupAttrib(group.id, "genRange", value); | ||||
|                 }} | ||||
|  | ||||
| @ -1,15 +1,46 @@ | ||||
| @import "../../Component/Theme/Theme.scss"; | ||||
| 
 | ||||
| div.object-list-color-value { | ||||
| div.object-list { | ||||
|     min-height: 100%; | ||||
| 
 | ||||
|     div.object-list-color-value { | ||||
|         height: calc( 100% - 6px); | ||||
|         margin: 3px 0; | ||||
|         margin-left: 3px; | ||||
|         border-radius: 1000px; | ||||
|         width: 3px; | ||||
| } | ||||
|     } | ||||
| 
 | ||||
| div.object-list { | ||||
|     min-height: 100%; | ||||
|     div.object-list-checkbox { | ||||
|         opacity: 1 !important; | ||||
|         padding-left: 2px; | ||||
| 
 | ||||
|         i.checkbox-icon { | ||||
|             display: none; | ||||
|             opacity: 0; | ||||
|         } | ||||
| 
 | ||||
|         i.type-icon { | ||||
|             display: inline-block; | ||||
|             opacity: 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     div.details-list-item:hover { | ||||
| 
 | ||||
|         div.object-list-checkbox { | ||||
|              | ||||
|             i.checkbox-icon { | ||||
|                 display: inline-block; | ||||
|                 opacity: 1; | ||||
|             } | ||||
| 
 | ||||
|             i.type-icon { | ||||
|                 display: none; | ||||
|                 opacity: 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| div.object-list-command-bar { | ||||
|  | ||||
| @ -4,6 +4,7 @@ import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; | ||||
| import { useSetting, IMixinSettingProps } from "@Context/Setting"; | ||||
| import { Localization } from "@Component/Localization/Localization"; | ||||
| import { ObjectID } from "@Model/Renderer"; | ||||
| import { Icon } from "@fluentui/react"; | ||||
| import "./ObjectList.scss"; | ||||
| 
 | ||||
| @useSetting | ||||
| @ -71,6 +72,22 @@ class ObjectList extends Component<IMixinStatusProps & IMixinSettingProps> { | ||||
|                     this.props.status.setFocusObject(new Set<ObjectID>()); | ||||
|                 } | ||||
|             }} | ||||
|             renderCheckbox={(item, click) => { | ||||
|                 let icon = "CheckMark"; | ||||
|                 if (item.key.slice(0, 1) === "R") { | ||||
|                     icon = "CubeShape"; | ||||
|                 } | ||||
|                 if (item.key.slice(0, 1) === "G") { | ||||
|                     icon = "WebAppBuilderFragment"; | ||||
|                 } | ||||
|                 return <div  | ||||
|                     className="object-list-checkbox details-list-checkbox" | ||||
|                     onClick={click} | ||||
|                 > | ||||
|                     <Icon className="checkbox-icon" iconName="CheckMark"></Icon> | ||||
|                     <Icon className="type-icon" iconName={icon}></Icon> | ||||
|                 </div>; | ||||
|             }} | ||||
|             columns={[ | ||||
|                 { | ||||
|                     key: "color", | ||||
|  | ||||
| @ -13,50 +13,24 @@ import "./RangeDetails.scss"; | ||||
| @useStatusWithEvent("rangeAttrChange", "focusObjectChange", "rangeLabelChange") | ||||
| class RangeDetails extends Component<IMixinStatusProps> { | ||||
| 
 | ||||
|     private renderAttrInput( | ||||
|         id: ObjectID, key: AllI18nKeys, val: string | number | undefined, | ||||
|         change: (val: string, status: Status) => any, | ||||
|         step?: number, max?: number, min?: number | ||||
|     ) { | ||||
|         const handelFunc = (e: string) => { | ||||
|             if (this.props.status) { | ||||
|                 change(e, this.props.status); | ||||
|             } | ||||
|         } | ||||
|         if (step) { | ||||
|             return <AttrInput | ||||
|                 id={id} isNumber={true} step={step} keyI18n={key} | ||||
|                 value={val} max={max} min={min} | ||||
|                 valueChange={handelFunc} | ||||
|             /> | ||||
|         } else { | ||||
|             return <AttrInput | ||||
|                 id={id} keyI18n={key} value={val} | ||||
|                 valueChange={handelFunc} | ||||
|             /> | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private renderFrom(range: Range) { | ||||
| 
 | ||||
|         return <> | ||||
| 
 | ||||
|             <Message i18nKey="Common.Attr.Title.Basic" isTitle first/> | ||||
| 
 | ||||
|             {this.renderAttrInput( | ||||
|                 range.id, "Common.Attr.Key.Display.Name", range.displayName, | ||||
|                 (val, status) => { | ||||
|                     status.changeRangeAttrib(range.id, "displayName", val); | ||||
|                 } | ||||
|             )} | ||||
|             <AttrInput | ||||
|                 id={range.id} keyI18n="Common.Attr.Key.Display.Name" value={range.displayName} | ||||
|                 valueChange={(val) => { | ||||
|                     this.props.status?.changeRangeAttrib(range.id, "displayName", val); | ||||
|                 }} | ||||
|             /> | ||||
| 
 | ||||
|             <ColorInput | ||||
|                 keyI18n="Common.Attr.Key.Color" | ||||
|                 value={range.color} normal | ||||
|                 valueChange={(color) => { | ||||
|                     if (this.props.status) { | ||||
|                         this.props.status.changeRangeAttrib(range.id, "color", color); | ||||
|                     } | ||||
|                     this.props.status?.changeRangeAttrib(range.id, "color", color); | ||||
|                 }} | ||||
|             /> | ||||
| 
 | ||||
| @ -64,32 +38,17 @@ class RangeDetails extends Component<IMixinStatusProps> { | ||||
|                 keyI18n="Common.Attr.Key.Label" | ||||
|                 labels={range.allLabels()} | ||||
|                 labelAdd={(label) => { | ||||
|                     if (this.props.status) { | ||||
|                         this.props.status.addRangeLabel(range.id, label); | ||||
|                     } | ||||
|                     this.props.status?.addRangeLabel(range.id, label); | ||||
|                 }} | ||||
|                 labelDelete={(label) => { | ||||
|                     if (this.props.status) { | ||||
|                         this.props.status.deleteRangeLabel(range.id, label); | ||||
|                     } | ||||
|                     this.props.status?.deleteRangeLabel(range.id, label); | ||||
|                 }} | ||||
|             /> | ||||
|              | ||||
|             <TogglesInput | ||||
|                 keyI18n="Common.Attr.Key.Display" | ||||
|                 value={range.display} valueChange={(val) => { | ||||
|                     if (this.props.status) { | ||||
|                         this.props.status.changeRangeAttrib(range.id, "display", val); | ||||
|                     } | ||||
|                 }} | ||||
|             /> | ||||
| 
 | ||||
|             <TogglesInput | ||||
|                 keyI18n="Common.Attr.Key.Update" | ||||
|                 value={range.update} valueChange={(val) => { | ||||
|                     if (this.props.status) { | ||||
|                         this.props.status.changeRangeAttrib(range.id, "update", val); | ||||
|                     } | ||||
|                     this.props.status?.changeRangeAttrib(range.id, "display", val); | ||||
|                 }} | ||||
|             /> | ||||
| 
 | ||||
| @ -106,53 +65,59 @@ class RangeDetails extends Component<IMixinStatusProps> { | ||||
| 
 | ||||
|             <Message i18nKey="Common.Attr.Title.Spatial" isTitle/> | ||||
| 
 | ||||
|             {this.renderAttrInput( | ||||
|                 range.id, "Common.Attr.Key.Position.X",  | ||||
|                 range.position[0], (val, status) => { | ||||
|             <AttrInput | ||||
|                 id={range.id} isNumber={true} step={.1} keyI18n={"Common.Attr.Key.Position.X"} | ||||
|                 value={range.position[0]} | ||||
|                 valueChange={(val) => { | ||||
|                     range.position[0] = (val as any) / 1; | ||||
|                     status.changeRangeAttrib(range.id, "position", range.position); | ||||
|                 }, .1 | ||||
|             )} | ||||
|                     this.props.status?.changeRangeAttrib(range.id, "position", range.position); | ||||
|                 }} | ||||
|             /> | ||||
| 
 | ||||
|             {this.renderAttrInput( | ||||
|                 range.id, "Common.Attr.Key.Position.Y", | ||||
|                 range.position[1],(val, status) => { | ||||
|             <AttrInput | ||||
|                 id={range.id} isNumber={true} step={.1} keyI18n={"Common.Attr.Key.Position.Y"} | ||||
|                 value={range.position[1]} | ||||
|                 valueChange={(val) => { | ||||
|                     range.position[1] = (val as any) / 1; | ||||
|                     status.changeRangeAttrib(range.id, "position", range.position); | ||||
|                 }, .1 | ||||
|             )} | ||||
|                     this.props.status?.changeRangeAttrib(range.id, "position", range.position); | ||||
|                 }} | ||||
|             /> | ||||
| 
 | ||||
|             {this.renderAttrInput( | ||||
|                 range.id, "Common.Attr.Key.Position.Z", | ||||
|                 range.position[2], (val, status) => { | ||||
|             <AttrInput | ||||
|                 id={range.id} isNumber={true} step={.1} keyI18n={"Common.Attr.Key.Position.Z"} | ||||
|                 value={range.position[2]} | ||||
|                 valueChange={(val) => { | ||||
|                     range.position[2] = (val as any) / 1; | ||||
|                     status.changeRangeAttrib(range.id, "position", range.position); | ||||
|                 }, .1 | ||||
|             )} | ||||
|                     this.props.status?.changeRangeAttrib(range.id, "position", range.position); | ||||
|                 }} | ||||
|             /> | ||||
| 			 | ||||
|             {this.renderAttrInput( | ||||
|                 range.id, "Common.Attr.Key.Radius.X", | ||||
|                 range.radius[0], (val, status) => { | ||||
|             <AttrInput | ||||
|                 id={range.id} isNumber={true} step={.1} keyI18n={"Common.Attr.Key.Radius.X"} | ||||
|                 value={range.radius[0]} min={0} | ||||
|                 valueChange={(val) => { | ||||
|                     range.radius[0] = (val as any) / 1; | ||||
|                     status.changeRangeAttrib(range.id, "radius", range.radius); | ||||
|                 }, .1, undefined, 0 | ||||
|             )} | ||||
|                     this.props.status?.changeRangeAttrib(range.id, "radius", range.radius); | ||||
|                 }} | ||||
|             /> | ||||
| 
 | ||||
|             {this.renderAttrInput( | ||||
|                 range.id, "Common.Attr.Key.Radius.Y", | ||||
|                 range.radius[1], (val, status) => { | ||||
|             <AttrInput | ||||
|                 id={range.id} isNumber={true} step={.1} keyI18n={"Common.Attr.Key.Radius.Y"} | ||||
|                 value={range.radius[1]} min={0} | ||||
|                 valueChange={(val) => { | ||||
|                     range.radius[1] = (val as any) / 1; | ||||
|                     status.changeRangeAttrib(range.id, "radius", range.radius); | ||||
|                 }, .1, undefined, 0 | ||||
|             )} | ||||
|                     this.props.status?.changeRangeAttrib(range.id, "radius", range.radius); | ||||
|                 }} | ||||
|             /> | ||||
| 
 | ||||
|             {this.renderAttrInput( | ||||
|                 range.id, "Common.Attr.Key.Radius.Z", | ||||
|                 range.radius[2], (val, status) => { | ||||
|             <AttrInput | ||||
|                 id={range.id} isNumber={true} step={.1} keyI18n={"Common.Attr.Key.Radius.Z"} | ||||
|                 value={range.radius[2]} min={0} | ||||
|                 valueChange={(val) => { | ||||
|                     range.radius[2] = (val as any) / 1; | ||||
|                     status.changeRangeAttrib(range.id, "radius", range.radius); | ||||
|                 }, .1, undefined, 0 | ||||
|             )} | ||||
|                     this.props.status?.changeRangeAttrib(range.id, "radius", range.radius); | ||||
|                 }} | ||||
|             /> | ||||
| 		</> | ||||
|     } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user