Optmi renderer model & renderer shape parameter #38

Merged
MrKBear merged 5 commits from dev-mrkbear into master 2022-04-09 17:50:38 +08:00
22 changed files with 283 additions and 139 deletions

View File

@ -1,7 +1,7 @@
import { ReactNode, createElement } from "react"; import { ReactNode, createElement } from "react";
import { Emitter } from "@Model/Emitter"; import { Emitter } from "@Model/Emitter";
import { Localization } from "@Component/Localization/Localization"; import { Localization } from "@Component/Localization/Localization";
import { IAnyObject } from "@Model/Renderer"; import { IAnyObject } from "@Model/Model";
enum ResizeDragDirection { enum ResizeDragDirection {
top = 1, top = 1,

View File

@ -44,6 +44,11 @@ class BasicGroup extends DisplayObject<GroupShader> {
*/ */
public color = [1, 1, 1]; public color = [1, 1, 1];
/**
*
*/
public shape: number = 0;
/** /**
* *
*/ */
@ -66,6 +71,9 @@ class BasicGroup extends DisplayObject<GroupShader> {
// 半径传递 // 半径传递
this.shader.radius(this.size); this.shader.radius(this.size);
// 形状传递
this.shader.shape(this.shape);
// 指定颜色 // 指定颜色
this.shader.color(this.color); this.shader.color(this.color);

View File

@ -1,4 +1,4 @@
import { AbstractRenderer, IRendererParam, IAnyObject } from "@Model/Renderer"; import { AbstractRenderer, IRendererParam } from "@Model/Renderer";
import { EventType } from "@Model/Emitter"; import { EventType } from "@Model/Emitter";
import { GLCanvas, GLCanvasOption } from "./GLCanvas"; import { GLCanvas, GLCanvasOption } from "./GLCanvas";
import { GLContext } from "./GLContext"; import { GLContext } from "./GLContext";
@ -16,19 +16,13 @@ type IRendererParams = IRendererOwnParams & GLCanvasOption;
abstract class BasicRenderer< abstract class BasicRenderer<
P extends IRendererParam = {}, P extends IRendererParam = {},
M extends IAnyObject = {},
E extends Record<EventType, any> = {} E extends Record<EventType, any> = {}
> extends AbstractRenderer<P, M & IRendererParams, E & {loop: number}> { > extends AbstractRenderer<P, E & {loop: number}> {
public get dom() { public get dom() {
return this.canvas.dom return this.canvas.dom
} }
/**
*
*/
public param: Partial<M & IRendererParams> = {};
/** /**
* 使 * 使
*/ */
@ -44,19 +38,16 @@ abstract class BasicRenderer<
*/ */
protected clock: Clock; protected clock: Clock;
public constructor(param: Partial<M & IRendererParams> = {}) { public constructor() {
super(); super();
// 初始化参数
this.param = {
autoResize: param.autoResize ?? true,
mouseEvent: param.autoResize ?? true,
eventLog: param.eventLog ?? false,
className: param.className ?? ""
} as M & IRendererParams;
// 实例化画布对象 // 实例化画布对象
this.canvas = new GLCanvas(param.canvas, this.param); this.canvas = new GLCanvas(undefined, {
autoResize: true,
mouseEvent: true,
eventLog: false,
className: "canvas"
});
// 实例化摄像机 // 实例化摄像机
this.camera = new Camera(this.canvas); this.camera = new Camera(this.canvas);

View File

@ -1,27 +1,49 @@
import { ObjectID, ObjectData, ICommonParam } from "@Model/Renderer"; import { ObjectData } from "@Model/Renderer";
import { ObjectID } from "@Model/Model";
import { IParameterValue, getDefaultValue } from "@Model/Parameter";
import { BasicRenderer } from "./BasicRenderer"; import { BasicRenderer } from "./BasicRenderer";
import { BasicsShader } from "./BasicShader"; import { BasicsShader } from "./BasicShader";
import { Axis } from "./Axis"; import { Axis } from "./Axis";
import { BasicCube } from "./BasicCube"; import { BasicCube } from "./BasicCube";
import { GroupShader } from "./GroupShader"; import { GroupShader } from "./GroupShader";
import { BasicGroup } from "./BasicGroup"; import { BasicGroup } from "./BasicGroup";
import DisplayObject from "./DisplayObject"; import { DisplayObject } from "./DisplayObject";
interface IClassicRendererParams {
point: {
size: number;
}
cube: {
radius: number[];
}
}
enum MouseMod { enum MouseMod {
Drag = 1, Drag = 1,
click = 2 click = 2
} }
class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> { type IClassicRendererParameter = {
renderer: {};
points: {
color: "color",
size: "number",
shape: "option"
};
cube: {
color: "color"
};
}
class ClassicRenderer extends BasicRenderer<IClassicRendererParameter> {
public override rendererParameterOption = {};
public override pointsParameterOption = {
color: { type: "color", name: "", defaultValue: [0, 0, 0] },
size: { type: "number", name: "Common.Attr.Key.Size", defaultValue: 60, numberStep: 10, numberMin: 0 },
shape: { type: "option", name: "Common.Render.Attr.Key.Display.Shape", defaultValue: "0", allOption: [
{ key: "0", name: "Common.Render.Attr.Key.Display.Shape.Square" },
{ key: "1", name: "Common.Render.Attr.Key.Display.Shape.Hollow.Square" },
{ key: "2", name: "Common.Render.Attr.Key.Display.Shape.Hollow.Plus" },
{ key: "3", name: "Common.Render.Attr.Key.Display.Shape.Hollow.Reduce" },
{ key: "4", name: "Common.Render.Attr.Key.Display.Shape.Hollow.Cross" },
{ key: "5", name: "Common.Render.Attr.Key.Display.Shape.Hollow.Checkerboard" }
]}
};
public override cubeParameterOption = {
color: { type: "color", name: "", defaultValue: [0, 0, 0] },
};
private basicShader: BasicsShader = undefined as any; private basicShader: BasicsShader = undefined as any;
private axisObject: Axis = undefined as any; private axisObject: Axis = undefined as any;
@ -51,6 +73,8 @@ class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
} }
public onLoad(): this { public onLoad(): this {
this.rendererParameter = getDefaultValue(this.rendererParameterOption);
// 自动调节分辨率 // 自动调节分辨率
this.autoResize(); this.autoResize();
@ -187,7 +211,7 @@ class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
points( points(
id: ObjectID, position?: ObjectData, id: ObjectID, position?: ObjectData,
param?: Readonly<Partial<ICommonParam & IClassicRendererParams["point"]>> param?: Readonly<IParameterValue<IClassicRendererParameter["points"]>>
): this { ): this {
let object = this.objectPool.get(id); let object = this.objectPool.get(id);
let group: BasicGroup; let group: BasicGroup;
@ -229,14 +253,19 @@ class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
if (param.size) { if (param.size) {
group.size = param.size; group.size = param.size;
} }
// 半径数据
if (param.shape) {
group.shape = parseInt(param.shape);
}
} }
return this; return this;
} }
cube( cube(
id: ObjectID, position?: ObjectData, id: ObjectID, position?: ObjectData, radius?: ObjectData,
param?: Readonly<Partial<ICommonParam & IClassicRendererParams["cube"]>> param?: Readonly<IParameterValue<IClassicRendererParameter["cube"]>>
): this { ): this {
let object = this.objectPool.get(id); let object = this.objectPool.get(id);
let cube: BasicCube; let cube: BasicCube;
@ -250,6 +279,13 @@ class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
cube.position[1] = position[1] ?? cube.position[1]; cube.position[1] = position[1] ?? cube.position[1];
cube.position[2] = position[2] ?? cube.position[2]; cube.position[2] = position[2] ?? cube.position[2];
} }
if (radius) {
cube.r[0] = radius[0] ?? cube.r[0];
cube.r[1] = radius[1] ?? cube.r[1];
cube.r[2] = radius[2] ?? cube.r[2];
}
} else { } else {
throw new Error("Renderer: Use duplicate ObjectID when drawing different types of objects"); throw new Error("Renderer: Use duplicate ObjectID when drawing different types of objects");
} }
@ -263,6 +299,12 @@ class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
cube.position[2] = position[2] ?? cube.position[2]; cube.position[2] = position[2] ?? cube.position[2];
} }
if (radius) {
cube.r[0] = radius[0] ?? cube.r[0];
cube.r[1] = radius[1] ?? cube.r[1];
cube.r[2] = radius[2] ?? cube.r[2];
}
this.objectPool.set(id, cube); this.objectPool.set(id, cube);
console.log(`Renderer: Create new cube object with id ${id}`); console.log(`Renderer: Create new cube object with id ${id}`);
} }
@ -271,21 +313,11 @@ class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> {
cube.isDraw = true; cube.isDraw = true;
// 参数传递 // 参数传递
if (param) { if (param && param.color) {
// 颜色数据 cube.color[0] = param.color[0] ?? cube.color[0];
if (param.color) { cube.color[1] = param.color[1] ?? cube.color[1];
cube.color[0] = param.color[0] ?? cube.color[0] cube.color[2] = param.color[2] ?? cube.color[2];
cube.color[1] = param.color[1] ?? cube.color[1]
cube.color[2] = param.color[2] ?? cube.color[2]
}
// 半径数据
if (param.radius) {
cube.r[0] = param.radius[0] ?? cube.r[0];
cube.r[1] = param.radius[1] ?? cube.r[1];
cube.r[2] = param.radius[2] ?? cube.r[2];
}
} }
return this; return this;

View File

@ -10,6 +10,7 @@ interface IGroupShaderAttribute {
interface IGroupShaderUniform { interface IGroupShaderUniform {
uRadius: number, uRadius: number,
uShape: number,
uMvp: ObjectData, uMvp: ObjectData,
uColor: ObjectData, uColor: ObjectData,
uFogColor: ObjectData, uFogColor: ObjectData,
@ -50,10 +51,42 @@ class GroupShader extends GLShader<IGroupShaderAttribute, IGroupShaderUniform>{
uniform vec3 uColor; uniform vec3 uColor;
uniform vec3 uFogColor; uniform vec3 uFogColor;
uniform int uShape;
varying float vFogPower; varying float vFogPower;
void main(){ void main(){
float dist = distance(gl_PointCoord, vec2(0.5, 0.5));
vec2 normalPos = (gl_PointCoord - vec2(0.5, 0.5)) * 2.;
if ( uShape == 1 && abs(normalPos.x) < .6 && abs(normalPos.y) < .6) {
discard;
}
if ( uShape == 2 && abs(normalPos.x) > .3 && abs(normalPos.y) > .3) {
discard;
}
if ( uShape == 3 && abs(normalPos.y) > .3) {
discard;
}
if ( uShape == 4 &&
(abs(normalPos.x) < .4 || abs(normalPos.y) < .4) &&
(abs(normalPos.x) > .4 || abs(normalPos.y) > .4)
) {
discard;
}
if ( uShape == 5 &&
(abs(normalPos.x) < .75 && abs(normalPos.y) < .75) &&
(abs(normalPos.x) < .28 || abs(normalPos.y) < .28) &&
(abs(normalPos.x) > .28 || abs(normalPos.y) > .28)
) {
discard;
}
gl_FragColor = vec4(mix(uColor, uFogColor, vFogPower), 1.); gl_FragColor = vec4(mix(uColor, uFogColor, vFogPower), 1.);
} }
`; `;
@ -111,4 +144,13 @@ class GroupShader extends GLShader<IGroupShaderAttribute, IGroupShaderUniform>{
this.uniformLocate("uFogDensity"), rgb this.uniformLocate("uFogDensity"), rgb
) )
} }
/**
*
*/
public shape(shape: number) {
this.gl.uniform1i(
this.uniformLocate("uShape"), shape
)
}
} }

View File

@ -1,7 +1,7 @@
import { Component, ReactNode } from "react"; import { Component, ReactNode } from "react";
import { Icon } from "@fluentui/react"; import { Icon } from "@fluentui/react";
import { AllI18nKeys } from "@Component/Localization/Localization"; import { AllI18nKeys } from "@Component/Localization/Localization";
import { ObjectID } from "@Model/Renderer"; import { ObjectID } from "@Model/Model";
import { TextField, ITextFieldProps } from "@Input/TextField/TextField"; import { TextField, ITextFieldProps } from "@Input/TextField/TextField";
import "./AttrInput.scss"; import "./AttrInput.scss";

View File

@ -1,7 +1,7 @@
import { Component, Fragment, ReactNode } from "react"; import { Component, Fragment, ReactNode } from "react";
import { useSettingWithEvent, IMixinSettingProps, Language } from "@Context/Setting"; import { useSettingWithEvent, IMixinSettingProps, Language } from "@Context/Setting";
import { AttrInput } from "@Input/AttrInput/AttrInput"; import { AttrInput } from "@Input/AttrInput/AttrInput";
import { ObjectID } from "@Model/Renderer"; import { ObjectID } from "@Model/Model";
import { TogglesInput } from "@Input/TogglesInput/TogglesInput"; import { TogglesInput } from "@Input/TogglesInput/TogglesInput";
import { ObjectPicker } from "@Input/ObjectPicker/ObjectPicker"; import { ObjectPicker } from "@Input/ObjectPicker/ObjectPicker";
import { AllI18nKeys, I18N } from "@Component/Localization/Localization"; import { AllI18nKeys, I18N } from "@Component/Localization/Localization";
@ -10,7 +10,7 @@ import { ColorInput } from "@Input/ColorInput/ColorInput";
import { ComboInput, IDisplayItem } from "@Input/ComboInput/ComboInput"; import { ComboInput, IDisplayItem } from "@Input/ComboInput/ComboInput";
import { import {
IParameter, IParameterOption, IParameterOptionItem, IParameter, IParameterOption, IParameterOptionItem,
IParameterValue, IParamValue, isObjectType, isVectorType IParameterValue, IParamValue, isObjectType
} from "@Model/Parameter"; } from "@Model/Parameter";
import "./Parameter.scss"; import "./Parameter.scss";
@ -20,6 +20,7 @@ interface IParameterProps<P extends IParameter = {}> {
key: ObjectID; key: ObjectID;
change: <K extends keyof P>(key: K, val: IParamValue<P[K]>) => any; change: <K extends keyof P>(key: K, val: IParamValue<P[K]>) => any;
i18n?: (key: string, language: Language) => string; i18n?: (key: string, language: Language) => string;
renderKey?: Array<keyof P>;
title?: AllI18nKeys; title?: AllI18nKeys;
titleOption?: Record<string, string>; titleOption?: Record<string, string>;
isFirst?: boolean; isFirst?: boolean;
@ -249,12 +250,18 @@ class Parameter<P extends IParameter> extends Component<IParameterProps<P> & IMi
} }
public render(): ReactNode { public render(): ReactNode {
const allOptionKeys: Array<keyof P> = Object.getOwnPropertyNames(this.props.option);
let allOptionKeys: Array<keyof P>;
if (this.props.renderKey) {
allOptionKeys = this.props.renderKey;
} else {
allOptionKeys = Object.getOwnPropertyNames(this.props.option);
}
return <> return <>
{ {
allOptionKeys.length <= 0 && this.props.title ? allOptionKeys.length > 0 && this.props.title ?
<Message <Message
isTitle isTitle
first={this.props.isFirst} first={this.props.isFirst}

View File

@ -76,6 +76,7 @@ const EN_US = {
"Common.Attr.Title.Individual.Generation": "Individual generation", "Common.Attr.Title.Individual.Generation": "Individual generation",
"Common.Attr.Title.Behaviors": "Behaviors list", "Common.Attr.Title.Behaviors": "Behaviors list",
"Common.Attr.Title.Individual.kill": "Individual kill", "Common.Attr.Title.Individual.kill": "Individual kill",
"Common.Attr.Title.Render.Parameter": "Render parameters",
"Common.Attr.Key.Display.Name": "Display name", "Common.Attr.Key.Display.Name": "Display name",
"Common.Attr.Key.Position.X": "Position X", "Common.Attr.Key.Position.X": "Position X",
"Common.Attr.Key.Position.Y": "Position Y", "Common.Attr.Key.Position.Y": "Position Y",
@ -106,6 +107,13 @@ const EN_US = {
"Common.Attr.Key.Generation.Error.Invalid.Label": "The specified label has expired", "Common.Attr.Key.Generation.Error.Invalid.Label": "The specified label has expired",
"Common.Attr.Key.Kill.Random": "Random kill", "Common.Attr.Key.Kill.Random": "Random kill",
"Common.Attr.Key.Kill.Count": "Kill count", "Common.Attr.Key.Kill.Count": "Kill count",
"Common.Render.Attr.Key.Display.Shape": "Display Shape",
"Common.Render.Attr.Key.Display.Shape.Square": "Square",
"Common.Render.Attr.Key.Display.Shape.Hollow.Square": "Hollow square",
"Common.Render.Attr.Key.Display.Shape.Hollow.Plus": "Plus",
"Common.Render.Attr.Key.Display.Shape.Hollow.Reduce": "Reduce",
"Common.Render.Attr.Key.Display.Shape.Hollow.Cross": "Cross",
"Common.Render.Attr.Key.Display.Shape.Hollow.Checkerboard": "Checkerboard",
"Panel.Info.Range.Details.Attr.Error.Not.Range": "Object is not a Range", "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.Range.Details.Attr.Error.Unspecified": "Unspecified range object",
"Panel.Info.Group.Details.Attr.Error.Not.Group": "Object is not a Group", "Panel.Info.Group.Details.Attr.Error.Not.Group": "Object is not a Group",

View File

@ -76,6 +76,7 @@ const ZH_CN = {
"Common.Attr.Title.Individual.Generation": "生成个体", "Common.Attr.Title.Individual.Generation": "生成个体",
"Common.Attr.Title.Behaviors": "行为列表", "Common.Attr.Title.Behaviors": "行为列表",
"Common.Attr.Title.Individual.kill": "消除个体", "Common.Attr.Title.Individual.kill": "消除个体",
"Common.Attr.Title.Render.Parameter": "渲染参数",
"Common.Attr.Key.Display.Name": "显示名称", "Common.Attr.Key.Display.Name": "显示名称",
"Common.Attr.Key.Position.X": "X 坐标", "Common.Attr.Key.Position.X": "X 坐标",
"Common.Attr.Key.Position.Y": "Y 坐标", "Common.Attr.Key.Position.Y": "Y 坐标",
@ -106,6 +107,13 @@ const ZH_CN = {
"Common.Attr.Key.Generation.Error.Invalid.Label": "指定的标签已失效", "Common.Attr.Key.Generation.Error.Invalid.Label": "指定的标签已失效",
"Common.Attr.Key.Kill.Random": "随机消除", "Common.Attr.Key.Kill.Random": "随机消除",
"Common.Attr.Key.Kill.Count": "消除数量", "Common.Attr.Key.Kill.Count": "消除数量",
"Common.Render.Attr.Key.Display.Shape": "显示形状",
"Common.Render.Attr.Key.Display.Shape.Square": "方形",
"Common.Render.Attr.Key.Display.Shape.Hollow.Square": "空心方形",
"Common.Render.Attr.Key.Display.Shape.Hollow.Plus": "加号",
"Common.Render.Attr.Key.Display.Shape.Hollow.Reduce": "减号",
"Common.Render.Attr.Key.Display.Shape.Hollow.Cross": "叉号",
"Common.Render.Attr.Key.Display.Shape.Hollow.Checkerboard": "棋盘",
"Panel.Info.Range.Details.Attr.Error.Not.Range": "对象不是一个范围", "Panel.Info.Range.Details.Attr.Error.Not.Range": "对象不是一个范围",
"Panel.Info.Range.Details.Attr.Error.Unspecified": "未指定范围对象", "Panel.Info.Range.Details.Attr.Error.Unspecified": "未指定范围对象",
"Panel.Info.Group.Details.Attr.Error.Not.Group": "对象不是一个群", "Panel.Info.Group.Details.Attr.Error.Not.Group": "对象不是一个群",

View File

@ -1,6 +1,6 @@
import { LabelObject } from "@Model/Label" import { LabelObject } from "@Model/Label"
import type { Model } from "@Model/Model"; import type { IAnyObject, Model } from "@Model/Model";
import type { ObjectID } from "@Model/Renderer"; import type { ObjectID } from "@Model/Model";
/** /**
* *
@ -37,6 +37,11 @@ class CtrlObject extends LabelObject {
*/ */
protected model: Model; protected model: Model;
/**
*
*/
public renderParameter: IAnyObject = {};
/** /**
* *
*/ */

View File

@ -3,6 +3,8 @@ import { CtrlObject } from "@Model/CtrlObject";
import type { Behavior } from "@Model/Behavior"; import type { Behavior } from "@Model/Behavior";
import { Label } from "@Model/Label"; import { Label } from "@Model/Label";
import { Range } from "@Model/Range"; import { Range } from "@Model/Range";
import { Model, ObjectID } from "@Model/Model";
import { getDefaultValue } from "@Model/Parameter";
enum GenMod { enum GenMod {
Point = "p", Point = "p",
@ -380,11 +382,15 @@ class Group extends CtrlObject {
}); });
return dataBuffer; return dataBuffer;
} }
public constructor(model: Model, id: ObjectID) {
/** super(model, id);
*
*/ if (model.renderer) {
public size: number = 60; this.renderParameter = getDefaultValue(model.renderer.pointsParameterOption);
}
}
} }
export default Group; export default Group;

View File

@ -1,5 +1,5 @@
import type { Group } from "@Model/Group"; import type { Group } from "@Model/Group";
import { ObjectID } from "@Model/Renderer"; import { ObjectID } from "@Model/Model";
/** /**
* *

View File

@ -1,5 +1,4 @@
import type { Model } from "@Model/Model"; import type { Model, ObjectID } from "@Model/Model";
import { ObjectID } from "@Model/Renderer";
/** /**
* *

View File

@ -5,9 +5,19 @@ import { IParamValue } from "@Model/Parameter";
import { Individual } from "@Model/Individual"; import { Individual } from "@Model/Individual";
import { CtrlObject } from "@Model/CtrlObject"; import { CtrlObject } from "@Model/CtrlObject";
import { Emitter, EventType, EventMixin } from "@Model/Emitter"; import { Emitter, EventType, EventMixin } from "@Model/Emitter";
import { ObjectID, AbstractRenderer } from "@Model/Renderer"; import { AbstractRenderer } from "@Model/Renderer";
import { Behavior, IAnyBehavior, IAnyBehaviorRecorder } from "@Model/Behavior"; import { Behavior, IAnyBehavior, IAnyBehaviorRecorder } from "@Model/Behavior";
/**
*
*/
type ObjectID = string;
/**
*
*/
type IAnyObject = Record<string, any>;
type ModelEvent = { type ModelEvent = {
labelChange: Label[]; labelChange: Label[];
objectChange: CtrlObject[]; objectChange: CtrlObject[];
@ -352,17 +362,12 @@ 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];
object.renderParameter.color = object.color;
if (object.display && object instanceof Group) { if (object.display && object instanceof Group) {
this.renderer.points(object.id, object.exportPositionData(), { this.renderer.points(object.id, object.exportPositionData(), object.renderParameter);
color: object.color,
size: object.size
} as any);
} }
if (object.display && object instanceof Range) { if (object.display && object instanceof Range) {
this.renderer.cube(object.id, object.position, { this.renderer.cube(object.id, object.position, object.radius, object.renderParameter);
color: object.color,
radius: object.radius
} as any);
} }
} }
} }
@ -376,5 +381,6 @@ export {
EventMixin, EventMixin,
Model, Model,
CtrlObject, CtrlObject,
ObjectID ObjectID,
IAnyObject
} }

