@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
208 lines (207 loc) • 6.85 kB
JavaScript
"use strict";
import { TypedSopNode } from "./_Base";
import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig";
import { CoreLoaderFont } from "../../../core/loader/font/CoreFontLoader";
import { Poly } from "../../Poly";
import { DEFAULT_FONT_URL_JSON } from "../../../core/Assets";
import { TextSopJustifiyMode, TEXT_SOP_JUSTIFY_MODES } from "../../../core/geometry/text/TextJustify";
import { textBuildGeometries } from "../../../core/geometry/text/TextGeometries";
import { TextType, TEXT_TYPES } from "../../../core/geometry/text/TextType";
import { textMergeLetters } from "../../../core/geometry/text/TextMergeLetters";
import { isBooleanTrue } from "../../../core/Type";
import { SopType } from "../../poly/registers/nodes/types/Sop";
import { EXTENSIONS_BY_NODE_TYPE_BY_CONTEXT } from "../../../core/loader/FileExtensionRegister";
import { NodeContext } from "../../poly/NodeContext";
const GENERATION_ERROR_MESSAGE = `failed to generate geometry. Try to remove some characters`;
class TextSopParamsConfig extends NodeParamsConfig {
constructor() {
super(...arguments);
/** @param font used */
this.font = ParamConfig.STRING(DEFAULT_FONT_URL_JSON, {
fileBrowse: { extensions: EXTENSIONS_BY_NODE_TYPE_BY_CONTEXT[NodeContext.SOP][SopType.TEXT] }
});
/** @param text created */
this.text = ParamConfig.STRING("polygonjs", {
multiline: true
});
/** @param type of geometry created */
this.type = ParamConfig.INTEGER(0, {
menu: {
entries: TEXT_TYPES.map((type, i) => {
return {
name: type,
value: i
};
})
}
});
/** @param font size */
this.size = ParamConfig.FLOAT(1, {
range: [0, 1],
rangeLocked: [true, false]
});
/** @param extrude depth */
this.extrude = ParamConfig.FLOAT(0.1, {
visibleIf: {
type: TEXT_TYPES.indexOf(TextType.MESH)
}
});
/** @param segments count */
this.segments = ParamConfig.INTEGER(1, {
range: [1, 20],
rangeLocked: [true, false],
visibleIf: {
type: TEXT_TYPES.indexOf(TextType.MESH)
}
});
/** @param bevelEnabled */
this.bevelEnabled = ParamConfig.BOOLEAN(false, {
visibleIf: {
type: TEXT_TYPES.indexOf(TextType.MESH)
}
});
/** @param bevelThickness */
this.bevelThickness = ParamConfig.FLOAT(0, {
visibleIf: {
type: TEXT_TYPES.indexOf(TextType.MESH),
bevelEnabled: true
}
});
/** @param bevelSize */
this.bevelSize = ParamConfig.FLOAT(0, {
visibleIf: {
type: TEXT_TYPES.indexOf(TextType.MESH),
bevelEnabled: true
}
});
/** @param bevelOffset */
this.bevelOffset = ParamConfig.FLOAT(0, {
visibleIf: {
type: TEXT_TYPES.indexOf(TextType.MESH),
bevelEnabled: true
}
});
/** @param bevelSegments */
this.bevelSegments = ParamConfig.INTEGER(2, {
range: [1, 6],
visibleIf: {
type: TEXT_TYPES.indexOf(TextType.MESH),
bevelEnabled: true
}
});
/** @param stroke width */
this.strokeWidth = ParamConfig.FLOAT(0.02, {
visibleIf: {
type: TEXT_TYPES.indexOf(TextType.STROKE)
}
});
/** @param line height */
this.lineHeight = ParamConfig.FLOAT(1, {
range: [0, 2],
rangeLocked: [false, false]
});
/** @param create one object per letter */
this.splitPerLetter = ParamConfig.BOOLEAN(0);
/** @param when creating one object per letter, define if the characters like space create an object */
this.keepEmptyGeometries = ParamConfig.BOOLEAN(0, { visibleIf: { splitPerLetter: 1 } });
/** @param justify mode */
this.justifyMode = ParamConfig.INTEGER(TEXT_SOP_JUSTIFY_MODES.indexOf(TextSopJustifiyMode.LEFT), {
menu: {
entries: TEXT_SOP_JUSTIFY_MODES.map((name, value) => ({ name, value }))
}
});
/** @param open advanced options */
this.tadvanced = ParamConfig.BOOLEAN(0);
/** @param is counter clock wise: defines the vertex order when parsing the font */
this.isCCW = ParamConfig.BOOLEAN(0, {
visibleIf: { tadvanced: true }
});
}
/** @param defines if holes should be found when parsing the font */
// noHoles = ParamConfig.BOOLEAN(0, {
// visibleIf: {tadvanced: true},
// });
}
const ParamsConfig = new TextSopParamsConfig();
export class TextSopNode extends TypedSopNode {
constructor() {
super(...arguments);
this.paramsConfig = ParamsConfig;
this._loadedFonts = /* @__PURE__ */ new Map();
}
static type() {
return SopType.TEXT;
}
dispose() {
super.dispose();
Poly.blobs.clearBlobsForNode(this);
}
setTextType(type) {
this.p.type.set(TEXT_TYPES.indexOf(type));
}
async cook() {
const fontUrl = this.pv.font;
let font = this._loadedFonts.get(fontUrl);
try {
if (!font) {
const loader = new CoreLoaderFont(this.pv.font, this);
font = await loader.load();
if (font) {
this._loadedFonts.set(fontUrl, font);
}
}
} catch (err) {
console.warn("error:", err);
this.states.error.set(`could not load font (${this.pv.font}, reason:${err.message})`);
this.cookController.endCook();
return;
}
if (!font) {
this.cookController.endCook();
return;
}
const textType = TEXT_TYPES[this.pv.type];
if (!TEXT_TYPES.includes(textType)) {
this.cookController.endCook();
return;
}
const textWithoutSpaces = this.pv.text.replace(/\s/g, "");
if (textWithoutSpaces.length == 0) {
return this.setObjects([]);
}
const geometries = await await textBuildGeometries({
text: this.pv.text,
textType,
font,
size: this.pv.size,
extrude: this.pv.extrude,
curveSegments: this.pv.segments,
strokeWidth: this.pv.strokeWidth,
// bevel
bevelEnabled: this.pv.bevelEnabled,
bevelThickness: this.pv.bevelThickness,
bevelSize: this.pv.bevelSize,
bevelOffset: this.pv.bevelOffset,
bevelSegments: this.pv.bevelSegments,
//
isCCW: isBooleanTrue(this.pv.tadvanced) ? this.pv.isCCW : void 0
// noHoles: isBooleanTrue(this.pv.tadvanced) ? this.pv.noHoles : undefined,
});
if (geometries) {
const objects = textMergeLetters({
text: this.pv.text,
geometries,
textType,
splitPerLetter: this.pv.splitPerLetter,
keepEmptyGeometries: this.pv.keepEmptyGeometries,
justifyMode: TEXT_SOP_JUSTIFY_MODES[this.pv.justifyMode],
lineHeight: this.pv.lineHeight
});
if (objects) {
return this.setObjects(objects);
}
}
this.states.error.set(GENERATION_ERROR_MESSAGE);
this.cookController.endCook();
}
}