mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2025-03-07 14:06:41 +08:00
Last changes for 0.4
This commit is contained in:
parent
5f7ad8e21d
commit
37cccb72dd
@ -11,7 +11,6 @@ You can either download the [latest release](https://github.com/LucasDower/ObjTo
|
|||||||
* Navigate to `/ObjToSchematic-main`.
|
* Navigate to `/ObjToSchematic-main`.
|
||||||
* Run `npm install`.
|
* Run `npm install`.
|
||||||
* Run `npm start`.
|
* Run `npm start`.
|
||||||
* Note, for now, all .obj models **must** be triangulated before importing.
|
|
||||||
|
|
||||||
Support for choosing the block palette is not yet supported. Instead, you can edit `/tools/default-ignore-list.txt` to include blocks you don't want to be used and then run `npm run-script atlas`. You can also place custom textures in `/tools/blocks/` for more accurate block-colour matching when building with resource packs.
|
Support for choosing the block palette is not yet supported. Instead, you can edit `/tools/default-ignore-list.txt` to include blocks you don't want to be used and then run `npm run-script atlas`. You can also place custom textures in `/tools/blocks/` for more accurate block-colour matching when building with resource packs.
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "objtoschematic",
|
"name": "objtoschematic",
|
||||||
"version": "0.3.0",
|
"version": "0.4.0",
|
||||||
"description": "A tool to convert .obj files into voxels and then into Minecraft Schematic files",
|
"description": "A tool to convert .obj files into voxels and then into Minecraft Schematic files",
|
||||||
"main": "./dist/main.js",
|
"main": "./dist/main.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --fix ./src/*.ts",
|
"lint": "eslint --fix ./src/**/*.ts",
|
||||||
"build": "npm run lint && tsc",
|
"build": "npm run lint && tsc",
|
||||||
"start": "npm run build && electron ./dist/main.js --enable-logging",
|
"start": "npm run build && electron ./dist/main.js --enable-logging",
|
||||||
"atlas": "npx ts-node tools/build-atlas.ts",
|
"atlas": "npx ts-node tools/build-atlas.ts",
|
||||||
|
@ -13,6 +13,7 @@ import fs from 'fs';
|
|||||||
import { ButtonElement } from './ui/elements/button';
|
import { ButtonElement } from './ui/elements/button';
|
||||||
import { LabelElement } from './ui/elements/label';
|
import { LabelElement } from './ui/elements/label';
|
||||||
import { OutputElement } from './ui/elements/output';
|
import { OutputElement } from './ui/elements/output';
|
||||||
|
import { CustomError } from './util';
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export enum ActionReturnType {
|
export enum ActionReturnType {
|
||||||
@ -93,7 +94,8 @@ export class AppContext {
|
|||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
label: new LabelElement('label3', 'Ratio'),
|
label: new LabelElement('label3', 'Ratio'),
|
||||||
type: new SliderElement('ratio', 0.0, 1.0, 0.01, 0.5) },
|
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'),
|
output: new OutputElement('output2'),
|
||||||
@ -103,7 +105,7 @@ export class AppContext {
|
|||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
label: new LabelElement('label4', 'Voxel size'),
|
label: new LabelElement('label4', 'Voxel size'),
|
||||||
type: new SliderElement('voxelSize', 0.01, 0.5, 0.01, 0.1),
|
type: new SliderElement('voxelSize', 0.01, 0.5, 0.01, 0.10001),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: new LabelElement('label5', 'Ambient occlusion'),
|
label: new LabelElement('label5', 'Ambient occlusion'),
|
||||||
@ -182,9 +184,15 @@ export class AppContext {
|
|||||||
public do(action: Action) {
|
public do(action: Action) {
|
||||||
const status = this._actionMap.get(action)!();
|
const status = this._actionMap.get(action)!();
|
||||||
if (status) {
|
if (status) {
|
||||||
this._ui[action].output.setMessage(status.message, status.type);
|
|
||||||
if (status.error) {
|
if (status.error) {
|
||||||
console.error(status.error);
|
console.error('CAUGHT', status.error);
|
||||||
|
if (status.error instanceof CustomError) {
|
||||||
|
this._ui[action].output.setMessage(status.error.message, status.type);
|
||||||
|
} else {
|
||||||
|
this._ui[action].output.setMessage(status.message, status.type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._ui[action].output.setMessage(status.message, status.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,7 +214,7 @@ export class AppContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._loadedMesh = new Mesh(objPath, this._gl);
|
this._loadedMesh = new Mesh(objPath, mtlPath);
|
||||||
this._loadedMesh.loadTextures(this._gl);
|
this._loadedMesh.loadTextures(this._gl);
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
return { error: err, message: 'Could not load mesh', type: ActionReturnType.Failure };
|
return { error: err, message: 'Could not load mesh', type: ActionReturnType.Failure };
|
||||||
|
24
src/mesh.ts
24
src/mesh.ts
@ -4,7 +4,7 @@ import * as path from 'path';
|
|||||||
|
|
||||||
import { Triangle } from './triangle';
|
import { Triangle } from './triangle';
|
||||||
import { Vector3 } from './vector';
|
import { Vector3 } from './vector';
|
||||||
import { RGB, UV } from './util';
|
import { RGB, UV, CustomError } from './util';
|
||||||
import { TextureFormat } from './texture';
|
import { TextureFormat } from './texture';
|
||||||
import { triangleArea } from './math';
|
import { triangleArea } from './math';
|
||||||
|
|
||||||
@ -113,32 +113,26 @@ class Material {
|
|||||||
export class Mesh {
|
export class Mesh {
|
||||||
public materials: Array<Material>;
|
public materials: Array<Material>;
|
||||||
|
|
||||||
constructor(objPathString: string, gl: WebGLRenderingContext) {
|
constructor(objPathString: string, mtlPathString: string) {
|
||||||
// Parse .obj
|
// Parse .obj
|
||||||
const wavefrontString = fs.readFileSync(objPathString).toString('utf8');
|
const wavefrontString = fs.readFileSync(objPathString).toString('utf8');
|
||||||
const parsedOBJ = this._parseOBJFile(wavefrontString);
|
const parsedOBJ = this._parseOBJFile(wavefrontString);
|
||||||
|
|
||||||
// TODO: Create blank .mtl when not found
|
|
||||||
if (!parsedOBJ.mtlPath) {
|
|
||||||
throw Error('No .mtl file found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const objPath = path.parse(objPathString);
|
const objPath = path.parse(objPathString);
|
||||||
if (!path.isAbsolute(parsedOBJ.mtlPath)) {
|
|
||||||
parsedOBJ.mtlPath = path.join(objPath.dir, parsedOBJ.mtlPath);
|
|
||||||
}
|
|
||||||
parsedOBJ.mtlPath = parsedOBJ.mtlPath.trimEnd();
|
|
||||||
|
|
||||||
// Parse .mtl
|
// Parse .mtl
|
||||||
const materialString = fs.readFileSync(parsedOBJ.mtlPath).toString('utf8');
|
const materialString = fs.readFileSync(mtlPathString).toString('utf8');
|
||||||
const parsedMTL = this._parseMaterial(materialString, objPath);
|
const parsedMTL = this._parseMaterial(materialString, objPath);
|
||||||
|
|
||||||
|
|
||||||
this.materials = this._mergeMaterialData(parsedOBJ, parsedMTL);
|
this.materials = this._mergeMaterialData(parsedOBJ, parsedMTL);
|
||||||
this._centreMesh();
|
this._centreMesh();
|
||||||
this._normaliseMesh();
|
this._normaliseMesh();
|
||||||
|
|
||||||
console.log(this.materials);
|
// TODO: Throw at source
|
||||||
|
for (const material of this.materials) {
|
||||||
|
if (material.materialData === undefined) {
|
||||||
|
throw new CustomError('Could not link .obj with .mtl, possible mismatch?');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addMaterial(materialsJSON: Materials, materialName: string, materialDiffuseColour: RGB, materialDiffuseTexturePath: string, materialFormat: TextureFormat) {
|
private _addMaterial(materialsJSON: Materials, materialName: string, materialDiffuseColour: RGB, materialDiffuseTexturePath: string, materialFormat: TextureFormat) {
|
||||||
|
@ -195,6 +195,8 @@ export class Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public registerMesh(mesh: Mesh) {
|
public registerMesh(mesh: Mesh) {
|
||||||
|
this._gl.disable(this._gl.CULL_FACE);
|
||||||
|
|
||||||
mesh.materials.forEach((material) => {
|
mesh.materials.forEach((material) => {
|
||||||
const materialBuffer = new BottomlessBuffer([
|
const materialBuffer = new BottomlessBuffer([
|
||||||
{ name: 'position', numComponents: 3 },
|
{ name: 'position', numComponents: 3 },
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { BaseUIElement } from "../layout";
|
import { BaseUIElement } from '../layout';
|
||||||
import { assert } from "../../util";
|
import { assert } from '../../util';
|
||||||
|
|
||||||
export class ButtonElement extends BaseUIElement {
|
export class ButtonElement extends BaseUIElement {
|
||||||
private _label: string;
|
private _label: string;
|
||||||
@ -24,7 +24,7 @@ export class ButtonElement extends BaseUIElement {
|
|||||||
const element = document.getElementById(this._id) as HTMLDivElement;
|
const element = document.getElementById(this._id) as HTMLDivElement;
|
||||||
assert(element !== null);
|
assert(element !== null);
|
||||||
|
|
||||||
element.addEventListener("click", () => {
|
element.addEventListener('click', () => {
|
||||||
if (this._isEnabled) {
|
if (this._isEnabled) {
|
||||||
this._onClick();
|
this._onClick();
|
||||||
}
|
}
|
||||||
@ -36,11 +36,11 @@ export class ButtonElement extends BaseUIElement {
|
|||||||
assert(element !== null);
|
assert(element !== null);
|
||||||
|
|
||||||
if (this._isEnabled) {
|
if (this._isEnabled) {
|
||||||
//element.classList.add("button");
|
// element.classList.add("button");
|
||||||
element.classList.remove("button-disabled");
|
element.classList.remove('button-disabled');
|
||||||
} else {
|
} else {
|
||||||
element.classList.add("button-disabled");
|
element.classList.add('button-disabled');
|
||||||
//element.classList.remove("button");
|
// element.classList.remove("button");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { BaseUIElement } from "../layout";
|
import { BaseUIElement } from '../layout';
|
||||||
import { assert } from "../../util";
|
import { assert } from '../../util';
|
||||||
|
|
||||||
export interface ComboBoxItem {
|
export interface ComboBoxItem {
|
||||||
id: string;
|
id: string;
|
||||||
@ -15,7 +15,7 @@ export class ComboBoxElement extends BaseUIElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public generateHTML() {
|
public generateHTML() {
|
||||||
let itemsHTML = "";
|
let itemsHTML = '';
|
||||||
for (const item of this._items) {
|
for (const item of this._items) {
|
||||||
itemsHTML += `<option value="${item.id}">${item.displayText}</option>`;
|
itemsHTML += `<option value="${item.id}">${item.displayText}</option>`;
|
||||||
}
|
}
|
||||||
@ -34,7 +34,6 @@ export class ComboBoxElement extends BaseUIElement {
|
|||||||
const element = document.getElementById(this._id) as HTMLSelectElement;
|
const element = document.getElementById(this._id) as HTMLSelectElement;
|
||||||
assert(element !== null);
|
assert(element !== null);
|
||||||
return this._items[element.selectedIndex].id;
|
return this._items[element.selectedIndex].id;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _onEnabledChanged() {
|
protected _onEnabledChanged() {
|
||||||
@ -42,4 +41,4 @@ export class ComboBoxElement extends BaseUIElement {
|
|||||||
assert(element !== null);
|
assert(element !== null);
|
||||||
element.disabled = !this._isEnabled;
|
element.disabled = !this._isEnabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { BaseUIElement } from "../layout";
|
import { BaseUIElement } from '../layout';
|
||||||
import { assert } from "../../util";
|
import { assert } from '../../util';
|
||||||
|
|
||||||
import { remote } from "electron";
|
import { remote } from 'electron';
|
||||||
import * as path from "path";
|
import * as path from 'path';
|
||||||
|
|
||||||
export class FileInputElement extends BaseUIElement {
|
export class FileInputElement extends BaseUIElement {
|
||||||
private _fileExtension: string;
|
private _fileExtension: string;
|
||||||
@ -11,7 +11,7 @@ export class FileInputElement extends BaseUIElement {
|
|||||||
public constructor(id: string, fileExtension: string) {
|
public constructor(id: string, fileExtension: string) {
|
||||||
super(id);
|
super(id);
|
||||||
this._fileExtension = fileExtension;
|
this._fileExtension = fileExtension;
|
||||||
this._loadedFilePath = "";
|
this._loadedFilePath = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public generateHTML() {
|
public generateHTML() {
|
||||||
@ -32,18 +32,18 @@ export class FileInputElement extends BaseUIElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const files = remote.dialog.showOpenDialogSync({
|
const files = remote.dialog.showOpenDialogSync({
|
||||||
title: "Load file",
|
title: 'Load file',
|
||||||
buttonLabel: "Load",
|
buttonLabel: 'Load',
|
||||||
filters: [{
|
filters: [{
|
||||||
name: 'Waveform obj file',
|
name: 'Waveform obj file',
|
||||||
extensions: [`${this._fileExtension}`]
|
extensions: [`${this._fileExtension}`],
|
||||||
}]
|
}],
|
||||||
});
|
});
|
||||||
if (files && files.length === 1) {
|
if (files && files.length === 1) {
|
||||||
const filePath = files[0];
|
const filePath = files[0];
|
||||||
this._loadedFilePath = filePath;
|
this._loadedFilePath = filePath;
|
||||||
} else {
|
} else {
|
||||||
this._loadedFilePath = "";
|
this._loadedFilePath = '';
|
||||||
}
|
}
|
||||||
const parsedPath = path.parse(this._loadedFilePath);
|
const parsedPath = path.parse(this._loadedFilePath);
|
||||||
element.innerHTML = parsedPath.name + parsedPath.ext;
|
element.innerHTML = parsedPath.name + parsedPath.ext;
|
||||||
@ -59,11 +59,11 @@ export class FileInputElement extends BaseUIElement {
|
|||||||
assert(element !== null);
|
assert(element !== null);
|
||||||
|
|
||||||
if (this._isEnabled) {
|
if (this._isEnabled) {
|
||||||
//element.classList.add("button");
|
// element.classList.add("button");
|
||||||
element.classList.remove("input-text-disabled");
|
element.classList.remove('input-text-disabled');
|
||||||
} else {
|
} else {
|
||||||
element.classList.add("input-text-disabled");
|
element.classList.add('input-text-disabled');
|
||||||
//element.classList.remove("button");
|
// element.classList.remove("button");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { assert } from "../../util";
|
import { assert } from '../../util';
|
||||||
|
|
||||||
export class LabelElement {
|
export class LabelElement {
|
||||||
private _id: string;
|
private _id: string;
|
||||||
@ -22,9 +22,9 @@ export class LabelElement {
|
|||||||
assert(element !== null);
|
assert(element !== null);
|
||||||
|
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
element.classList.remove("sub-left-disabled");
|
element.classList.remove('sub-left-disabled');
|
||||||
} else {
|
} else {
|
||||||
element.classList.add("sub-left-disabled");
|
element.classList.add('sub-left-disabled');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { BaseUIElement } from "../layout";
|
import { assert } from '../../util';
|
||||||
import { assert } from "../../util";
|
import { ActionReturnType } from '../../app_context';
|
||||||
import { ActionReturnType } from "../../app_context";
|
|
||||||
|
|
||||||
export class OutputElement {
|
export class OutputElement {
|
||||||
private _id: string;
|
private _id: string;
|
||||||
@ -21,12 +20,12 @@ export class OutputElement {
|
|||||||
assert(element !== null);
|
assert(element !== null);
|
||||||
|
|
||||||
element.innerHTML = message;
|
element.innerHTML = message;
|
||||||
element.classList.remove("border-warning");
|
element.classList.remove('border-warning');
|
||||||
element.classList.remove("border-error");
|
element.classList.remove('border-error');
|
||||||
if (returnType === ActionReturnType.Warning) {
|
if (returnType === ActionReturnType.Warning) {
|
||||||
element.classList.add("border-warning");
|
element.classList.add('border-warning');
|
||||||
} else if (returnType === ActionReturnType.Failure) {
|
} else if (returnType === ActionReturnType.Failure) {
|
||||||
element.classList.add("border-error");
|
element.classList.add('border-error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { BaseUIElement } from "../layout";
|
import { BaseUIElement } from '../layout';
|
||||||
import { assert } from "../../util";
|
import { assert } from '../../util';
|
||||||
import { clamp } from "../../math";
|
import { clamp } from '../../math';
|
||||||
|
|
||||||
export class SliderElement extends BaseUIElement {
|
export class SliderElement extends BaseUIElement {
|
||||||
private _min: number;
|
private _min: number;
|
||||||
@ -36,13 +36,13 @@ export class SliderElement extends BaseUIElement {
|
|||||||
this._dragging = true;
|
this._dragging = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener("mousemove", (e: any) => {
|
document.addEventListener('mousemove', (e: any) => {
|
||||||
if (this._dragging) {
|
if (this._dragging) {
|
||||||
this._updateValue(e);
|
this._updateValue(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("mouseup", (e: any) => {
|
document.addEventListener('mouseup', (e: any) => {
|
||||||
if (this._dragging) {
|
if (this._dragging) {
|
||||||
this._updateValue(e);
|
this._updateValue(e);
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ export class SliderElement extends BaseUIElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const element = document.getElementById(this._id) as HTMLDivElement;
|
const element = document.getElementById(this._id) as HTMLDivElement;
|
||||||
const elementBar = document.getElementById(this._id + "-bar") as HTMLDivElement;
|
const elementBar = document.getElementById(this._id + '-bar') as HTMLDivElement;
|
||||||
assert(element !== null && elementBar !== null);
|
assert(element !== null && elementBar !== null);
|
||||||
|
|
||||||
const mouseEvent = e as MouseEvent;
|
const mouseEvent = e as MouseEvent;
|
||||||
@ -73,17 +73,17 @@ export class SliderElement extends BaseUIElement {
|
|||||||
|
|
||||||
protected _onEnabledChanged() {
|
protected _onEnabledChanged() {
|
||||||
const element = document.getElementById(this._id) as HTMLDivElement;
|
const element = document.getElementById(this._id) as HTMLDivElement;
|
||||||
const elementBar = document.getElementById(this._id + "-bar") as HTMLDivElement;
|
const elementBar = document.getElementById(this._id + '-bar') as HTMLDivElement;
|
||||||
assert(element !== null && elementBar !== null);
|
assert(element !== null && elementBar !== null);
|
||||||
|
|
||||||
if (this._isEnabled) {
|
if (this._isEnabled) {
|
||||||
//element.classList.add("button");
|
// element.classList.add("button");
|
||||||
element.classList.remove("new-slider-disabled");
|
element.classList.remove('new-slider-disabled');
|
||||||
elementBar.classList.remove("new-slider-bar-disabled");
|
elementBar.classList.remove('new-slider-bar-disabled');
|
||||||
} else {
|
} else {
|
||||||
element.classList.add("new-slider-disabled");
|
element.classList.add('new-slider-disabled');
|
||||||
elementBar.classList.add("new-slider-bar-disabled");
|
elementBar.classList.add('new-slider-bar-disabled');
|
||||||
//element.classList.remove("button");
|
// element.classList.remove("button");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ButtonElement } from "./elements/button";
|
import { ButtonElement } from './elements/button';
|
||||||
import { LabelElement } from "./elements/label";
|
import { LabelElement } from './elements/label';
|
||||||
import { OutputElement } from "./elements/output";
|
import { OutputElement } from './elements/output';
|
||||||
|
|
||||||
export interface Group {
|
export interface Group {
|
||||||
label: string;
|
label: string;
|
||||||
@ -47,7 +47,7 @@ function buildSubcomp(subcomp: Component) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildComponent(componentParams: Group) {
|
function buildComponent(componentParams: Group) {
|
||||||
let innerHTML = "";
|
let innerHTML = '';
|
||||||
for (const subcomp of componentParams.components) {
|
for (const subcomp of componentParams.components) {
|
||||||
innerHTML += buildSubcomp(subcomp);
|
innerHTML += buildSubcomp(subcomp);
|
||||||
}
|
}
|
||||||
@ -77,9 +77,9 @@ export function registerUI(uiGroups: Group[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function buildUI(myItems: Group[]) {
|
export function buildUI(myItems: Group[]) {
|
||||||
let itemHTML = "";
|
let itemHTML = '';
|
||||||
for (const item of myItems) {
|
for (const item of myItems) {
|
||||||
itemHTML += `
|
itemHTML += `
|
||||||
<div class="item item-body">
|
<div class="item item-body">
|
||||||
<div class="sub-right">
|
<div class="sub-right">
|
||||||
<div class="h-div">
|
<div class="h-div">
|
||||||
@ -94,10 +94,10 @@ export function buildUI(myItems: Group[]) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
itemHTML += buildComponent(item);
|
itemHTML += buildComponent(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById("properties")!.innerHTML = `<div class="menu"><div class="container">
|
document.getElementById('properties')!.innerHTML = `<div class="menu"><div class="container">
|
||||||
` + itemHTML + `</div></div>`;
|
` + itemHTML + `</div></div>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -107,4 +107,4 @@ export function setEnabled(group: Group, isEnabled: boolean) {
|
|||||||
comp.label.setEnabled(isEnabled);
|
comp.label.setEnabled(isEnabled);
|
||||||
}
|
}
|
||||||
group.submitButton.setEnabled(isEnabled);
|
group.submitButton.setEnabled(isEnabled);
|
||||||
}
|
}
|
||||||
|
@ -54,3 +54,9 @@ export function assert(condition: boolean, errorMessage = 'Assertion Failed') {
|
|||||||
throw Error(errorMessage);
|
throw Error(errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export class CustomError extends Error {
|
||||||
|
constructor(msg: string) {
|
||||||
|
super(msg);
|
||||||
|
Object.setPrototypeOf(this, CustomError.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user