View File

@ -1,4 +1,6 @@
import { CtrlObject } from "@Model/CtrlObject"; import { CtrlObject } from "@Model/CtrlObject";
import { Model, ObjectID } from "@Model/Model";
import { getDefaultValue } from "@Model/Parameter";
/** /**
* *
@ -15,6 +17,15 @@ class Range extends CtrlObject {
*/ */
public radius: number[] = [1, 1, 1]; public radius: number[] = [1, 1, 1];
public constructor(model: Model, id: ObjectID) {
super(model, id);
if (model.renderer) {
this.renderParameter = getDefaultValue(model.renderer.cubeParameterOption);
}
}
} }
export default Range; export default Range;

View File

@ -1,24 +1,31 @@
import { Emitter, EventType } from "@Model/Emitter"; import { Emitter, EventType } from "@Model/Emitter";
import { IAnyObject, ObjectID } from "@Model/Model";
import { IParameter, IParameterOption, IParameterValue } from "@Model/Parameter";
/** /**
* *
*/ */
type IAnyObject = Record<string, any>; type IDefaultType<T, D> = T extends undefined ? D : T;
/** /**
* *
*/ */
interface IRendererParam { interface IRendererParam {
/**
*
*/
renderer?: IParameter;
/** /**
* *
*/ */
points?: IAnyObject points?: IParameter;
/** /**
* *
*/ */
cube?: IAnyObject cube?: IParameter;
} }
/** /**
@ -32,22 +39,15 @@ interface ICommonParam {
color?: ObjectData; color?: ObjectData;
} }
/**
*
*/
type ObjectID = string;
/** /**
* *
*/ */
type ObjectData = Array<number> | Float32Array; type ObjectData = Array<number> | Float32Array;
interface IRendererConstructor< interface IRendererConstructor {
M extends IAnyObject = {} new (): AbstractRenderer<IRendererParam, AbstractRendererEvent>
> {
new (canvas: HTMLCanvasElement, param?: M): AbstractRenderer<
IRendererParam, IAnyObject, AbstractRendererEvent
>
} }
type AbstractRendererEvent = { type AbstractRendererEvent = {
@ -63,7 +63,6 @@ type AbstractRendererEvent = {
*/ */
abstract class AbstractRenderer< abstract class AbstractRenderer<
P extends IRendererParam = {}, P extends IRendererParam = {},
M extends IAnyObject = {},
E extends AbstractRendererEvent = {loop: number} E extends AbstractRendererEvent = {loop: number}
> extends Emitter<E> { > extends Emitter<E> {
@ -72,28 +71,17 @@ abstract class AbstractRenderer<
/** /**
* *
*/ */
abstract param: Partial<M>; public rendererParameterOption: IParameterOption<IDefaultType<P["renderer"], {}>> = {} as any;
public pointsParameterOption: IParameterOption<IDefaultType<P["points"], {}>> = {} as any;
public cubeParameterOption: IParameterOption<IDefaultType<P["cube"], {}>> = {} as any;
/** /**
* *
*/ */
get isRenderer() { public rendererParameter: IParameterValue<IDefaultType<P["renderer"], {}>> = {} as any;
return true;
}
/** /**
* * @function clean
*/
public static isRenderer(render: any): render is AbstractRenderer {
if (render instanceof Object) {
return !!(render as AbstractRenderer).isRenderer;
} else {
return false;
}
};
/**
* @function start
* \ * \
* \ * \
* \ * \
@ -110,7 +98,9 @@ abstract class AbstractRenderer<
* @param id 使 * @param id 使
* @param position * @param position
*/ */
abstract points(id: ObjectID, position?: ObjectData, param?: Readonly<P["points"] & ICommonParam>): this; abstract points(id: ObjectID, position?: ObjectData, param?:
Readonly<IParameterValue<IDefaultType<P["points"], {}>>>
): this;
/** /**
* @function cube * @function cube
@ -120,12 +110,13 @@ abstract class AbstractRenderer<
* *
* 注意: 这里的半径指的是立方体重心与立方体任意一面几何中心的距离 * 注意: 这里的半径指的是立方体重心与立方体任意一面几何中心的距离
*/ */
abstract cube(id: ObjectID, position?: ObjectData, param?: Readonly<P["cube"] & ICommonParam>): this; abstract cube(id: ObjectID, position?: ObjectData, radius?: ObjectData, param?:
Readonly<IParameterValue<IDefaultType<P["cube"], {}>>>
): this;
} }
export default AbstractRenderer; export default AbstractRenderer;
export { export {
AbstractRenderer, ObjectID, IAnyObject, AbstractRenderer, ICommonParam, IRendererParam,
ICommonParam, IRendererParam,
ObjectData, IRendererConstructor ObjectData, IRendererConstructor
}; };

View File

@ -21,7 +21,7 @@ class Laboratory extends Component {
throw new Error("Laboratory: 重复引用 canvas 节点"); throw new Error("Laboratory: 重复引用 canvas 节点");
} }
const renderer = new ClassicRenderer({ className: "canvas" }).onLoad(); const renderer = new ClassicRenderer().onLoad();
this.canvasContRef.current.appendChild(renderer.canvas.dom); this.canvasContRef.current.appendChild(renderer.canvas.dom);
let model = new Model().bindRenderer(renderer); let model = new Model().bindRenderer(renderer);
@ -39,22 +39,24 @@ class Laboratory extends Component {
// 测试渲染器 // 测试渲染器
if (false) { if (false) {
renderer.points("0"); // renderer.points("0");
renderer.points("1", new Array(100 * 3).fill(0).map(() => (Math.random() - .5) * 2)); // renderer.points("1", new Array(100 * 3).fill(0).map(() => (Math.random() - .5) * 2));
renderer.points("2", new Array(100 * 3).fill(0).map(() => (Math.random() - .5) * 2), { // renderer.points("2", new Array(100 * 3).fill(0).map(() => (Math.random() - .5) * 2), {
size: 100, // size: 100,
color: [1, 0, 1] // color: [1, 0, 1]
}); // });
renderer.points("3", new Array(100 * 3).fill(0).map(() => (Math.random() - .5) * 2), { // renderer.points("3", new Array(100 * 3).fill(0).map(() => (Math.random() - .5) * 2), {
size: 80, // size: 80,
color: [0, 1, 1] // color: [0, 1, 1]
}); // });
renderer.points("2"); // renderer.points("2");
renderer.cube("4"); // renderer.cube("4");
renderer.cube("5", new Array(3).fill(0).map(() => (Math.random() - .5) * 2), { // renderer.cube("5",
radius: new Array(3).fill(0).map(() => Math.random() * 1.2), // new Array(3).fill(0).map(() => (Math.random() - .5) * 2),
color: [1, 1, 0] // new Array(3).fill(0).map(() => Math.random() * 1.2), {
}) // color: [1, 1, 0]
// }
// )
} }
(window as any).renderer = renderer; (window as any).renderer = renderer;

View File

@ -35,9 +35,9 @@ class SimulatorWeb extends Component {
(window as any).setting = (this.setting as any); (window as any).setting = (this.setting as any);
// TODO: 这里要读取存档 // TODO: 这里要读取存档
const classicRender = new ClassicRenderer().onLoad();
this.status = new Status(); this.status = new Status();
this.status.renderer = new ClassicRenderer({ className: "canvas" }).onLoad(); this.status.bindRenderer(classicRender);
this.status.bindRenderer(this.status.renderer);
this.status.setting = this.setting; this.status.setting = this.setting;
// 测试代码 // 测试代码

View File

@ -4,7 +4,7 @@ import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
import { useSetting, IMixinSettingProps } from "@Context/Setting"; import { useSetting, IMixinSettingProps } from "@Context/Setting";
import { ComboInput, IDisplayItem } from "@Input/ComboInput/ComboInput"; import { ComboInput, IDisplayItem } from "@Input/ComboInput/ComboInput";
import { Message } from "@Input/Message/Message"; import { Message } from "@Input/Message/Message";
import { ObjectID } from "@Model/Renderer"; import { ObjectID } from "@Model/Model";
import { ColorInput } from "@Input/ColorInput/ColorInput"; import { ColorInput } from "@Input/ColorInput/ColorInput";
import { TogglesInput } from "@Input/TogglesInput/TogglesInput"; import { TogglesInput } from "@Input/TogglesInput/TogglesInput";
import { LabelPicker } from "@Input/LabelPicker/LabelPicker"; import { LabelPicker } from "@Input/LabelPicker/LabelPicker";
@ -13,6 +13,7 @@ import { AllI18nKeys } from "@Component/Localization/Localization";
import { ObjectPicker } from "@Input/ObjectPicker/ObjectPicker"; import { ObjectPicker } from "@Input/ObjectPicker/ObjectPicker";
import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup";
import { BehaviorPicker } from "@Input/BehaviorPicker/BehaviorPicker"; import { BehaviorPicker } from "@Input/BehaviorPicker/BehaviorPicker";
import { Parameter } from "@Input/Parameter/Parameter";
import "./GroupDetails.scss"; import "./GroupDetails.scss";
interface IGroupDetailsProps {} interface IGroupDetailsProps {}
@ -54,14 +55,6 @@ class GroupDetails extends Component<IGroupDetailsProps & IMixinStatusProps & IM
}} }}
/> />
<AttrInput
id={group.id} isNumber={true} step={10} keyI18n="Common.Attr.Key.Size"
value={group.size} min={0}
valueChange={(val) => {
this.props.status?.changeGroupAttrib(group.id, "size", (val as any) / 1);
}}
/>
<LabelPicker <LabelPicker
keyI18n="Common.Attr.Key.Label" keyI18n="Common.Attr.Key.Label"
labels={group.allLabels()} labels={group.allLabels()}
@ -114,6 +107,23 @@ class GroupDetails extends Component<IGroupDetailsProps & IMixinStatusProps & IM
} }
}} }}
/> />
<Parameter
key={group.id}
option={this.props.status?.renderer.pointsParameterOption ?? {}}
title={"Common.Attr.Title.Render.Parameter"}
value={group.renderParameter}
renderKey={
Object.getOwnPropertyNames(this.props.status?.renderer.pointsParameterOption ?? {})
.filter((key) => key !== "color")
}
change={(key, value) => {
group.renderParameter[key as any] = value;
this.props.status?.changeGroupAttrib(
group.id, "renderParameter", group.renderParameter
);
}}
/>
<Message i18nKey="Common.Attr.Title.Behaviors" isTitle/> <Message i18nKey="Common.Attr.Title.Behaviors" isTitle/>

