Action output message, more options to toggle

This commit is contained in:
Lucas Dower 2022-01-16 02:25:28 +00:00
parent e2dce3f5a0
commit e4bec861c0
7 changed files with 167 additions and 38 deletions

3
.gitignore vendored
View File

@ -5,4 +5,5 @@ ObjToSchematic-win32-x64
/resources/*.mtl
/dist
/tools/blocks
/tools/models
/tools/models
notes.md

View File

@ -12,19 +12,35 @@ import { remote } from "electron";
import fs from "fs";
import { ButtonElement } from "./ui/elements/button";
import { LabelElement } from "./ui/elements/label";
import { OutputElement } from "./ui/elements/output";
export enum ActionReturnType {
Success,
Warning,
Failure
}
interface ReturnStatus {
message: string,
//style: ToastStyle,
type: ActionReturnType,
error?: unknown
}
export enum Action {
Load = 0,
Simplify = 1,
Voxelise = 2,
Palette = 3,
Export = 4,
}
export class AppContext {
public ambientOcclusion: boolean;
public dithering: boolean;
private _gl: WebGLRenderingContext;
private _loadedMesh?: Mesh;
private _ui: Group[];
private _actionMap = new Map<Action, (() => ReturnStatus | void)>();
private static _instance: AppContext;
@ -34,6 +50,13 @@ export class AppContext {
private constructor() {
this.ambientOcclusion = true;
this.dithering = true;
this._actionMap.set(Action.Load, () => { return this._load(); });
this._actionMap.set(Action.Simplify, () => {});
this._actionMap.set(Action.Voxelise, () => { return this._voxelise(); });
this._actionMap.set(Action.Palette, () => { return this._palette(); } );
this._actionMap.set(Action.Export, () => { return this._export(); });
this._ui = [
{
@ -48,7 +71,8 @@ export class AppContext {
type: new FileInputElement("mtlFile", "mtl")
},
],
submitButton: new ButtonElement("loadMesh", "Load mesh", () => { this._load(); })
submitButton: new ButtonElement("loadMesh", "Load mesh", () => { this.do(Action.Load); }),
output: new OutputElement("output1")
},
{
label: "Simplify",
@ -57,7 +81,8 @@ export class AppContext {
label: new LabelElement("label3", "Ratio"),
type: new SliderElement("ratio", 0.0, 1.0, 0.01, 0.5) },
],
submitButton: new ButtonElement("simplifyMesh", "Simplify mesh", () => { })
submitButton: new ButtonElement("simplifyMesh", "Simplify mesh", () => { }),
output: new OutputElement("output2")
},
{
label: "Build",
@ -74,25 +99,34 @@ export class AppContext {
])
},
],
submitButton: new ButtonElement("voxeliseMesh", "Voxelise mesh", () => { this._voxelise(); })
submitButton: new ButtonElement("voxeliseMesh", "Voxelise mesh", () => { this.do(Action.Voxelise); }),
output: new OutputElement("output3")
},
{
label: "Palette",
components: [
{
label: new LabelElement("label6", "Block palette"),
type: new ComboBoxElement("blockPalette", [])
type: new ComboBoxElement("blockPalette", [
{ id: "default", displayText: "Default" }
])
},
{
label: new LabelElement("label7", "Choice method"),
type: new ComboBoxElement("choiceMethod", [])
type: new ComboBoxElement("choiceMethod", [
{ id: "euclidian", displayText: "Euclidian distance" }
])
},
{
label: new LabelElement("label8", "Dithering"),
type: new ComboBoxElement("dithering", [])
type: new ComboBoxElement("dithering", [
{ id: "on", displayText: "On (recommended)" },
{ id: "off", displayText: "Off" },
])
},
],
submitButton: new ButtonElement("assignBlocks", "Assign blocks", () => { this._export(); })
submitButton: new ButtonElement("assignBlocks", "Assign blocks", () => { this.do(Action.Palette); }),
output: new OutputElement("output4")
},
{
label: "Export",
@ -106,7 +140,8 @@ export class AppContext {
])
},
],
submitButton: new ButtonElement("exportStructure", "Export structure", () => { this._export(); })
submitButton: new ButtonElement("exportStructure", "Export structure", () => { this.do(Action.Export); }),
output: new OutputElement("output5")
}
]
@ -124,25 +159,35 @@ export class AppContext {
this._gl = gl;
}
public do(action: Action) {
const status = this._actionMap.get(action)!();
if (status) {
this._ui[action].output.setMessage(status.message, status.type);
if (status.error) {
console.error(status.error);
}
}
}
private _load(): ReturnStatus {
setEnabled(this._ui[2], false);
setEnabled(this._ui[4], false);
const objPath = (<FileInputElement>this._ui[0].components[0].type).getValue();
if (!fs.existsSync(objPath)) {
return { message: "Selected .obj cannot be found" };
return { message: "Selected .obj cannot be found", type: ActionReturnType.Failure };
}
const mtlPath = (<FileInputElement>this._ui[0].components[1].type).getValue();
if (!fs.existsSync(mtlPath)) {
return { message: "Selected .mtl cannot be found" };
return { message: "Selected .mtl cannot be found", type: ActionReturnType.Failure };
}
try {
this._loadedMesh = new Mesh(objPath, this._gl);
this._loadedMesh.loadTextures(this._gl);
} catch (err: unknown) {
return { error: err, message: "Could not load mesh" };
return { error: err, message: "Could not load mesh", type: ActionReturnType.Failure };
}
try {
@ -151,19 +196,19 @@ export class AppContext {
renderer.registerMesh(this._loadedMesh);
renderer.compile();
} catch (err: unknown) {
return { message: "Could not render mesh" };
return { message: "Could not render mesh", type: ActionReturnType.Failure };
}
setEnabled(this._ui[2], true);
return { message: "Loaded successfully" };
return { message: "Loaded successfully", type: ActionReturnType.Success };
}
private _voxelise(): ReturnStatus {
setEnabled(this._ui[3], false);
setEnabled(this._ui[4], false);
const voxelSize = (<SliderElement>this._ui[2].components[0].type).getValue();
const ambientOcclusion = (<ComboBoxElement>this._ui[2].components[1].type).getValue();
this.ambientOcclusion = ambientOcclusion === "on";
try {
@ -176,15 +221,36 @@ export class AppContext {
renderer.registerVoxelMesh();
renderer.compile();
} catch (err: any) {
return { error: err, message: "Could not register voxel mesh" };//, style: ToastStyle.Failure };
return { error: err, message: "Could not register voxel mesh", type: ActionReturnType.Failure };
}
setEnabled(this._ui[3], true);
return { message: "Voxelised successfully", type: ActionReturnType.Success };
}
private _palette(): ReturnStatus {
setEnabled(this._ui[4], false);
const dithering = (<ComboBoxElement>this._ui[3].components[2].type).getValue();
this.dithering = dithering === "on";
try {
const voxelManager = VoxelManager.Get;
voxelManager.assignBlocks();
const renderer = Renderer.Get;
renderer.clear();
renderer.registerVoxelMesh();
renderer.compile();
} catch (err: any) {
return { error: err, message: "Could not register voxel mesh", type: ActionReturnType.Failure };
}
setEnabled(this._ui[4], true);
return { message: "Voxelised successfully" };//, style: ToastStyle.Success };
return { message: "Blocks assigned successfully", type: ActionReturnType.Success };
}
private _export(): ReturnStatus {
console.log(this._ui[4].components[0]);
const exportFormat = (<ComboBoxElement>this._ui[4].components[0].type).getValue();
let exporter: Exporter;
if (exportFormat === "schematic") {
@ -200,16 +266,16 @@ export class AppContext {
});
if (filePath === undefined) {
return { message: "Output cancelled" };//, style: ToastStyle.Success };
return { message: "Output cancelled", type: ActionReturnType.Warning };
}
try {
exporter.export(filePath);
} catch (err: unknown) {
return { error: err, message: "Failed to export" };//, style: ToastStyle.Failure };
return { error: err, message: "Failed to export", type: ActionReturnType.Failure };
}
return { message: "Successfully exported" };//, style: ToastStyle.Success };
return { message: "Successfully exported", type: ActionReturnType.Success };
}
public draw() {

View File

@ -3,7 +3,7 @@ import { assert } from "../../util";
export class ButtonElement extends BaseUIElement {
private _label: string;
private _onClick: () => void
private _onClick: () => void;
public constructor(id: string, label: string, onClick: () => void) {
super(id);
@ -26,7 +26,7 @@ export class ButtonElement extends BaseUIElement {
element.addEventListener("click", () => {
if (this._isEnabled) {
this._onClick()
this._onClick();
}
});
}

32
src/ui/elements/output.ts Normal file
View File

@ -0,0 +1,32 @@
import { BaseUIElement } from "../layout";
import { assert } from "../../util";
import { ActionReturnType } from "../../app_context";
export class OutputElement {
private _id: string;
public constructor(id: string) {
this._id = id;
}
public generateHTML() {
return `
<div class="item-body-sunken" id="${this._id}">
</div>
`;
}
public setMessage(message: string, returnType: ActionReturnType) {
const element = document.getElementById(this._id) as HTMLDivElement;
assert(element !== null);
element.innerHTML = message;
element.classList.remove("border-warning");
element.classList.remove("border-failure");
if (returnType === ActionReturnType.Warning) {
element.classList.add("border-warning");
} else if (returnType === ActionReturnType.Failure) {
element.classList.add("border-error");
}
}
}

View File

@ -1,15 +1,17 @@
import { ButtonElement } from "./elements/button";
import { LabelElement } from "./elements/label";
import { OutputElement } from "./elements/output";
export interface Group {
label: string,
components: Component[]
submitButton: ButtonElement
label: string;
components: Component[];
submitButton: ButtonElement;
output: OutputElement;
}
export interface Component {
label : LabelElement,
type: BaseUIElement,
label: LabelElement;
type: BaseUIElement;
}
export abstract class BaseUIElement {
@ -59,9 +61,7 @@ function buildComponent(componentParams: Group) {
</div>
<div class="item item-body">
<div class="sub-right">
<div class="item-body-sunken">
Nothing
</div>
${componentParams.output.generateHTML()}
</div>
</div>
`;

View File

@ -8,7 +8,7 @@ import { Mesh, MaterialType } from "./mesh";
import { triangleArea } from "./math";
import { Axes, generateRays, rayIntersectTriangle } from "./ray";
import { BasicBlockAssigner, OrderedDitheringBlockAssigner } from "./block_assigner.js";
import { AppConfig } from "./config.js";
import { AppContext } from "./app_context.js";
interface Block {
position: Vector3;
@ -77,6 +77,7 @@ export class VoxelManager {
public assignBlocks() {
this.blockPalette = [];
this.voxelTexcoords = [];
let meanSquaredError = 0.0;
for (let i = 0; i < this.voxels.length; ++i) {
@ -84,7 +85,8 @@ export class VoxelManager {
const averageColour = getAverageColour(voxel.colours!);
const blockAssigner = AppConfig.DITHERING_ENABLED ? new OrderedDitheringBlockAssigner() : new BasicBlockAssigner();
const ditheringEnabled = AppContext.Get.dithering;
const blockAssigner = ditheringEnabled ? new OrderedDitheringBlockAssigner() : new BasicBlockAssigner();
const block = blockAssigner.assignBlock(averageColour, voxel.position);
const squaredError = Math.pow(255 * (block.colour.r - averageColour.r), 2) + Math.pow(255 * (block.colour.g - averageColour.g), 2) + Math.pow(255 * (block.colour.b - averageColour.b), 2);
@ -97,6 +99,17 @@ export class VoxelManager {
console.log("Mean Squared Error:", meanSquaredError);
}
public assignBlankBlocks() {
this.blockPalette = [];
const whiteColour = { r: 1.0, g: 1.0, b: 1.0 };
const block = new BasicBlockAssigner().assignBlock(whiteColour, new Vector3(0, 0, 0));
for (let i = 0; i < this.voxels.length; ++i) {
this._assignBlock(i, block);
}
}
public addVoxel(pos: Vector3, block: BlockInfo) {
// Is there already a voxel in this position?
let voxel = this.voxelsHash.get(pos);
@ -176,6 +189,10 @@ export class VoxelManager {
});
}
public paintMesh() {
this.assignBlocks();
}
voxeliseMesh(mesh: Mesh) {
this._clearVoxels();
@ -194,7 +211,8 @@ export class VoxelManager {
});
});
this.assignBlocks();
this.assignBlankBlocks();
//this.assignBlocks();
}
}

View File

@ -5,6 +5,7 @@
--canvas-width: calc(100% - var(--properties-width));
--pill-radius: 6px;
--prim-color: #008C74;
--prim-color-hover: #009e84;
--prim-color-disabled: #004e41;
--prop-bg-color: #333333;
}
@ -116,7 +117,7 @@ canvas {
font-size: 90%;
padding: 12px 18px;
line-height: 180%;
border: 1px solid rgb(255, 255, 255, 0);
min-height: 22px;
}
.round-top {
@ -147,11 +148,14 @@ select {
color: #A8A8A8;
background-color: #2F2F2F;
}
select:hover {
color: #C6C6C6;
background-color: #383838;
}
select:disabled {
background-color: #282828 !important;
color: #535353;
}
.file-input {
border-radius: 5px;
@ -209,7 +213,7 @@ select:hover {
}
.button:hover {
border: 1px solid rgb(255, 255, 255, 0.2);
background-color: #00A6D8;
background-color: var(--prim-color-hover);
cursor: pointer;
}
.button-disabled {
@ -273,4 +277,12 @@ select:hover {
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.2);
}
.border-warning {
border: 1.5px solid orange;
}
.border-error {
border: 1.5px solid red;
}