View File

@ -2,7 +2,7 @@ import { Component, ReactNode } from "react";
import { BackgroundLevel, FontLevel, Theme } from "@Component/Theme/Theme"; import { BackgroundLevel, FontLevel, Theme } from "@Component/Theme/Theme";
import { useStatus, IMixinStatusProps } from "@Context/Status"; import { useStatus, IMixinStatusProps } from "@Context/Status";
import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup";
import { ObjectID } from "@Model/Renderer"; import { ObjectID } from "@Model/Model";
import { Icon } from "@fluentui/react"; import { Icon } from "@fluentui/react";
import "./ObjectList.scss"; import "./ObjectList.scss";

View File

@ -3,7 +3,7 @@ import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
import { useSetting, IMixinSettingProps } from "@Context/Setting"; import { useSetting, IMixinSettingProps } from "@Context/Setting";
import { Localization } from "@Component/Localization/Localization"; import { Localization } from "@Component/Localization/Localization";
import { DetailsList } from "@Component/DetailsList/DetailsList"; import { DetailsList } from "@Component/DetailsList/DetailsList";
import { ObjectID } from "@Model/Renderer"; import { ObjectID } from "@Model/Model";
import { Icon } from "@fluentui/react"; import { Icon } from "@fluentui/react";
import "./ObjectList.scss"; import "./ObjectList.scss";

View File

@ -3,11 +3,12 @@ import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
import { AttrInput } from "@Input/AttrInput/AttrInput"; import { AttrInput } from "@Input/AttrInput/AttrInput";
import { Message } from "@Input/Message/Message"; import { Message } from "@Input/Message/Message";
import { Range } from "@Model/Range"; import { Range } from "@Model/Range";
import { ObjectID } from "@Model/Renderer"; import { ObjectID } from "@Model/Model";
import { ColorInput } from "@Input/ColorInput/ColorInput"; import { ColorInput } from "@Input/ColorInput/ColorInput";
import { TogglesInput } from "@Input/TogglesInput/TogglesInput"; import { TogglesInput } from "@Input/TogglesInput/TogglesInput";
import { LabelPicker } from "@Input/LabelPicker/LabelPicker"; import { LabelPicker } from "@Input/LabelPicker/LabelPicker";
import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup";
import { Parameter } from "@Input/Parameter/Parameter";
import "./RangeDetails.scss"; import "./RangeDetails.scss";
@useStatusWithEvent("rangeAttrChange", "focusObjectChange", "rangeLabelChange") @useStatusWithEvent("rangeAttrChange", "focusObjectChange", "rangeLabelChange")
@ -72,6 +73,23 @@ class RangeDetails extends Component<IMixinStatusProps> {
}} }}
/> />
<Parameter
key={range.id}
option={this.props.status?.renderer.cubeParameterOption ?? {}}
title={"Common.Attr.Title.Render.Parameter"}
value={range.renderParameter}
renderKey={
Object.getOwnPropertyNames(this.props.status?.renderer.cubeParameterOption ?? {})
.filter((key) => key !== "color")
}
change={(key, value) => {
range.renderParameter[key as any] = value;
this.props.status?.changeRangeAttrib(
range.id, "renderParameter", range.renderParameter
);
}}
/>
<Message i18nKey="Common.Attr.Title.Spatial" isTitle/> <Message i18nKey="Common.Attr.Title.Spatial" isTitle/>
<AttrInput <AttrInput