/******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ // The require scope /******/ var __webpack_require__ = {}; /******/ /************************************************************************/ /******/ /* webpack/runtime/global */ /******/ (() => { /******/ __webpack_require__.g = (function() { /******/ if (typeof globalThis === 'object') return globalThis; /******/ try { /******/ return this || new Function('return this')(); /******/ } catch (e) { /******/ if (typeof window === 'object') return window; /******/ } /******/ })(); /******/ })(); /******/ /************************************************************************/ var __webpack_exports__ = {}; // UNUSED EXPORTS: BaseProperty, Behavior, Block, DefaultValues, FontLibrary, FontVariant, InheritableProperty, Inline, InlineBlock, InlineGlyph, MSDFFontMaterialUtils, MaterialTransformers, MeshUIBaseElement, ShaderChunkUI, Text, TypographicFont, TypographicGlyph, default, update ;// CONCATENATED MODULE: ./src/core/DefaultValues.js /** List the default values of the lib components */ const _values = { fontFamily: null, fontSize: 0.05, fontKerning: 'auto', fontStyle: 'normal', fontWeight : 'normal', offset: 0.005, lineHeight: 1.2, lineBreak: '- ,.:?!\n',// added '\n' to also acts as friendly breaks when white-space:normal whiteSpace: 'pre-line', flexDirection : 'column', justifyContent : 'start', alignItems : 'start', backgroundImage: null, textAlign : 'left', boxSizing: 'content-box', position: 'static', color: 0xffffff, fontColor: 0xffffff, fontOpacity: 1, opacity: 1, fontPXRange: 4, fontSupersampling: true, fontSmooth: 'antialiased', borderRadius: 0, borderWidth: 0, borderColor: 'black', borderOpacity: 1, backgroundSize: "cover", backgroundColor: 0x000000, backgroundOpacity: 0, overflow: 'visible', letterSpacing: 0, invertAlpha : false, segments: 1 }; /** * @param {import('./../core/elements/MeshUIBaseElement').Options} overrideProperties */ const set = function ( overrideProperties ) { for ( const property in overrideProperties ) { _values[property] = overrideProperties[property]; } } /** * * @param {string} property * @return {any} */ const get = function ( property ) { if( !Object.prototype.hasOwnProperty.call( _values, property) ) { console.warn( `ThreeMeshUI::DefaultValues is trying to retrieve non-existing property '${property}'`); } return _values[property]; } ;// CONCATENATED MODULE: ./src/core/properties/BaseProperty.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class BaseProperty { /** * * @param {string} propertyId * @param {any} [value=null] * @param primitive */ constructor( propertyId, value = null, primitive = true ) { /** * * @type {string} * @internal */ this._id = propertyId; /** * * @type {any} * @internal */ this._value = value; /** * * @type {boolean} * @internal */ this._needsUpdate = true; /** * * @type {boolean} * @internal */ this._needsProcess = false; /** * * @type {boolean} * @internal */ this._needsRender = false; /** * * @type {boolean} * @protected */ this._isPrimitive = primitive; } /** * * @return {string} */ get id() { return this._id; } /** * * @return {any} */ get value() { return this._value; } /** * * @param {any} value */ set value( value ) { if ( !this.isValid( value ) ) return; if ( this._value !== value ) { this._value = value; this._needsUpdate = true; } } /* eslint-disable no-unused-vars */ /** * * @param element * @param {Object.} out */ update( element, out ) { /* eslint-enable no-unused-vars */ // the value has been updated from setter // if there is no additional logic // then just output it // => out[this._id] = this._value; this.output( out ); // ?? //this.computeOutputValue( element ); // if( this._isPrimitive ) this.output( out ); } /* eslint-disable no-unused-vars */ /** * Output this property in a dictionnary * @param {Object.} out */ output( out ) { /* eslint-enable no-unused-vars */ // ie: // out['borderRadius'] = this; // out[this._id] = this._value; } /** * * @param {Out} out */ _outputValue( out ) { out[ this._id ] = this._value; } /* eslint-disable no-unused-vars */ /** * Execute additional process after all properties have been updated * @param {MeshUIBaseElement} element */ process( element ) { /* eslint-enable no-unused-vars */ } /* eslint-disable no-unused-vars */ /** * Execute additional process after all properties have been updated * @param {MeshUIBaseElement} element */ render( element ) { /* eslint-enable no-unused-vars */ } /** * * @param {MeshUIBaseElement} element */ getInheritedInput( element ) { if ( this._value !== 'inherit' ) return this._value; const parent = element._parent._value; if ( parent && parent[ `_${this._id}` ] ) { return parent[ `_${this._id}` ].getInheritedInput( parent ) } return this.getDefaultValue(); } /** * * @return {any} */ getDefaultValue() { return get( this._id ); } /* eslint-disable no-unused-vars */ /** * * @param {any} value * @return {boolean} */ isValid( value ) { /* eslint-enable no-unused-vars */ return true; } /** * */ emptyStrategyLogic() { throw new Error( `ThreeMeshUI::${this.constructor.name} has empty strategy. Update has not been processed.` ); } requestUpdate() { this._needsUpdate = true; } requestProcess() { this._needsProcess = false; } requestRender() { this._needsRender = false; } } /** * @typedef Out * @type {Object & Object.} */ ;// CONCATENATED MODULE: ./src/core/properties/RenderOrderProperty.js class RenderOrderProperty extends BaseProperty{ constructor() { super( 'renderOrder', 'auto', true); this.output = this._outputValue; this._actualValue = 0; } /** * * @param {number} value */ set value( value ) { if( ! this.isValid( value) ) return; this._value = value; this._needsUpdate = true; } update( element, out ) { if( this._value !== 'auto' ) { this._actualValue = this._value; } else { const parent = element._parent._value; if( parent !== null ) { const parentIndex = parent._renderOrder._actualValue; const positionInParent = 1 + parent._children._uis.indexOf( element ); this._actualValue = parentIndex + positionInParent; } } // update any children for ( const childUIElement of element._children._uis ) { const property = childUIElement[`_renderOrder`]; if( property._value === 'auto' ) childUIElement[`_renderOrder`]._needsUpdate = true; } this._outputValue( out ); } _outputValue( out ) { out[this._id] = this._actualValue; } /** * * @return {number} */ get value() { return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/InheritableProperty.js class InheritableProperty extends BaseProperty { /** * * @param {string} propertyId * @param {any} [value=null] * @param primitive */ constructor( propertyId, value = null, primitive = true ) { super( propertyId, value, primitive ); // @TODO : I would like to remove this rules ( here ) this.output = this._outputValue; this._notInheritedValue = null; } update( element , out ) { /* eslint-enable no-unused-vars */ this._notInheritedValue = this._value; if( this._notInheritedValue === 'inherit' ) { this._notInheritedValue = this.getInheritedInput( element ); } // else // { // this.propagate( element ); // } // @TODO: Evaluate. This might be too much this.propagate( element ); this._outputValue( out ); } propagate( element ) { // rebuild same properties on children 'inheritance' for ( const childUIElement of element._children._uis ) { const property = childUIElement[`_${this._id}`]; if( property !== undefined && property._value === 'inherit' ) { childUIElement[`_${this._id}`]._needsUpdate = true; } } } /** * Output this property in a dictionnary * @override */ _outputValue( out ) { /* eslint-enable no-unused-vars */ out[this._id] = this._notInheritedValue; } set value ( value ) { if( ! this.isValid( value) ) return; if( this._value !== value ) { this._value = value; this._needsUpdate = true; } } /** * * @override * @return {any|"inherit"} */ get value() { if( this._value === 'inherit' ) return this._notInheritedValue; return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/OffsetProperty.js class OffsetProperty extends InheritableProperty { constructor( ) { super( 'offset', 'inherit', false ); } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ super.update( element, out); // only process if element has ui parent if( element._parent._value !== null ) element.position.z = this._notInheritedValue; } } ;// CONCATENATED MODULE: ./src/core/properties/FontSmoothProperty.js class FontSmoothProperty extends InheritableProperty{ constructor() { super( 'fontSmooth', 'inherit', true); // configure this._needsUpdate = false; this.isValid = _isValid; this.output = this._outputValue; } } const AVAILABLE_VALUES = ['inherit','none','antialiased']; /** * * @param {string} value * @return {boolean} * @private */ function _isValid( value ) { if( AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn(`.fontSmoothing value '${value}' is not valid. Aborted`); return false; } return true; } ;// CONCATENATED MODULE: external "THREE" const external_THREE_namespaceObject = THREE; ;// CONCATENATED MODULE: ./src/core/properties/style-properties/SubStyleProperty.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /* eslint-disable no-unused-vars */ class SubStyleProperty extends BaseProperty{ /** * * @param {string} propertyId * @param {boolean} [primitive=true] * @param {any} defaultValue */ constructor( propertyId, defaultValue, primitive = true) { super( propertyId, 'unset', primitive ); /** * @type {any} * @internal */ this._input = 'inherit'; /** * * @type {boolean} * @protected */ this._allowsInherit = true; /** * The input value that won't be 'inherit' * @type {any} * @protected */ this._inheritedInput = undefined; /** * * @type {any} * @internal */ this._inline = undefined; } /** * * @param {MeshUIBaseElement} element * @param {Object. } out */ update( element, out ) { if( !this._allowsInherit ) { this._inheritedInput = this.getInheritedInput( element ); } this.computeOutputValue( element ); // rebuild same properties on children 'inheritance' for ( const childUIElement of element._children._uis ) { const property = childUIElement[`_${this._id}`]; const target = property._input ? property._input : property._value; if( target === 'inherit' ) childUIElement[`_${this._id}`]._needsUpdate = true; } this.output( out ); } /** * * @param {MeshUIBaseElement} element */ computeOutputValue( element ) { this._value = this._input; } /** * * @param {MeshUIBaseElement} element */ _computeFromInherited( element ) { this._value = this._inheritedInput; } /** * @override * @deprecated * @param {any} v */ set value( v ) { console.warn(".(style) sub-property cannot be directly set. It must comes from inline or computed setter.") } /** * * @param {any} value */ set inline( value ) { if( ! this.isValidValue( value ) ) return; if( value === this._inline ) { // do nothing no update, the value hasn't changed return; } this._input = this._inline = value; this._needsUpdate = true; } /** * * @return {any} */ get inline() { return this._inline; } /** * * @param {any} value * @return {boolean} */ isValidValue( value ) { return true; } /** * @param {MeshUIBaseElement} element */ getInheritedInput ( element ) { if( this._input !== 'inherit' ) return this._input; const parent = element._parent._value; if( parent ) { return parent[`_${this._id}`].getInheritedInput( parent ) } return this.getDefaultValue(); } } /* eslint-enable no-unused-vars */ ;// CONCATENATED MODULE: ./src/core/properties/style-properties/StyleVector4Property.js class StyleVector4Property extends SubStyleProperty { constructor( propertyId, defaultValue ) { super( propertyId, defaultValue, false ); /** * * @type {Vector4} * @private */ this._input = new external_THREE_namespaceObject.Vector4(0,0,0,0); /** * * @type {any} * @internal */ this._inline = null; /** * @override * @type {Vector4} * @protected */ this._value = new external_THREE_namespaceObject.Vector4(0,0,0,0); } /** * @override * @return {Vector4} */ get value(){ return this._value; } /* eslint-disable no-unused-vars */ /** * @override */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ this._vector4ValueSetter( this._value, this._input ); } set inline( value ) { this._vector4ValueSetter( this._input, value ); if( this._input.equals( this._value) ) return; this._needsUpdate = true; } /** * * @param {Number} v */ set top( v ) { if( this._input.x === v ) return; this._input.x = v; this._needsUpdate = true; } /** * * @returns {number} */ get top() { return this._input.x; } /** * * @param {Number} v */ set right( v ) { if( this._input.y === v ) return; this._input.y = v; this._needsUpdate = true; } /** * * @returns {number} */ get right() { return this._input.y; } /** * * @param {Number} v */ set bottom( v ) { if( this._input.z === v ) return; this._input.z = v; this._needsUpdate = true; } /** * * @returns {number} */ get bottom() { return this._input.z; } /** * * @param {Number} v */ set left( v ) { if( this._input.w === v ) return; this._input.w = v; this._needsUpdate = true; } /** * * @returns {number} */ get left() { return this._input.w; } dispose(){ this._computed = null; this._inline = null; this._input = null; this._output = null; } /** * * @param {Vector4} vector4 * @param {Vector4|Array.|Number|String} value * @protected */ _vector4ValueSetter( vector4, value ) { if ( value instanceof external_THREE_namespaceObject.Vector4 ) { vector4.copy( value ); return; } if ( typeof value === 'string' || value instanceof String ) { value = value.split( ' ' ); } if ( Array.isArray( value ) ) { value = value.map( v => parseFloat( v ) ); switch ( value.length ) { case 1: vector4.setScalar( value[ 0 ] ); return; case 2: vector4.x = vector4.z = value[ 0 ]; vector4.y = vector4.w = value[ 1 ]; return; case 3: vector4.x = value[ 0 ]; vector4.y = value[ 1 ]; vector4.z = value[ 2 ]; return; case 4: vector4.x = value[ 0 ]; vector4.y = value[ 1 ]; vector4.z = value[ 2 ]; vector4.w = value[ 3 ]; return; default: console.error( 'StyleVector4Property::set() Four Dimension property had more than four values' ); return; } } if ( !isNaN( value ) ) { vector4.setScalar( value ); } } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/bounds/PaddingProperty.js class PaddingProperty extends StyleVector4Property { constructor() { super('padding', new external_THREE_namespaceObject.Vector4(0,0,0,0) ) } computeOutputValue( element ) { super.computeOutputValue( element ); element._bounds._needsUpdate = true; element._bounds._needsRender = true; element._layouter._needsProcess = true; element._renderer._needsRender = true; if( element._parent._value ){ element._parent._value._layouter._needsProcess = true; } } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/bounds/MarginProperty.js class MarginProperty extends StyleVector4Property { constructor() { super('margin', new external_THREE_namespaceObject.Vector4(0,0,0,0) ) } computeOutputValue( element ) { super.computeOutputValue( element ); element._renderer._needsRender = true; if( element._parent._value ){ element._parent._value._flexDirection._needsProcess = true; } } } ;// CONCATENATED MODULE: ./src/utils/mediator/transformers/CommonTransformers.js /** * Transfer the alphaTest value from MeshUIComponent to material * @type {import('../Mediator').MediationTransformer} */ const directTransfer = function ( target, targetProperty, value ) { target[targetProperty] = value; } const directTransferNotNull = function( target, targetProperty, value ) { if( value === null ) return; target[targetProperty] = value; } ;// CONCATENATED MODULE: ./src/utils/mediator/Mediator.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * An option function to transform value from subject to target * @typedef {(target:any, targetProperty:string, value:any) => void} MediationTransformer * */ /** * @typedef {Object.<{subjectProperty:string, trans?:MediationTransformer}>} MediationDefinition * */ class Mediator{ /** * @constructor * @param {MediationDefinition} definition */ constructor( definition ) { /** * * @type {MediationDefinition} * @private */ this._definition = definition; } /** * * @param {MediationDefinition} value */ set definition( value ) { this._definition = value; } /** * * @param {MeshUIBaseElement} subject * @param {any} target * @param {Object.<(string|number), any>} options * @param {any} [secondTarget=null] */ mediate( subject, target, options, secondTarget = null ) { // Mediate each subject properties to material for ( const subjectProperty in this._definition ) { const mediationDefinition = this._definition[subjectProperty]; if ( options[subjectProperty] !== undefined ) { // retrieve the mediation transformer to use for this property const mediationTransformer = mediationDefinition.t ? mediationDefinition.t : directTransfer; mediationTransformer( target, mediationDefinition.m, options[subjectProperty] ); // Also transfert to second target is isset if( secondTarget ) { mediationTransformer( secondTarget, mediationDefinition.m, options[subjectProperty] ); } } } } /*********************************************************************************************************************** * STATIC **********************************************************************************************************************/ /** * * @param {MeshUIComponent} subject * @param {any} target * @param {Object.<(string|number), any>} options * @param {Object.<{subjectProperty:string, t?:(target:any, targetProperty:string, value:any) => void}>} mediationDefinitions * @param {any} [secondTarget=null] */ static mediate( subject, target, options, mediationDefinitions, secondTarget = null ) { // Cannot mediate if target not defined if( !target ) return; // Mediate each subject properties to material for ( const subjectProperty in mediationDefinitions ) { const definition = mediationDefinitions[subjectProperty]; if ( options[subjectProperty] !== undefined ) { // retrieve the mediation transformer to use for this property const mediationTransformer = definition.t ? definition.t : directTransfer; mediationTransformer( target, definition.m, options[subjectProperty] ); // Also transfert to second target is isset if( secondTarget ) { mediationTransformer( secondTarget, definition.m, options[subjectProperty] ); } } } } } ;// CONCATENATED MODULE: ./src/core/properties/hierarchy/ParentProperty.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class ParentProperty extends BaseProperty { constructor() { super('parent', null, false); } /* eslint-disable no-unused-vars */ /** * Update when : * - element has been added * - element has been removed * * @param element * @param out */ update( element, out ) { /* eslint-enable no-unused-vars */ if ( element.parent && element.parent.isUI ) { this._value = element.parent; // this.position.z = this.getOffset(); } else { this._value = null; } // @TODO : parentElement // // set elements as root // if ( element.isBlock && !this._value ) { // // ThreeMeshUI.addRoot( element ); // element.pseudoClassList.add('root'); // // } else { // // ThreeMeshUI.removeRoot( element ); // element.pseudoClassList.remove('root'); // // } } set value( value ) { console.warn('ParentProperty is readonly'); } /** * * @return {MeshUIBaseElement} */ get value() { return this._value; } /** * * @param {(p:Object3D)=>boolean } conditionCallback */ find( conditionCallback ) { if( this._value ) { if( conditionCallback( this._value) ) { return this._value; } return this._value._parent.find( conditionCallback ); } return null; } /** * */ dispose() { this._value = null; } } ;// CONCATENATED MODULE: ./src/utils/NumberUtils.js /** * Get rid of the precision issue * @param numA * @param numB * @param precision * @return {boolean} */ const numberEquals = function ( numA, numB, precision = 6 ) { return numA.toFixed(precision) === numB.toFixed(precision) } /** * * @param unprecisedNumber * @param precision * @return {number} */ const numberPrecise = function ( unprecisedNumber, precision = 6 ) { return parseFloat( unprecisedNumber.toFixed( precision ) ); } ;// CONCATENATED MODULE: ./src/core/properties/NumberProperty.js class NumberProperty extends BaseProperty{ /** * * @param {string} propertyId * @param {number} [value] */ constructor( propertyId, value ) { super( propertyId, value, true); this.output = this._outputValue; } /** * * @param {number} value */ set value( value ) { if( ! this.isValid( value) ) return; if( numberEquals(this._value, value) ) return; this._value = value; this._needsUpdate = true; } /** * * @return {number} */ get value() { return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/SideProperty.js /** * @property {number|"inherit"} value */ class SideProperty extends InheritableProperty { /** * * @param {string} propertyId */ constructor( propertyId ) { super( propertyId, 'inherit', true); this.isValid = SideProperty_isValid; } } const SideProperty_AVAILABLE_VALUES = [ external_THREE_namespaceObject.FrontSide, external_THREE_namespaceObject.BackSide, external_THREE_namespaceObject.DoubleSide ]; /** * * @param {any} value * @return {boolean} * @private */ function SideProperty_isValid( value ) { if( SideProperty_AVAILABLE_VALUES.indexOf( value) === -1 ){ console.warn(`SideProperty value '${value}' is not valid. Abort`); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/elements/glyphs/Inline.js /** * This is the abstract/base class / interface of any inline * Inline can be positioned according to text rules */ class Inline { constructor() { /** @protected */ this._offsetX = 0; /** @protected */ this._offsetY = 0; /** @protected */ this._lineBreak = null; /** @protected */ this._kerning = 0; /** @protected */ this._fontFactor = 1; /** @protected */ this._fontSize = 0; /** @protected */ this._cumulativeWidth = 0; /** @protected */ this._paddingLeft = 0; /** @protected */ this._paddingRight = 0; /** @protected */ this._marginLeft = 0; /** @protected */ this._marginRight = 0; } /** * @returns {void} */ resetOffsets() { this._offsetX = this._offsetY = 0; this._cumulativeWidth = 0; } /** * The horizontal distance this inline fills * @returns {number} */ get xadvance() { return 0 } /** * The offset x of this inline in a line * @returns {number} */ get xoffset() { return 0 } /** * The offset y of this inline in a line * @returns {number} */ get yoffset() { return 0 } /** * * @returns {number} */ get width() { return 0 } /** * * @returns {number} */ get height() { return 0 } /** * * @param {string|null} value */ set lineBreak( value ) { this._lineBreak = value; } /** * * @returns {string|null} */ get lineBreak() { return this._lineBreak; } /** * * @returns {number} */ get anchor() { return 0 } /** * * @returns {number} */ get kerning() { return this._kerning * this._fontFactor; } /** * * @param {number} value */ set kerning( value ) { this._kerning = value; } /** * * @returns {number} */ get fontSize() { return this._fontSize } /** * * @param {number} value */ set fontSize( value ) { this._fontSize = value; } /** * * @returns {number} */ get lineHeight() { return 0 } /** * * @returns {number} */ get offsetX() { return this._offsetX; } /** * * @param value */ set offsetX( value ){ this._offsetX = value; } /** * * @returns {number} */ get offsetY() { return this._offsetY; } /** * * @param {number} value */ set offsetY( value ){ this._offsetY = value; } /** * * @return {number} */ get cumulativeWidth() { return this._cumulativeWidth; } /** * * @param {number} value */ set cumulativeWidth( value ) { this._cumulativeWidth = value; } /** * * @return {number} */ get marginLeft() { return this._marginLeft; } /** * * @param {number} value */ set marginLeft( value ) { this._marginLeft = value; } /** * * @return {number} */ get marginRight() { return this._marginRight; } /** * * @param {number} value */ set marginRight( value ) { this._marginRight = value; } /** * * @return {number} */ get paddingLeft() { return this._paddingLeft; } /** * * @param {number} value */ set paddingLeft( value ) { this._paddingLeft = value; } /** * * @return {number} */ get paddingRight() { return this._paddingRight; } /** * * @param {number} value */ set paddingRight( value ) { this._paddingRight = value; } /** * * @returns {number} */ get lineBase() { return 0 } /** * * @param {number} value */ set fontFactor( value ){ this._fontFactor = value; } /** * * @returns {number} */ get fontFactor() { return this._fontFactor } } ;// CONCATENATED MODULE: ./src/font/TypographicGlyph.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * @class * @abstract */ class TypographicGlyph { /** * * @param {TypographicFont} typographicFont */ constructor( typographicFont ) { /** @protected */ this._char = ""; /** @protected */ this._width = 1; /** @protected */ this._heigth = 1; /** @protected */ this._xadvance = 1; /** @protected */ this._xoffset = 0; /** @protected */ this._yoffset = 0; /** * * @type {TypographicFont} * @protected */ this._font = typographicFont; } /** * * @returns {TypographicFont} */ get font() { return this._font; } /** * * @return {string} */ get char() { return this._char; } /** * * @returns {number} */ get width() { return this._width; } /** * * @returns {number} */ get height() { return this._heigth; } /** * * @returns {number} */ get xadvance() { return this._xadvance; } /** * * @returns {number} */ get xoffset() { return this._xoffset; } /** * * @returns {number} */ get yoffset() { return this._yoffset; } /** * * @param value */ set yoffset( value ) { this._yoffset = value; } /** * * @abstract * @param {string} otherChar * @returns {TypographicGlyph} */ /* eslint-disable no-unused-vars */ clone( otherChar ) { /* eslint-enable no-unused-vars */ throw new Error("Abstract... Need to be implemented"); } /** * * @abstract * @returns {InlineGlyph} */ asInlineGlyph() { throw new Error("Abstract... Need to be implemented") } } ;// CONCATENATED MODULE: ./src/font/InlineGlyph.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class InlineGlyph extends Inline { /** * * @param {TypographicGlyph} characterDesc */ constructor( characterDesc ) { super(); /** @protected */ this._typographic = characterDesc; } /** * * @returns {TypographicGlyph} */ get typographic(){ return this._typographic; } /********************************************************************************************************************* * GETTERS FROM CHARACTER DESCRIPTION ********************************************************************************************************************/ /** * @override * @returns {number} */ get xadvance() { return this._typographic.xadvance * this._fontFactor; } /** * @override * @returns {number} */ get xoffset() { return this._typographic.xoffset * this._fontFactor; } /** * @override * @returns {number} */ get yoffset() { return this._typographic.yoffset * this._fontFactor; } /** * @override * @returns {number} */ get width() { return this._typographic.width * this._fontFactor ; } /** * @override * @returns {number} */ get height() { return this._typographic.height * this._fontFactor; } /** * * @return {string} */ get char() { return this._typographic.char; } /** * @override * @returns {number} */ get anchor() { // const lineHeight = this._typographic.font.lineHeight; // const lineBase = this._typographic.font.lineBase; // // return ( ( this._typographic.yoffset + this._typographic.height - lineBase ) * this._fontSize ) / lineHeight; return this.yoffset; } /** * @override * @returns {number} */ get lineHeight() { return this._typographic.font.lineHeight * this._fontFactor; } /** * @override * @returns {number} */ get lineBase() { return this._typographic.font.lineBase * this._fontFactor; } } ;// CONCATENATED MODULE: ./src/font/utils/FontUtils.js const FONT_WEIGHT_LOOK_UP_TABLE = { 'light' : '100', 'normal' : '400', 'bold' : '700', 'bolder' : '900' } /** * * @param weight * @return {string} */ function uniformizeFontWeight( weight ) { if( !isNaN(weight) ) return weight.toString(); const converted = FONT_WEIGHT_LOOK_UP_TABLE[weight]; if( converted ) return converted; return weight; } ;// CONCATENATED MODULE: ./src/font/FontVariant.js // JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * @abstract */ class FontVariant extends external_THREE_namespaceObject.EventDispatcher { /** * * @param {import('./../core/elements/MeshUIBaseElement').FontWeightFormat} weight * @param {"normal"|"italic"} style */ constructor( weight, style ) { super(); /** @private */ this._isReady = false; /** @protected */ this._weight = uniformizeFontWeight( weight ); /** @protected */ this._style = style; /** @protected */ this._size = 42; /** @protected */ this._lineHeight = 42; /** @protected */ this._lineBase = 42; /** * * @type {TypographicFont} * @protected */ this._font = null; } /** * * @returns {TypographicFont} */ get typographic() { return this._font; } /** * * @returns {boolean} */ get isReady() { return this._isReady; } /** * * @returns {string} */ get weight() { return this._weight; } /** * * @returns {string} */ get style() { return this._style; } /** * * @returns {Texture} */ get texture() { return this._texture; } /** * @param {Function.} v * @abstract */ set fontMaterial( v ) { throw Error( `FontVariant('${this.id}')::fontMaterial - is abstract.` ); } /** * @return {Function.} * @abstract */ get fontMaterial() { throw Error( `FontVariant('${this.id}')::fontMaterial - is abstract.` ); } /** * * @returns {string} */ get id(){ return `${this._name}(w:${this.weight},s:${this.style})`; } /** * * @param {string} character * @returns {TypographicGlyph} */ getTypographicGlyph( character ) { let typographicGlyph = this._chars[ character ]; if ( typographicGlyph ) return typographicGlyph; // Auto generate whitespace chars if ( character.match( /\s/ ) ) return this._chars[ " " ]; const fallbackCharacter = this._getFallbackCharacter( character ); if( fallbackCharacter ) { typographicGlyph = this._chars[ fallbackCharacter ]; if ( typographicGlyph ) return typographicGlyph; } throw Error( `FontVariant('${this.id}')::getTypographicGlyph() - character('${character}') and/or fallback character were not found in provided msdf charset.` ); } /* eslint-disable no-unused-vars */ /** * @abstract * @protected * @param {string} missingChar * @returns {string|null} */ _getFallbackCharacter( missingChar ) { /* eslint-enable no-unused-vars */ throw new Error(`FontVariant(${typeof this})::_getFallbackCharacter() is abstract and should therefore be overridden.`); } /* eslint-disable no-unused-vars */ /** * Convert an InlineCharacter to a geometry * * @abstract * @param {InlineGlyph} inline * @param {MeshUIBaseElement} element * @returns {BufferGeometry|Array.} */ getGeometricGlyph( inline, element ) { throw new Error(`FontVariant(${typeof this})::getGeometryCharacter() is abstract and should therefore be overridden.`); } /* eslint-enable no-unused-vars */ /** * Obtain the kerning amount of a glyphPair * @param {string} glyphPair * @returns {number} */ getKerningAmount( glyphPair ){ //or zero offset if kerning glyphPais is not defined return this._kernings[ glyphPair ] ? this._kernings[ glyphPair ] : 0; } /** * Perform some changes on the character description of this font * @param {Object.>} adjustmentObject */ adjustTypographicGlyphs( adjustmentObject ){ for ( const char in adjustmentObject ) { const typographicGlyph = this.getTypographicGlyph( char ); const glyphAdjustment = adjustmentObject[ char ]; for ( const propertyToAdjust in glyphAdjustment ) { typographicGlyph["_"+propertyToAdjust] = adjustmentObject[char][propertyToAdjust]; } } } /** * * @private */ _checkReadiness() { if ( this._readyCondition() ) { _setReady( this ); } } /* eslint-disable no-unused-vars */ /** * @abstract * @param element * @internal */ _alterElementProperties( element ) { /* eslint-enable no-unused-vars */ throw new Error(`FontVariant(${typeof this})::_alterElementProperties() is abstract and should therefore be overridden.`); } /** * * @abstract * @returns {boolean} * @protected */ _readyCondition () { // ie: MSDFFontVariant // Must have chars and a texture // return this._chars && this._texture throw new Error(`FontVariant(${typeof this})::_readyCondition() is abstract and should therefore be overridden.`); } } /*********************************************************************************************************************** * INTERNAL STUFF **********************************************************************************************************************/ const _readyEvent = { type: 'ready' }; /** * Set the ready status of a fontVariant * @param {FontVariant} fontVariant * @private */ function _setReady( fontVariant ) { fontVariant._isReady = true; fontVariant.dispatchEvent( _readyEvent ); } /** * @typedef {Object.} KerningPairs * */ /* harmony default export */ const font_FontVariant = (FontVariant); ;// CONCATENATED MODULE: ./src/core/properties/FontProperty.js class FontProperty extends BaseProperty{ /** * * @param {FontVariant} [value=null] */ constructor( value = null ) { super( 'font', value, false); this._needsUpdate = false; /** * * @type {FontVariant|null} * @internal */ this._fontVariant = null; /** * @typedef ReadyClosure * @type { ()=> void|null } */ /** * * @type {ReadyClosure} * @private */ this._handleFontReadyClosure = null; /** * @override */ this.isValid = FontProperty_isValid; } output( out ) { out[this._id] = this._fontVariant; } /* eslint-disable no-unused-vars */ /** * * @override */ update( element, out ) { /* eslint-enable no-unused-vars */ // if a previous font isset, be sure no event remains if ( this._fontVariant && !this._fontVariant.isReady ) { this._fontVariant.removeEventListener( 'ready', this._handleFontReadyClosure ); } // obtain font from value or from style combinaison if( this._value && this._value instanceof font_FontVariant ) { this._fontVariant = this._value; } else { const fontFamily = element._fontFamily._value; if( fontFamily ) { this._fontVariant = fontFamily.getVariant( element._fontWeight._value, element._fontStyle._value, ); } } if( !this._fontVariant ) return; this._fontVariant._alterElementProperties( element ); this._handleFontReadyClosure = _readyClosure( element, this ); // new font, means rebuild inlines, now or soon if ( !this._fontVariant.isReady ) { // @TODO : clear inlines components // this.inlines = null; this._fontVariant.addEventListener( 'ready', this._handleFontReadyClosure ); } else { this._handleFontReadyClosure(); } // Set the default material if( !element._fontMaterial._defaultMaterial || !(element._fontMaterial._defaultMaterial instanceof this._fontVariant.fontMaterial) ) { element._fontMaterial._defaultMaterial = new this._fontVariant.fontMaterial(); element._fontMaterial._needsUpdate = true; } } /** * @override * @param {FontVariant} value */ set value( value ) { if( ! this.isValid( value) ) return; if( this._value !== value ) { this._value = value; this._needsUpdate = true; } } /** * * @return {FontVariant} */ get value() { return this._value; } /** * * @return {FontVariant|null} */ get fontVariant() { return this._fontVariant; } /** * */ dispose () { if( this._handleFontReadyClosure ) { this._fontVariant.removeEventListener( 'ready', this._handleFontReadyClosure ); this._handleFontReadyClosure = null; } this._value = null; this._fontVariant = null; } } /** * * @param {number} value * @return {boolean} * @private */ function FontProperty_isValid( value ) { if( ! ( value instanceof font_FontVariant ) ) { console.warn(`.font value '${value}' is not valid. It requires a FontVariant instance. Aborted`); return false; } return true; } /** * * @param {MeshUIBaseElement} element * @param {FontProperty} fontProperty * @return {() => void} * @private */ function _readyClosure( element, fontProperty ) { return function () { fontProperty._needsUpdate = true;// ? update itself? element._glyphs._needsProcess = true; // this._transferToMaterial(); // request parse update and parent layout // this.update( true, true, false ); // this.getHighestParent().update( false, true, false ); // remove the listener fontProperty._fontVariant.removeEventListener( 'ready', fontProperty._handleFontReadyClosure ); fontProperty._handleFontReadyClosure = null; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/visibility/Display.js class Display extends SubStyleProperty { constructor( defaultValue ) { super( 'display', defaultValue ); // configure this._value = 'flex'; this._allowsInherit = false; this._needsUpdate = false; this.isValidValue = Display_isValid; } computeOutputValue( element ) { element._visible._value = this._output !== 'none'; } } const Display_AVAILABLE_VALUES = ['none','flex']; /** * * @param {any} value * @return {boolean} * @private */ function Display_isValid( value ) { if( Display_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) display value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/bounds/BoxSizing.js class BoxSizing extends SubStyleProperty { constructor( defaultValue ) { super( 'boxSizing', defaultValue ); // Configure this._allowsInherit = false; this.isValidValue = BoxSizing_isValid; } computeOutputValue( element ) { this._value = this._inheritedInput; element._bounds._needsUpdate = true; } } /** * * @type {Array.} */ const BoxSizing_AVAILABLE_VALUES = ['border-box', 'content-box']; /** * * @param {any} value * @return {boolean} * @private */ function BoxSizing_isValid( value ) { if( BoxSizing_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) boxSizing value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/StyleColorProperty.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class StyleColorProperty extends SubStyleProperty { constructor( propertyId, defaultValue ) { super( propertyId, defaultValue, false ); /** * @type {Color} * @protected */ this._value = new external_THREE_namespaceObject.Color(); this.output = this._outputValue; } /* eslint-disable no-unused-vars */ /** * @override */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ if( this._input !== 'inherit' ) { this._value.set(this._input); } } set inline( value ) { // Colors are too wide to perform validation checks each time // if( ! this.isValidValue( value ) ) return; this._input = this._inline = value; this._needsUpdate = true; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/StyleFactorProperty.js class StyleFactorProperty extends SubStyleProperty { /** * * @param {string} propertyId * @param {any} defaultValue */ constructor( propertyId, defaultValue ) { super( propertyId, defaultValue, true ); this.isValidValue = StyleFactorProperty_isValid; this._allowsInherit = false; this._input = defaultValue; this._value = defaultValue; this.output = this._outputValue; this.computeOutputValue = this._computeFromInherited; } _outputValue( out ) { out[this._id] = this._inheritedInput; } } /** * * @param {any} value * @return {boolean} * @private */ function StyleFactorProperty_isValid( value ) { if ( value < 0 && value > 1.0 ) { console.warn( `(.style) styleFactorProperty('${this.id}') value '${value}' is not valid)` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/background/BackgroundImage.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class BackgroundImage extends SubStyleProperty { constructor( defaultValue ) { super( 'backgroundImage', defaultValue, true ); this._input = null; // configure this._allowsInherit = false; /** * * @type {Vector2} * @internal */ this._textureSize = new external_THREE_namespaceObject.Vector2( 1,1 ); this.isValidValue = BackgroundImage_isValid; } /** * @override * @return {any|Texture|null} */ get value() { return this._value; } output( out ) { out[this._id] = this._value; out['tSize'] = this._textureSize; } /* eslint-disable no-unused-vars */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ // @TODO : URL this._value = this._inheritedInput; // ? // out[this.id] = this._value; if( this._value instanceof external_THREE_namespaceObject.Texture && !this._value.image ) { console.warn( `ThreeMeshUI - .backgroundImage :: Please provide preloaded texture in order to have accurate sizing.`); return; } this._needsProcess = true; } /* eslint-disable no-unused-vars */ /** * * @param element */ process( element ) { /* eslint-enable no-unused-vars */ if( this._value ) { this._textureSize.set( this._value.image.width, this._value.image.height ); } else { this._textureSize.set( 1 , 1 ); } } } /* eslint-disable no-unused-vars */ /** * * @param {any} value * @return {boolean} * @private */ function BackgroundImage_isValid( value ) { /* eslint-enable no-unused-vars */ // @TODO : Texture or URL() or String or ID ? //console.log( "todo, validate image value", value); return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/background/BackgroundSize.js /** * @property {"cover"|"contain"|"stretch"} value */ class BackgroundSize extends SubStyleProperty { constructor( defaultValue ) { super( 'backgroundSize', defaultValue, true ); this.isValidValue = BackgroundSize_isValid; this.output = this._outputValue; } } const BackgroundSize_AVAILABLE_VALUES = ['cover','contain','stretch']; /** * * @param {any} value * @return {boolean} * @private */ function BackgroundSize_isValid( value ) { if( BackgroundSize_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) backgroundSize value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/visibility/Overflow.js class Overflow extends SubStyleProperty { constructor( defaultValue ) { super( 'overflow', defaultValue, true ); this.isValidValue = Overflow_isValid; /** * * @type {Array.|null} * @internal */ this._clippingPlanes = null; this._renderStrategy = this._emptyRender; } /** * Update of overflow is a bit different, as parent may trigger changes on it * @override */ update( element, out ) { // in any case, it will compute value. It doesn't have updateRequire evaluation // let updateRequired = true; // Inline has priority if set if ( this._inline !== undefined && this._inline !== 'unset' ) { this._input = this._inline; } // or fallback on computed else if ( this._computed !== undefined ) { this._input = this._computed; } if ( !this._allowsInherit ) { this._inheritedInput = this.getInheritedInput( element ); } this.computeOutputValue( element ); // rebuild same properties on children 'inheritance' for ( const childUIElement of element._children._uis ) { childUIElement[ `_overflow` ]._needsUpdate = true; } this.output( out ); } output( out ) { out['clippingPlanes'] = this._clippingPlanes; } computeOutputValue( element ) { // update self -------------------- super.computeOutputValue( element ); if( this._value === 'hidden' ) { this._renderStrategy = this._propagateRender; }else{ this._renderStrategy = this._emptyRender; this._clippingPlanes = null; } const parent = element._parent._value; if( parent !== null ) { // Check that parent is hiddenOverflow or has clippingPlanes const overflowParent = parent._overflow; if ( ( overflowParent._value === 'hidden' || overflowParent._clippingPlanes !== null ) && !this._clippingPlanes ) { // add planes and render this._clippingPlanes = [ // top new external_THREE_namespaceObject.Plane( new external_THREE_namespaceObject.Vector3( 0, -1, 0 ), 1 ), // right new external_THREE_namespaceObject.Plane( new external_THREE_namespaceObject.Vector3( -1, 0, 0 ), 1 ), // bottom new external_THREE_namespaceObject.Plane( new external_THREE_namespaceObject.Vector3( 0, 1, 0 ), 1 ), // left new external_THREE_namespaceObject.Plane( new external_THREE_namespaceObject.Vector3( 1, 0, 0 ), 1 ), ]; // bind the parent to the clipping plane in a custom property for ( let i = 0; i < this._clippingPlanes.length; i++ ) { this._clippingPlanes[ i ].parent = parent; } // Also add parent clipping planes if isset if( overflowParent._clippingPlanes !== null ) { this._clippingPlanes.push( ...overflowParent._clippingPlanes ); } this._renderStrategy = this._hiddenRender; this._needsRender = true; } else if ( ( overflowParent._value === 'visible' || overflowParent._clippingPlanes === null ) && this._clippingPlanes !== null ){ // remove planes and render this._clippingPlanes = null; this._renderStrategy = this._emptyRender; this._needsRender = true; } } } render( element) { this._renderStrategy( element ); } /* eslint-disable no-unused-vars */ _emptyRender( element ) { /* eslint-enable no-unused-vars */ } _hiddenRender( element ) { const parentUI = element._parent._value; const yLimit = parentUI._bounds._offsetHeight; const xLimit = parentUI._bounds._offsetWidth; const padding = parentUI._padding._value; const border = parentUI._borderWidth._value; for ( let i = 0; i < 4 && i < this._clippingPlanes.length ; i++ ) { const clippingPlane = this._clippingPlanes[ i ]; switch ( i % 4 ) { // top case 0: clippingPlane.constant = yLimit / 2 - ( padding.x + border.x ); break; // right case 1: clippingPlane.constant = xLimit / 2 - ( padding.y + border.y ); break; // bottom case 2: clippingPlane.constant = yLimit / 2 - ( padding.z + border.z ); break; // left case 3: clippingPlane.constant = xLimit / 2 - ( padding.w + border.w ); break; } clippingPlane.applyMatrix4( parentUI.matrixWorld ) } for ( let i = 0; i < element._children._uis.length; i++ ) { const ui = element._children._uis[ i ]; ui._overflow._needsRender = true; } } _propagateRender( element ) { for ( let i = 0; i < element._children._uis.length; i++ ) { const ui = element._children._uis[ i ]; ui._overflow._needsRender = true; } } } const Overflow_AVAILABLE_VALUES = ['visible', 'hidden']; function Overflow_isValid( value ) { if( Overflow_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) overflow value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/utils/Units.js const WORLD_UNITS = 'rem'; // const CENTIMETERS = 'cm'; const MILLIMETERS = 'mm'; const INCHES = 'in'; const UV = 'em'; const PERCENT = '%'; const availableUnits = [ WORLD_UNITS, UV, PERCENT ]; /** * Obtain a valid unit * @param {string} requestedUnits * @returns {string} */ const validateUnits = function( requestedUnits ) { // Sent default units if requested units not available if( availableUnits.indexOf( requestedUnits) === -1 ) return WORLD_UNITS; return requestedUnits; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/border/BorderRadius.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class BorderRadius extends StyleVector4Property { /** * * @param {Vector4} defaultValue */ constructor( defaultValue ) { super( 'borderRadius', defaultValue ); /** * * @type {Vector4} * @private */ this._valueUV = this._value.clone(); /** * * @type {Vector4} * @private */ this._input = new external_THREE_namespaceObject.Vector4(0,0,0,0); /** * * @type {boolean} * @private */ this._mediation = true; /** * * @type {Vector2} * @private */ this._cornerTL = new external_THREE_namespaceObject.Vector2(0, 1); /** * * @type {Vector2} * @private */ this._cornerTR = new external_THREE_namespaceObject.Vector2(1, 1); /** * * @type {Vector2} * @private */ this._cornerBR = new external_THREE_namespaceObject.Vector2(1, 0); /** * * @type {Vector2} * @private */ this._cornerBL = new external_THREE_namespaceObject.Vector2(0, 0); const mediationTop = new BorderRadiusMediator( this._valueUV, [ 'x', 'y' ] ); // bottom const mediationBottom = new BorderRadiusMediator( this._valueUV, [ 'z', 'w'] ); // top const mediationLeft = new BorderRadiusMediator( this._valueUV, [ 'x', 'w'] ); // right const mediationRight = new BorderRadiusMediator( this._valueUV, [ 'y', 'z'] ); // left mediationTop.complementaryMediation = mediationBottom; mediationBottom.complementaryMediation = mediationTop; mediationLeft.complementaryMediation = mediationRight; mediationRight.complementaryMediation = mediationLeft; /** * * @type {Array.} * @private */ this._sideMediators = [ mediationTop, mediationBottom, mediationLeft, mediationRight ]; this._units = WORLD_UNITS; } /** * * @param {string} units */ set units( units ) { this._units = validateUnits( units ); this._needsProcess = true; } /** * * @returns {string} */ get units() { return this._units; } /** * * @param {boolean} v */ set mediation ( v) { if( v !== this._mediation ){ this._mediation = v; this._needsUpdate = true; } } /** * * @returns {boolean} */ get mediation () { return this._mediation; } /** * * @param {Object.} out */ output( out ) { out["cornerTL"] = this._cornerTL; out["cornerTR"] = this._cornerTR; out["cornerBR"] = this._cornerBR; out["cornerBL"] = this._cornerBL; } /** * * @override */ /* eslint-disable no-unused-vars */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ this._vector4ValueSetter( this._value, this._input ); this._needsProcess = true; } /* eslint-disable no-unused-vars */ /** * * @override */ process( element ){ /* eslint-enable no-unused-vars */ this._needsRender = true; } /** * * @override */ render( element ){ this._valueUV.copy( this._value ); const elementWidth = element._bounds._offsetWidth; const elementHeight = element._bounds._offsetHeight; // @TODO: Units process could be strategies if( this._units === PERCENT ) { this._valueUV.divideScalar(100); } // @TODO: Units process could be strategies if( this._units === WORLD_UNITS ) { this._valueUV.divideScalar( Math.min(elementWidth , elementHeight) ); } /** * mediate * Be sure no side is greater than 1 (uv units) */ if( this._mediation ) { do { this._sideMediators.forEach( srm => srm.computeValue() ); this._sideMediators.sort( ( a, b ) => { return a.value < b.value ? 1 : -1 } ); if ( this._sideMediators[ 0 ].value > 1.0 ) { this._sideMediators[ 0 ].mediate(); } } while ( this._sideMediators[ 0 ].value > 1.0 ); } let sX = elementWidth > elementHeight ? elementHeight/elementWidth : 1; let sY = elementWidth < elementHeight ? elementWidth/elementHeight : 1; // Do not scale pourcentages, allowing ovals if( this._units === PERCENT ){ sX = sY = 1.0; } this._cornerTL.x = this._valueUV.x * sX; this._cornerTL.y = 1 - (this._valueUV.x * sY ); this._cornerTR.x = 1 - (this._valueUV.y * sX ); this._cornerTR.y = 1 - (this._valueUV.y * sY ); this._cornerBR.x = 1 - (this._valueUV.z * sX ); this._cornerBR.y = this._valueUV.z * sY; this._cornerBL.x = this._valueUV.w * sX; this._cornerBL.y = this._valueUV.w * sY; } /** * */ dispose() { for ( const sideMediator of this._sideMediators ) { sideMediator.dispose(); } this._sideMediators = null; this._cornerTL = null; this._cornerTR = null; this._cornerBR = null; this._cornerBL = null; super.dispose(); } /** * * @param {Number} v */ set topLeft( v ) { if( this._input.x === v ) return; this._input.x = v; this._needsUpdate = true; } /** * * @returns {number} */ get topLeft() { return this._input.x; } /** * * @param {Number} v */ set topRight( v ) { if( this._input.y === v ) return; this._input.y = v; this._needsUpdate = true; } /** * * @returns {number} */ get topRight() { return this._input.y; } /** * * @param {Number} v */ set bottomRight( v ) { if( this._input.z === v ) return; this._input.z = v; this._needsUpdate = true; } /** * * @returns {number} */ get bottomRight() { return this._input.z; } /** * * @param {Number} v */ set bottomLeft( v ) { if( this._input.w === v ) return; this._input.w = v; this._needsUpdate = true; } /** * * @returns {number} */ get bottomLeft() { return this._input.w; } /** * @override * @param {Number} v */ set top( v ) { if( this._input.x === v && this._input.y === v ) return; this._input.x = this._input.y = v; this._needsUpdate = true; } /** * @override * @returns {number} */ get top() { return (this._input.x + this._input.y) / 2; } /** * @override * @param {Number} v */ set right( v ) { if( this._input.y === v && this._input.z === v ) return; this._input.y = this._input.z = v; this._needsUpdate = true; } /** * @override * @returns {number} */ get right() { return (this._input.y + this._input.z) / 2; } /** * @override * @param {Number} v */ set bottom( v ) { if( this._input.z === v && this._input.w === v ) return; this._input.z = this._input.w = v; this._needsUpdate = true; } /** * @override * @returns {number} */ get bottom() { return (this._input.z + this._input.w) / 2; } /** * @override * @param {Number} v */ set left( v ) { if( this._input.w === v && this._input.x === v ) return; this._input.w = this._input.x = v; this._needsUpdate = true; } /** * @override * @returns {number} */ get left() { return (this._input.w + this._input.x) / 2; } } /** * Job: Contains two border radiuses values of the same side * If their sums is greater than 1 (uv units) mediation could occurs */ class BorderRadiusMediator { /** * * @param {Vector4} borderRadiuses * @param {Array.} sideProperties */ constructor( borderRadiuses, sideProperties ) { /** * * @type {Vector4} * @private */ this._borderRadiuses = borderRadiuses; /** * * @type {Array} * @private */ this._sideProperties = sideProperties; /** * * @type {BorderRadiusMediator|null} * @private */ this._complementaryMediation = null; /** * * @type {number} * @private */ this._value = 0; } /** * The sum of the border radius of that side * @returns {number} */ get value(){ return this._value; } /** * A complementary side mediation ie: For top, complementary is bottom * @param {BorderRadiusMediator} brm */ set complementaryMediation( brm ){ this._complementaryMediation = brm; } /** * Adds all side property to compute the value of that side */ computeValue(){ let totalRadius = 0; for ( const radius of this._sideProperties ) { totalRadius += this._borderRadiuses[radius]; } this._value = totalRadius; } /** * * @param {boolean} [mediateOpposite=true] */ mediate( mediateOpposite = true ){ // Mediation only occurs when sum of radius are greater than 1 (uv units) if( this._value < 1.0 ) return; // Simply divide each component by the sum for ( const radius of this._sideProperties ) { this._borderRadiuses[radius] /= this._value; } if( mediateOpposite ) { // and also mediate the complementary this._complementaryMediation.mediate( false ); } } /** * */ dispose() { this._complementaryMediation = null; this._borderRadiuses = null; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/border/BorderWidth.js class BorderWidth extends StyleVector4Property{ /** * * @param defaultValue */ constructor( defaultValue ) { super ( 'borderWidth', defaultValue, false ); this._valueUV = this._value.clone(); // configure this.output = this._outputValue; this._units = WORLD_UNITS; } /** * * @param {string} units */ set units( units ) { this._units = validateUnits( units ); this._needsUpdate = true; } /** * * @returns {string} */ get units() { return this._units; } /* eslint-disable no-unused-vars */ /** * * @override */ computeOutputValue( element) { /* eslint-enable no-unused-vars */ this._vector4ValueSetter( this._value, this._input ); this._needsProcess = true; element._bounds._needsUpdate = true; element._layouter._needsUpdate = true; // for ( let i = 0; i < element._children._uis.length; i++ ) { // const child = element._children._uis[ i ]; // element._bounds._needsUpdate = true; // } } _outputValue( out ) { out[this._id] = this._valueUV; } /** * * @override */ process( element ) { this._needsRender = true; element._borderRadius._needsRender = true; } /** * @override */ render( element ) { this._valueUV.copy( this._value ); const offsetWidth = element._bounds._offsetWidth; const offsetHeight = element._bounds._offsetHeight; // @TODO: Units process could be strategies if( this._units === PERCENT ){ console.log( "Percent" ); // this._valueUV.divideScalar( 100 ); console.log( this._valueUV ); } // @TODO: Units process could be strategies if( this._units === WORLD_UNITS ) { if( offsetWidth !== 0) { this._valueUV.w /= offsetWidth; this._valueUV.y /= offsetWidth; } if( offsetHeight !== 0 ) { this._valueUV.x /= offsetHeight; this._valueUV.z /= offsetHeight; } } else if( this._units === UV ) { // @TODO: Units process could be strategies if( offsetWidth !== 0 ) { const sX = offsetWidth > offsetHeight ? offsetHeight/offsetWidth : 1; this._valueUV.y *= sX; this._valueUV.w *= sX; } if( offsetHeight !== 0 ) { const sY = offsetWidth < offsetHeight ? offsetWidth/offsetHeight : 1; this._valueUV.x *= sY; this._valueUV.z *= sY; } } } } ;// CONCATENATED MODULE: ./src/core/properties/VisibleProperty.js class VisibleProperty extends BaseProperty{ /** * * @param {string} propertyId * @param {any} [value=null] */ constructor( propertyId, value = true ) { super( 'visible', value, true ); this._needsUpdate = false; } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ element.visible = this._value; if( element._parent._value ) { element._parent._value._children._needsUpdate = true; } } set value( value ) { if( ! this.isValid( value) ) return; if( this._value !== value ) { this._value = value; this._needsUpdate = true; } } /** * * @return {boolean} */ get value() { return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/background/BackgroundColorProperty.js class BackgroundColorProperty extends StyleColorProperty { constructor( defaultValue ) { super( 'backgroundColor', defaultValue, false ); this._input = 'transparent'; this._allowsInherit = false; } /* eslint-disable no-unused-vars */ /** * * @param {MeshUIBaseElement} element */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ element._backgroundMesh.visible = !(this._input === 'none' || this._input === 'transparent'); if( this._input === 'inherit' ) { // @TODO: Background should not be inheritable this._value.set(this.getInheritedInput( element )); } else if( !(this._input === 'transparent' || this._input === 'none') ) { this._value.set( this._input ); } } } ;// CONCATENATED MODULE: ./src/core/properties/EmptyProperty.js class EmptyProperty extends BaseProperty { /** * * @param {string} propertyId */ constructor( propertyId = 'untitled' ) { super( propertyId, undefined, false ); } /* eslint-disable no-unused-vars */ /** * * @param element * @param {Object.} out */ update( element , out ) { /* eslint-enable no-unused-vars */ } /* eslint-disable no-unused-vars */ /** * Output this property in a dictionnary * @param {Object.} out */ output( out ) { /* eslint-enable no-unused-vars */ } } ;// CONCATENATED MODULE: ./src/core/properties/InlineJustificator.js class InlineJustificator extends BaseProperty { constructor() { super( 'inlineJustificator', null, false ); /** * * @type {Lines} * @private */ this._value = null; } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ } /** * * @override */ process( element ) { const INNER_HEIGHT = element._bounds._innerHeight; const lines = element._layouter._value; const textHeight = Math.abs( lines.height ); // Line vertical positioning let justificationOffset = ( () => { switch ( element._alignItems._value ) { case 'inherit': case 'start': return INNER_HEIGHT / 2 ; case 'end': return textHeight - ( INNER_HEIGHT / 2 ); case 'stretch': // @TODO : Stretch should trigger an error in own property case 'center': return textHeight/2; } } )(); //console.log( justificationOffset ); // Apply padding const padding = element._padding._value; const border = element._borderWidth._value; justificationOffset += ( - padding.x + padding.z ) / 2 + ( - border.x + border.z ) / 2; // lines.forEach( ( line ) => { line.y += justificationOffset; line.forEach( ( inline ) => { inline.offsetY += justificationOffset; } ); } ); } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/flex/AlignItemsProperty.js /** * * @type {Array.} */ const AlignItemsProperty_AVAILABLE_VALUES = ['start', 'center', 'end', 'stretch']; /** * * @param {any} value * @return {boolean} * @private */ const AlignItemsProperty_isValid = function ( value ) { if( AlignItemsProperty_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) alignItems value '${value}' is not valid. Aborted` ); return false; } return true; } class AlignItemsProperty extends SubStyleProperty { constructor() { super( 'alignItems', 'inherit', true ); this.isValidValue = AlignItemsProperty_isValid; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/TextAlignProperty.js class TextAlignProperty extends SubStyleProperty { constructor() { super( 'textAlign', 'inherit', true ); this.isValidValue = TextAlignProperty_isValid; } } /** * * @type {Array.} */ const TextAlignProperty_AVAILABLE_VALUES = ['left', 'right', 'center', 'justify', 'justify-left', 'justify-right','justify-center']; /** * * @param {any} value * @return {boolean} * @private */ const TextAlignProperty_isValid = function ( value ) { if( TextAlignProperty_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) textAlign value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/flex/FlexDirectionProperty.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class FlexDirectionProperty extends SubStyleProperty { constructor() { super( 'flexDirection', 'inherit', true ); // configure this.isValid = FlexDirectionProperty_isValid; } } const FlexDirectionProperty_AVAILABLE_VALUES = ['row','row-reverse', 'column', 'column-reverse']; /** * * @param {any} value * @return {boolean} * @private */ function FlexDirectionProperty_isValid( value ) { if( FlexDirectionProperty_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) flexDirection value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/flex/JustifyContentProperty.js class JustifyContentProperty extends SubStyleProperty { constructor() { super( 'justifyContent', 'inherit', true ); /** * * @override */ this.isValidValue = JustifyContentProperty_isValid; } } /** * * @type {Array.} */ const JustifyContentProperty_AVAILABLE_VALUES = [ 'start', 'center', 'end', 'space-between', 'space-around', 'space-evenly' ]; /** * * @param {any} value * @return {boolean} * @private */ function JustifyContentProperty_isValid( value ) { if ( JustifyContentProperty_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) justifyContent value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/flex/OrderProperty.js class OrderProperty extends SubStyleProperty { constructor( ) { super( 'order', 0, true ); this._value = 0; this._input = 0; // configure this._allowsInherit = false; } /* eslint-disable no-unused-vars */computeOutputValue( element ) { /* eslint-enable no-unused-vars */ this._value = this._inheritedInput; // require parent children (order) update, which will require layout update if( element._parent._value ) { element._parent._value._children._needsProcess = true; } } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/PositionProperty.js class PositionProperty extends SubStyleProperty { constructor( ) { super( 'position', 'static', true ); // configure this._allowsInherit = false; this._value = 'static'; this._needsUpdate = false; this.computeOutputValue = this._computeFromInherited; this.isValidValue = PositionProperty_isValid; } _computeFromInherited( element ) { super._computeFromInherited( element ); //console.log( "Position update") // require parent to compute children -> bounds -> etc... if( element._parent._value ) element._parent._value._children._needsProcess = true; } } const PositionProperty_AVAILABLE_VALUES = ['static', 'absolute']; function PositionProperty_isValid( value ) { if( PositionProperty_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) position value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/bounds/StyleSideProperty.js class StyleSideProperty extends SubStyleProperty { /** * * @param {string} propertyId * @param {number} defaultValue */ constructor( propertyId, defaultValue = null ) { super( propertyId, defaultValue, true ); /** * @type {any} * @internal */ this._input = 'auto'; /** * * @type {boolean} * @internal */ this._auto = true; /** * * @type {boolean} * @internal */ this._relative = false; this._updateRequired = true; } /** * * @param {any} value */ set inline( value ) { if( ! this.isValidValue( value ) ) return; if( value === this._inline ) { // do nothing no update, the value hasn't changed return; } this._inline = value; if( this._input === this._inline ) return; this._parseInput(); } get inline() { return this._inline; } _parseInput() { let updateRequired = true; // Inline has priority if set if( this._inline !== undefined && this._inline !== 'unset' ) { this._input = this._inline; } // or fallback on computed else if( this._computed !== undefined ) { // do not require an update if the value remains if( this._computed === this._input ) updateRequired = false; this._input = this._computed; } // or fallback on default value else { updateRequired = this._input === 'inherit'; } if( updateRequired ) { this._auto = !this._input || this._input === 'auto'; if ( !this._auto ) { // string can be percentages // console.log( this._input, typeof this._input, this._input.endsWith('%')) if ( ( typeof this._input === 'string' || this._input instanceof String ) && this._input.endsWith( '%' ) ) { this._relative = true; this._value = 0; const floatValue = parseFloat( this._input.replace( '%', '' ).trim() ); if ( !isNaN( floatValue ) ) { this._value = floatValue / 100; } } else { this._relative = false; this._value = this._input; } } else { this._relative = false; } this._needsUpdate = this._updateRequired = updateRequired; } } update( element, out ) { if( this._updateRequired ) { this._updateRequired = false; if( !this._allowsInherit ) { this._inheritedInput = this.getInheritedInput( element ); } this.computeOutputValue( element ); // rebuild same properties on children 'inheritance' for ( const childUIElement of element._children._uis ) { childUIElement[`_${this._id}`]._needsUpdate = true; } this.output( out ); if( element._parent._value ) element._parent._value._layouter._needsProcess = true; } } /* eslint-disable no-unused-vars */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ element._bounds._needsUpdate = true; element._renderer._needsRender = true; // element._autoSize._needsProcess = true; } getInheritedInput ( element ) { if( this._input !== 'inherit' && !this._auto ) return this._input; const parent = element._parent._value; if( parent ) { return parent[`_${this._id}`].getInheritedInput( parent ) } return this.getDefaultValue(); } getDefaultValue() { return 0; } /** * * @return {number} */ get value() { return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/bounds/WidthProperty.js class WidthProperty extends StyleSideProperty { constructor() { super( 'width' ); } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/bounds/HeightProperty.js class HeightProperty extends StyleSideProperty { constructor() { super( 'height' ); } computeOutputValue( element ) { super.computeOutputValue( element ); } } ;// CONCATENATED MODULE: ./src/core/properties/TextContentEmpty.js class TextContentEmpty extends EmptyProperty{ constructor() { super( "textContent" ); this._needsUpdate = false; } /* eslint-disable no-unused-vars */ set value( v ) { /* eslint-enable no-unused-vars */ } /* eslint-disable no-unused-vars */ process( element ) { /* eslint-enable no-unused-vars */ let content = ""; for ( let i = 0; i < element.children.length; i++ ) { const child = element.children[i]; if( child.isUI ) { content += child.textContent; } } this._value = content; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontStyleProperty.js class FontStyleProperty extends SubStyleProperty { constructor( defaultValue ) { super( 'fontStyle', defaultValue, true ); this.isValidValue = FontStyleProperty_isValid; } } const FontStyleProperty_AVAILABLE_VALUES = ['normal', 'italic']; function FontStyleProperty_isValid( value ) { if( FontStyleProperty_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) fontStyle value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontWeightProperty.js class FontWeightProperty extends SubStyleProperty { constructor( ) { super( 'fontWeight', 'inherit', true ); this.isValid = FontWeightProperty_isValid; } } const FontWeightProperty_AVAILABLE_VALUES = ['100','200','300','400','500','600','700','800','900','light','normal','bold','bolder']; function FontWeightProperty_isValid( value ) { if( FontWeightProperty_AVAILABLE_VALUES.indexOf( value.toString() ) === -1 ) { console.warn( `(.style) fontWeight value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/font/TypographicFont.js class TypographicFont { constructor() { /** @protected */ this._size = 42; /** @protected */ this._lineHeight = 42; /** @protected */ this._lineBase = 38; /** @protected */ this._name = "-"; /** @protected */ this._charset = ""; } /** * * @returns {number} */ get size() { return this._size; } /** * * @returns {number} */ get lineHeight() { return this._lineHeight; } /** * * @returns {number} */ get lineBase() { return this._lineBase; } /** * * @returns {string} */ get name() { return this._name; } /** * * @returns {string} */ get charset() { return this._charset; } } ;// CONCATENATED MODULE: ./src/font/msdf/MSDFTypographicFont.js class MSDFTypographicFont extends TypographicFont{ /** * * @param {import('./MSDFFontVariant').MSDFJson} json */ constructor( json ) { super(); // base description this._size = json.info.size; this._lineHeight = json.common.lineHeight; this._lineBase = json.common.base; this._name = json.info.face; // MSDF this._textureWidth = json.common.scaleW; this._textureHeight = json.common.scaleH; this._charset = json.chars.map( char => char.char ).join(""); } /** * * @returns {number} */ get textureWidth() { return this._textureWidth; } /** * * @returns {number} */ get textureHeight() { return this._textureHeight; } } ;// CONCATENATED MODULE: ./src/font/msdf/MSDFInlineGlyph.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * @extends InlineGlyph */ class MSDFInlineGlyph extends InlineGlyph{ /** * * @param {MSDFTypographicGlyph} characterDesc */ constructor( characterDesc ) { super( characterDesc ); } /** * * @returns {{left:number, right:number, top:number, bottom:number}|null} */ get uv() { return this.typographic.uv; } } ;// CONCATENATED MODULE: ./src/font/msdf/MSDFTypographicGlyph.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * @property {MSDFTypographicFont} _font */ class MSDFTypographicGlyph extends TypographicGlyph { /** * @param {MSDFTypographicFont} fontDescription * @param {import('./MSDFFontVariant').MSDFJsonChar} characterData */ constructor( fontDescription, characterData ) { super( fontDescription ); this._char = characterData.char; this._width = characterData.width; this._heigth = characterData.height; this._xadvance = characterData.xadvance ? characterData.xadvance : this._width; this._xoffset = characterData.xoffset ? characterData.xoffset : 0; this._yoffset = characterData.yoffset ? characterData.yoffset : 0; // Msdf requires uvs this._uv = characterData.uv ? characterData.uv : null; if ( !isNaN( characterData.x ) ) { // transform absolute pixel values into uv values [0,1] this._uv = { left: characterData.x / fontDescription.textureWidth, right: ( characterData.x + characterData.width ) / fontDescription.textureWidth, top: 1 - ( ( characterData.y + characterData.height ) / fontDescription.textureHeight ), bottom: 1 - ( characterData.y / fontDescription.textureHeight ) }; } } /** * * @returns {{left: number, right: number, top: number, bottom: number}|null} */ get uv() { return this._uv; } /** * @override * @param {string} otherChar * @returns {MSDFTypographicGlyph} */ clone( otherChar ) { return new MSDFTypographicGlyph( this._font, { char: otherChar, width: this._width, height: this._heigth, xadvance: this._xadvance, xoffset: this._xoffset, yoffset: this._yoffset, // Msdf requires uvs uv: null } ); } /** * @override * @returns {MSDFInlineGlyph} */ asInlineGlyph() { return new MSDFInlineGlyph( this ); } } ;// CONCATENATED MODULE: ./src/font/msdf/MSDFGeometricGlyph.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class MSDFGeometricGlyph extends external_THREE_namespaceObject.PlaneGeometry { /** * * @param {MSDFInlineGlyph} inline * @param {MeshUIBaseElement} element */ constructor( inline, element ) { // default w & h segments let wS = 1, hS=1; // If charOBJ, try to distribute segments proportionally const typographicFontSize = inline.typographic.font.size; const segments = element._segments.value; wS = Math.ceil((inline.typographic.width / typographicFontSize) * segments ); hS = Math.ceil((inline.typographic.height / typographicFontSize) * segments ); super( inline.width, inline.height, wS, hS ); // If inline has UVs if ( inline.uv ) { this._mapUVs( inline ); this._transformGeometry( inline ); // White spaces (we don't want our plane geometry to have a visual width nor a height) } else { this._nullifyUVs(); this.scale( 0, 0, 1 ); this.translate( 0, inline.fontSize / 2, 0 ); } this.name = "GlyphGeometry"; // Demo alter geometry // const maxOffset = inline.fontSize / 10; // this.translate(0 , -maxOffset + Math.random() * maxOffset*2, 0 ) // this.rotateZ(-0.1 + 0.2 * Math.random() ) } /** * Compute the right UVs that will map the MSDF texture so that the passed character * will appear centered in full size * @param {MSDFInlineGlyph} inline * @private */ _mapUVs( inline ) { const width = inline.uv.right - inline.uv.left; const height = inline.uv.bottom - inline.uv.top; const originalUvArray = this.getAttribute('uv').array.slice() const uvGlyph = []; for (let i = 0; i < originalUvArray.length; i += 2) { const u = originalUvArray[i]; const v = originalUvArray[i + 1]; uvGlyph.push(inline.uv.left + width * u); uvGlyph.push(inline.uv.top + height * v); } this.setAttribute('uvG', new external_THREE_namespaceObject.BufferAttribute(new Float32Array(uvGlyph), 2)); } /** * Set all UVs to 0, so that none of the glyphs on the texture will appear * @private * */ _nullifyUVs() { // const uvAttribute = this.attributes.uv; // // for ( let i = 0; i < uvAttribute.count; i++ ) { // // uvAttribute.setXY( i, 0, 0 ); // // } const uvGlyph = []; const length = this.getAttribute('uv').array.length; for ( let i = 0; i < length; i++ ) { uvGlyph.push(0); } this.setAttribute('uvG', new external_THREE_namespaceObject.BufferAttribute(new Float32Array(uvGlyph), 2)); } /** * * @TODO: Apply pivot properties when splitText isset * Gives the previously computed scale and offset to the geometry * @param {MSDFInlineGlyph} inline * @private */ _transformGeometry( inline ) { // // @TODO : Evaluate this as being a property. It can wait until splitGeometry this.translate( inline.width / 2, -inline.height/2, 0 ); } } ;// CONCATENATED MODULE: ./src/font/msdf/renderers/ShaderChunks/msdf-alphaglyph.pars.vertex.glsl.js /** * * @type {string} */ const program = /* glsl */` attribute vec2 uvG; varying vec2 vUvG; `; /* harmony default export */ const msdf_alphaglyph_pars_vertex_glsl = (program); ;// CONCATENATED MODULE: ./src/font/msdf/renderers/ShaderChunks/msdf-alphaglyph.vertex.glsl.js /** * * @type {string} */ const msdf_alphaglyph_vertex_glsl_program = /* glsl */ ` vUvG = uvG; `; /* harmony default export */ const msdf_alphaglyph_vertex_glsl = (msdf_alphaglyph_vertex_glsl_program); ;// CONCATENATED MODULE: ./src/font/msdf/renderers/ShaderChunks/msdf-offsetglyph.vertex.glsl.js /** * * @type {string} */ const msdf_offsetglyph_vertex_glsl_program = /* glsl */` gl_Position.z -= 0.00001; `; /* harmony default export */ const msdf_offsetglyph_vertex_glsl = (msdf_offsetglyph_vertex_glsl_program); ;// CONCATENATED MODULE: ./src/font/msdf/renderers/ShaderChunks/msdf-alphaglyph.pars.fragment.glsl.js /** * * @type {string} */ const msdf_alphaglyph_pars_fragment_glsl_program = /* glsl */` varying vec2 vUvG; uniform sampler2D glyphMap; uniform vec2 unitRange; // functions from the original msdf repo: // https://github.com/Chlumsky/msdfgen#using-a-multi-channel-distance-field float median(float r, float g, float b) { return max(min(r, g), min(max(r, g), b)); } float screenPxRange() { // precomputed unitRange as recommended by Chlumsky // vec2 unitRange = vec2(pxRange)/vec2(textureSize(glyphMap, 0)); vec2 screenTexSize = vec2(1.0)/fwidth(vUvG); return max(0.5*dot(unitRange, screenTexSize), 1.0); } float tap(vec2 offsetUV) { vec3 msd = texture( glyphMap, offsetUV ).rgb; float sd = median(msd.r, msd.g, msd.b); float screenPxDistance = screenPxRange() * (sd - 0.5); float alpha = clamp(screenPxDistance + 0.5, 0.0, 1.0); return alpha; } `; /* harmony default export */ const msdf_alphaglyph_pars_fragment_glsl = (msdf_alphaglyph_pars_fragment_glsl_program); ;// CONCATENATED MODULE: ./src/font/msdf/renderers/ShaderChunks/msdf-alphaglyph.fragment.glsl.js /** * * @type {string} */ const msdf_alphaglyph_fragment_glsl_program = /* glsl */` float alpha; #ifdef NO_RGSS alpha = tap( vUvG ); #else // shader-based supersampling based on https://bgolus.medium.com/sharper-mipmapping-using-shader-based-supersampling-ed7aadb47bec // per pixel partial derivatives vec2 dx = dFdx(vUvG); vec2 dy = dFdy(vUvG); // rotated grid uv offsets vec2 uvOffsets = vec2(0.125, 0.375); vec2 offsetUV = vec2(0.0, 0.0); // supersampled using 2x2 rotated grid alpha = 0.0; offsetUV.xy = vUvG + uvOffsets.x * dx + uvOffsets.y * dy; alpha += tap(offsetUV); offsetUV.xy = vUvG - uvOffsets.x * dx - uvOffsets.y * dy; alpha += tap(offsetUV); offsetUV.xy = vUvG + uvOffsets.y * dx - uvOffsets.x * dy; alpha += tap(offsetUV); offsetUV.xy = vUvG - uvOffsets.y * dx + uvOffsets.x * dy; alpha += tap(offsetUV); alpha *= 0.25; #endif alpha = clamp( alpha, 0.0, 1.0 ); #ifdef INVERT_ALPHA alpha = 1.0 - alpha; #endif diffuseColor.a *= alpha; `; /* harmony default export */ const msdf_alphaglyph_fragment_glsl = (msdf_alphaglyph_fragment_glsl_program); ;// CONCATENATED MODULE: ./src/utils/mediator/transformers/MaterialTransformers.js /** * Transfer the alphaTest value from MeshUIComponent to material * @type {import('../Mediator').MediationTransformer} */ const alphaTestTransformer = function ( target, targetProperty, value) { // set the value in the material target.alphaTest = value; toPreprocessorTriggerTransformer(target, 'USE_ALPHATEST', value > 0 ); } /** * Transform a value as a preprocessor trigger * @type {import('../Mediator').MediationTransformer} */ const toPreprocessorTriggerTransformer = function ( target, targetProperty, value) { if( !target.defines ) return; if ( value ) { if( target.defines[targetProperty] === undefined ) { target.defines[targetProperty] = ''; target.needsUpdate = true; } } else if( target.defines[targetProperty] !== undefined ) { delete target.defines[targetProperty]; target.needsUpdate = true; } } /** * Transform a value as a preprocessor value * @type {import('../Mediator').MediationTransformer} */ const asPreprocessorValueTransformer = function ( target, targetProperty, value) { // abort if nothing to update, same value if( target.defines[targetProperty] && target.defines[targetProperty] === value ) return; // or change the preprocessor and update target.defines[targetProperty] = value; target.needsUpdate = true; } /** * Transform a value as a uniform or userData value * Non primitive values are bounds * @type {import('../Mediator').MediationTransformer} */ const uniformOrUserDataTransformer = function( material, property, value ) { if( material.userData[property] ) { material.userData[property].value = value; }else{ material.uniforms[property].value = value; } } const toUserDataTransformer = function( material, property, value ) { material.userData[property].value = value; } ;// CONCATENATED MODULE: ./src/font/msdf/utils/MSDFFontMaterialUtils.js /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * MSDFFontMaterialUtils provides utilities * for customizing other threejs or custom materials * into a three-mesh-ui MSDFFontMaterial */ class MSDFFontMaterialUtils { /** * Alter a material options with required fontMaterial options and or default values * @param {Object.} materialOptions */ static ensureMaterialOptions( materialOptions ) { materialOptions.transparent = true; materialOptions.alphaTest = materialOptions.alphaTest || 0.02; } /** * As three-mesh-ui FontMaterial relies on webgl preprocessors, * lets force the material to have a proper defines object * @param {Material|ShaderMaterial} threeMaterial */ static ensureDefines( threeMaterial ) { if ( !threeMaterial.defines ) { threeMaterial.defines = {}; } } /** * * @param {Material|ShaderMaterial} threeMaterial * @param {Object.} materialOptions */ static ensureUserData( threeMaterial, materialOptions ) { threeMaterial.userData.glyphMap = { value: materialOptions.glyphMap }; threeMaterial.userData.unitRange = { value: new external_THREE_namespaceObject.Vector2() }; } /** * * @param {any} shader * @param {Material|ShaderMaterial} threeMaterial */ static bindUniformsWithUserData( shader, threeMaterial ) { shader.uniforms.glyphMap = threeMaterial.userData.glyphMap; shader.uniforms.unitRange = threeMaterial.userData.unitRange; } /** * * @param shader */ static injectShaderChunks( shader ) { MSDFFontMaterialUtils.injectVertexShaderChunks( shader ); MSDFFontMaterialUtils.injectFragmentShaderChunks( shader ); } /** * * @param shader */ static injectVertexShaderChunks( shader ) { shader.vertexShader = shader.vertexShader.replace( '#include ', '#include \n' + msdf_alphaglyph_pars_vertex_glsl ); // vertex chunks shader.vertexShader = shader.vertexShader.replace( '#include ', '#include \n' + msdf_alphaglyph_vertex_glsl ) shader.vertexShader = shader.vertexShader.replace( '#include ', '#include \n' + msdf_offsetglyph_vertex_glsl ) } /** * * @param shader */ static injectFragmentShaderChunks( shader ) { shader.fragmentShader = shader.fragmentShader.replace( '#include ', '#include \n' + msdf_alphaglyph_pars_fragment_glsl ) // fragment chunks shader.fragmentShader = shader.fragmentShader.replace( '#include ', '#include \n' + msdf_alphaglyph_fragment_glsl ) } /** * Mix a threejs Material into a three-mesh-ui FontMaterial * @param {typeof Material|ShaderMaterial} materialClass * @returns {typeof Material|ShaderMaterial} */ static from( materialClass ) { return class extends materialClass { /** * * @abstract * @returns {Object.<{m:string, t?:(fontMaterial:Material|ShaderMaterial, materialProperty:string, value:any) => void}>} */ static get fontMaterialProperties() { return MSDFFontMaterialUtils.mediation; } constructor( options = {} ) { // same as FontMaterial extension MSDFFontMaterialUtils.ensureMaterialOptions( options ); super( options ); MSDFFontMaterialUtils.ensureDefines( this ); MSDFFontMaterialUtils.ensureUserData( this, options ); // defines two internal properties in order to kept // user allowed to use onBeforeCompile for its own stuff // 1- store an callback for user /* eslint-disable no-unused-vars */ this._userDefinedOnBeforeCompile = (shader) => {}; /* eslint-enable no-unused-vars */ // 2- store the cumulative callback this._onBeforeCompile = this._cumulativeOnBeforeCompile; } //////////////////////////// // OnBeforeCompile Override /////////////////////////// /** * Override the setter of onBeforeCompile in order to never overwrite * the three-mesh-ui fontMaterial onBeforeCompile * @param { (shader:any) => void }fct */ set onBeforeCompile( fct ) { // only store it as userDefinedCallback this._userDefinedOnBeforeCompile = fct; } /** * Override the getter of onBeforeCompile in order to * always deliver the cumulativeCallbacks to threejs * @returns { (shader:any) => void } */ get onBeforeCompile() { return this._onBeforeCompile; } /** * * On before compile that first run three-mesh-ui fontMaterial * then user defined onBeforeCompile * @param shader * @private */ _cumulativeOnBeforeCompile = ( shader ) => { // bind uniforms MSDFFontMaterialUtils.bindUniformsWithUserData( shader, this ); // inject both vertex and fragment shaders MSDFFontMaterialUtils.injectShaderChunks( shader ); // user defined additional onBeforeCompile this._userDefinedOnBeforeCompile( shader ); } } } /** * * @returns {Object<{m: string, t?: (function((Material|ShaderMaterial), string, *): void)}>} */ static get mediation() { return mediationDefinitions; } } /** * Convert a fontVariant to a material glyphMap texture * @type {(fontMaterial:Material|ShaderMaterial, materialProperty:string, value:any) => void } * @private */ const _fontToGlyphMapTransformer = function( fontMaterial, materialProperty, value) { const texture = value ? value.texture : null; const unitRange = value ? value.unitRange : new external_THREE_namespaceObject.Vector2(); if( fontMaterial[materialProperty] !== undefined ) { fontMaterial.glyphMap = texture; fontMaterial.unitRange = unitRange; return; } if( fontMaterial.userData && fontMaterial.userData.glyphMap ) { fontMaterial.userData.glyphMap.value = texture; fontMaterial.userData.unitRange.value = unitRange; } } /** * * @type {(fontMaterial:Material|ShaderMaterial, materialProperty:string, value:any) => void } * @private */ const _RGSSTransformer = function( fontMaterial, materialProperty, value){ if ( value && value !== 'antialiased' ) { fontMaterial.defines['NO_RGSS'] = ''; } else { delete fontMaterial.defines['NO_RGSS']; } fontMaterial.needsUpdate = true; } /** * * @type {Object.<{m:string, t?:(fontMaterial:Material|ShaderMaterial, materialProperty:string, value:any) => void}>} */ const mediationDefinitions = { clippingPlanes: { m: 'clippingPlanes' }, fontAlphaTest: { m: 'alphaTest', t: alphaTestTransformer }, fontSide: { m: 'side' }, font: { m: "glyphMap", t: _fontToGlyphMapTransformer }, color: { m: 'color' }, fontOpacity: { m: 'opacity' }, fontSmooth: { m: 'NO_RGSS', t: _RGSSTransformer }, invertAlpha: { m: 'INVERT_ALPHA', t: toPreprocessorTriggerTransformer }, } ;// CONCATENATED MODULE: ./src/font/msdf/renderers/ShaderLib/msdf-fontmaterial.glsl.js /** * * @type {string} */ const vertexShader = /* glsl */` ${msdf_alphaglyph_pars_vertex_glsl} #include void main() { ${msdf_alphaglyph_vertex_glsl} #include #include ${msdf_offsetglyph_vertex_glsl} #include } ` /** * * @type {string} */ const fragmentShader = /* glsl */` uniform vec3 diffuse; uniform float opacity; ${msdf_alphaglyph_pars_fragment_glsl} #include #include void main() { // instead of : vec4 diffuseColor vec4 diffuseColor = vec4( diffuse, opacity ); ${msdf_alphaglyph_fragment_glsl} #include // instead of gl_FragColor = diffuseColor; #include } ` ;// CONCATENATED MODULE: ./src/font/msdf/materials/MSDFFontMaterial.js // JSDoc related import /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ const ALPHA_TEST = 0.02; /** * This material implements the msdf rendering shader */ class MSDFFontMaterial extends external_THREE_namespaceObject.ShaderMaterial { /** * This static method is mandatory for extending ThreeMeshUI.MSDFFontMaterial * It will provide a transfer description for properties from ThreeMeshUI.Text to THREE.Material * @see {MSDFFontMaterialUtils.mediation} * @returns {Object.<{m:string, t?:(fontMaterial:Material|ShaderMaterial, materialProperty:string, value:any) => void}>} */ static get mediation() { return MSDFFontMaterialUtils.mediation; } constructor( materialOptions = {} ) { super( { uniforms: { 'glyphMap': { value: null }, // texture 'diffuse': { value: null }, // vec3 'opacity': { value: 1 }, 'unitRange': { value: new external_THREE_namespaceObject.Vector2(0,0) }, // vec2 'alphaTest': { value: ALPHA_TEST }, }, transparent: true, clipping: true, vertexShader: vertexShader, fragmentShader: fragmentShader, extensions: { derivatives: true }, } ); // webgl preprocessor AlphaTest set by default this.defines[ 'USE_ALPHATEST' ] = ''; this.needsUpdate = true; // initiate additional properties this.noRGSS = materialOptions.noRGSS || false; } /** * The color will be the diffuse uniform * @returns {Color} */ get color() { return this.uniforms.diffuse.value; } /** * * @param {Color} v */ set color( v ) { this.uniforms.diffuse.value = v; } /** * * @param {number} v */ set opacity( v ) { if( this.uniforms ) this.uniforms.opacity.value = v; } /** * The color will be the diffuse uniform * @returns {number} */ get opacity() { return this.uniforms.opacity.value; } /** * The color will be the diffuse uniform * @returns {Vector2} */ get unitRange() { return this.uniforms.unitRange.value; } /** * * @param {Vector2} v */ set unitRange( v ) { this.uniforms.unitRange.value.copy( v ); } /** * * @returns {Texture} */ get glyphMap() { return this.uniforms.glyphMap.value; } /** * * @param {Texture} v */ set glyphMap( v ) { this.uniforms.glyphMap.value = v; } /** * Is this a default fontMaterial instance * @returns {boolean} */ get isDefault() { return this.constructor === MSDFFontMaterial; } /** * * @returns {number} */ get alphaTest() { return this.uniforms.alphaTest.value; } /** * * @param {number} v */ set alphaTest( v ) { this.uniforms.alphaTest.value = v; } } ;// CONCATENATED MODULE: ./src/font/msdf/MSDFFontVariant.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * @extends {FontVariant} */ class MSDFFontVariant extends font_FontVariant { constructor( weight, style, json, texture ) { super(weight, style); // provide default values this._unitRange = new external_THREE_namespaceObject.Vector2( 1, 1 ); if ( json.pages ) { this._buildData( json ); } else { _loadJson( this, json ); } if ( texture instanceof external_THREE_namespaceObject.Texture ) { this._texture = texture; this._buildTexture( texture ); } else if( typeof(texture) === 'string' || texture instanceof String ){ _loadTexture( this, texture ); } else { throw new Error(`ThreeMeshUI::MSDFVariant provided 'texture' parameter is '${typeof texture}'. Only Texture and String allowed.`) } this._defaultMaterialClass = MSDFFontMaterial; this._checkReadiness(); } get texture() { return this._texture; } get unitRange() { return this._unitRange; } /** * @param {Function.} v * @override */ set fontMaterial( v ) { this._defaultMaterialClass = v; } /** * * @override * @returns {Function.} */ get fontMaterial() { return this._defaultMaterialClass; } /** * * @param {MSDFJson} json * @private */ _buildData( json ) { this._font = new MSDFTypographicFont( json ); /** * * @type {import('../FontVariant').KerningPairs} * @private */ this._kernings = this._buildKerningPairs( json ); this._chars = this._buildCharacters( json ); this._chars[ " " ] = this._buildCharacterWhite( json ); this._chars[ "\n" ] = this._buildCharacterWhite( json, '\n' , 0.001, 1); this._chars[ "\t" ] = this._buildCharacterWhite( json, '\t' , 4, 1); this._size = json.info.size; this._lineHeight = json.common.lineHeight; this._lineBase = json.common.base; this._distanceRange = json.distanceField.distanceRange; // precompute the unit range as recommended by chlumsky // @see https://github.com/Chlumsky/msdfgen // "I would suggest precomputing unitRange as a uniform variable instead of pxRange for better performance." this._unitRange = new external_THREE_namespaceObject.Vector2(this._distanceRange, this._distanceRange) .divide( new external_THREE_namespaceObject.Vector2( json.common.scaleW, json.common.scaleH ) ); } /** * * @param texture * @private */ _buildTexture( texture ) { texture.generateMipmaps = false; texture.minFilter = external_THREE_namespaceObject.LinearFilter; texture.magFilter = external_THREE_namespaceObject.LinearFilter; texture.needsUpdate = true; } /** * @abstract * @protected * @param {string} missingChar * @returns {string|null} */ _getFallbackCharacter( missingChar ) { return font_FontLibrary.missingCharacter( this, missingChar ); } /** * * @override * @param {import('./../InlineGlyph').default|import('./MSDFInlineGlyph').default} inline * @param {import('./../../core/elements/MeshUIBaseElement').default} element * @returns {MSDFGeometricGlyph} */ getGeometricGlyph( inline, element ) { return new MSDFGeometricGlyph( inline, element ); } /** * Abstraction implementation * * @returns {boolean} * @private */ _readyCondition() { return this._chars && this._texture && this._texture.image; } /** * Ensure that each font variant has its kerning dictionary * @see src/font/msdf/FontVariantMSDF.js for an implementation * * @param {MSDFJson} json * @returns {import('../FontVariant').KerningPairs} * @private */ _buildKerningPairs( json ) { const friendlyKernings = {}; // Loop through each kernings pairs defined in msdf json for ( let i = 0; i < json.kernings.length; i++ ) { const kerning = json.kernings[ i ]; // ignore zero kerned glyph pair if ( kerning.amount === 0 ) continue; // Build and store the glyph paired characters "ij","WA", ... as keys, referecing their kerning amount const glyphPair = String.fromCharCode( kerning.first, kerning.second ); // This would then be available for fast access friendlyKernings[ glyphPair ] = kerning.amount; } // update the font to keep it return friendlyKernings; } /** * * @param {MSDFJson} json * @private */ _buildCharacters( json ) { const friendlyChars = {}; for ( let i = 0; i < json.chars.length; i++ ) { const charOBJ = json.chars[ i ]; friendlyChars[ charOBJ.char ] = new MSDFTypographicGlyph( this._font, charOBJ ); } return friendlyChars; } /** * * @param {MSDFJson} json * @param char * @param scaleX * @param scaleY * @private */ _buildCharacterWhite( json, char = " ", scaleX = 1, scaleY = 1 ) { return new MSDFTypographicGlyph( this._font, { char, width: (json.info.size / 3)*scaleX, height: (json.info.size * 0.7)*scaleY, }); } /* eslint-disable no-unused-vars */ /** * * @param element * @private */ _alterElementProperties( element ) { /* eslint-enable no-unused-vars */ } } /*********************************************************************************************************************** * INTERNAL STUFF **********************************************************************************************************************/ /** * Load a msdf json then build fontVariant data * * @param {FontVariant} fontVariant * @param {string} jsonUrl * @private */ function _loadJson( fontVariant, jsonUrl ) { new external_THREE_namespaceObject.FileLoader().setResponseType( 'json' ) .load( jsonUrl, ( response ) => { fontVariant._buildData( response ); fontVariant._checkReadiness(); } ); } /** * Load a msdf texture then build texture * * @param {FontVariant} fontVariant * @param {string} textureUrl * @private */ function _loadTexture( fontVariant, textureUrl ) { fontVariant._texture = new external_THREE_namespaceObject.TextureLoader().load( textureUrl, ( texture ) => { fontVariant._buildTexture( texture ); fontVariant._checkReadiness(); } ); } /*********************************************************************************************************************** * MSDF FILE FORMAT DESCRIPTION * @see https://www.angelcode.com/products/bmfont/doc/file_format.html **********************************************************************************************************************/ /** * @typedef {Object} MSDFJson * * @property {MSDFJsonInfo} info * @property {MSDFJsonCommon} common * @property {Array.} pages * @property {Array.} chars * @property {{fieldType:string, distanceRange:number}} distanceField * @property {Array.} kernings */ /** * * @typedef {Object} MSDFJsonInfo * * @property {string} face This is the name of the true type font. * @property {number} size The size of the true type font. * @property {boolean} bold The font is bold. * @property {boolean} italic The font is italic. * @property {string[]} charset The name of the OEM charset used (when not unicode). * @property {boolean} unicode Set to 1 if it is the unicode charset. * @property {number} stretchH The font height stretch in percentage. 100% means no stretch. * @property {number} smooth Set to 1 if smoothing was turned on. * @property {number} aa The supersampling level used. 1 means no supersampling was used. * @property {Array.} padding TThe padding for each character (up, right, down, left). * @property {Array.} spacing The spacing for each character (horizontal, vertical). * @property {number} outline (not found) The outline thickness for the characters. */ /** * * @typedef {Object} MSDFJsonCommon * * @property {number} lineHeight This is the distance in pixels between each line of text. * @property {number} base The number of pixels from the absolute top of the line to the base of the characters. * @property {number} scaleW The width of the texture, normally used to scale the x pos of the character image. * @property {number} scaleH The height of the texture, normally used to scale the y pos of the character image. * @property {number} pages The number of texture pages included in the font. * @property {boolean} packed * @property {number} alphaChnl * @property {number} redChnl * @property {number} greenChnl * @property {number[]} blueChnl */ /** * @typedef {Object} MSDFJsonPage * * @property {string} id The page id. * @property {string} file The texture file name. */ /** * * @typedef {Object} MSDFJsonChar * * @property {number} id The character id. * @property {number} index The character index. * @property {string} char The character. * @property {number} x The left position of the character image in the texture. * @property {number} y The top position of the character image in the texture. * @property {number} width The width of the character image in the texture. * @property {number} height The height of the character image in the texture. * @property {number} xoffset How much the current position should be offset when copying the image from the texture to the screen. * @property {number} yoffset How much the current position should be offset when copying the image from the texture to the screen. * @property {number} xadvance How much the current position should be advanced after drawing the character. * @property {string} page The texture page where the character image is found. * @property {number} chnl The texture channel where the character image is found (1 = blue, 2 = green, 4 = red, 8 = alpha, 15 = all channels). * @property {Object} [uv] * / /** * * @typedef {Object} MSDFJsonKerning * * @property {number} first The first character id. * @property {number} second The second character id. * @property {number} amount How much the x position should be adjusted when drawing the second character immediately following the first. * */ ;// CONCATENATED MODULE: ./src/font/FontFamily.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class FontFamily extends external_THREE_namespaceObject.EventDispatcher { /** * * @param {string} name */ constructor( name ) { super(); /** * * @type {string} * @private */ this._name = name; /** * * @type {Array.} * @private */ this._variants = []; /** * * @type {boolean} * @private */ this._isReady = false; } get isReady() { return this._isReady; } /** * * @param {string|number} weight * @param {string} style * @param {string|Object} json * @param {string|Texture} texture * @param {boolean} [override=false] */ addVariant( weight, style, json, texture, override = false){ if( override || !this.getVariant( weight, style) ){ this._isReady = false; const newVariant = new MSDFFontVariant( weight, style, json, texture); this._variants.push( newVariant ); if( !newVariant.isReady ){ newVariant.addEventListener( "ready", this._checkReadiness ) } else { this._checkReadiness(); } } else { console.warn(`FontFamily('${this._name}')::addVariant() - Variant(${weight}, ${style}) already exists.`); } return this; } /** * * @param {FontVariant} variantImplementation * @param {boolean} [override=false] */ addCustomImplementationVariant( variantImplementation, override = false){ if( override || !this.getVariant( variantImplementation.weight, variantImplementation.style) ){ this._isReady = false; this._variants.push( variantImplementation ); if( !variantImplementation.isReady ){ variantImplementation.addEventListener( "ready", this._checkReadiness ) } else { this._checkReadiness(); } } else { console.warn(`FontFamily('${this._name}')::addCustomImplementationVariant() - Variant(${variantImplementation.weight}, ${variantImplementation.style}) already exists.`); } return this; } /** * * @param {string|number} weight * @param {string} style * @returns {FontVariant|null} */ getVariant( weight, style ){ weight = uniformizeFontWeight(weight); return this._variants.find( fontVariant => fontVariant.weight === weight && fontVariant.style === style ); } /** * * @return {string} */ get name(){ return this._name; } _checkReadiness = () => { if( this._variants.every( v => v.isReady ) ) { FontFamily_setReady( this ); } } } const FontFamily_readyEvent = { type: 'ready' }; /** * Set the ready status of a fontVariant * @param {FontFamily} fontFamily * @private */ function FontFamily_setReady( fontFamily ) { fontFamily._isReady = true; fontFamily.dispatchEvent( FontFamily_readyEvent ); } ;// CONCATENATED MODULE: ./src/font/FontLibrary.js // JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ const _fontFamilies = {}; /* eslint-disable no-unused-vars */ /** * * @param {FontFamily} fontFamily * @returns {Promise} */ const prepare = function ( fontFamily ) { /** * * @type {FontFamily[]} */ const families = [ ...arguments ]; // Check all family are right instance families.forEach( f => { if( !(f instanceof FontFamily) ) { throw new Error(`FontLibrary::prepare() - One of the provided parameter is not a FontFamily. Instead ${typeof f} given.`); } }) /** * Check that all provided families are loaded * @returns {boolean} */ const areAllLoaded = function() { return families.every( f => f.isReady ); } // @TODO: Should handle possible rejection return new Promise((resolve,reject)=>{ // Direct resolve if all loaded if ( areAllLoaded() ){ resolve(); } else { // Add listener on each family not ready for ( let i = 0; i < families.length; i++ ) { const family = families[ i ]; if( !family.isReady ){ family.addEventListener( "ready" , ()=> { // Resolve if all other families are loaded if( areAllLoaded() ) { resolve(); } }); } } } }); } /* eslint-enable no-unused-vars */ /** * * @param {string} name * @returns {FontFamily} */ const addFontFamily = function ( name ) { if ( _fontFamilies[ name ] ) { console.error( `FontLibrary::addFontFamily - Font('${name}') is already registered` ); } _fontFamilies[ name ] = new FontFamily( name ); return _fontFamilies[ name ]; } /** * * @param {string} name * @returns {FontFamily} */ const getFontFamily = function( name ) { return _fontFamilies[ name ]; } /** * * @param { (fontVariant:FontVariant, character:string ) => string|null } handler */ const setMissingCharacterHandler = function ( handler ) { _missingCharacterHandler = handler; } /** * * @type { (fontVariant:FontVariant, character:string ) => string|null } * @private */ let _missingCharacterHandler = function ( fontVariant, character ) { console.error( `The character '${character}' is not included in the font characters set.` ); // return a glyph has fallback return " "; }; /** * * @param {FontVariant} fontVariant * @param {string} character * * @returns {string} */ function missingCharacter( fontVariant, character ) { // Execute the user defined handled return _missingCharacterHandler( fontVariant, character ); } // const FontLibrary = { addFontFamily, getFontFamily, prepare, setMissingCharacterHandler, missingCharacter }; /* harmony default export */ const font_FontLibrary = (FontLibrary); ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontFamilyProperty.js class FontFamilyProperty extends SubStyleProperty { constructor( ) { super( 'fontFamily', 'inherit' , true ); } /* eslint-disable no-unused-vars */ /** * * @param element */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ if( this._input instanceof FontFamily ) { this._value = this._input; } else if ( this._input === 'inherit' ) { // do nothing } else if ( typeof this._input === 'string' ) { // string - family const fontFamily = font_FontLibrary.getFontFamily( this._input ); if( fontFamily ) { this._value = fontFamily; } else { console.warn( `(.style) fontFamily, the font '${this._input}' is not registered. Aborted.`) } } else { console.warn( `(.style) fontFamily requires a registered fontFamily instance, or the id of a registered fontFamily.`); console.warn( `If you want to set a specific font, please use .font property instead.`); } } /** * @override * @return {any|FontFamily|null} */ get value() { return this._value; } getInheritedInput ( element ) { if( this._input !== 'inherit' ) return this._input; const parent = element._parent._value; if( parent ) { return parent[`_${this._id}`].getInheritedInput( parent ) } return this.getDefaultValue(); } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/LineHeightProperty.js class LineHeightProperty extends SubStyleProperty { /** * */ constructor() { super( 'lineHeight', 'inherit', true ); } update( element, out ) { super.update( element, out ); element._layouter._needsProcess = true; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/WhiteSpaceProperty.js class WhiteSpaceProperty extends SubStyleProperty { constructor() { super( 'whiteSpace', 'inherit' ); this.isValidValue = WhiteSpaceProperty_isValid; } } /** * * @type {Array.} */ const WhiteSpaceProperty_AVAILABLE_VALUES = ['normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap']; /** * * @param {any} value * @return {boolean} * @private */ function WhiteSpaceProperty_isValid( value ) { if( WhiteSpaceProperty_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) whiteSpace value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/LetterSpacingProperty.js class LetterSpacingProperty extends SubStyleProperty { constructor() { super( 'letterSpacing', 'inherit', true ); } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontSizeProperty.js class FontSizeProperty extends SubStyleProperty { constructor() { super( 'fontSize', 'inherit', true ); } } ;// CONCATENATED MODULE: ./src/core/properties/geometry/SegmentsProperty.js class SegmentsProperty extends BaseProperty { constructor() { super( 'segments', 1, false ); } } ;// CONCATENATED MODULE: ./src/core/properties/InvertAlphaProperty.js /** * Class definition * @property {boolean|"inherit"} value - propriety description * */ class InvertAlphaProperty extends InheritableProperty { constructor() { super( 'invertAlpha', 'inherit' ); } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontKerningProperty.js class FontKerningProperty extends SubStyleProperty { constructor() { super( 'fontKerning', 'inherit' ); this.isValidValue = FontKerningProperty_isValid; } } const FontKerningProperty_AVAILABLE_VALUES = ['normal', 'none', 'inherit']; function FontKerningProperty_isValid( value ) { if( FontKerningProperty_AVAILABLE_VALUES.indexOf( value ) === -1 ) { console.warn( `(.style) fontKerning value '${value}' is not valid. Aborted` ); return false; } return true; } ;// CONCATENATED MODULE: ./src/core/properties/InheritableBooleanProperty.js /** * @property {boolean|"inherit"} value */ class InheritableBooleanProperty extends InheritableProperty { /** * * @param {string} propertyId */ constructor( propertyId) { super( propertyId, 'inherit', true ); } } ;// CONCATENATED MODULE: ./src/core/properties/InheritableMaterialProperty.js /** * @property {Material|null|"inherit"} value */ class InheritableMaterialProperty extends InheritableProperty { /** * * @param {string} propertyId */ constructor( propertyId ) { super( propertyId, 'inherit', false ); /** * * @type {Object.<{m:string, t?:(target:any, targetProperty:string, value:any) => void}>} * @internal */ this._mediation = {}; /** * * @type {null} * @internal */ this._defaultMaterial = null; } update( element, out ) { /* eslint-enable no-unused-vars */ this._notInheritedValue = this._value; if ( this._notInheritedValue === 'inherit' ) { this._notInheritedValue = this.getInheritedInput( element ); } else { this.propagate( element ); } // no material if ( !this._notInheritedValue ) { // reset mediation this._mediation = {}; } else if ( this._notInheritedValue.constructor.mediation ) { this._mediation = { ...this._notInheritedValue.constructor.mediation }; } else { this._mediation = { clippingPlanes: { m: 'clippingPlanes' }, fontAlphaTest: { m: 'alphaTest', t: alphaTestTransformer }, fontSide: { m: 'side' }, color: { m: 'color' }, fontOpacity: { m: 'opacity' } }; } element._transferToFontMaterial(); // dispatch to children this._outputValue( out ); } /** * @override */ getInheritedInput( element ) { if ( this._value !== 'inherit' ) return this._value; let recursiveParent = element; let inheritedValue = null; while ( recursiveParent._parent._value ) { recursiveParent = recursiveParent._parent._value; if ( recursiveParent[ `_${this._id}` ]._value !== 'inherit' ) { inheritedValue = recursiveParent[ `_${this._id}` ]._value; break; } } if ( inheritedValue !== null ) { return inheritedValue; } return this.getDefaultValue(); } getDefaultValue() { return this._defaultMaterial; } } ;// CONCATENATED MODULE: ./src/utils/mediator/transformers/MeshTransformers.js const renderOrderTransformer = function ( target, targetProperty, value ) { /** * Propagate the render order to each child */ target.traverse( ( child ) => { child.renderOrder = value; } ); } const MeshTransformers_layer = function ( target, targetProperty, value ) { /** * Propagate the layer to each child */ target.parent.traverse( ( child ) => { child.layers.set( value ); } ); } ;// CONCATENATED MODULE: ./src/core/elements/MeshUIBaseElement.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class MeshUIBaseElement extends external_THREE_namespaceObject.Object3D { /** * * @param {Properties} properties * @param {Options} values */ constructor( properties, values) { super(); Object.defineProperties( this, { isUI: { configurable: false, enumerable: true, value: true } } ); /** * * @type {Mesh|null} * @internal */ this._backgroundMesh = null; /** * * @type {Material} * @internal */ this._backgroundMaterial = null; /** * * @type {Material} * @protected */ this._backgroundCustomDepthMaterial = null; /** * * @type {Object.<{m:string, t?:(target:any, targetProperty:string, value:any) => void}>} * @protected */ this._backgroundMaterialMediation = {}; /** * * @type {Object.<{m:string, t?:(value:any) => any}>} * @private */ this._backgroundMeshMediation = { backgroundCastShadow: { m: 'castShadow' }, backgroundReceiveShadow: { m: 'receiveShadow' }, renderOrder: {m: 'renderOrder', t: renderOrderTransformer } }; /** * * @type {Mesh|null} * @internal */ this._fontMesh = null; /** * * @type {InheritableMaterialProperty} * @internal */ this._fontMaterial = new InheritableMaterialProperty('fontMaterial'); /** * * @type {InheritableMaterialProperty} * @private */ this._fontCustomDepthMaterial = new InheritableMaterialProperty('fontCustomDepthMaterial'); /** * * @type {Object.<{m:string, t?:(value:any) => any}>} * @private */ this._fontMeshMediation = { fontMaterial: { m: 'material' }, fontCustomDepthMaterial: { m : 'customDepthMaterial', t:directTransferNotNull}, fontCastShadow: { m: 'castShadow' }, fontReceiveShadow: { m: 'receiveShadow' }, renderOrder: {m: 'renderOrder' } }; // Children lists /** * * @type {EmptyProperty|ChildrenBox|ChildrenText} * @internal */ this._children = properties.children ? new properties.children : new EmptyProperty("children"); this._parent = new ParentProperty(); // update parentUI when this component will be added or removed this.addEventListener( 'added', this._rebuildParentUI ); this.addEventListener( 'removed', this._rebuildParentUI ); //material properties this._backgroundSide = new SideProperty( 'backgroundSide' ); this._fontSide = new SideProperty( 'fontSide' ); this._backgroundAlphaTest = new NumberProperty( 'backgroundAlphaTest', 0.02 ); this._fontAlphaTest = new NumberProperty( 'fontAlphaTest', 0.02 ); // mesh properties this._visible = new VisibleProperty( 'visible', true ); this._backgroundCastShadow = new InheritableBooleanProperty( 'backgroundCastShadow' ); this._fontCastShadow = new InheritableBooleanProperty( 'fontCastShadow' ); this._backgroundReceiveShadow = new InheritableBooleanProperty( 'backgroundReceiveShadow' ); this._fontReceiveShadow = new InheritableBooleanProperty( 'fontReceiveShadow' ); // @TODO: RenderOrder for background and fonts this._renderOrder = new RenderOrderProperty(); // @TODO : background & Text this._segments = properties.segments ? new properties.segments() : new SegmentsProperty(); /** * * @type {BoundsBox|BoundsText|EmptyProperty} * @ignore * @internal */ this._bounds = properties.bounds ? new properties.bounds() : new EmptyProperty("bounds"); // styles ---; this._order = new OrderProperty(); this._padding = new PaddingProperty(); this._margin = new MarginProperty(); this._position = new PositionProperty(); /** * * @type {FlexDirectionProperty} * @internal */ this._flexDirection = properties.flexDirection ? new properties.flexDirection() : new FlexDirectionProperty(); this._justifyContent = properties.justifyContent ? new properties.justifyContent() : new JustifyContentProperty(); this._alignItems = properties.alignItems ? new properties.alignItems() : new AlignItemsProperty(); this._display = new Display( 'flex' ); this._boxSizing = new BoxSizing( 'border-box' ); this._width = new WidthProperty(); this._height = new HeightProperty(); this._backgroundColor = properties.backgroundColor ? new properties.backgroundColor() : new BackgroundColorProperty(); this._backgroundOpacity = new StyleFactorProperty('backgroundOpacity', 0.5 ); this._backgroundImage = new BackgroundImage(); this._backgroundSize = new BackgroundSize( 'cover' ); this._color = properties.color ? new properties.color() : new StyleColorProperty('color', 'inherit'); this._fontOpacity = new StyleFactorProperty( 'fontOpacity', 'inherit'); this._whiteSpace = properties.whiteSpace ? new properties.whiteSpace() : new WhiteSpaceProperty(); this._fontFamily = properties.fontFamily ? new properties.fontFamily() : new FontFamilyProperty(); this._fontStyle = properties.fontStyle ? new properties.fontStyle() : new FontStyleProperty( 'normal' ); this._fontWeight = properties.fontWeight ? new properties.fontWeight() : new FontWeightProperty(); this._fontSize = properties.fontSize ? new properties.fontSize() : new FontSizeProperty(); this._lineHeight = properties.lineHeight ? new properties.lineHeight() : new LineHeightProperty(); this._fontKerning = properties.fontKerning ? new properties.fontKerning() : new FontKerningProperty(); this._letterSpacing = properties.letterSpacing ? new properties.letterSpacing() : new LetterSpacingProperty(); this._overflow = new Overflow( 'visible' ); this._borderRadius = new BorderRadius( 0 ); this._borderWidth = new BorderWidth( 0 ); this._borderColor = new StyleColorProperty( 'borderColor', 0xff00ff ); this._borderOpacity = new StyleFactorProperty( 'borderOpacity', 1); // styles ---; this._font = new FontProperty(); this._lineBreak = properties.lineBreak ? new properties.lineBreak() : new EmptyProperty("lineBreak"); /** * * @type {TextContentEmpty|TextContentText|TextContentInline} * @internal */ this._textContent = properties.textContent ? new properties.textContent() : new TextContentEmpty(); /** * * @type {GlyphsProperty} * @internal */ this._glyphs = properties.glyphs ? new properties.glyphs() : new EmptyProperty("glyphs"); this._inlines = properties.inlines ? new properties.inlines() : new EmptyProperty("inlines"); /** * * @type {BoxLayouter|TextLayouter|EmptyProperty} * @internal */ this._layouter = properties.layouter ? new properties.layouter() : new EmptyProperty("layouter"); this._inlineJustificator = new InlineJustificator(); this._textAlign = properties.textAlign ? new properties.textAlign() : new TextAlignProperty(); this._autoSize = properties.autoSize ? new properties.autoSize() : new EmptyProperty("autoSize"); this._renderer = properties.renderer ? new properties.renderer() : new EmptyProperty("renderer"); this._offset = new OffsetProperty(); // adds this._invertAlpha = new InvertAlphaProperty(); this._fontSmooth = properties.fontSmooth ? new properties.fontSmooth() : new FontSmoothProperty(); /** * * @type {Array.} * @internal */ this._components = [ this._textContent, this._children, this._parent, this._autoSize, this._fontFamily, this._fontStyle, this._fontWeight, this._font, this._whiteSpace, this._glyphs, this._inlines, this._visible, // Meshes interfaces this._backgroundSide, this._fontSide, this._backgroundAlphaTest, this._fontAlphaTest, this._backgroundCastShadow, this._fontCastShadow, this._backgroundReceiveShadow, this._fontReceiveShadow, this._renderOrder, this._segments, // styles ---; this._padding, this._margin, this._width, this._height, this._borderWidth, this._boxSizing, this._bounds, this._position, this._flexDirection, this._justifyContent, this._alignItems, this._order, this._display, this._backgroundColor, this._backgroundOpacity, this._backgroundImage, this._backgroundSize, this._fontOpacity, this._color, // font : update order : WhiteSpace > Glyph > Inlines > Kerning > newlineBreakability > LineBreak > FontSize // font : process order : ?? // this._font, this._fontSize, this._lineHeight, this._fontKerning, this._letterSpacing, this._borderRadius, this._borderColor, this._borderOpacity, // this._styles, // styles ---; this._lineBreak, this._offset, this._layouter, this._inlineJustificator, this._textAlign, // !! this._renderer renderer MUST NOT BE in components !! this._invertAlpha, this._fontSmooth, this._fontMaterial, this._fontCustomDepthMaterial, this._overflow, this._renderer, ] /** * * @type {*[]} * @private */ this._onAfterUpdates = []; // breaks inheritance chains // if( !values ) values = {}; if( !values.backgroundSide ) values.backgroundSide = 0; // FrontSide if( values ) this.set( values ); } /////////////// /// UPDATE /////////////// update( ) { // console.log( "Update Element", this.name , this.constructor.name ); const out = {}; for ( const component of this._components ) { if( component._needsUpdate ) { // console.log( ' ', component.id ) component.update( this, out ); component._needsUpdate = false; } } this._transferToBackgroundMesh( out ); this._transferToFontMesh( out ); this._transferToBackgroundMaterial( out ); this._transferToFontMaterial( out ); // update children for ( const child of this._children._uis ) { child.update(); } } process() { // process first time : Natural size for ( const child of this._children._uis ) { child.process(); } // console.log( 'Process ', this.name ); for ( const component of this._components ) { if( component._needsProcess ) { // console.log( ' ', component.id ); component.process( this ); component._needsProcess = false; } } } render() { // console.log( 'render ', this.name ); for ( let i = 0; i < this._components.length; i++ ) { const component = this._components[ i ]; if( component._needsRender ) { // console.log( ' ', component.id); component.render( this ); component._needsRender = false; } } // render all children for ( const child of this._children._uis ) { child.render(); } } /** * * @param {Options} options */ set( options ) { // Retro compatibility, when not recommended way // 2. < v7.x.x way if( options.fontTexture ) { console.warn( "ThreeMeshUI::set( {fontTexture} ) is deprecated. Please use fontLibrary to register font families and variants.") if( options.fontFamily ) { // Set from old way, check if that family is already registered const fontName = options.fontFamily.pages ? options.fontFamily.info.face : options.fontFamily; let fontFamily = font_FontLibrary.getFontFamily( fontName ); if ( !fontFamily ) { const fontStyle = options.fontStyle ? options.fontStyle : 'normal'; const fontWeight = options.fontWeight ? options.fontWeight : '400'; fontFamily = font_FontLibrary.addFontFamily( fontName ) .addVariant( fontWeight, fontStyle, options.fontFamily, options.fontTexture ); } options['fontFamily'] = fontFamily; delete options['fontTexture']; } } for ( let prop of Object.keys( options ) ) { const value = options[prop]; // 1. replace deprecated properties switch ( prop ){ case 'contentDirection': console.warn('ThreeMeshUI v7xx: property `contentDirection` is deprecated and has been renamed as `flexDirection`'); prop = 'flexDirection'; break; case 'interLine': console.warn('ThreeMeshUI v7xx: property `interLine` is deprecated and has been renamed as `lineHeight`'); prop = 'lineHeight'; break; case 'content': console.warn( 'ThreeMeshUI v7xx: property `content` is deprecated and has been renamed as `textContent`'); prop = 'textContent'; break; case 'fontColor': console.warn( 'ThreeMeshUI v7xx: property `fontColor` is deprecated and has been renamed as `color`'); prop = 'color'; break; case 'hiddenOverflow': console.warn( 'ThreeMeshUI v7xx: property `hiddenOverflow` is deprecated and has been renamed as `overflow`'); prop = 'overflow'; break; case 'backgroundTexture': console.warn( 'ThreeMeshUI v7xx: property `backgroundTexture` is deprecated and has been renamed as `backgroundImage`'); prop = 'backgroundImage'; break; case 'alignContent': console.warn( 'ThreeMeshUI v7xx: property `alignContent` is deprecated and has been renamed as `alignItems`'); prop = 'alignItems'; break; case "borderTopColor": case "borderBottomColor": case "borderLeftColor": case "borderRightColor": prop = 'borderColor'; break; } switch ( prop ) { // properties // As textContent property might alter the hierarchy, do not wait until update // case 'textContent' : case 'fontSmooth': case 'renderOrder': case 'segments' : case 'visible' : case 'offset': this[`_${prop}`].value = value; break; // styles properties case 'flexDirection' : case 'justifyContent' : case 'alignItems' : case 'color' : case 'fontFamily' : case 'fontOpacity' : case 'fontKerning' : case 'fontSize' : case 'fontStyle' : case 'fontWeight' : case 'textAlign' : case 'letterSpacing' : case 'lineHeight' : case 'whiteSpace': case 'breakOn': // Not valid anymore? case 'width' : case 'height' : case 'padding': case 'margin' : case 'backgroundColor' : case 'backgroundOpacity' : case 'backgroundImage' : case 'backgroundSize' : case 'borderColor' : case 'borderOpacity' : case 'borderRadius' : case 'borderWidth': case 'overflow' : case 'order': case 'boxSizing': if( this[`_${prop}`] ){ this[`_${prop}`].inline = value; } break; case 'paddingTop': this._padding.top = value; break; case 'paddingRight': this._padding.right = value; break; case 'paddingBottom': this._padding.bottom = value; break; case 'paddingLeft': this._padding.left = value; break; case 'marginTop': this._margin.top = value; break; case 'marginRight': this._margin.right = value; break; case 'marginBottom': this._margin.bottom = value; break; case 'marginLeft': this._margin.left = value; break; case 'borderTopWidth': this._borderWidth.top = value; break; case 'borderRightWidth': this._borderWidth.right = value; break; case 'borderBottomWidth': this._borderWidth.bottom = value; break; case 'borderLeftWidth': this._borderWidth.left = value; break; case 'borderTopLeftRadius': this._borderRadius.topLeft = value; break; case 'borderTopRightRadius': this._borderRadius.topRight = value; break; case 'borderBottomRightRadius': this._borderRadius.bottomRight = value; break; case 'borderBottomLeftRadius': this._borderRadius.bottomLeft = value; break; // Back & Front linked properties case 'side': case 'castShadow': case 'receiveShadow': const upperCamelCaseProperty = prop.charAt(0).toUpperCase()+prop.substr(1); this[`_background${upperCamelCaseProperty}`].value = value; this[`_font${upperCamelCaseProperty}`].value = value; break; // Meshes & material properties case 'fontSide': case 'backgroundSide': case 'fontCastShadow': case 'backgroundCastShadow': case 'fontReceiveShadow': case 'backgroundReceiveShadow': case 'fontMaterial': case 'fontCustomDepthMaterial': this[`_${prop}`].value = value; break; default: if( this[`_${prop}`] !== undefined ) { this[`_${prop}`].value = value; } else { this[ prop ] = value } } } } get ( property ) { switch ( property ) { case 'overflow': case 'width' : case 'height' : return this[`_${property}`].inline; } } /** * Filters children in order to compute only one times children lists * @private */ _rebuildChildrenLists() { //console.log( this.name, 'child added' ); this._children._needsUpdate = true; } /** * Try to retrieve parentUI after each structural change * @protected */ _rebuildParentUI = () => { this._parent._needsUpdate = true; // set elements as root if ( this.parent && !this.parent.isUI ) { UpdateManager.register( this ); this.activatePseudoState('root'); } else { UpdateManager.remove( this ); this.deactivatePseudoState('root'); } }; /** * When the user calls component.add, it registers for updates, * then call THREE.Object3D.add. */ /* eslint-disable no-unused-vars */ /** * * @override * @param {...Object3D} object * @return {this} */ add( object ) { let addedUIChildren = false; for ( let i = 0; i < arguments.length; i++ ) { super.add( arguments[ i ] ); if( arguments[i].isUI ) { addedUIChildren = true; } } if( addedUIChildren ) this._rebuildChildrenLists(); return this; } /** * When the user calls component.remove, it registers for updates, * then call THREE.Object3D.remove. * @override * @param {...Object3D} object * @return {this} */ remove( object ) { for ( const id of Object.keys( arguments ) ) { // An inline component relies on its parent for positioning if ( arguments[ id ].isInline ) this.update( null, true ); } super.remove( ...arguments ); this._rebuildChildrenLists(); return this; } /** * * @return {Object3D} */ clear() { this.removeFromParent(); this.traverse( ( obj ) => { if ( obj.material ) obj.material.dispose(); if ( obj.geometry ) obj.geometry.dispose(); } ); super.clear(); // remove properties this._backgroundMesh = null; this._backgroundMaterial = null; this._backgroundMaterialMediation = null; this._backgroundMeshMediation = null; this._children.dispose(); this._children = null; this._parent.dispose(); this._parent = null; this._backgroundSide = null; this._backgroundAlphaTest = null; this._visible = null; this._backgroundCastShadow = null; this._backgroundReceiveShadow = null; this._renderOrder = null; this._segments = null; this._bounds = null; // styles properties this._boxSizing = null; this._padding = null; this._margin = null; this._position = null; this._flexDirection = null; this._justifyContent = null; this._alignItems = null; this._display = null; this._backgroundColor = null; this._backgroundOpacity = null; this._backgroundSize = null; this._fontOpacity = null; this._color = null; this._whiteSpace = null; this._fontFamily = null; this._fontStyle = null; this._fontWeight = null; this._lineHeight = null; this._fontKerning = null; this._letterSpacing = null; this._overflow = null; this._textAlign = null; this._font = null; this._lineBreak = null; this._layouter = null; return this; } /** * * @return {string} */ get textContent() { this._textContent.process( this ); return this._textContent._value; } /*********************************************************************************************************************** * TO MATERIAL HOLDER **********************************************************************************************************************/ /** * * @returns {Material|ShaderMaterial} */ get backgroundMaterial() { return this._backgroundMaterial; } /** * * @param {Material|ShaderMaterial} material */ set backgroundMaterial( material ) { this._backgroundMaterial = material; // Update the fontMaterialProperties that need to be transferred to this._backgroundMaterialMediation = { ...material.constructor.mediation }; // transfer all the properties to material this._transferToBackgroundMaterial(); if ( this._backgroundMesh ) { this._backgroundMesh.material = this._backgroundMaterial; uniformOrUserDataTransformer( material, 'frameSize', this._backgroundMesh.scale ); } } /** * * @param {Material|null} material */ set backgroundCustomDepthMaterial( material ) { this._backgroundCustomDepthMaterial = material; this._transferToBackgroundMaterial(); if ( this._backgroundMesh ) { // transfer to the main if isset this._backgroundMesh.customDepthMaterial = this._backgroundCustomDepthMaterial; } } /** * * @returns {Material|null} */ get backgroundCustomDepthMaterial() { return this._backgroundCustomDepthMaterial; } /** * According to the list of materialProperties * some properties are sent to material * @param {Object} [options=null] * @private */ _transferToBackgroundMaterial( options = null ) { if( !options ) { options = {}; for ( const component of this._components ) { component.output( options ); } } Mediator.mediate( this, this._backgroundMaterial, options, this._backgroundMaterialMediation, this._backgroundCustomDepthMaterial ); } /** * * @param {number} value */ set backgroundSide( value ) { this._backgroundSide.value = value; if ( this._backgroundMaterial ) this._backgroundMaterial.side = value; } /** * * @return {number} */ get backgroundSide() { return this._backgroundSide.value; } /** * * @param {number} value */ set backgroundAlphaTest ( value ) { this._backgroundAlphaTest.value = value; if( this._backgroundMaterial ) this._backgroundMaterial.alphaTest = value; } /** * * @return {number} */ get backgroundAlphaTest () { return this._backgroundAlphaTest.value; } /** Font Material ----------------------------------------------------------*/ /** * * @returns {Material|ShaderMaterial} */ // get fontMaterial() { return this._fontMaterial__; } get fontMaterial() { return this._fontMaterial.value; } /** * * @param {Material|ShaderMaterial} material */ set fontMaterial( material ) { this._fontMaterial.value = material; } /** * * @param {Material|null} material */ set fontCustomDepthMaterial( material ) { this._fontCustomDepthMaterial.value = material; } /** * * @returns {Material|null} */ get fontCustomDepthMaterial() { return this._fontCustomDepthMaterial.value; } /** * According to the list of materialProperties * some properties are sent to material * @param {Object} [options=null] * @private */ _transferToFontMaterial( options = null ) { const fontMat = this._fontMaterial.value; if( !fontMat ) return; if( !options ) { options = {}; for ( const component of this._components ) { component.output( options ); } } Mediator.mediate( this, fontMat, options, this._fontMaterial._mediation, this._fontCustomDepthMaterial.value ); } /** * * @param {number} value */ set fontSide( value ) { this._fontSide.value = value; } /** * * @return {number} */ get fontSide() { return this._fontSide.value; } /** * * @param {number} value */ set fontAlphaTest ( value ) { this._fontAlphaTest.value = value; } /** * * @return {number} */ get fontAlphaTest () { return this._fontAlphaTest.value; } /********************************************************************************************************************* * MESH MEDIATION ********************************************************************************************************************/ /** * According to the list of meshProperties * some properties are sent to mesh * @param {Object} [options=null] * @private */ _transferToBackgroundMesh( options = null ) { if( !options ) { options = {}; for ( const component of this._components ) { component.output( options ); } } Mediator.mediate( this, this._backgroundMesh, options, this._backgroundMeshMediation ); } /** * @internal * @param {Mesh|Array.|null} mesh */ setBackgroundMesh( mesh ) { if( this._backgroundMesh ) { this.remove( this._backgroundMesh ); this.unbindBackgroundMeshProperties(); } this._backgroundMesh = mesh; if ( this._backgroundMesh ) { this.bindBackgroundMeshProperties(); if( this._backgroundCustomDepthMaterial ) { this._backgroundMesh.customDepthMaterial = this._backgroundCustomDepthMaterial; } if( this._backgroundMaterial ) { uniformOrUserDataTransformer( this._backgroundMaterial, 'frameSize', this._backgroundMesh.scale ); } this._transferToBackgroundMesh(); this.add( this._backgroundMesh ); } } /** * */ bindBackgroundMeshProperties () { } /** * */ unbindBackgroundMeshProperties () { } activatePseudoState ( state ) { } deactivatePseudoState ( state ) { } togglePseudoState ( state ) { } hasPseudoState( state ) { return false; } set borderRadiusMediation ( value ) { this._borderRadius.mediation = value; } /** * * @param {boolean} value */ set backgroundCastShadow( value ) { if( this._backgroundCastShadow ) this._backgroundCastShadow.value = value; } /** * * @return {boolean} */ get backgroundCastShadow() { return this._backgroundCastShadow; } /** * * @param {boolean} value */ set backgroundReceiveShadow( value ) { if( this._backgroundReceiveShadow ) this._backgroundReceiveShadow.value = value; } /** * * @return {boolean} */ get backgroundReceiveShadow() { return this._backgroundReceiveShadow; } /** * * @param {number} value */ set renderOrder( value ) { if( this._renderOrder ) this._renderOrder.value = value; } /** * * @return {number} */ get renderOrder() { return this._renderOrder.value; } /** Font Mesh --------------------------------------------------------------*/ /** * According to the list of meshProperties * some properties are sent to mesh * @param {Object} [options=null] * @private */ _transferToFontMesh( options = null ) { if( !this._fontMesh ) return; if( !options ) { options = {}; for ( const component of this._components ) { component.output( options ); } } Mediator.mediate( this, this._fontMesh, options, this._fontMeshMediation ); } /** * @internal * @param {Mesh|Array.|null} mesh */ setFontMesh( mesh ) { if( this._fontMesh ) { this.remove( this._fontMesh ); if ( this._fontMesh.material ) this._fontMesh.material.dispose(); if ( this._fontMesh.geometry ) this._fontMesh.geometry.dispose(); this._fontMesh = null; // deepDelete( this._fontMesh ); this.unbindFontMeshProperties(); } this._fontMesh = mesh; if ( this._fontMesh ) { this._fontMesh.raycast = () => {}; this.bindFontMeshProperties(); this._transferToFontMaterial(); this._transferToFontMesh(); this.add( this._fontMesh ); } } /** * */ bindFontMeshProperties () { } /** * */ unbindFontMeshProperties () { } /** * * @param {boolean} value */ set fontCastShadow( value ) { if( this._fontCastShadow ) this._fontCastShadow.value = value; } /** * * @return {boolean} */ get fontCastShadow() { return this._fontCastShadow; } /** * * @param {boolean} value */ set fontReceiveShadow( value ) { if( this._fontReceiveShadow ) this._fontReceiveShadow.value = value; } /** * * @return {boolean} */ get fontReceiveShadow() { return this._fontReceiveShadow; } /*********************************************************************************************************************** * GEOMETRY **********************************************************************************************************************/ /** * * @param {Number} v */ set segments (v) { this._segments.value = v; // @TODO : Geometry Update } /** * * @return {number} */ get segments () { return this._segments.value; } /*********************************************************************************************************************** * HOOKS & ALTERS **********************************************************************************************************************/ /** * * @param {Function} func */ set onAfterUpdate( func ) { console.warn( 'ThreeMeshUI v7xx : `onAfterUpdate()` property has been deprecated, please rely on `addAfterUpdate` instead.' ); this.addAfterUpdate( func ); } /** * * @param {Function} func */ addAfterUpdate( func ) { this._onAfterUpdates.push( func ); } /** * * @param {Function} func */ removeAfterUpdate( func ) { const index = this._onAfterUpdates.indexOf( func ); if ( index !== -1 ) { this._onAfterUpdates.splice( index, 1 ); } } /** * @todo: afterUpdate not called anymore */ performAfterUpdate() { for ( let i = 0; i < this._onAfterUpdates.length; i++ ) { this._onAfterUpdates[ i ](); } } /** * Retrieve a property * @param propertyName * @return {BaseProperty|null} */ getProperty( propertyName ){ if( this[`_${propertyName}`] ){ return this[`_${propertyName}`]; } return null; } /** * * @param {string} name * @param {BaseProperty} instance * @returns {void} */ appendProperty( name, instance ) { this[`_${name}`] = instance; this._components.push( instance ); } /** * * @param {string} name * @param {BaseProperty} instance * @returns {BaseProperty} */ replaceProperty( name, instance ) { const oldProperty = this[`_${name}`]; const index = this._components.indexOf( oldProperty ); this._components[index] = this[`_${name}`] = instance; instance.needsUpdate = true; return oldProperty; } } /** * @typedef Properties * @type {Object.} */ /** * @typedef Options * @type {DocumentedOptions & Object.} */ /** * * @typedef {Object} DocumentedOptions * * @property [options.name] {string} * @property [options.flexDirection] {"row"|"row-reverse"|"column"|"column-reverse"} * @property [options.justifyContent] {"start"|"center"|"end"|"space-around"|"space-between"|"space-evenly"} * @property [options.alignItems] {"start"|"center"|"end"|"stretch"} * @property [options.overflow] {"visible"|"hidden"} * @property [options.fontKerning] {"normal"|"none"} * @property [options.segments] {number} * @property [options.fontFamily] {FontFamily|string} * @property [options.fontStyle] {"normal"|"italic"} * @property [options.fontWeight] {"light"|"normal"|"bold"|"bolder"|100|200|300|400|500|600|700|800|900|"100"|"200"|"300"|"400"|"500"|"600"|"700"|"800"|"900"} * * @property [options.color]{Color|number|string} The font color * * @property [options.backgroundColor]{Color|number|string} The background color * @property [options.backgroundOpacity] {number} * @property [options.backgroundSize] {"cover"|"contain"|"stretch"} * @property [options.backgroundImage] {Texture|string} * * * @property [options.borderColor] {Color|number|string} * @property [options.borderOpacity] {number} * @property [options.borderRadius] {Vector4|Array.|number|string} * @property [options.borderWidth] {Vector4|Array.|number|string} * * @property [options.boxSizing] {"content-box"|"border-box"} * @property [options.width] {number|string|"100%"|"auto"} * @property [options.height] {number|string|"100%"|"auto"} * @property [options.padding] {Vector4|Array.|number|string} * @property [options.margin] {Vector4|Array.|number|string} * * @property [options.textAlign] {"left"|"right"|"center"|"justify"|"justify-left"|"justify-right"} * @property [options.visible] {boolean} * @property [options.letterSpacing] {number} * * @property [options.whiteSpace] {"normal"|"nowrap"|"pre"|"pre-line"|"pre-wrap"} * @property [options.fontTexture] {Texture|string} @deprecated * @property [options.textContent] {string} * */ /** * @typedef {"light"|"normal"|"bold"|"bolder"|100|200|300|400|500|600|700|800|900|"100"|"200"|"300"|"400"|"500"|"600"|"700"|"800"|"900"} FontWeightFormat */ ;// CONCATENATED MODULE: ./src/components/core/UpdateManager.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * Job: * - recording components required updates * - trigger those updates when 'update' is called * * This module is a bit special. It is, with FontLibrary, one of the only modules in the 'component' * directory not to be used in component composition (Object.assign). * * When MeshUIComponent is instanciated, it calls UpdateManager.register(). * * Then when MeshUIComponent receives new attributes, it doesn't update the component right away. * Instead, it calls UpdateManager.requestUpdate(), so that the component is updated when the user * decides it (usually in the render loop). * * This is best for performance, because when a UI is created, thousands of componants can * potentially be instantiated. If they called updates function on their ancestors right away, * a given component could be updated thousands of times in one frame, which is very ineficient. * * Instead, redundant update request are moot, the component will update once when the use calls * update() in their render loop. */ class UpdateManager { static register( component ) { if ( !this.elements.includes( component ) ) { this.elements.push( component ); } } static remove( component ) { const index = this.elements.indexOf( component ); if ( index !== -1 ) { this.elements.splice( index, 1 ); } } static update() { for ( const UIElement of this.elements ) { UIElement.update(); UIElement.process(); // Natural process UIElement.process(); // Actual process (optional) - For auto size and stretch UIElement.render(); } } } /** * @internal * @type {Array.} */ UpdateManager.elements = []; ;// CONCATENATED MODULE: ./src/frame/renderers/ShaderChunk/frame-border.pars.vertex.glsl.js /** * * @type {string} */ const frame_border_pars_vertex_glsl_program = /* glsl */` // FrameBorder vertex pars attribute vec2 uvB; varying vec2 vUvB; `; /* harmony default export */ const frame_border_pars_vertex_glsl = (frame_border_pars_vertex_glsl_program); ;// CONCATENATED MODULE: ./src/frame/renderers/ShaderChunk/frame-border.vertex.glsl.js /** * * @type {string} */ const frame_border_vertex_glsl_program = /* glsl */` // FrameBorder vertex shader vUvB = uvB; `; /* harmony default export */ const frame_border_vertex_glsl = (frame_border_vertex_glsl_program); ;// CONCATENATED MODULE: ./src/frame/renderers/ShaderChunk/frame-border.pars.fragment.glsl.js /** * * @type {string} */ const frame_border_pars_fragment_glsl_program = /* glsl */` // borders sequences are : x:TOP, y:RIGHT, z:BOTTOM, w:LEFT uniform vec4 borderWidth; uniform vec3 borderColor; uniform float borderOpacity; uniform vec4 borderRadius; uniform vec2 cornerTL; uniform vec2 cornerTR; uniform vec2 cornerBR; uniform vec2 cornerBL; varying vec2 vUvB; float getEllipticFactor( vec2 uv, vec2 center, float radiusX, float radiusY ) { float edx = uv.x - center.x; float edy = uv.y - center.y; float ddx = (edx * edx) / (radiusX * radiusX); float ddy = (edy * edy) / (radiusY * radiusY); return ddx + ddy; } `; /* harmony default export */ const frame_border_pars_fragment_glsl = (frame_border_pars_fragment_glsl_program); ;// CONCATENATED MODULE: ./src/frame/renderers/ShaderChunk/frame-border.fragment.glsl.js /** * * @type {string} */ const frame_border_fragment_glsl_program = /* glsl */` vec4 borderColor = vec4( borderColor, borderOpacity ); // This could be tweak to produce more smoothing float mult = 1.0; // Step 1 ---------------------------------------------- // Draw the four borders ( top - right - bottom - left ) // Without worrying about radiuses ( Straight boorders ) // Top float topBorderUVy = 1.0 - borderWidth.x; if( borderWidth.x > 0.0 && vUvB.y > topBorderUVy ) { float w = fwidth( 1.0 - vUvB.y ) * mult; float step = smoothstep( topBorderUVy , topBorderUVy + w , vUvB.y ); diffuseColor = mix( diffuseColor, borderColor, step ); } // Left float leftBorderUVx = borderWidth.w; if( borderWidth.w > 0.0 && vUvB.x < leftBorderUVx ) { float w = fwidth( vUvB.x ) * mult ; float step = smoothstep( leftBorderUVx , leftBorderUVx - w , vUvB.x ); diffuseColor = mix( diffuseColor, borderColor, step ); } // Bottom float bottomBorderUVy = borderWidth.z; if( borderWidth.z > 0.0 && vUvB.y < bottomBorderUVy ) { float w = fwidth( vUvB.y ) * mult; float step = smoothstep( bottomBorderUVy , bottomBorderUVy - w , vUvB.y ); diffuseColor = mix( diffuseColor, borderColor, step ); } // Right float rightBorderUVx = 1.0 - borderWidth.y; if( borderWidth.y > 0.0 && vUvB.x > rightBorderUVx ) { float w = fwidth( 1.0 - vUvB.x ) * mult; float step = smoothstep( rightBorderUVx , rightBorderUVx + w , vUvB.x ); diffuseColor = mix( diffuseColor, borderColor, step ); } // Step 2 ---------------------------------------------- // Process each corners ( topLeft, topRight, bottomRight, bottomLeft ) // To transparentize outside radiuses // To draw ellipse border on the corner // Top Left corner if( vUvB.x < cornerTL.x && vUvB.y > cornerTL.y ) { // Only draw border if width is set if( borderWidth.w + borderWidth.x > 0.0 ){ float borderFactor = getEllipticFactor( vUvB, cornerTL, cornerTL.x - borderWidth.w, ( 1.0 - cornerTL.y ) - borderWidth.x ); float step = smoothstep( 1.0, 1.0 + fwidth( borderFactor ) * mult, borderFactor ); diffuseColor = mix( diffuseColor, borderColor, step ); } // Then then radius float radiusFactor = getEllipticFactor( vUvB, cornerTL, cornerTL.x, 1.0 - cornerTL.y ); float alphaStep = smoothstep( 1.0 , 1.0 + fwidth(radiusFactor) * mult , radiusFactor ); diffuseColor.a = mix( diffuseColor.a, 0.0, alphaStep ); } // Bottom Left if( vUvB.x < cornerBL.x && vUvB.y < cornerBL.y ) { if( borderWidth.w + borderWidth.z > 0.0 ){ float borderFactor = getEllipticFactor( vUvB, cornerBL, cornerBL.x - borderWidth.w, cornerBL.y - borderWidth.z ); float step = smoothstep( 1.0, 1.0 + fwidth( borderFactor ) * mult, borderFactor ); diffuseColor = mix( diffuseColor, borderColor, step ); } float radiusFactor = getEllipticFactor( vUvB, cornerBL, cornerBL.x, cornerBL.y ); float alphaStep = smoothstep( 1.0 , 1.0 + fwidth(radiusFactor) * mult , radiusFactor ); diffuseColor.a = mix( diffuseColor.a, 0.0, alphaStep ); } // Top Right if( vUvB.x > cornerTR.x && vUvB.y > cornerTR.y ) { if( borderWidth.y + borderWidth.x > 0.0 ){ float borderFactor = getEllipticFactor( vUvB, cornerTR, ( 1.0 - cornerTR.x ) - borderWidth.y, ( 1.0 - cornerTR.y ) - borderWidth.x ); float step = smoothstep( 1.0, 1.0 + fwidth( borderFactor ) * mult, borderFactor ); diffuseColor = mix( diffuseColor, borderColor, step ); } float radiusFactor = getEllipticFactor( vUvB, cornerTR, 1.0 - cornerTR.x, 1.0 - cornerTR.y ); float alphaStep = smoothstep( 1.0 , 1.0 + fwidth(radiusFactor) * mult , radiusFactor ); diffuseColor.a = mix( diffuseColor.a, 0.0, alphaStep ); } // Bottom Right if( vUvB.x > cornerBR.x && vUvB.y < cornerBR.y ) { if( borderWidth.y + borderWidth.z > 0.0 ){ float borderFactor = getEllipticFactor( vUvB, cornerBR, ( 1.0 - cornerBR.x ) - borderWidth.y, cornerBR.y - borderWidth.z ); float step = smoothstep( 1.0, 1.0 + fwidth( borderFactor ) * mult, borderFactor ); diffuseColor = mix( diffuseColor, borderColor, step ); } float radiusFactor = getEllipticFactor( vUvB, cornerBR, 1.0 - cornerBR.x, cornerBR.y ); float alphaStep = smoothstep( 1.0 , 1.0 + fwidth(radiusFactor) * mult , radiusFactor ); diffuseColor.a = mix( diffuseColor.a, 0.0, alphaStep ); } `; /* harmony default export */ const frame_border_fragment_glsl = (frame_border_fragment_glsl_program); ;// CONCATENATED MODULE: ./src/frame/renderers/ShaderChunk/frame-common.pars.fragment.glsl.js /** * * @type {string} */ const frame_common_pars_fragment_glsl_program = /* glsl */` // To be removed - required for both border and background uniform vec3 frameSize; uniform vec2 textureSize; `; /* harmony default export */ const frame_common_pars_fragment_glsl = (frame_common_pars_fragment_glsl_program); ;// CONCATENATED MODULE: ./src/frame/renderers/ShaderChunk/frame-background.pars.fragment.glsl.js /** * * @type {string} */ const frame_background_pars_fragment_glsl_program = /* glsl */` #ifdef USE_MAP vec4 sampleTexture() { vec2 uv = vUv; // default stretch #if BACKGROUND_MAPPING != 0 float textureRatio = textureSize.x / textureSize.y; float panelRatio = frameSize.x / frameSize.y; float ratio = panelRatio / textureRatio; float ratio2 = textureRatio / panelRatio; // contain #if BACKGROUND_MAPPING == 1 if ( textureRatio < panelRatio ) { // repeat on X float newX = uv.x * ratio; newX += 0.5 - 0.5 * ratio; uv.x = newX; } else { // repeat on Y float newY = uv.y * ratio2; newY += 0.5 - 0.5 * ratio2; uv.y = newY; } #else // cover if ( textureRatio < panelRatio ) { // stretch on Y float newY = uv.y * ratio2; newY += 0.5 - 0.5 * ratio2; uv.y = newY; } else { // stretch on X float newX = uv.x * ratio; newX += 0.5 - 0.5 * ratio; uv.x = newX; } #endif #endif return texture2D( map, uv ); } #endif `; /* harmony default export */ const frame_background_pars_fragment_glsl = (frame_background_pars_fragment_glsl_program); ;// CONCATENATED MODULE: ./src/frame/renderers/ShaderChunk/frame-background.fragment.glsl.js /** * * @type {string} */ const frame_background_fragment_glsl_program = /* glsl */` #ifdef USE_MAP vec4 textureSample = sampleTexture(); diffuseColor *= textureSample; #endif `; /* harmony default export */ const frame_background_fragment_glsl = (frame_background_fragment_glsl_program); ;// CONCATENATED MODULE: ./src/renderers/shaders/ShaderChunkUI.js /* eslint-disable camelcase */ /** * @typedef {Object} ChunksUI * @property msdf_alphaglyph_vertex {string} * @property frame_border_fragment {string} * @property frame_background_pars_fragment {string} * @property frame_common_pars {string} * @property msdf_alphaglyph_pars_vertex {string} * @property frame_border_pars_fragment {string} * @property msdf_offset_vertex {string} * @property frame_border_pars_vertex {string} * @property msdf_alphaglyph_pars_fragment {string} * @property frame_border_vertex {string} * @property frame_background_fragment {string} * @property msdf_alphaglyph_fragment {string} */ const ShaderChunkUI = { msdfAlphaglyphParsVertexGlsl: msdf_alphaglyph_pars_vertex_glsl, msdfAlphaglyphVertexGlsl: msdf_alphaglyph_vertex_glsl, msdfOffsetglyphVertexGlsl: msdf_offsetglyph_vertex_glsl, msdfAlphaglyphParsFragmentGlsl: msdf_alphaglyph_pars_fragment_glsl, msdfAlphaglyphFragmentGlsl: msdf_alphaglyph_fragment_glsl, frameBorderParsVertexGlsl: frame_border_pars_vertex_glsl, frameBorderVertexGlsl: frame_border_vertex_glsl, frameCommonParsFragmentGlsl: frame_common_pars_fragment_glsl, frameBorderParsFragmentGlsl: frame_border_pars_fragment_glsl, frameBorderFragmentGlsl: frame_border_fragment_glsl, frameBackgroundParsFragmentGlsl: frame_background_pars_fragment_glsl, frameBackgroundFragmentGlsl: frame_background_fragment_glsl, }; /* eslint-enable camelcase */ ;// CONCATENATED MODULE: ./src/frame/renderers/ShaderLib/framematerial.glsl.js const framematerial_glsl_vertexShader = /* glsl */` // Would be automatic on three materials and from USE_UV #ifdef USE_MAP varying vec2 vUv; #endif ${frame_border_pars_vertex_glsl} #include void main() { #ifdef USE_MAP vUv = uv; #endif ${frame_border_vertex_glsl} vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * mvPosition; #include } ` const framematerial_glsl_fragmentShader = /* glsl */` // Basic uniform vec3 diffuse; uniform float opacity; #ifdef USE_ALPHATEST uniform float alphaTest; #endif ${frame_common_pars_fragment_glsl} ${frame_border_pars_fragment_glsl} #ifdef USE_MAP varying vec2 vUv; uniform sampler2D map; #endif ${frame_background_pars_fragment_glsl} #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); // map ${frame_background_fragment_glsl} ${frame_border_fragment_glsl} #ifdef USE_ALPHATEST if ( diffuseColor.a < alphaTest ) discard; #endif // output gl_FragColor = diffuseColor; #include } ` ;// CONCATENATED MODULE: ./src/frame/utils/FrameMaterialUtils.js //JSDoc related import /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class FrameMaterialUtils { /** * * @returns {Object<{m: string, t?: (function((Material|ShaderMaterial), string, *): void)}>} */ static get mediation() { return _mediationDefinitions; } /** * Alter a material options with required fontMaterial options and or default values * @param {Object.} materialOptions */ static ensureMaterialOptions( materialOptions ) { materialOptions.transparent = true; materialOptions.alphaTest = materialOptions.alphaTest || 0.02; } /** * As three-mesh-ui FontMaterial relies on webgl preprocessors, * lets force the material to have a proper defines object * @param {Material|ShaderMaterial} threeMaterial */ static ensureDefines( threeMaterial ) { if ( !threeMaterial.defines ) { threeMaterial.defines = {}; } } /* eslint-disable no-unused-vars */ /** * * @param {Material|ShaderMaterial} threeMaterial * @param {Object.} materialOptions */ static ensureUserData( threeMaterial, materialOptions ) { threeMaterial.userData.borderColor = { value: null }; threeMaterial.userData.borderRadius = { value: new external_THREE_namespaceObject.Vector4(0,0,0,0) }; // Store corners based on borderRadiuses threeMaterial.userData.cornerTL = { value : new external_THREE_namespaceObject.Vector2(0,1) }; threeMaterial.userData.cornerTR = { value : new external_THREE_namespaceObject.Vector2(1,1) }; threeMaterial.userData.cornerBR = { value : new external_THREE_namespaceObject.Vector2(1,0) }; threeMaterial.userData.cornerBL = { value : new external_THREE_namespaceObject.Vector2(0,0) }; threeMaterial.userData.borderWidth = { value: new external_THREE_namespaceObject.Vector4(0,0,0,0) }; threeMaterial.userData.borderOpacity = { value: null }; threeMaterial.userData.frameSize = { value: new external_THREE_namespaceObject.Vector3( 1, 1, 1 ) }; threeMaterial.userData.textureSize = { value: new external_THREE_namespaceObject.Vector2( 1, 1 ) }; } /* eslint-enable no-unused-vars */ /** * * @param {any} shader * @param {Material|ShaderMaterial} threeMaterial */ static bindUniformsWithUserData( shader, threeMaterial ) { shader.uniforms.borderColor = threeMaterial.userData.borderColor; // Border radiuses and corners shader.uniforms.borderRadius = threeMaterial.userData.borderRadius; shader.uniforms.cornerTL = threeMaterial.userData.cornerTL; shader.uniforms.cornerTR = threeMaterial.userData.cornerTR; shader.uniforms.cornerBR = threeMaterial.userData.cornerBR; shader.uniforms.cornerBL = threeMaterial.userData.cornerBL; shader.uniforms.borderWidth = threeMaterial.userData.borderWidth; shader.uniforms.borderOpacity = threeMaterial.userData.borderOpacity; shader.uniforms.frameSize = threeMaterial.userData.frameSize; shader.uniforms.textureSize = threeMaterial.userData.textureSize; } /** * * @param shader */ static injectShaderChunks( shader ) { FrameMaterialUtils.injectVertexShaderChunks( shader ); FrameMaterialUtils.injectFragmentShaderChunks( shader ); } /** * * @param shader */ static injectVertexShaderChunks( shader ) { shader.vertexShader = shader.vertexShader.replace( '#include ', '#include \n' + frame_border_pars_vertex_glsl ); // vertex chunks shader.vertexShader = shader.vertexShader.replace( '#include ', '#include \n' + frame_border_vertex_glsl ) } /** * * @param shader */ static injectFragmentShaderChunks( shader ) { shader.fragmentShader = shader.fragmentShader.replace( '#include ', '#include \n' + frame_background_pars_fragment_glsl ) shader.fragmentShader = shader.fragmentShader.replace( '#include ', '#include \n' + frame_border_pars_fragment_glsl ) shader.fragmentShader = shader.fragmentShader.replace( '#include ', '#include \n' + frame_common_pars_fragment_glsl ) // fragment chunks shader.fragmentShader = shader.fragmentShader.replace( '#include ', frame_background_fragment_glsl ) shader.fragmentShader = shader.fragmentShader.replace( '#include ', frame_border_fragment_glsl+'\n#include ' ) } } /** * * @param target * @param property * @param value * @private */ const _backgroundSizeTransformer = function( target, property, value ) { value = ['stretch','contain','cover'].indexOf(value); asPreprocessorValueTransformer(target, 'BACKGROUND_MAPPING', value); } // /** // * // * @type {Object.<{m:string, t?:(fontMaterial:Material|ShaderMaterial, materialProperty:string, value:any) => void}>} // */ // const _mediationDefinitions = { // alphaTest: { m: 'alphaTest', t: alphaTestTransformer }, // backgroundTexture: { m: 'map' }, // backgroundColor: { m: 'color' }, // backgroundOpacity: { m:'opacity' }, // backgroundSize: { m: 'u_backgroundMapping', t: _backgroundSizeTransformer }, // _borderWidthComponent: { m: 'borderWidth', t: _linkComponentOutput }, // borderColor: { m: 'borderColor', t: uniformOrUserDataTransformer }, // _borderRadiusComponent: { m: 'computedCorners', t: _linkCornersOutput }, // borderOpacity: { m: 'borderOpacity', t: uniformOrUserDataTransformer }, // size: { m: 'frameSize', t: uniformOrUserDataTransformer }, // tSize: { m: 'textureSize', t: uniformOrUserDataTransformer } // } /** * 7xx * @type {Object.<{m:string, t?:(fontMaterial:Material|ShaderMaterial, materialProperty:string, value:any) => void}>} */ const _mediationDefinitions = { clippingPlanes : {m: 'clippingPlanes'}, backgroundAlphaTest: { m: 'alphaTest', t: alphaTestTransformer }, backgroundSide: { m: 'side' }, // backgroundTexture: { m: 'map' }, backgroundImage: { m: 'map'}, backgroundColor: { m: 'color' }, backgroundOpacity: { m:'opacity' }, backgroundSize: { m: 'computedBackgroundSize', t: _backgroundSizeTransformer }, borderWidth: { m: 'borderWidth', t: uniformOrUserDataTransformer }, borderColor: { m: 'borderColor', t: uniformOrUserDataTransformer }, cornerTL : { m: 'cornerTL', t: uniformOrUserDataTransformer }, cornerTR : { m: 'cornerTR', t: uniformOrUserDataTransformer }, cornerBR : { m: 'cornerBR', t: uniformOrUserDataTransformer }, cornerBL : { m: 'cornerBL', t: uniformOrUserDataTransformer }, borderOpacity: { m: 'borderOpacity', t: uniformOrUserDataTransformer }, size: { m: 'frameSize', t: uniformOrUserDataTransformer }, tSize: { m: 'textureSize', t: uniformOrUserDataTransformer } } ;// CONCATENATED MODULE: ./src/frame/materials/FrameMaterial.js class FrameMaterial extends external_THREE_namespaceObject.ShaderMaterial { /** * This static method is mandatory for extending ThreeMeshUI.FrameMaterial * It will provide a transfer description for properties from ThreeMeshUI.Text to THREE.Material * @see {FrameMaterialUtils.mediation} * @returns {Object.<{m:string, t?:(frameMaterial:Material|ShaderMaterial, materialProperty:string, value:any) => void}>} */ static get mediation() { return FrameMaterialUtils.mediation; } constructor() { super ( { uniforms: { alphaTest: { value: 0.02 }, map: { value: null }, diffuse: { value: new external_THREE_namespaceObject.Color(0xffffff) }, opacity: { value: 1.0 }, borderColor: { value: new external_THREE_namespaceObject.Color(0x000000) }, borderOpacity: { value: 0 }, borderRadius: { value: new external_THREE_namespaceObject.Vector4(0,0,0,0) }, // Corners for customized radius not all starting on center [0.5,0.5]; // Corners will be generated from borderRadiuses cornerTL: { value : new external_THREE_namespaceObject.Vector2(0,1) }, cornerTR: { value : new external_THREE_namespaceObject.Vector2(1,1) }, cornerBR: { value : new external_THREE_namespaceObject.Vector2(1,0) }, cornerBL: { value : new external_THREE_namespaceObject.Vector2(0,0) }, borderWidth: { value: new external_THREE_namespaceObject.Vector4(0,0,0,0) }, frameSize: { value: new external_THREE_namespaceObject.Vector3( 1, 1, 1 ) }, textureSize: { value: new external_THREE_namespaceObject.Vector2( 1, 1 ) } }, side: external_THREE_namespaceObject.FrontSide, transparent: true, clipping: true, vertexShader: framematerial_glsl_vertexShader, fragmentShader: framematerial_glsl_fragmentShader, extensions: { derivatives: true } } ); // webgl preprocessor AlphaTest set by default this.defines[ 'USE_ALPHATEST' ] = ''; this.needsUpdate = true; } set map( value ) { this.uniforms.map.value = value; if( !value ) { if( this.defines['USE_UV'] !== undefined ) { delete this.defines['USE_UV']; this.needsUpdate = true; } } else if( this.defines['USE_UV'] === undefined ) { this.defines['USE_UV'] = ''; this.needsUpdate = true; } this.needsUpdate = true; } get map(){ return this.uniforms.map.value; } /** * * @returns {number} */ get alphaTest() { return this.uniforms.alphaTest.value; } /** * * @param {number} v */ set alphaTest( v ) { this.uniforms.alphaTest.value = v; } /** * * @param {number} v */ set opacity( v ) { if( this.uniforms ) this.uniforms.opacity.value = v; } /** * The color will be the diffuse uniform * @returns {number} */ get opacity() { return this.uniforms.opacity.value; } /** * The color will be the diffuse uniform * @returns {Color} */ get color() { return this.uniforms.diffuse.value; } /** * * @param {Color} v */ set color( v ) { this.uniforms.diffuse.value = v; } } ;// CONCATENATED MODULE: ./src/core/properties/hierarchy/ChildrenBox.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class ChildrenBox extends BaseProperty { constructor() { super( 'children', null, false ); /** * * @type {Array.} * @private */ this._uis = []; /** * * @type {Array.} * @internal */ this._boxes = []; } /* eslint-disable no-unused-vars */ /** * Update requested when : * - New child has been added * - Existing child has been removed * * @param element * @param out */ update( element, out ) { /* eslint-enable no-unused-vars */ this._compute( element ); element._layouter._needsUpdate = true; element._renderOrder._needsUpdate = true; } /** * Process when : * - Existing child visibility changed * * @param element */ process( element ) { this._compute( element ); element._flexDirection._needsProcess = true; element._layouter._needsProcess = true; element._overflow._needsRender = true; } _compute( element ) { // Stores all children that are box this._uis = element.children.filter( child => child.visible && child.isUI ); this._boxes = this._uis.filter( child => child.isBox ).sort( this._sortOrder ); // @TODO: check if it has changes boxes values? with array join to 'fingerprint'? // computation to remove computation? Does it worth it? When would it worth it? // // Changed order property of children but doesn't impact the output of boxes => Order have change, okay to have more computation // // Removed the Added the same element, at the same position => Rare case // Conclusion : Not worth it at the time of writing } /** * */ dispose() { this._uis = null; this._boxes = null; } /** * * Sort children according to their .style.order property or fallback on children index * * @param {HTMLElementVR} a * @param {HTMLElementVR} b * @return {number} * @private */ _sortOrder = ( a, b ) => { if( a._order._value < b._order._value ) return -1; if( a._order._value > b._order._value ) return 1; // if both children have the same order value, use their children index to order them if( this._uis.indexOf(a) < this._uis.indexOf(b) ) { return -1; } return 1; } } ;// CONCATENATED MODULE: ./src/core/properties/BoundsBox.js class BoundsBox extends BaseProperty { constructor() { super( 'bounds', null, false ); /** * * @type {Vector3} * @internal */ this._size = new external_THREE_namespaceObject.Vector3( 1, 1, 1 ); /** * * @type {number} * @internal */ this._offsetWidth = 0; /** * * @type {number} * @internal */ this._offsetHeight = 0; /** * * @type {number} * @internal */ this._innerWidth = 0; /** * * @type {number} * @internal */ this._innerHeight = 0; /** * * @type {number} * @internal */ this._centerX = 0.5; /** * * @type {number} * @internal */ this._centerY = 0.5; this._needsProcess = true; } /** * Set the value of the width 100% * @param element * @param value */ setReferenceWidth( element, value ) { const width = element._width; const padding = element._padding._value; const borderWidth = element._borderWidth._value; const margin = element._margin._value; const factor = width._auto ? 1 : width._value; // const newOffsetWidth = (value * factor) - (margin.y + margin.w); const newOffsetWidth = (value * factor) - (margin.y + margin.w); if ( numberEquals( newOffsetWidth, this._offsetWidth ) ) return; this._offsetWidth = newOffsetWidth; this._innerWidth = this._offsetWidth - ( padding.y + padding.w + borderWidth.y + borderWidth.w ); this._centerX = _computeCenterX( element ); this._propagateWidth( element ); this._triggerCascadingDependencies( element ); } /** * Set the value of the height 100% * @param element * @param value */ setReferenceHeight( element, value ) { const height = element._height; const padding = element._padding._value; const borderWidth = element._borderWidth._value; const margin = element._margin._value; const factor = height._auto ? 1 : height._value; const newOffsetHeight = (value * factor) - ( margin.x + margin.z ); if ( numberEquals( newOffsetHeight, this._offsetHeight ) ) return; this._offsetHeight = newOffsetHeight; this._innerHeight = this._offsetHeight - ( padding.x + padding.z + borderWidth.x + borderWidth.z ); this._centerY = _computeCenterY( element ); this._propagateHeight( element ); this._triggerCascadingDependencies( element ); } setChildrenWidth( element, value ) { const padding = element._padding._value; const border = element._borderWidth._value; this._innerWidth = value; this._offsetWidth = this._innerWidth + ( padding.y + padding.w + border.y + border.w ) this._centerX = _computeCenterX( element ); this._propagateWidth( element ); this._triggerCascadingDependencies( element ); } setChildrenHeight( element, value ) { const padding = element._padding._value; const border = element._borderWidth._value; this._innerHeight = value; this._offsetHeight = this._innerHeight + ( padding.x + padding.z + border.x + border.z ) this._centerY = _computeCenterY( element ); this._propagateHeight( element ); this._triggerCascadingDependencies( element ); } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ const padding = element._padding._value; const border = element._borderWidth._value; // only compute new width if explicitely defined const width = element._width; if( !width._auto && !width._relative ) { if ( element._boxSizing._value === 'content-box' ) { this._innerWidth = width._value; this._offsetWidth = this._innerWidth + padding.y + padding.w + border.y + border.w; } else { this._offsetWidth = width._value; this._innerWidth = this._offsetWidth - ( padding.y + padding.w + border.y + border.w ); } this._centerX = _computeCenterX( element ); this._needsProcess = true; // tells children width has changed this._propagateWidth( element ); this._triggerCascadingDependencies( element ); } const height = element._height; if( !height._auto && !height._relative ) { if ( element._boxSizing._value === 'content-box' ) { this._innerHeight = height._value; this._offsetHeight = this._innerHeight + padding.x + padding.z + border.x + border.z; } else { this._offsetHeight = height._value; this._innerHeight = this._offsetHeight - ( padding.x + padding.z + border.x + border.z ); } this._centerY = _computeCenterY( element ); this._needsProcess = true; // tells children height has changed this._propagateHeight( element ); this._triggerCascadingDependencies( element ); } } /* eslint-disable no-unused-vars */ render( element ) { /* eslint-enable no-unused-vars */ this._size.x = this._offsetWidth; this._size.y = this._offsetHeight; if( element._backgroundMesh ){ element._backgroundMesh.updateScale(); } element._renderer._needsRender = true; } /** * * @param {Object.} out */ output( out ) { out[ 'size' ] = this._size; } /* eslint-disable no-unused-vars */ /** * @override */ process( element ) { /* eslint-enable no-unused-vars */ // this._triggerCascadingDependencies( element ) //console.log( 'process bounds box', element.name ); // update primitives or unbinded values // require cascading processes element._overflow._needsRender = true; } /** * * @param element * @internal */ _computeChildrenSideWidth( element ) { return _computeChildrenSideWidth( element ); } /** * * @param element * @internal */ _computeChildrenSideHeight( element ) { return _computeChildrenSideHeight( element ); } _propagateWidth( element ) { for ( let i = 0; i < element._children._boxes.length; i++ ) { const box = element._children._boxes[ i ]; const width = box._width; if( width._relative ) box._bounds.setReferenceWidth( box, this._innerWidth ); } } _propagateHeight( element ) { for ( let i = 0; i < element._children._boxes.length; i++ ) { const box = element._children._boxes[ i ]; const height = box._height; if( height._relative ) box._bounds.setReferenceHeight( box, this._innerHeight ); } } _triggerCascadingDependencies( element ) { // also change parent when require if ( element._parent._value ) { element._parent._value._autoSize._needsProcess = true; } element._flexDirection._needsProcess = true; element._fontSize._needsProcess = true; element._layouter._needsProcess = true; this._needsRender = true; element._borderWidth._needsRender = true; element._borderRadius._needsRender = true; element._overflow._needsRender = true; } } /*********************************************************************************************************************** * INTERNAL FUNCTIONS **********************************************************************************************************************/ /** * Retrieve the center X according to box sized dimensions * @param {MeshUIBaseElement} element * @return {number} */ function _computeCenterX( element ) { const padding = element._padding._value; const borderWidth = element._borderWidth._value; const leftSide = padding.w + borderWidth.w; const rightSide = padding.y + borderWidth.y; return ( leftSide - rightSide ) / 2; } /** * Retrieve the center Y according to box sized dimensions * @param {MeshUIBaseElement} element * @return {number} */ function _computeCenterY( element ) { const padding = element._padding._value; const borderWidth = element._borderWidth._value; const topSide = padding.x + borderWidth.x; const bottomSide = padding.z + borderWidth.z; return ( bottomSide - topSide ) / 2; } /** * Return the sum of all this component's children width * @param {MeshUIBaseElement} element * @return {number} */ function _computeChildrenSideWidth( element ) { return element._children._boxes.reduce( ( accu, child ) => { // if ( child._bounds._needsProcess ) child._bounds.process( child ); const margin = child._margin._value; const CHILD_SIZE = child._bounds._offsetWidth + margin.y + margin.w; return accu + CHILD_SIZE; }, 0 ); } /** * Return the sum of all this component's children width * @param {MeshUIBaseElement} element * @return {number} */ function _computeChildrenSideHeight( element ) { return element._children._boxes.reduce( ( accu, child ) => { // if ( child._bounds._needsProcess ) child._bounds.process( child ); const margin = child._margin._value; const CHILD_SIZE = child._bounds._offsetHeight + margin.x + margin.z; return accu + CHILD_SIZE; }, 0 ); } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/flex/AlignItemsPropertyBox.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class AlignItemsPropertyBox extends AlignItemsProperty { constructor( ) { super(); // configure this property this._allowsInherit = false; this._needsUpdate = true; // strategies /** * * @type {(element:MeshUIBaseElement, (child:MeshUIBaseElement, parentOffset:number )=> number ) => void } * @private */ this._process = this.emptyStrategyLogic; /** * * @type {(child:MeshUIBaseElement, parentOffset:number )=> number} * @private */ this._childAlign = this.emptyStrategyLogic; } /** * * @param {MeshUIBaseElement} element */ computeOutputValue( element ) { // Stretch : Current or previous requires a bounds update of children // if( this._value === 'stretch' || this._input === 'stretch' ) { // // for ( let i = 0; i < element._children._boxes.length; i++ ) { // element._children._boxes[ i ]._bounds._needsProcess = true; // } // // } this._value = this._inheritedInput; switch( element._flexDirection._value ) { case 'row': case 'row-reverse': this._process = _processRow; switch ( this._value ) { case 'start': this._childAlign = _alignChildRowStart; break; case 'end': this._childAlign = _alignChildRowEnd; break; default: this._childAlign = _alignChild; } break; case 'column': case 'column-reverse': this._process = _processColumn; switch ( this._value ) { case 'start': this._childAlign = _alignChildColumnStart; break; case 'end': this._childAlign = _alignChildColumnEnd; break; default: this._childAlign = _alignChild; } break; } this._needsProcess = true; // @TODO: Store children here element._autoSize._needsProcess = true; element._flexDirection._needsProcess = true; //not mandatory element._justifyContent._needsProcess = true; this._needsProcess = true; element._fontSize._needsProcess = true; element._layouter._needsProcess = true; } /** * * @param element */ process( element ) { // return; // if( !element._children._boxes.length ) return; this._process( element, this._childAlign ); // @TODO : Could be strategized let snap = 'center'; let snapXon = 'center'; let snapYon = 'center'; const padding = element._padding._value; const border = element._borderWidth._value; if( element._flexDirection._value.indexOf('column') !== -1 ) { if( this._value === 'start' ) { snap = snapXon = 'left'; }else if( this._value === 'end' ){ snap = snapXon ='right'; }else { snap = 'centerX'; } } else { /* eslint-disable no-lonely-if */ if( this._value === 'start' ) { snap = snapYon = 'top'; }else if( this._value === 'end' ){ snap = snapYon ='bottom'; }else{ snap = 'centerY'; } /* eslint-enable no-lonely-if */ } // apply 4 directional padding and borders let y = -(padding.x - padding.z) / 2 - (border.x - border.z) / 2; let x = -(padding.y - padding.w) / 2 - ( border.y - border.w ) / 2; if( snapXon === 'left' ) { x = (padding.w - padding.y) / 2 + (border.w - border.y) / 2; } else if( snapXon === 'right' ) { x = - ( padding.y - padding.w ) / 2 - ( border.y - border.w ) / 2; } if( snapYon === 'top' ) { y = - (padding.x - padding.z) / 2 - (border.x - border.z) / 2; } else if( snapYon === 'bottom' ) { y = (padding.z - padding.x) / 2 + (border.z - border.x) / 2; } element._children._boxes.forEach( ( child ) => { let marginX = 0; let marginY = 0; // let marginY = ( -child._margin._value.x + child._margin._value.z ) /2; // let marginY = ( -child._margin._value.x + child._margin._value.z ) /2; if( snap === 'top' ) { marginY = - child._margin._value.x; } else if( snap === 'bottom' ) { marginY = child._margin._value.z; } else if( snap === 'left' ) { marginX = child._margin._value.w; } else if( snap === 'right' ) { marginX = - child._margin._value.y; } else if( snap === 'centerX' ) { marginX = ( child._margin._value.w - child._margin._value.y ) /2; } else if( snap === 'centerY' ) { marginY = ( - child._margin._value.x + child._margin._value.z ) /2; } element._layouter._childrenPos[ child.id ].x += x + marginX; element._layouter._childrenPos[ child.id ].y += y + marginY; } ); } } /*********************************************************************************************************************** * STRATEGIES **********************************************************************************************************************/ function _alignChild() { return 0; } /** * * @param child * @param parentOffset * @return {number} * @private */ function _alignChildRowEnd( child, parentOffset ) { return - parentOffset + ( child._bounds._offsetHeight / 2 ); } function _alignChildRowStart( child, parentOffset ) { return parentOffset - ( child._bounds._offsetHeight / 2 ); } function _alignChildColumnEnd( child, parentOffset ) { return parentOffset - ( child._bounds._offsetWidth / 2 ); } function _alignChildColumnStart( child, parentOffset ) { return - parentOffset + ( child._bounds._offsetWidth / 2 ); } function _processColumn( element, childAligner ) { const AXIS_TARGET = element._bounds._innerWidth / 2; element._children._boxes.forEach( ( child ) => { element._layouter._childrenPos[ child.id ].x = childAligner( child, AXIS_TARGET ); } ); } function _processRow( element, childAligner ) { const AXIS_TARGET = element._bounds._innerHeight / 2; element._children._boxes.forEach( ( child ) => { element._layouter._childrenPos[ child.id ].y = childAligner( child, AXIS_TARGET ); } ); } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/flex/FlexDirectionPropertyBox.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class FlexDirectionPropertyBox extends FlexDirectionProperty { constructor( ) { super(); // Configure this._allowsInherit = false; this._needsUpdate = true; /** * * @type {number} * @internal */ this._offset = 0; /** * * @type {number} * @internal */ this._reverse = 1; /** * * @param { (element:MeshUIBaseElement) => void} element * @private */ this._process = this.emptyStrategyLogic; } computeOutputValue( element ) { this._value = this._inheritedInput; switch ( this._value ) { case "row": this._process = FlexDirectionPropertyBox_processRow; // this._offset = - element._bounds._innerWidth / 2; break; case "row-reverse": this._process = _processRowReverse; // this._offset = element._bounds._innerWidth / 2; break; case "column": this._process = FlexDirectionPropertyBox_processColumn; // this._offset = element._bounds._innerHeight / 2; break; case "column-reverse": this._process = _processColumnReverse; // this._offset = - element._bounds._innerHeight / 2; break; } // also update dependencies if( !element._justifyContent._needsUpdate ) element._justifyContent.computeOutputValue( element ); if( !element._alignItems._needsUpdate ) element._alignItems.computeOutputValue( element ); this._needsProcess = true; } process( element ) { // this will be defined from strategy //console.log( element.name, 'flexDirection process'); switch ( this._value ) { case "row": this._offset = - element._bounds._innerWidth / 2; break; case "row-reverse": this._offset = element._bounds._innerWidth / 2; break; case "column": this._offset = element._bounds._innerHeight / 2; break; case "column-reverse": this._offset = - element._bounds._innerHeight / 2; break; } this._reverse = -Math.sign( this._offset ); if( this._reverse === 0 ) { this._reverse = 1; } this._process( element ); element._justifyContent._needsProcess = true; element._layouter._needsProcess = true; } } /*********************************************************************************************************************** * STRATEGIES **********************************************************************************************************************/ function FlexDirectionPropertyBox_processRow( element ) { // end to end children let accu = element._flexDirection._offset; const REVERSE = element._flexDirection._reverse; const boxes = element._children._boxes; // Refactor reduce into fori in order to get rid of this keyword for ( let i = 0; i < boxes.length; i++ ) { /** * * @type {MeshUIBaseElement} */ const child = boxes[ i ]; const CHILD_ID = child.id; // @TODO : use getter instead of compute function if possible const CHILD_SIZE = child._bounds._offsetWidth; // increase with the left margin before placing the child accu += child._margin._value.w * REVERSE; const position = element._layouter._childrenPos[ CHILD_ID ]; position.x = accu + ( CHILD_SIZE / 2 ) * REVERSE; position.y = 0; // increase the next child with this child right margin accu += REVERSE * ( CHILD_SIZE + child._margin._value.y ) ; } } function _processRowReverse( element ) { // end to end children let accu = element._flexDirection._offset; const REVERSE = element._flexDirection._reverse; const boxes = element._children._boxes; // Refactor reduce into fori in order to get rid of this keyword for ( let i = 0; i < boxes.length; i++ ) { /** * * @type {MeshUIBaseElement} */ const child = boxes[ i ]; const CHILD_ID = child.id; // @TODO : use getter instead of compute function if possible const CHILD_SIZE = child._bounds._offsetWidth; // decrease with the right margin before placing the child accu += child._margin._value.y * REVERSE; const position = element._layouter._childrenPos[ CHILD_ID ]; position.x = accu + ( CHILD_SIZE / 2 ) * REVERSE; position.y = 0; // decrease the next child with this child left margin accu += (CHILD_SIZE + child._margin._value.w) * REVERSE ; } } function FlexDirectionPropertyBox_processColumn( element ) { // end to end children let accu = element._flexDirection._offset; const REVERSE = element._flexDirection._reverse; const boxes = element._children._boxes; // Refactor reduce into fori in order to get rid of this keyword for ( let i = 0; i < boxes.length; i++ ) { const child = boxes[ i ]; const CHILD_ID = child.id; // @TODO : use getter instead of compute function if possible const CHILD_SIZE = child._bounds._offsetHeight; // increase with the top margin before placing the child accu += child._margin._value.x * REVERSE; const position = element._layouter._childrenPos[ CHILD_ID ]; position.x = 0; position.y = accu + ( CHILD_SIZE / 2 ) * REVERSE; // increase the next child with this child bottom margin accu += (CHILD_SIZE + child._margin._value.z) * REVERSE ; } } function _processColumnReverse( element ) { // end to end children let accu = element._flexDirection._offset; const REVERSE = element._flexDirection._reverse; const boxes = element._children._boxes; // Refactor reduce into fori in order to get rid of this keyword for ( let i = 0; i < boxes.length; i++ ) { const child = boxes[ i ]; const CHILD_ID = child.id; // @TODO : use getter instead of compute function if possible const CHILD_SIZE = child._bounds._offsetHeight; // decrease with the bottom margin before placing the child accu += child._margin._value.z * REVERSE; const position = element._layouter._childrenPos[ CHILD_ID ]; position.x = 0; position.y = accu + ( CHILD_SIZE / 2 ) * REVERSE; // decrease the next child with this child top margin accu += ( CHILD_SIZE + child._margin._value.x ) * REVERSE ; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/flex/JustifyContentPropertyBox.js class JustifyContentPropertyBox extends JustifyContentProperty { constructor( defaultValue ) { super( 'justifyContent', defaultValue, true ); // configure this._allowsInherit = false; this._needsUpdate = true; // strategies /** * * @type {(axisOffset:number) => number} * @private */ this._computeOffset = this.emptyStrategyLogic; /** * * @type {(element:MeshUIBaseElement, availableSpace:number, reverse:number) => Array. } * @private */ this._computeMargin = this.emptyStrategyLogic; /** * * @type {(element:MeshUIBaseElement) => void} * @private */ this._process = this.emptyStrategyLogic; } computeOutputValue( element ) { this._value = this._inheritedInput; //console.log( element._flexDirection._value ); switch ( element._flexDirection._value ) { case 'column-reverse': case 'column': this._process = _column.bind( this ); break; case 'row-reverse': case 'row': this._process = _row.bind( this ); break; } switch ( this._value ) { case 'end': this._computeOffset = _justificationOffsetEnd; this._computeMargin = _justificationMargin; break; case 'center': this._computeOffset = _justificationOffsetCenter; this._computeMargin = _justificationMargin; break; case 'start': this._computeOffset = _justificationOffset; this._computeMargin = _justificationMargin; break; case 'space-between': this._computeOffset = _justificationOffset; this._computeMargin = _justificationMarginSpaceBetween; break; case 'space-around': this._computeOffset = _justificationOffset; this._computeMargin = _justificationMarginSpaceAround; break; case 'space-evenly': this._computeOffset = _justificationOffset; this._computeMargin = _justificationMarginSpaceEvenly; break; } // @TODO : If flexDirection was keeping its children position, // it won't be necessary to compute the same result again // but it will increase the memory footprint element._flexDirection._needsProcess = true; } process( element ) { this._process( element ); element._alignItems._needsProcess = true; // not mandatory : Layout could sum each } } function _row( element ) { const startPos = element._flexDirection._offset; const { usedDirectionSpace, remainingSpace } = _rowRemainingSpace( element ); // Items Offset const axisOffset = ( startPos * 2 ) - ( usedDirectionSpace * Math.sign( startPos ) ); const justificationOffset = this._computeOffset( axisOffset ); // Items margin // const justificationMargins = _getJustificationMargin( boxComponent.childrenBoxes, remainingSpace, JUSTIFICATION, REVERSE ); const justificationMargins = this._computeMargin( element, remainingSpace, element._flexDirection._reverse ); // Apply element._children._boxes.forEach( ( child, childIndex ) => { element._layouter._childrenPos[ child.id ].x -= justificationOffset - justificationMargins[ childIndex ]; } ); } function _column( element ) { const startPos = element._flexDirection._offset; const { usedDirectionSpace, remainingSpace } = _columnRemainingSpace( element ); // Items Offset const axisOffset = ( startPos * 2 ) - ( usedDirectionSpace * Math.sign( startPos ) ); const justificationOffset = this._computeOffset( axisOffset ); // Items margin const justificationMargins = this._computeMargin( element, remainingSpace, element._flexDirection._reverse ); // Apply element._children._boxes.forEach( ( child, childIndex ) => { element._layouter._childrenPos[ child.id ].y -= justificationOffset - justificationMargins[ childIndex ]; } ); } /*********************************************************************************************************************** * STRATEGIES **********************************************************************************************************************/ /** * * @param {MeshUIBaseElement} element * @return {{usedDirectionSpace: *, remainingSpace: number}} * @private */ function _rowRemainingSpace( element ) { const usedDirectionSpace = element._bounds._computeChildrenSideWidth( element ); return { usedDirectionSpace, remainingSpace: element._bounds._innerWidth - usedDirectionSpace }; } function _columnRemainingSpace( element ) { const usedDirectionSpace = element._bounds._computeChildrenSideHeight( element ); return { usedDirectionSpace, remainingSpace: element._bounds._innerHeight - usedDirectionSpace }; } /* eslint-disable no-unused-vars */ function _justificationOffset( axisOffset ) { /* eslint-enable no-unused-vars */ return 0; } function _justificationOffsetEnd( axisOffset ) { return axisOffset; } function _justificationOffsetCenter( axisOffset ) { return axisOffset / 2; } /* eslint-disable no-unused-vars */ function _justificationMargin( element, availableSpace = 0, reverse = 1 ) { /* eslint-enable no-unused-vars */ return Array( element._children._boxes.length ).fill( 0 ); } function _justificationMarginSpaceBetween( element, availableSpace = 0, reverse = 1 ) { const boxes = element._children._boxes; const length = boxes.length; const justificationMargins = Array( length ).fill( 0 ); if ( availableSpace > 0 ) { // only one children would act as start if ( length > 1 ) { const margin = availableSpace / ( length - 1 ) * reverse; // set this margin for any children // except for first child justificationMargins[ 0 ] = 0; for ( let i = 1; i < length; i++ ) { justificationMargins[ i ] = margin * i; } } } return justificationMargins; } function _justificationMarginSpaceEvenly( element, availableSpace = 0, reverse = 1 ) { const boxes = element._children._boxes; const length = boxes.length; const justificationMargins = Array( length ).fill( 0 ); if ( availableSpace > 0 ) { const margin = availableSpace / ( length + 1 ) * reverse; // set this margin for any children for ( let i = 0; i < length; i++ ) { justificationMargins[ i ] = margin * ( i + 1 ); } } return justificationMargins; } function _justificationMarginSpaceAround( element, availableSpace = 0, reverse = 1 ) { const boxes = element._children._boxes; const length = boxes.length; const justificationMargins = Array( length ).fill( 0 ); if ( availableSpace > 0 ) { const margin = availableSpace / ( length ) * reverse; const start = margin / 2; justificationMargins[ 0 ] = start; // set this margin for any children for ( let i = 1; i < length; i++ ) { justificationMargins[ i ] = start + margin * i; } } return justificationMargins; } ;// CONCATENATED MODULE: ./src/frame/Frame.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ let _hiddenMaterial; /** * Returns a basic plane mesh. */ class Frame extends external_THREE_namespaceObject.Mesh { /** * * @param {MeshUIBaseElement} element */ constructor( element) { const slice = element.slice; const slices = {}; let w =1, h=1; if( slice ) { const segments = 1; w = slice.width ? slice.width : 1; h = slice.height ? slice.height : 1; if ( slice.top ) { if ( slice.left ) { const topLeftGeometry = new external_THREE_namespaceObject.PlaneGeometry( slice.left*w, slice.top*h, segments, segments ); _sliceUV( topLeftGeometry, 0, slice.left, 1 - slice.top, 1 ); topLeftGeometry.translate( slice.left * w / 2, -slice.top*h / 2, 0 ); slices.topLeft = topLeftGeometry; } const topGeometry = new external_THREE_namespaceObject.PlaneGeometry( 1, slice.top*h, segments, segments ); _sliceUV( topGeometry, slice.left, 1 - slice.right, 1 - slice.top, 1 ); topGeometry.translate( 0, -slice.top*h / 2, 0 ); slices.top = topGeometry; if ( slice.right ) { const topRightGeometry = new external_THREE_namespaceObject.PlaneGeometry( slice.right*w, slice.top*h, segments, segments ); _sliceUV( topRightGeometry, 1 - slice.right, 1, 1 - slice.top, 1 ); topRightGeometry.translate( -slice.right*w / 2, -slice.top*h / 2, 0 ); slices.topRight = topRightGeometry; } } if ( slice.left ) { const leftGeometry = new external_THREE_namespaceObject.PlaneGeometry( slice.left*w, 1, segments, segments ) _sliceUV( leftGeometry, 0, slice.left, slice.bottom, 1 - slice.top ); leftGeometry.translate( slice.left * w / 2, 0, 0 ); slices.left = leftGeometry; } const center = new external_THREE_namespaceObject.PlaneGeometry( 1, 1, segments, segments ); _sliceUV( center, slice.left, 1 - slice.right, slice.bottom, 1 - slice.top ); // center.translate( 0,0,-0.1) slices.middle = center; if ( slice.right ) { const rightGeometry = new external_THREE_namespaceObject.PlaneGeometry( slice.right*w, 1, segments, segments ); _sliceUV( rightGeometry, 1 - slice.right, 1, slice.bottom, 1 - slice.top ); rightGeometry.translate( -slice.right * w/ 2, 0, 0 ); slices.right = rightGeometry; } if ( slice.bottom ) { if ( slice.left ) { const bottomLeftGeometry = new external_THREE_namespaceObject.PlaneGeometry( slice.left * w, slice.bottom * h, segments, segments ); _sliceUV( bottomLeftGeometry, 0, slice.left, 0, slice.bottom ); bottomLeftGeometry.translate( slice.left * w / 2, slice.bottom * h / 2, 0 ) slices.bottomLeft = bottomLeftGeometry; } const bottomGeometry = new external_THREE_namespaceObject.PlaneGeometry( 1, slice.bottom * h, segments, segments ); _sliceUV( bottomGeometry, slice.left, 1 - slice.right, 0, slice.bottom ); bottomGeometry.translate( 0, slice.bottom * h / 2, 0 ) slices.bottom = bottomGeometry; if ( slice.right ) { const bottomRightGeometry = new external_THREE_namespaceObject.PlaneGeometry( slice.right * w, slice.bottom * h, segments, segments ); _sliceUV( bottomRightGeometry, 1 - slice.right, 1, 0, slice.bottom ); bottomRightGeometry.translate( -slice.right * w / 2, slice.bottom * h / 2, 0 ) slices.bottomRight = bottomRightGeometry; } } } let material = element.backgroundMaterial; if( slice ){ if( !_hiddenMaterial ) _hiddenMaterial = new external_THREE_namespaceObject.MeshBasicMaterial({alphaTest:1.1}); material = _hiddenMaterial; } const geometry = new external_THREE_namespaceObject.PlaneGeometry( 1, 1, element._segments.value, element._segments.value ); // Add additional uv for borders computations by copying initial uv const uvB = new external_THREE_namespaceObject.BufferAttribute( new Float32Array( geometry.getAttribute('uv').array ), 2); geometry.setAttribute('uvB', uvB ).name = 'uvB'; super( geometry, material ); this.name = 'UIBackgroundBox'; if( slice ) { this.slice = slice; this.sliceSize = new external_THREE_namespaceObject.Vector3( 1 - ( slice.left + slice.right ), 1 - ( slice.bottom + slice.top ), 1 ) this.sliceScale = new external_THREE_namespaceObject.Vector3(w,h,1); // build slice meshes for ( const sliceSide in slices ) { const slice = new external_THREE_namespaceObject.Mesh( slices[ sliceSide ], element.backgroundMaterial ); this.add( slice ); slices[ sliceSide ] = slice; } this.updateScale = this.updateScaleSlice; this.slices = slices; } } updateScale(){} updateScaleSlice(){ const s = new external_THREE_namespaceObject.Vector3(1,1,1); if( this.scale.x < (this.slice.left+this.slice.right)* this.sliceScale.x ){ s.x = this.scale.x / ((this.slice.left+this.slice.right) * this.sliceScale.x); } if( this.scale.y < (this.slice.bottom+this.slice.top)* this.sliceScale.y ){ s.y = this.scale.y / ((this.slice.bottom+this.slice.top) * this.sliceScale.y); } for ( const sliceSide in this.slices ) { const slice = this.slices[sliceSide]; // Invert size to have constant slice.scale.set( 1/this.scale.x, 1/this.scale.y, this.scale.y ) const offset = _slicePositions[sliceSide]; for ( const offsetAxis in offset ) { slice.position[offsetAxis] = this.scale[offsetAxis] * offset[offsetAxis] * slice.scale[offsetAxis]; } const scale = _sliceScales[sliceSide]; if( scale ) { // offset if( scale.x ){ slice.position.x = this.sliceScale.x * 0.5 * (this.slice.left - this.slice.right) * (1/this.scale.x); } if( scale.y ){ slice.position.y = this.sliceScale.y *0.5 * (this.slice.bottom - this.slice.top) * (1/this.scale.y); } for ( const scaleAxis in scale ) { const natural = this.scale[scaleAxis]-((1-this.sliceSize[scaleAxis])*this.sliceScale[scaleAxis]) slice.scale[ scaleAxis ] = Math.max(0,natural * (1/this.scale[scaleAxis]) ) ; } } } if( s.x !== 1){ this.slices.left.scale.x *= s.x; this.slices.topLeft.scale.x *= s.x; this.slices.bottomLeft.scale.x *= s.x; this.slices.right.scale.x *= s.x; this.slices.topRight.scale.x *= s.x; this.slices.bottomRight.scale.x *= s.x; } if( s.y !== 1){ this.slices.top.scale.y *= s.y; this.slices.topLeft.scale.y *= s.y; this.slices.topRight.scale.y *= s.y; this.slices.bottom.scale.y *= s.y; this.slices.bottomLeft.scale.y *= s.y; this.slices.bottomRight.scale.y *= s.y; } } } const _slicePositions = { topLeft: {x:-0.5,y:0.5}, top: {y:0.5}, topRight: {x:0.5,y:0.5}, left: {x:-0.5}, right: {x:0.5}, bottomLeft: {x:-0.5,y:-0.5}, bottom: {y:-0.5}, bottomRight : {x:0.5,y:-0.5} } const _sliceScales = { top: {x:1}, left: {y:1}, right: {y:1}, bottom: {x:1}, middle:{x:1,y:1} } function _sliceUV( geometry, uMin, uMax, vMin, vMax ){ const uLength = uMax-uMin; const vLength = vMax-vMin; const uvAttribute = geometry.attributes.uv; for ( let i = 0; i < uvAttribute.count; i ++ ) { const u = uvAttribute.getX( i ); const v = uvAttribute.getY( i ); uvAttribute.setXY( i, uMin + u*uLength, vMin + v*vLength ); } // Add additional uv for borders computations by copying initial uv const uvB = new external_THREE_namespaceObject.BufferAttribute( new Float32Array( geometry.getAttribute('uv').array ), 2); geometry.setAttribute('uvB', uvB ).name = 'uvB'; } ;// CONCATENATED MODULE: ./src/core/properties/rendering/RendererPropertyBox.js class RendererPropertyBox extends BaseProperty{ constructor() { super( 'renderer' ); } render( element ) { if( !element._backgroundMesh ) { element.setBackgroundMesh( new Frame(element) ); } element.performAfterUpdate(); } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/PositionPropertyBox.js class PositionPropertyBox extends PositionProperty { constructor( ) { super( 'position'); } update( element, out ) { super.update( element, out ); this._needsProcess = true; } } ;// CONCATENATED MODULE: ./src/core/properties/AutoSizePropertyBox.js /** * Autosize are only trigger when natural size changed */ class AutoSizePropertyBox extends BaseProperty { constructor() { super( 'autosize' ); this._needsProcess = true; } process( element ) { // if( parent ) return; // has auto size get the height from children if ( element._width._auto ) _processAutoWidth( element ); if ( element._height._auto ) _processAutoHeight( element ); const stretch = element._alignItems._value === 'stretch'; const stretchChildrenWidth = stretch && element._flexDirection._value.indexOf( 'column' ) !== -1; const stretchChildrenHeight = stretch && !stretchChildrenWidth; for ( const box of element._children._boxes ) { if ( ( box._width._auto && stretchChildrenWidth ) || box._width._relative ) { box._bounds.setReferenceWidth( box, element._bounds._innerWidth ); } if ( ( box._height._auto && stretchChildrenHeight ) || box._height._relative ) { box._bounds.setReferenceHeight( box, element._bounds._innerHeight ); } } // // justify stretch - Not that easy // const stretchD = element._justifyContent._value === 'stretch'; // const stretchChildrenWidthD = stretchD && element._flexDirection._value.indexOf( 'row' ) !== -1; // const stretchChildrenHeightD = stretchD && !stretchChildrenWidthD; // // // if ( stretchChildrenWidthD ) { // // const used = _computeChildrenSideWidth( element ); // const available = element._bounds._innerWidth - used; // if ( available > 0 ) { // // const autoElement = element._children._uis.filter( c => c._width._auto ); // const distributed = available / autoElement.length; // // for ( const child of autoElement ) { // // const width = child._bounds._offsetWidth + distributed; // child._bounds.setReferenceWidth( child, width ); // // } // // element._layouter._needsProcess = true; // element._flexDirection._needsProcess = true; // // } // // } else if ( stretchChildrenHeightD ) { // // const used = _computeChildrenSideHeight( element ); // const available = element._bounds._innerHeight - used; // if ( available > 0 ) { // // const autoElement = element._children._uis.filter( c => c._height._auto ); // const distributed = available / autoElement.length; // // for ( const child of autoElement ) { // // const height = child._bounds._offsetHeight + distributed; // child._bounds.setReferenceHeight( child, height ); // // } // // element._layouter._needsProcess = true; // element._flexDirection._needsProcess = true; // // } // // } } } function _processAutoWidth( element ) { // column : retrieve the biggest child width // row : retrieve the sum of children width element._bounds.setChildrenWidth( element, _computeAutoWidth( element ) ); } function _processAutoHeight( element ) { // column : retrieve the sum of children height // row : retrieve the biggest child height element._bounds.setChildrenHeight( element, _computeAutoHeight( element ) ); } /** * Retrieve the automatic height from children boxes * @param {MeshUIBaseElement} element * @return {number} */ function _computeAutoHeight( element ) { switch ( element._flexDirection._value ) { case 'row' : case 'row-reverse' : return _computeHighestChildHeight( element ); case 'column' : case 'column-reverse' : return AutoSizePropertyBox_computeChildrenSideHeight( element ); } } /** * @param {MeshUIBaseElement} element * @return {number} * */ function _computeAutoWidth( element ) { switch ( element._flexDirection._value ) { case 'row' : case 'row-reverse' : return AutoSizePropertyBox_computeChildrenSideWidth( element ); case 'column' : case 'column-reverse' : return _computeHighestChildWidth( element ); } } /** * Return the sum of all this component's children width * @param {MeshUIBaseElement} element * @return {number} */ function AutoSizePropertyBox_computeChildrenSideWidth( element ) { let sumWidth = 0; for ( const box of element._children._boxes ) { if ( box._position._value !== 'static' ) continue; const margin = box._margin._value; const width = box._bounds._offsetWidth + margin.y + margin.w; sumWidth += width; } return sumWidth; } /** * Return the sum of all this component's children width * @param {MeshUIBaseElement} element * @return {number} */ function AutoSizePropertyBox_computeChildrenSideHeight( element ) { let sumHeight = 0; for ( const box of element._children._boxes ) { if ( box._position._value !== 'static' ) continue; const margin = box._margin._value; const height = box._bounds._offsetHeight + margin.x + margin.z; sumHeight += height; } return sumHeight; } /** * Returns the highest linear dimension among all the children of the passed component * MARGIN INCLUDED * @param {MeshUIBaseElement} element * @return {number} */ function _computeHighestChildWidth( element ) { let maxWidth = 0; for ( const box of element._children._boxes ) { if ( box._position._value !== 'static' ) continue; const margin = box._margin._value; const width = box._bounds._offsetWidth + margin.y + margin.w; if ( width > maxWidth ) maxWidth = width; } return maxWidth; } /** * Returns the highest linear dimension among all the children of the passed component * MARGIN INCLUDED * @param {MeshUIBaseElement} element * @return {number} */ function _computeHighestChildHeight( element ) { let maxHeight = 0; for ( const box of element._children._boxes ) { if ( box._position._value !== 'static' ) continue; const margin = box._margin._value; const height = box._bounds._offsetHeight + margin.x + margin.z; if ( height > maxHeight ) maxHeight = height; } return maxHeight; } ;// CONCATENATED MODULE: ./src/elements/basic/BoxElement.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class BoxElement extends MeshUIBaseElement { /** * * @param {import('./../../core/elements/MeshUIBaseElement').Properties} properties * @param {import('./../../core/elements/MeshUIBaseElement').Options} values */ constructor( properties, values) { BoxElement.definePropertiesValues( properties, values ); super( properties, values ); BoxElement.init( this ); } /** * When the backgroundMesh has been set, bind properties * @override */ bindBackgroundMeshProperties () { // bind the background scale with bounds this._bounds._size = this._backgroundMesh.scale; this._bounds._needsProcess = true; } /** * When the backgroundMesh has been unset, unbind properties * @override */ unbindBackgroundMeshProperties () { // detach bounds size this._bounds._size = new external_THREE_namespaceObject.Vector3(1,1,1); this._bounds._needsProcess = true; } /** * * @param {import('./../../core/elements/MeshUIBaseElement').Properties} properties * @param {import('./../../core/elements/MeshUIBaseElement').Options} values */ static definePropertiesValues( properties, values ) { // customize property if( !properties.children ) properties.children = ChildrenBox; if( !properties.bounds ) properties.bounds = BoundsBox; if( !properties.flexDirection ) properties.flexDirection = FlexDirectionPropertyBox; if( !properties.justifyContent ) properties.justifyContent = JustifyContentPropertyBox; if( !properties.alignItems ) properties.alignItems = AlignItemsPropertyBox; if( !properties.position ) properties.position = PositionPropertyBox; if( !properties.autoSize ) properties.autoSize = AutoSizePropertyBox; if( !properties.renderer ) properties.renderer = RendererPropertyBox; // configure // /* ie: * /if ( !values.width ) values.width = '100%'; // break inheritance chains if ( !values.fontSide ) values.fontSide = 0; // FrontSide; if ( !values.invertAlpha ) values.invertAlpha = false; if ( !values.fontCastShadow ) values.fontCastShadow = false; if ( !values.fontReceiveShadow ) values.fontReceiveShadow = false; if ( !values.backgroundCastShadow ) values.backgroundCastShadow = false; if ( !values.backgroundReceiveShadow ) values.backgroundReceiveShadow = false; } /** * * @param {MeshUIBaseElement} element */ static init( element ) { Object.defineProperties( element, { isBox: { configurable: false, enumerable: true, value: true } } ); element.backgroundMaterial = new FrameMaterial(); element._renderer.render( element ); element._backgroundMesh.visible = false; } } ;// CONCATENATED MODULE: ./src/core/elements/glyphs/Line.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * Line represents an horizontal combination of positioned inlines with additional properties */ class Line extends Array { /** * * @param {Inline[]} items */ constructor(...items) { super(...items); /** * The width of this line * @type {number} */ this.width = 0; /** * The maximum lineBase of this line of inlines * @type {number} */ this.lineBase = 0; /** * The maximum lineHeight of this line of inlines * @type {number} */ this.lineHeight = 0; /** * The vertical position of this line * @type {number} */ this.y = 0; } } ;// CONCATENATED MODULE: ./src/core/properties/BoxLayouter.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class BoxLayouter extends BaseProperty { constructor() { super( 'layouter', null, false); // configure this._needsUpdate = true; /** * @typedef ChildrenPos * @type {Object & Object.} */ /** * * @type {ChildrenPos} * @internal */ this._childrenPos = {}; } /* eslint-disable no-unused-vars */ /** * Updated when : * - New child added * - Child removed * - Child position changed * - Child visibility changed * - ...? * @override */ update( element, out ) { /* eslint-enable no-unused-vars */ //console.log( "BoxLayouter update", element.name ); // reset this._childrenPos = {}; for ( const uiBoxElement of element._children._boxes ) { //console.log( uiBoxElement._position._value ) if( uiBoxElement._position._value === 'static' ) { // bind position this._childrenPos[ uiBoxElement.id ] = uiBoxElement.position; } } } /** * * @override */ /* eslint-disable no-unused-vars */ process( element ) { /* eslint-enable no-unused-vars */ // As _childrenPos are bounds with child.position, this is not required anymore // // element._position._needsProcess = true; // // for ( const box of element._children._boxes ) { // // if( this._childrenPos[box.id] ) { // // box.position.x = this._childrenPos[box.id].x; // box.position.y = this._childrenPos[box.id].y; // // } // // } } } ;// CONCATENATED MODULE: ./src/elements/basic/BlockElement.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class BlockElement extends BoxElement { /** * * @param {import('./../../core/elements/MeshUIBaseElement').Options} [values={}] */ constructor( values = {} ) { const properties = {}; BlockElement.definePropertiesValues( properties, values ); super( properties , values ); BlockElement.init( this ); } /* eslint-disable no-unused-vars */ /** * A Block Element can only contains box elements * @override * @param {...Object3D} object * @return {this} */ add( object ) { /* eslint-enable no-unused-vars */ /** * * @type {Array.} */ const validChildren = []; for ( let i = 0; i < arguments.length; i++ ) { const argument = arguments[ i ]; if ( !argument.isUI || argument.isBox ) { validChildren.push( argument ); } else { console.warn( 'Block element can only contain Box elements.', argument ); } } return super.add( ...validChildren ); } /* eslint-disable no-unused-vars */ /** * * @param {import('./../../core/elements/MeshUIBaseElement').Properties} properties * @param {import('./../../core/elements/MeshUIBaseElement').Options} values */ static definePropertiesValues( properties, values ) { /* eslint-enable no-unused-vars */ properties.layouter = BoxLayouter; } /** * * @param {MeshUIBaseElement} element */ static init ( element ) { Object.defineProperties( element , { isBlock: { configurable: false, enumerable: true, value: true } } ); } } ;// CONCATENATED MODULE: ./src/core/properties/TextContentInline.js class TextContentInline extends BaseProperty{ constructor() { super( "textContent", null, false ); } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ element._glyphs._needsUpdate = true; element._whiteSpace._needsProcess = true; } } ;// CONCATENATED MODULE: ./src/core/properties/InlinesProperty.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class InlinesProperty extends BaseProperty{ constructor() { super( "inlines", null, false ); /** * * @type {Array.} * @private */ this._value = null; // value // 3. Inlines // this._textContentInlines = this._textContentGlyphs.map( ( glyphBox ) => glyphBox.asInlineGlyph() ); // 4. kerning // this._buildContentKernings(); // 5.? Apply margin and padding on first and last inlines // if( this._textContentInlines.length ) { // // // First gets left side // this._textContentInlines[0].paddingLeft = this._padding.w; // this._textContentInlines[0].marginLeft = this._margin.w; // // // Last gets right side // const lastIndex = this._textContentInlines.length - 1; // this._textContentInlines[lastIndex].paddingRight = this._padding.y; // this._textContentInlines[lastIndex].marginRight = this._margin.y; // // } } process( element ) { this._value = element._glyphs._value.map( ( glyphBox ) => glyphBox.asInlineGlyph() ); if( this._value.length ) { // First gets left side this._value[0].paddingLeft = element._padding._value.w; this._value[0].marginLeft = element._margin._value.w; // Last gets right side const lastIndex = this._value.length - 1; this._value[lastIndex].paddingRight = element._padding._value.y; this._value[lastIndex].marginRight = element._margin._value.y; } element._fontSize._needsProcess = true; element._lineBreak._needsProcess = true; element._fontKerning._needsProcess = true; element._layouter._needsProcess = true; } /** * * @return {Array.} */ get value() { return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/GlyphsProperty.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class GlyphsProperty extends BaseProperty{ constructor() { super( "glyphs", null, false); this._needsUpdate = false; /** * * @type {Array.} * @private */ this._value = null; // value // 1. collapsed whiteSpace; // this._textContent = Whitespace.collapseWhitespaceOnString( this.content, this.getWhiteSpace() ); // 2. glyphs // this._textContentGlyphs = this._textContent.split( '' ).map( ( char ) => this._font.getTypographicGlyph( char ) ); // 3. Inlines // this._textContentInlines = this._textContentGlyphs.map( ( glyphBox ) => glyphBox.asInlineGlyph() ); // 4. kerning // this._buildContentKernings(); // 5.? Apply margin and padding on first and last inlines // if( this._textContentInlines.length ) { // // // First gets left side // this._textContentInlines[0].paddingLeft = this._padding.w; // this._textContentInlines[0].marginLeft = this._margin.w; // // // Last gets right side // const lastIndex = this._textContentInlines.length - 1; // this._textContentInlines[lastIndex].paddingRight = this._padding.y; // this._textContentInlines[lastIndex].marginRight = this._margin.y; // // } } process( element ) { if( !element._font._fontVariant ) return; if( !element._font._fontVariant.isReady ) return; this._value = element._whiteSpace._whiteSpacedContent.split( '' ).map( ( char ) => element._font._fontVariant.getTypographicGlyph( char ) ); // @TODO : Even if the value is removed it should trigger a rebuild. if( this._value ) element._inlines._needsProcess = true; } /** * * @return {Array.} */ get value() { return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/ColorProperty.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class ColorProperty extends StyleColorProperty { constructor( ) { super( 'color', 'inherit', false ); this.output = this._outputValue; } /* eslint-disable no-unused-vars */ /** * * @param {MeshUIBaseElement} element */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ if( this._input === 'inherit' ) { this._value.set( this.getInheritedInput( element ) ); } else { this._value.set( this._input); } } } ;// CONCATENATED MODULE: ./src/core/properties/LineBreakProperty.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class LineBreakProperty extends BaseProperty{ constructor( defaultValue = "- ,.:?!\n" ) { super( "lineBreak", defaultValue, true ); /** * * @type {"mandatory"|"possible"|null} * @private */ this._newLineBreakability = null; } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ this._needsProcess = true; } process( element ) { const newLineBreakability = element._whiteSpace._newLineBreakability; if( !element._inlines._value ) return; // update inlines properties before inline placements in lines for ( let i = 0; i < element._inlines._value.length; i++ ) { const inline = element._inlines._value[ i ]; const char = inline.char; // Whitespace Breakability --------------------------------------------------------------------------------------- let lineBreak = null; // could be inlineBlock without char if( char !== undefined ) { // @question : Does it worth to be strategy? I don't really think so if ( newLineBreakability !== 'nowrap' ) { if ( this._value.includes( char ) || char.match( /\s/g ) ) lineBreak = 'possible'; } if ( char.match( /\n/g ) ) { lineBreak = newLineBreakability; } } inline.lineBreak = lineBreak; } } /** * @override * @return {string} */ get value() { return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/InlineLayouter.js class InlineLayouter extends BaseProperty { constructor() { super( 'layouter', null, false ); /** * * @type {MeshUIBaseElement} * @private */ this._value = null; } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ // find the first text parent; this._value = element._parent.find( (p) => { return p.isUI && p.isText } ); this._needsProcess = true; } /* eslint-disable no-unused-vars */ /** * * @override */ process( element ) { /* eslint-enable no-unused-vars */ // layout has been changed if( this._value ) { this._value._layouter._needsProcess = true; } } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/background/BackgroundColorPropertyInline.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class BackgroundColorPropertyInline extends StyleColorProperty { constructor( defaultValue ) { super( 'backgroundColor', defaultValue, false ); this._allowsInherit = false; this._input = 0x000000; } /* eslint-disable no-unused-vars */ /** * * @param {MeshUIBaseElement} element */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ // @TODO : Changes multiple mesh visibility // element._backgroundMesh.visible = !(this._input === 'none' || this._input === 'transparent'); if( this._input === 'inherit' ) { this._value.set(this.getInheritedInput( element )); } else { this._value.set( this._input ); } } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontStylePropertyInline.js class FontStylePropertyInline extends FontStyleProperty { constructor() { super(); // configure this._allowsInherit = false; this.computeOutputValue = this._computeFromInherited; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontWeightPropertyInline.js class FontWeightPropertyInline extends FontWeightProperty { constructor() { super(); } computeOutputValue( element ) { this._value = uniformizeFontWeight( this.getInheritedInput( element ) ); } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontFamilyPropertyInline.js class FontFamilyPropertyInline extends FontFamilyProperty { constructor( ) { super( 'fontFamily', 'inherit' , true ); this._input = 'inherit'; this._needsUpdate = true; // configure this._allowsInherit = false; } /* eslint-disable no-unused-vars */ /** * * @param element */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ let abstractedInput = this._inheritedInput; if( abstractedInput === 'inherit' ) { abstractedInput = this.getInheritedInput( element ); } if( abstractedInput instanceof FontFamily ) { this._value = abstractedInput; element._font._needsUpdate = true; } else if ( typeof abstractedInput === 'string' ) { // string - family const fontFamily = font_FontLibrary.getFontFamily(abstractedInput); if( fontFamily ) { this._value = fontFamily; element._font._needsUpdate = true; } else { console.warn( `(.style) fontFamily, the font '${abstractedInput}' is not registered. Aborted.`) } } else { console.warn( `(.style) fontFamily requires a registered fontFamily instance, or the id of a registered fontFamily.`); console.warn( `If you want to set a specific font, please use .font property instead.`); } } /** * @override * @return {any|FontFamilyPropertyInline|null} */ get value() { return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/WhiteSpacePropertyInline.js /** * @typedef StringCollapserStrategy * @type {(textContent:{string}) => string} */ /** * @typedef InlineCollapserStrategy * @type {(line:{Line}) => number } */ /** * @typedef InlineWrapperStrategy * @type {(inlines:{Array}, i:{number}, lastInlineOffset:{number}, options:Object) => boolean} */ class WhiteSpacePropertyInline extends WhiteSpaceProperty { constructor() { super(); // configure this._allowsInherit = false; this.computeOutputValue = this._computeFromInherited; this._whiteSpacedContent = ''; // strategies /** * * @type {StringCollapserStrategy} * @internal */ this._stringCollapser = this.emptyStrategyLogic; /** * * @type {InlineCollapserStrategy} * @internal */ this._inlineCollapser = this.emptyStrategyLogic; /** * * @type {InlineWrapperStrategy} * @internal */ this._inlineWrapper = this.emptyStrategyLogic; } /* eslint-disable no-unused-vars */ /** * * @param element * @private */ _computeFromInherited( element ) { /* eslint-enable no-unused-vars */ super._computeFromInherited( element ); // set strategies this._newLineBreakability = _newlineBreakability( this._value ); // REDO Whitespace Matrix // https://developer.mozilla.org/en-US/docs/Web/CSS/white-space switch ( this._value ) { case 'nowrap': case 'normal': this._stringCollapser = _stringCollapseNewLine; break; case 'pre-line': this._stringCollapser = _stringCollapseMultipleSpace; break; default: this._stringCollapser = _stringCollapseNothing; } switch ( this._value ) { case 'pre-line': case 'nowrap': case 'normal': this._inlineCollapser = _inlineCollapseMultiple; break; case 'pre-wrap': this._inlineCollapser = _inlineCollapseSingle; break; default: this._inlineCollapser = _inlineCollapseNothing; } switch ( this._value ) { case 'pre-line': case 'pre-wrap': case 'normal': this._inlineWrapper = _lineBreakerWrapText; break; case 'pre': this._inlineWrapper = _lineBreakerLineBreakOnly; break; default: this._inlineWrapper = _lineBreakerNoWrap; } this._needsProcess = true; } process( element ) { // @TODO: Make a property for Text -> inlineCollapser if( element.isInline && !element.isInlineBlock ) { this._whiteSpacedContent = this._stringCollapser( element._textContent._value ); element._glyphs._needsProcess = true; } } } /*********************************************************************************************************************** * STRATEGIES **********************************************************************************************************************/ /** * @see https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace#whitespace_helper_functions * * Throughout, whitespace is defined as one of the characters * "\t" TAB \u0009 * "\n" LF \u000A * "\r" CR \u000D * " " SPC \u0020 * * This does not use Javascript's "\s" because that includes non-breaking * spaces (and also some other characters). **/ const WHITE_CHARS = { '\t': '\u0009', '\n': '\u000A', '\r': '\u000D', ' ': '\u0020' }; /** * Get the breakability of a newline character according to white-space property * * @param whiteSpace * @returns {string|null} */ const _newlineBreakability = function ( whiteSpace ) { switch ( whiteSpace ) { case 'pre': case 'pre-wrap': case 'pre-line': return 'mandatory'; } // case NOWRAP: // case NORMAL: // default: return null; }; // STRING COLLAPSER ----------------------------------------------------- /** * Treat newlines as spaces * @param textContentValue * @return {*} * @private * */ function _stringCollapseNewLine( textContentValue ) { return _stringCollapseMultipleSpace( textContentValue.replace( /\n/g, ' ' ) ); } /** * Treat sequences of spaces as only one space * @param textContentValue * @return {*} * @private */ function _stringCollapseMultipleSpace( textContentValue ) { return textContentValue.replace( /[ ]{2,}/g, ' ' ); } /** * * @param textContentValue * @return {*} * @private */ function _stringCollapseNothing ( textContentValue ) { return textContentValue; } // LineBreakers ----------------------------------------------------- /** * * @param inlines * @param i * @param lastInlineOffset * @param options * @return {boolean} * @private */ function _lineBreakerWrapText( inlines, i, lastInlineOffset, options ) { const inline = inlines[ i ]; // prevent additional computation if line break is mandatory if ( inline.lineBreak === 'mandatory' ) return true; // ?? Missing letterSpacing ? // prevent additional computation if this character already exceed the available size if ( lastInlineOffset + inline.xadvance + inline.xoffset + inline.kerning > options.INNER_WIDTH ) return true; const nextBreak = _distanceToNextBreak( inlines, i, options ); return _shouldFriendlyBreak( inlines[ i - 1 ], lastInlineOffset, nextBreak, options ); } /* eslint-disable no-unused-vars */ /** * * @param inlines * @param i * @param lastInlineOffset * @param options * @return {boolean} * @private */ function _lineBreakerLineBreakOnly( inlines, i, lastInlineOffset, options ) { /* eslint-enable no-unused-vars */ return inlines[ i ].lineBreak === 'mandatory'; } /** * * @return {boolean} * @private */ function _lineBreakerNoWrap() { return false; } // Inlines collapser ----------------------------------------------------- /** * * @param line * @return {number} * @private */ function _inlineCollapseSingle( line ) { if ( !line[ 0 ] ) return 0; const firstInline = line[ 0 ]; const lastInline = line[ line.length - 1 ]; // only process whiteChars glyphs inlines // if( firstInline.glyph && whiteChars[firstInline.glyph] && line.length > 1 ){ if ( firstInline.char && firstInline.char === '\n' && line.length > 1 ) { // if ( firstInline.char && WHITE_CHARS[ firstInline.char ] && line.length > 1 ) { _collapseLeftInlines( [ firstInline ], line[ 1 ] ); } // if( lastInline.glyph && whiteChars[lastInline.glyph] && line.length > 1 ){ if ( lastInline.char && lastInline.char === '\n' && line.length > 1 ) { // if ( lastInline.char && WHITE_CHARS[ firstInline.char ] && line.length > 1 ) { _collapseRightInlines( [ lastInline ], line[ line.length - 2 ] ); } return firstInline.offsetX; } function _inlineCollapseMultiple( line ) { if ( !line[ 0 ] ) return 0; let inlinesToCollapse = []; let collapsingTarget; // collect starting whitespaces to collapse for ( let i = 0; i < line.length; i++ ) { const inline = line[ i ]; if ( inline.char && WHITE_CHARS[ inline.char ] && line.length > i ) { inlinesToCollapse.push( inline ); collapsingTarget = line[ i + 1 ]; continue; } break; } _collapseLeftInlines( inlinesToCollapse, collapsingTarget ); inlinesToCollapse = []; collapsingTarget = null; // collect ending whitespace to collapse for ( let i = line.length - 1; i > 0; i-- ) { const inline = line[ i ]; if ( inline.char && WHITE_CHARS[ inline.char ] && i > 0 ) { inlinesToCollapse.push( inline ); collapsingTarget = line[ i - 1 ]; continue; } break; } _collapseRightInlines( inlinesToCollapse, collapsingTarget ); return line[ 0 ].offsetX; } /** * * @param line * @return {number|*} * @private */ function _inlineCollapseNothing( line ) { if ( !line[ 0 ] ) return 0; return line[ 0 ].offsetX; } /*********************************************************************************************************************** * Internal logics **********************************************************************************************************************/ /** * Visually collapse inlines from right to left ( endtrim ) * @param {Array} inlines * @param targetInline * @private */ function _collapseRightInlines( inlines, targetInline ) { if ( !targetInline ) return; for ( let i = 0; i < inlines.length; i++ ) { const inline = inlines[ i ]; inline.fontFactor = 0; inline.offsetX = targetInline.offsetX + targetInline.cumulativeWidth; inline.cumulativeWidth = 0; } } /** * Visually collapse inlines from left to right (starttrim) * @param {Array} inlines * @param targetInline * @private */ function _collapseLeftInlines( inlines, targetInline ) { if ( !targetInline ) return; for ( let i = 0; i < inlines.length; i++ ) { const inline = inlines[ i ]; inline.fontFactor = 0; // inline.offsetX += inline.cumulativeWidth; inline.offsetX = targetInline.offsetX; inline.cumulativeWidth = 0; } } /** * get the distance in world coord to the next glyph defined * as break-line-safe ( like whitespace for instance ) * @private */ function _distanceToNextBreak( inlines, currentIdx, options, accu ) { accu = accu || 0; // end of the text if ( !inlines[ currentIdx ] ) return accu; const inline = inlines[ currentIdx ]; // const kerning = inline.kerning ? inline.kerning : 0; // const xoffset = inline.xoffset ? inline.xoffset : 0; // const xadvance = inline.xadvance ? inline.xadvance : inline.width; // if inline.lineBreak is set, it is 'mandatory' or 'possible' if ( inline.lineBreak ) return accu + inline.xadvance; // no line break is possible on this character return _distanceToNextBreak( inlines, currentIdx + 1, options, accu + inline.xadvance + inline.xoffset + inline.kerning + options.LETTERSPACING ); } /** * Test if we should line break here even if the current glyph is not out of boundary. * It might be necessary if the last glyph was break-line-friendly (whitespace, hyphen..) * and the distance to the next friendly glyph is out of boundary. */ function _shouldFriendlyBreak( prevChar, lastInlineOffset, nextBreak, options ) { // We can't check if last glyph is break-line-friendly it does not exist if ( !prevChar || !prevChar.char ) return false; // Next break-line-friendly glyph is inside boundary if ( lastInlineOffset + nextBreak < options.INNER_WIDTH ) return false; // Previous glyph was break-line-friendly return options.BREAKON.indexOf( prevChar.char ) > -1; } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/LetterSpacingPropertyInline.js class LetterSpacingPropertyInline extends LetterSpacingProperty { constructor() { super(); // configure this._input = 'inherit'; this._allowsInherit = false; this.computeOutputValue = this._computeFromInherited; } _computeFromInherited( element ) { super._computeFromInherited( element ); element._fontSize._needsProcess = true; element._layouter._needsProcess = true; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontSizePropertyInline.js class FontSizePropertyInline extends SubStyleProperty { constructor( ) { super( 'fontSize', 'inherit', true ); // Configure this._allowsInherit = false; } computeOutputValue( element ) { this._value = this._inheritedInput; if( element._font._fontVariant ) { element._bounds._needsProcess = true; element._layouter._needsProcess = true; } } process( element ) { if( !element._font._fontVariant || !element._font._fontVariant.isReady ) return; const SCALE_MULT = this._value / element._font._fontVariant.typographic.size; // First character won't be kerned with its void lefthanded peer const inlines = element._inlines._value; // update inlines properties before inline placements in lines for ( let i = 0; i < inlines.length; i++ ) { const inline = inlines[ i ]; inline.resetOffsets(); inline.fontSize = this._value; inline.fontFactor = SCALE_MULT; } // element._layouter._needsProcess = true; } /** * * @return {number} */ get value() { return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/geometry/SegmentsPropertyText.js class SegmentsPropertyText extends SegmentsProperty { constructor() { super( 'segments', 1, false ); this._notInheritedValue = undefined; } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ this._notInheritedValue = this._value; if ( this._notInheritedValue === 'inherit' ) { this._notInheritedValue = this.getInheritedInput( element ); } element._layouter._needsUpdate = true; } /** * * @param {number|"inherit"} v */ set value( v ) { if ( this._value === v ) return; this._value = v; this._needsUpdate = true; } /** * * @override * @return {number} */ get value() { if ( this._value === 'inherit' ) return this._notInheritedValue; return this._value; } } ;// CONCATENATED MODULE: ./src/core/properties/geometry/SegmentsPropertyInline.js class SegmentsPropertyInline extends SegmentsPropertyText { constructor() { super(); this._value = 'inherit'; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontKerningPropertyInline.js class FontKerningPropertyInline extends FontKerningProperty { constructor() { super(); // Configure this._allowsInherit = false; this.computeOutputValue = this._computeFromInherited; } _computeFromInherited( element ) { super._computeFromInherited(element); // this._needsProcess = true; element._parent._value._layouter._needsProcess = false; } process( element ) { // apply kerning on inlines if ( this._value !== 'none' ) { // First character won't be kerned with its void lefthanded peer const whiteSpacedContent = element._whiteSpace._whiteSpacedContent; const inlines = element._inlines._value; for ( let i = 1; i < inlines.length; i++ ) { const glyphPair = whiteSpacedContent[ i - 1 ] + whiteSpacedContent[ i ]; // retrieve the kerning from the font // as set it on the characterInline inlines[ i ].kerning = element._font._fontVariant.getKerningAmount( glyphPair ); } } } } ;// CONCATENATED MODULE: ./src/core/properties/hierarchy/ChildrenInline.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class ChildrenInline extends BaseProperty { constructor() { super( 'children', null, false ); /** * * @type {Array.} * @internal */ this._uis = []; } /* eslint-disable no-unused-vars */ /** * Update requested when : * - New child has been added * - Existing child has been removed * * @param element * @param out */ update( element, out ) { /* eslint-enable no-unused-vars */ // this._compute( element ); // // this._needsProcess = true; } /* eslint-disable no-unused-vars */ /** * Process when : * - Existing child visibility changed * * @param element */ process( element ) { /* eslint-enable no-unused-vars */ // this._compute( element ); } /* eslint-disable no-unused-vars */ _compute( element ) { /* eslint-enable no-unused-vars */ // this._uis = element.children.filter( child => child.visible && child.isUI ); // // this._inlines = this._uis.filter( child => child.isInline ); } /** * */ dispose() { // this._inlines = null; } } ;// CONCATENATED MODULE: ./node_modules/three/examples/jsm/utils/BufferGeometryUtils.js function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) { if ( ! MikkTSpace || ! MikkTSpace.isReady ) { throw new Error( 'BufferGeometryUtils: Initialized MikkTSpace library required.' ); } if ( ! geometry.hasAttribute( 'position' ) || ! geometry.hasAttribute( 'normal' ) || ! geometry.hasAttribute( 'uv' ) ) { throw new Error( 'BufferGeometryUtils: Tangents require "position", "normal", and "uv" attributes.' ); } function getAttributeArray( attribute ) { if ( attribute.normalized || attribute.isInterleavedBufferAttribute ) { const dstArray = new Float32Array( attribute.count * attribute.itemSize ); for ( let i = 0, j = 0; i < attribute.count; i ++ ) { dstArray[ j ++ ] = attribute.getX( i ); dstArray[ j ++ ] = attribute.getY( i ); if ( attribute.itemSize > 2 ) { dstArray[ j ++ ] = attribute.getZ( i ); } } return dstArray; } if ( attribute.array instanceof Float32Array ) { return attribute.array; } return new Float32Array( attribute.array ); } // MikkTSpace algorithm requires non-indexed input. const _geometry = geometry.index ? geometry.toNonIndexed() : geometry; // Compute vertex tangents. const tangents = MikkTSpace.generateTangents( getAttributeArray( _geometry.attributes.position ), getAttributeArray( _geometry.attributes.normal ), getAttributeArray( _geometry.attributes.uv ) ); // Texture coordinate convention of glTF differs from the apparent // default of the MikkTSpace library; .w component must be flipped. if ( negateSign ) { for ( let i = 3; i < tangents.length; i += 4 ) { tangents[ i ] *= - 1; } } // _geometry.setAttribute( 'tangent', new BufferAttribute( tangents, 4 ) ); if ( geometry !== _geometry ) { geometry.copy( _geometry ); } return geometry; } /** * @param {Array} geometries * @param {Boolean} useGroups * @return {BufferGeometry} */ function mergeGeometries( geometries, useGroups = false ) { const isIndexed = geometries[ 0 ].index !== null; const attributesUsed = new Set( Object.keys( geometries[ 0 ].attributes ) ); const morphAttributesUsed = new Set( Object.keys( geometries[ 0 ].morphAttributes ) ); const attributes = {}; const morphAttributes = {}; const morphTargetsRelative = geometries[ 0 ].morphTargetsRelative; const mergedGeometry = new external_THREE_namespaceObject.BufferGeometry(); let offset = 0; for ( let i = 0; i < geometries.length; ++ i ) { const geometry = geometries[ i ]; let attributesCount = 0; // ensure that all geometries are indexed, or none if ( isIndexed !== ( geometry.index !== null ) ) { console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.' ); return null; } // gather attributes, exit early if they're different for ( const name in geometry.attributes ) { if ( ! attributesUsed.has( name ) ) { console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries, or in none of them.' ); return null; } if ( attributes[ name ] === undefined ) attributes[ name ] = []; attributes[ name ].push( geometry.attributes[ name ] ); attributesCount ++; } // ensure geometries have the same number of attributes if ( attributesCount !== attributesUsed.size ) { console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.' ); return null; } // gather morph attributes, exit early if they're different if ( morphTargetsRelative !== geometry.morphTargetsRelative ) { console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.' ); return null; } for ( const name in geometry.morphAttributes ) { if ( ! morphAttributesUsed.has( name ) ) { console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. .morphAttributes must be consistent throughout all geometries.' ); return null; } if ( morphAttributes[ name ] === undefined ) morphAttributes[ name ] = []; morphAttributes[ name ].push( geometry.morphAttributes[ name ] ); } if ( useGroups ) { let count; if ( isIndexed ) { count = geometry.index.count; } else if ( geometry.attributes.position !== undefined ) { count = geometry.attributes.position.count; } else { console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute' ); return null; } mergedGeometry.addGroup( offset, count, i ); offset += count; } } // merge indices if ( isIndexed ) { let indexOffset = 0; const mergedIndex = []; for ( let i = 0; i < geometries.length; ++ i ) { const index = geometries[ i ].index; for ( let j = 0; j < index.count; ++ j ) { mergedIndex.push( index.getX( j ) + indexOffset ); } indexOffset += geometries[ i ].attributes.position.count; } mergedGeometry.setIndex( mergedIndex ); } // merge attributes for ( const name in attributes ) { const mergedAttribute = mergeAttributes( attributes[ name ] ); if ( ! mergedAttribute ) { console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the ' + name + ' attribute.' ); return null; } mergedGeometry.setAttribute( name, mergedAttribute ); } // merge morph attributes for ( const name in morphAttributes ) { const numMorphTargets = morphAttributes[ name ][ 0 ].length; if ( numMorphTargets === 0 ) break; mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {}; mergedGeometry.morphAttributes[ name ] = []; for ( let i = 0; i < numMorphTargets; ++ i ) { const morphAttributesToMerge = []; for ( let j = 0; j < morphAttributes[ name ].length; ++ j ) { morphAttributesToMerge.push( morphAttributes[ name ][ j ][ i ] ); } const mergedMorphAttribute = mergeAttributes( morphAttributesToMerge ); if ( ! mergedMorphAttribute ) { console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the ' + name + ' morphAttribute.' ); return null; } mergedGeometry.morphAttributes[ name ].push( mergedMorphAttribute ); } } return mergedGeometry; } /** * @param {Array} attributes * @return {BufferAttribute} */ function mergeAttributes( attributes ) { let TypedArray; let itemSize; let normalized; let gpuType = - 1; let arrayLength = 0; for ( let i = 0; i < attributes.length; ++ i ) { const attribute = attributes[ i ]; if ( TypedArray === undefined ) TypedArray = attribute.array.constructor; if ( TypedArray !== attribute.array.constructor ) { console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.array must be of consistent array types across matching attributes.' ); return null; } if ( itemSize === undefined ) itemSize = attribute.itemSize; if ( itemSize !== attribute.itemSize ) { console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.itemSize must be consistent across matching attributes.' ); return null; } if ( normalized === undefined ) normalized = attribute.normalized; if ( normalized !== attribute.normalized ) { console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.normalized must be consistent across matching attributes.' ); return null; } if ( gpuType === - 1 ) gpuType = attribute.gpuType; if ( gpuType !== attribute.gpuType ) { console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.gpuType must be consistent across matching attributes.' ); return null; } arrayLength += attribute.count * itemSize; } const array = new TypedArray( arrayLength ); const result = new external_THREE_namespaceObject.BufferAttribute( array, itemSize, normalized ); let offset = 0; for ( let i = 0; i < attributes.length; ++ i ) { const attribute = attributes[ i ]; if ( attribute.isInterleavedBufferAttribute ) { const tupleOffset = offset / itemSize; for ( let j = 0, l = attribute.count; j < l; j ++ ) { for ( let c = 0; c < itemSize; c ++ ) { const value = attribute.getComponent( j, c ); result.setComponent( j + tupleOffset, c, value ); } } } else { array.set( attribute.array, offset ); } offset += attribute.count * itemSize; } if ( gpuType !== undefined ) { result.gpuType = gpuType; } return result; } /** * @param {BufferAttribute} * @return {BufferAttribute} */ function deepCloneAttribute( attribute ) { if ( attribute.isInstancedInterleavedBufferAttribute || attribute.isInterleavedBufferAttribute ) { return deinterleaveAttribute( attribute ); } if ( attribute.isInstancedBufferAttribute ) { return new InstancedBufferAttribute().copy( attribute ); } return new BufferAttribute().copy( attribute ); } /** * @param {Array} attributes * @return {Array} */ function interleaveAttributes( attributes ) { // Interleaves the provided attributes into an InterleavedBuffer and returns // a set of InterleavedBufferAttributes for each attribute let TypedArray; let arrayLength = 0; let stride = 0; // calculate the length and type of the interleavedBuffer for ( let i = 0, l = attributes.length; i < l; ++ i ) { const attribute = attributes[ i ]; if ( TypedArray === undefined ) TypedArray = attribute.array.constructor; if ( TypedArray !== attribute.array.constructor ) { console.error( 'AttributeBuffers of different types cannot be interleaved' ); return null; } arrayLength += attribute.array.length; stride += attribute.itemSize; } // Create the set of buffer attributes const interleavedBuffer = new InterleavedBuffer( new TypedArray( arrayLength ), stride ); let offset = 0; const res = []; const getters = [ 'getX', 'getY', 'getZ', 'getW' ]; const setters = [ 'setX', 'setY', 'setZ', 'setW' ]; for ( let j = 0, l = attributes.length; j < l; j ++ ) { const attribute = attributes[ j ]; const itemSize = attribute.itemSize; const count = attribute.count; const iba = new InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, attribute.normalized ); res.push( iba ); offset += itemSize; // Move the data for each attribute into the new interleavedBuffer // at the appropriate offset for ( let c = 0; c < count; c ++ ) { for ( let k = 0; k < itemSize; k ++ ) { iba[ setters[ k ] ]( c, attribute[ getters[ k ] ]( c ) ); } } } return res; } // returns a new, non-interleaved version of the provided attribute function deinterleaveAttribute( attribute ) { const cons = attribute.data.array.constructor; const count = attribute.count; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array = new cons( count * itemSize ); let newAttribute; if ( attribute.isInstancedInterleavedBufferAttribute ) { newAttribute = new InstancedBufferAttribute( array, itemSize, normalized, attribute.meshPerAttribute ); } else { newAttribute = new BufferAttribute( array, itemSize, normalized ); } for ( let i = 0; i < count; i ++ ) { newAttribute.setX( i, attribute.getX( i ) ); if ( itemSize >= 2 ) { newAttribute.setY( i, attribute.getY( i ) ); } if ( itemSize >= 3 ) { newAttribute.setZ( i, attribute.getZ( i ) ); } if ( itemSize >= 4 ) { newAttribute.setW( i, attribute.getW( i ) ); } } return newAttribute; } // deinterleaves all attributes on the geometry function deinterleaveGeometry( geometry ) { const attributes = geometry.attributes; const morphTargets = geometry.morphTargets; const attrMap = new Map(); for ( const key in attributes ) { const attr = attributes[ key ]; if ( attr.isInterleavedBufferAttribute ) { if ( ! attrMap.has( attr ) ) { attrMap.set( attr, deinterleaveAttribute( attr ) ); } attributes[ key ] = attrMap.get( attr ); } } for ( const key in morphTargets ) { const attr = morphTargets[ key ]; if ( attr.isInterleavedBufferAttribute ) { if ( ! attrMap.has( attr ) ) { attrMap.set( attr, deinterleaveAttribute( attr ) ); } morphTargets[ key ] = attrMap.get( attr ); } } } /** * @param {BufferGeometry} geometry * @return {number} */ function estimateBytesUsed( geometry ) { // Return the estimated memory used by this geometry in bytes // Calculate using itemSize, count, and BYTES_PER_ELEMENT to account // for InterleavedBufferAttributes. let mem = 0; for ( const name in geometry.attributes ) { const attr = geometry.getAttribute( name ); mem += attr.count * attr.itemSize * attr.array.BYTES_PER_ELEMENT; } const indices = geometry.getIndex(); mem += indices ? indices.count * indices.itemSize * indices.array.BYTES_PER_ELEMENT : 0; return mem; } /** * @param {BufferGeometry} geometry * @param {number} tolerance * @return {BufferGeometry} */ function mergeVertices( geometry, tolerance = 1e-4 ) { tolerance = Math.max( tolerance, Number.EPSILON ); // Generate an index buffer if the geometry doesn't have one, or optimize it // if it's already available. const hashToIndex = {}; const indices = geometry.getIndex(); const positions = geometry.getAttribute( 'position' ); const vertexCount = indices ? indices.count : positions.count; // next value for triangle indices let nextIndex = 0; // attributes and new attribute arrays const attributeNames = Object.keys( geometry.attributes ); const tmpAttributes = {}; const tmpMorphAttributes = {}; const newIndices = []; const getters = [ 'getX', 'getY', 'getZ', 'getW' ]; const setters = [ 'setX', 'setY', 'setZ', 'setW' ]; // Initialize the arrays, allocating space conservatively. Extra // space will be trimmed in the last step. for ( let i = 0, l = attributeNames.length; i < l; i ++ ) { const name = attributeNames[ i ]; const attr = geometry.attributes[ name ]; tmpAttributes[ name ] = new BufferAttribute( new attr.array.constructor( attr.count * attr.itemSize ), attr.itemSize, attr.normalized ); const morphAttr = geometry.morphAttributes[ name ]; if ( morphAttr ) { tmpMorphAttributes[ name ] = new BufferAttribute( new morphAttr.array.constructor( morphAttr.count * morphAttr.itemSize ), morphAttr.itemSize, morphAttr.normalized ); } } // convert the error tolerance to an amount of decimal places to truncate to const halfTolerance = tolerance * 0.5; const exponent = Math.log10( 1 / tolerance ); const hashMultiplier = Math.pow( 10, exponent ); const hashAdditive = halfTolerance * hashMultiplier; for ( let i = 0; i < vertexCount; i ++ ) { const index = indices ? indices.getX( i ) : i; // Generate a hash for the vertex attributes at the current index 'i' let hash = ''; for ( let j = 0, l = attributeNames.length; j < l; j ++ ) { const name = attributeNames[ j ]; const attribute = geometry.getAttribute( name ); const itemSize = attribute.itemSize; for ( let k = 0; k < itemSize; k ++ ) { // double tilde truncates the decimal value hash += `${ ~ ~ ( attribute[ getters[ k ] ]( index ) * hashMultiplier + hashAdditive ) },`; } } // Add another reference to the vertex if it's already // used by another index if ( hash in hashToIndex ) { newIndices.push( hashToIndex[ hash ] ); } else { // copy data to the new index in the temporary attributes for ( let j = 0, l = attributeNames.length; j < l; j ++ ) { const name = attributeNames[ j ]; const attribute = geometry.getAttribute( name ); const morphAttr = geometry.morphAttributes[ name ]; const itemSize = attribute.itemSize; const newarray = tmpAttributes[ name ]; const newMorphArrays = tmpMorphAttributes[ name ]; for ( let k = 0; k < itemSize; k ++ ) { const getterFunc = getters[ k ]; const setterFunc = setters[ k ]; newarray[ setterFunc ]( nextIndex, attribute[ getterFunc ]( index ) ); if ( morphAttr ) { for ( let m = 0, ml = morphAttr.length; m < ml; m ++ ) { newMorphArrays[ m ][ setterFunc ]( nextIndex, morphAttr[ m ][ getterFunc ]( index ) ); } } } } hashToIndex[ hash ] = nextIndex; newIndices.push( nextIndex ); nextIndex ++; } } // generate result BufferGeometry const result = geometry.clone(); for ( const name in geometry.attributes ) { const tmpAttribute = tmpAttributes[ name ]; result.setAttribute( name, new BufferAttribute( tmpAttribute.array.slice( 0, nextIndex * tmpAttribute.itemSize ), tmpAttribute.itemSize, tmpAttribute.normalized, ) ); if ( ! ( name in tmpMorphAttributes ) ) continue; for ( let j = 0; j < tmpMorphAttributes[ name ].length; j ++ ) { const tmpMorphAttribute = tmpMorphAttributes[ name ][ j ]; result.morphAttributes[ name ][ j ] = new BufferAttribute( tmpMorphAttribute.array.slice( 0, nextIndex * tmpMorphAttribute.itemSize ), tmpMorphAttribute.itemSize, tmpMorphAttribute.normalized, ); } } // indices result.setIndex( newIndices ); return result; } /** * @param {BufferGeometry} geometry * @param {number} drawMode * @return {BufferGeometry} */ function toTrianglesDrawMode( geometry, drawMode ) { if ( drawMode === TrianglesDrawMode ) { console.warn( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.' ); return geometry; } if ( drawMode === TriangleFanDrawMode || drawMode === TriangleStripDrawMode ) { let index = geometry.getIndex(); // generate index if not present if ( index === null ) { const indices = []; const position = geometry.getAttribute( 'position' ); if ( position !== undefined ) { for ( let i = 0; i < position.count; i ++ ) { indices.push( i ); } geometry.setIndex( indices ); index = geometry.getIndex(); } else { console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); return geometry; } } // const numberOfTriangles = index.count - 2; const newIndices = []; if ( drawMode === TriangleFanDrawMode ) { // gl.TRIANGLE_FAN for ( let i = 1; i <= numberOfTriangles; i ++ ) { newIndices.push( index.getX( 0 ) ); newIndices.push( index.getX( i ) ); newIndices.push( index.getX( i + 1 ) ); } } else { // gl.TRIANGLE_STRIP for ( let i = 0; i < numberOfTriangles; i ++ ) { if ( i % 2 === 0 ) { newIndices.push( index.getX( i ) ); newIndices.push( index.getX( i + 1 ) ); newIndices.push( index.getX( i + 2 ) ); } else { newIndices.push( index.getX( i + 2 ) ); newIndices.push( index.getX( i + 1 ) ); newIndices.push( index.getX( i ) ); } } } if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); } // build final geometry const newGeometry = geometry.clone(); newGeometry.setIndex( newIndices ); newGeometry.clearGroups(); return newGeometry; } else { console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unknown draw mode:', drawMode ); return geometry; } } /** * Calculates the morphed attributes of a morphed/skinned BufferGeometry. * Helpful for Raytracing or Decals. * @param {Mesh | Line | Points} object An instance of Mesh, Line or Points. * @return {Object} An Object with original position/normal attributes and morphed ones. */ function computeMorphedAttributes( object ) { const _vA = new Vector3(); const _vB = new Vector3(); const _vC = new Vector3(); const _tempA = new Vector3(); const _tempB = new Vector3(); const _tempC = new Vector3(); const _morphA = new Vector3(); const _morphB = new Vector3(); const _morphC = new Vector3(); function _calculateMorphedAttributeData( object, attribute, morphAttribute, morphTargetsRelative, a, b, c, modifiedAttributeArray ) { _vA.fromBufferAttribute( attribute, a ); _vB.fromBufferAttribute( attribute, b ); _vC.fromBufferAttribute( attribute, c ); const morphInfluences = object.morphTargetInfluences; if ( morphAttribute && morphInfluences ) { _morphA.set( 0, 0, 0 ); _morphB.set( 0, 0, 0 ); _morphC.set( 0, 0, 0 ); for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { const influence = morphInfluences[ i ]; const morph = morphAttribute[ i ]; if ( influence === 0 ) continue; _tempA.fromBufferAttribute( morph, a ); _tempB.fromBufferAttribute( morph, b ); _tempC.fromBufferAttribute( morph, c ); if ( morphTargetsRelative ) { _morphA.addScaledVector( _tempA, influence ); _morphB.addScaledVector( _tempB, influence ); _morphC.addScaledVector( _tempC, influence ); } else { _morphA.addScaledVector( _tempA.sub( _vA ), influence ); _morphB.addScaledVector( _tempB.sub( _vB ), influence ); _morphC.addScaledVector( _tempC.sub( _vC ), influence ); } } _vA.add( _morphA ); _vB.add( _morphB ); _vC.add( _morphC ); } if ( object.isSkinnedMesh ) { object.applyBoneTransform( a, _vA ); object.applyBoneTransform( b, _vB ); object.applyBoneTransform( c, _vC ); } modifiedAttributeArray[ a * 3 + 0 ] = _vA.x; modifiedAttributeArray[ a * 3 + 1 ] = _vA.y; modifiedAttributeArray[ a * 3 + 2 ] = _vA.z; modifiedAttributeArray[ b * 3 + 0 ] = _vB.x; modifiedAttributeArray[ b * 3 + 1 ] = _vB.y; modifiedAttributeArray[ b * 3 + 2 ] = _vB.z; modifiedAttributeArray[ c * 3 + 0 ] = _vC.x; modifiedAttributeArray[ c * 3 + 1 ] = _vC.y; modifiedAttributeArray[ c * 3 + 2 ] = _vC.z; } const geometry = object.geometry; const material = object.material; let a, b, c; const index = geometry.index; const positionAttribute = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; const normalAttribute = geometry.attributes.normal; const morphNormal = geometry.morphAttributes.position; const groups = geometry.groups; const drawRange = geometry.drawRange; let i, j, il, jl; let group; let start, end; const modifiedPosition = new Float32Array( positionAttribute.count * positionAttribute.itemSize ); const modifiedNormal = new Float32Array( normalAttribute.count * normalAttribute.itemSize ); if ( index !== null ) { // indexed buffer geometry if ( Array.isArray( material ) ) { for ( i = 0, il = groups.length; i < il; i ++ ) { group = groups[ i ]; start = Math.max( group.start, drawRange.start ); end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); for ( j = start, jl = end; j < jl; j += 3 ) { a = index.getX( j ); b = index.getX( j + 1 ); c = index.getX( j + 2 ); _calculateMorphedAttributeData( object, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition ); _calculateMorphedAttributeData( object, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal ); } } } else { start = Math.max( 0, drawRange.start ); end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); for ( i = start, il = end; i < il; i += 3 ) { a = index.getX( i ); b = index.getX( i + 1 ); c = index.getX( i + 2 ); _calculateMorphedAttributeData( object, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition ); _calculateMorphedAttributeData( object, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal ); } } } else { // non-indexed buffer geometry if ( Array.isArray( material ) ) { for ( i = 0, il = groups.length; i < il; i ++ ) { group = groups[ i ]; start = Math.max( group.start, drawRange.start ); end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); for ( j = start, jl = end; j < jl; j += 3 ) { a = j; b = j + 1; c = j + 2; _calculateMorphedAttributeData( object, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition ); _calculateMorphedAttributeData( object, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal ); } } } else { start = Math.max( 0, drawRange.start ); end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); for ( i = start, il = end; i < il; i += 3 ) { a = i; b = i + 1; c = i + 2; _calculateMorphedAttributeData( object, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition ); _calculateMorphedAttributeData( object, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal ); } } } const morphedPositionAttribute = new Float32BufferAttribute( modifiedPosition, 3 ); const morphedNormalAttribute = new Float32BufferAttribute( modifiedNormal, 3 ); return { positionAttribute: positionAttribute, normalAttribute: normalAttribute, morphedPositionAttribute: morphedPositionAttribute, morphedNormalAttribute: morphedNormalAttribute }; } function mergeGroups( geometry ) { if ( geometry.groups.length === 0 ) { console.warn( 'THREE.BufferGeometryUtils.mergeGroups(): No groups are defined. Nothing to merge.' ); return geometry; } let groups = geometry.groups; // sort groups by material index groups = groups.sort( ( a, b ) => { if ( a.materialIndex !== b.materialIndex ) return a.materialIndex - b.materialIndex; return a.start - b.start; } ); // create index for non-indexed geometries if ( geometry.getIndex() === null ) { const positionAttribute = geometry.getAttribute( 'position' ); const indices = []; for ( let i = 0; i < positionAttribute.count; i += 3 ) { indices.push( i, i + 1, i + 2 ); } geometry.setIndex( indices ); } // sort index const index = geometry.getIndex(); const newIndices = []; for ( let i = 0; i < groups.length; i ++ ) { const group = groups[ i ]; const groupStart = group.start; const groupLength = groupStart + group.count; for ( let j = groupStart; j < groupLength; j ++ ) { newIndices.push( index.getX( j ) ); } } geometry.dispose(); // Required to force buffer recreation geometry.setIndex( newIndices ); // update groups indices let start = 0; for ( let i = 0; i < groups.length; i ++ ) { const group = groups[ i ]; group.start = start; start += group.count; } // merge groups let currentGroup = groups[ 0 ]; geometry.groups = [ currentGroup ]; for ( let i = 1; i < groups.length; i ++ ) { const group = groups[ i ]; if ( currentGroup.materialIndex === group.materialIndex ) { currentGroup.count += group.count; } else { currentGroup = group; geometry.groups.push( currentGroup ); } } return geometry; } /** * Modifies the supplied geometry if it is non-indexed, otherwise creates a new, * non-indexed geometry. Returns the geometry with smooth normals everywhere except * faces that meet at an angle greater than the crease angle. * * @param {BufferGeometry} geometry * @param {number} [creaseAngle] * @return {BufferGeometry} */ function toCreasedNormals( geometry, creaseAngle = Math.PI / 3 /* 60 degrees */ ) { const creaseDot = Math.cos( creaseAngle ); const hashMultiplier = ( 1 + 1e-10 ) * 1e2; // reusable vectors const verts = [ new Vector3(), new Vector3(), new Vector3() ]; const tempVec1 = new Vector3(); const tempVec2 = new Vector3(); const tempNorm = new Vector3(); const tempNorm2 = new Vector3(); // hashes a vector function hashVertex( v ) { const x = ~ ~ ( v.x * hashMultiplier ); const y = ~ ~ ( v.y * hashMultiplier ); const z = ~ ~ ( v.z * hashMultiplier ); return `${x},${y},${z}`; } // BufferGeometry.toNonIndexed() warns if the geometry is non-indexed // and returns the original geometry const resultGeometry = geometry.index ? geometry.toNonIndexed() : geometry; const posAttr = resultGeometry.attributes.position; const vertexMap = {}; // find all the normals shared by commonly located vertices for ( let i = 0, l = posAttr.count / 3; i < l; i ++ ) { const i3 = 3 * i; const a = verts[ 0 ].fromBufferAttribute( posAttr, i3 + 0 ); const b = verts[ 1 ].fromBufferAttribute( posAttr, i3 + 1 ); const c = verts[ 2 ].fromBufferAttribute( posAttr, i3 + 2 ); tempVec1.subVectors( c, b ); tempVec2.subVectors( a, b ); // add the normal to the map for all vertices const normal = new Vector3().crossVectors( tempVec1, tempVec2 ).normalize(); for ( let n = 0; n < 3; n ++ ) { const vert = verts[ n ]; const hash = hashVertex( vert ); if ( ! ( hash in vertexMap ) ) { vertexMap[ hash ] = []; } vertexMap[ hash ].push( normal ); } } // average normals from all vertices that share a common location if they are within the // provided crease threshold const normalArray = new Float32Array( posAttr.count * 3 ); const normAttr = new BufferAttribute( normalArray, 3, false ); for ( let i = 0, l = posAttr.count / 3; i < l; i ++ ) { // get the face normal for this vertex const i3 = 3 * i; const a = verts[ 0 ].fromBufferAttribute( posAttr, i3 + 0 ); const b = verts[ 1 ].fromBufferAttribute( posAttr, i3 + 1 ); const c = verts[ 2 ].fromBufferAttribute( posAttr, i3 + 2 ); tempVec1.subVectors( c, b ); tempVec2.subVectors( a, b ); tempNorm.crossVectors( tempVec1, tempVec2 ).normalize(); // average all normals that meet the threshold and set the normal value for ( let n = 0; n < 3; n ++ ) { const vert = verts[ n ]; const hash = hashVertex( vert ); const otherNormals = vertexMap[ hash ]; tempNorm2.set( 0, 0, 0 ); for ( let k = 0, lk = otherNormals.length; k < lk; k ++ ) { const otherNorm = otherNormals[ k ]; if ( tempNorm.dot( otherNorm ) > creaseDot ) { tempNorm2.add( otherNorm ); } } tempNorm2.normalize(); normAttr.setXYZ( i3 + n, tempNorm2.x, tempNorm2.y, tempNorm2.z ); } } resultGeometry.setAttribute( 'normal', normAttr ); return resultGeometry; } ;// CONCATENATED MODULE: ./src/core/properties/rendering/RendererPropertyInline.js class RendererPropertyInline extends BaseProperty{ constructor() { super( 'renderer' ); } render( element ) { if( !element._inlines._value || !element._inlines._value.length ) return; const charactersAsGeometries = element._inlines._value.map( inline => element._font._fontVariant.getGeometricGlyph( inline, element ) .translate( inline.offsetX, inline.offsetY, 0 ) ); const mergedGeom = mergeGeometries( charactersAsGeometries ); element.setFontMesh( new external_THREE_namespaceObject.Mesh( mergedGeom, element.fontMaterial) ); element._fontMesh.renderOrder = Infinity; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/TextAlignPropertyInline.js class TextAlignPropertyInline extends TextAlignProperty { constructor() { super(); // configure this._allowsInherit = false; this._needsUpdate = false; } /* eslint-disable no-unused-vars */ computeOutputValue( element ) { /* eslint-enable no-unused-vars */ this._value = this._inheritedInput; element._layouter._needsProcess = true; } } ;// CONCATENATED MODULE: ./src/elements/basic/InlineElement.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class InlineElement extends MeshUIBaseElement { /** * * @param {import('./../../core/elements/MeshUIBaseElement').Options} [values={}] */ constructor( values = { }) { const properties = {}; InlineElement.definePropertiesValues( properties, values ); super( properties, values ); InlineElement.init( this ); } /* eslint-disable no-unused-vars */ /** * A Text Element can only contains inline elements * @override * @param {...Object3D} object * @return {this} */ add( object ) { /* eslint-enable no-unused-vars */ /** * * @type {Array.} */ const validChildren = []; for ( let i = 0; i < arguments.length; i++ ) { const argument = arguments[ i ]; if ( !argument.isUI || argument.isInline ) { validChildren.push( argument ); argument.position.z = 0.005; } else { console.warn( 'Block element can only contain Box elements.', argument ); } } return super.add( ...validChildren ); } _rebuildParentUI = () => { super._rebuildParentUI(); this._layouter._needsUpdate = true; } set textContent( value ) { this._textContent.value = value; } get textContent () { return this._textContent._value; } set invertAlpha( value ) { this._invertAlpha.value = value; } get invertAlpha () { return this._invertAlpha._value; } /* eslint-disable no-unused-vars */ /** * * @param {import('./../../core/elements/MeshUIBaseElement').Properties} properties * @param {import('./../../core/elements/MeshUIBaseElement').Options} values */ static definePropertiesValues( properties, values ) { /* eslint-enable no-unused-vars */ if( !properties.children ) properties.children = ChildrenInline; if( !properties.textContent ) properties.textContent = TextContentInline; if( !properties.glyphs ) properties.glyphs = GlyphsProperty; if( !properties.inlines ) properties.inlines = InlinesProperty; if( !properties.layouter ) properties.layouter = InlineLayouter; if( !properties.renderer ) properties.renderer = RendererPropertyInline; if( !properties.fontFamily ) properties.fontFamily = FontFamilyPropertyInline; if( !properties.fontWeight ) properties.fontWeight = FontWeightPropertyInline; if( !properties.fontStyle ) properties.fontStyle = FontStylePropertyInline; if( !properties.fontSize ) properties.fontSize = FontSizePropertyInline; if( !properties.color ) properties.color = ColorProperty; if( !properties.backgroundColor ) properties.backgroundColor = BackgroundColorPropertyInline; if( !properties.lineBreak ) properties.lineBreak = LineBreakProperty; if( !properties.letterSpacing ) properties.letterSpacing = LetterSpacingPropertyInline; if( !properties.whiteSpace ) properties.whiteSpace = WhiteSpacePropertyInline; if( !properties.segments ) properties.segments = SegmentsPropertyInline; if( !properties.textAlign ) properties.textAlign = TextAlignPropertyInline; if( !properties.fontKerning ) properties.fontKerning = FontKerningPropertyInline; // if( !properties.inlines ) properties.inlines = InlinesProperty; } /** * * @param {MeshUIBaseElement} element */ static init( element ) { Object.defineProperties( element, { isInline: { configurable: false, enumerable: true, value: true } } ); } } ;// CONCATENATED MODULE: ./src/core/properties/TextContentText.js class TextContentText extends TextContentEmpty{ constructor() { super( "textContent", null, false ); this._needsUpdate = false; } set value( value ) { // If content hasn't change, dont update it if( this._value !== value ) { this._value = value; this._needsUpdate = true; } } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ // prevent multiple update this._needsUpdate = false; // Remove all its children (Inlines) for ( let i = element.children.length - 1 ; i >= 0; i-- ) { const child = element.children[ i ]; if( child.isUI ) { element.remove( child ); child.clear(); } } // Rebuild its child list element._children._uis = []; // If a value, add a child if( this._value ) element.add( new InlineElement({name:'anonymousInline',textContent:this._value})); } } ;// CONCATENATED MODULE: ./src/core/elements/glyphs/Lines.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * Lines represents a vertical succession of Line */ class Lines extends Array { /** * * @param {Line} items */ constructor(...items) { super(...items); /** * The maximum width of Line items * @type {number} */ this.width = 0; /** * The addition of height of any Line * @type {number} */ this.height = 0; } } ;// CONCATENATED MODULE: ./src/core/properties/TextLayouter.js class TextLayouter extends BaseProperty { constructor() { super( 'layouter', null, false ); /** * * @type {Lines} * @private */ this._value = null; } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ } /** * * @override */ process( element ) { let INNER_WIDTH = element._width._value; if ( element._width._auto ) { INNER_WIDTH = Infinity; } else { INNER_WIDTH = element._bounds._innerWidth; } // Compute lines const INTERLINE = element._lineHeight._value; // Will stock the characters of each line, so that we can // correct lines position before to merge const lines = new Lines( new Line() ); let lastInlineOffset = 0; element._children._inlines.forEach( ( inlineElement ) => { // Abort condition if ( !inlineElement._inlines.value ) return; this._resetInlines( inlineElement ); ////////////////////////////////////////////////////////////// // Compute offset of each children according to its dimensions ////////////////////////////////////////////////////////////// // @TODO: Fontsize best fit const FONTSIZE = inlineElement._fontSize._value; const LETTERSPACING = inlineElement._letterSpacing._value * FONTSIZE; const WHITESPACE = inlineElement._whiteSpace._value; const BREAKON = inlineElement._lineBreak._value; const whiteSpaceOptions = { WHITESPACE, LETTERSPACING, BREAKON, INNER_WIDTH } const inlineWrapper = inlineElement._whiteSpace._inlineWrapper; lastInlineOffset += inlineElement._margin._value.w + inlineElement._padding._value.w; inlineElement._inlines.value.forEach( ( inline, i, inlines ) => { const line = lines[lines.length - 1]; // Line break const shouldBreak = inlineWrapper(inlines,i,lastInlineOffset, whiteSpaceOptions ); if ( shouldBreak ) { lines.push( new Line( inline ) ); inline.offsetX = inline.xoffset; // restart the lastInlineOffset as zero. if ( inline.width === 0 ) { lastInlineOffset = 0; return; } // compute lastInlineOffset normally // except for kerning which won't apply // as there is visually no lefthanded glyph to kern with inline.cumulativeWidth = inline.xadvance + LETTERSPACING; lastInlineOffset = inline.cumulativeWidth; return; } lines[ lines.length - 1 ].push( inline ); inline.offsetX = lastInlineOffset + inline.xoffset + inline.kerning; inline.cumulativeWidth = inline.xadvance + inline.kerning + LETTERSPACING; lastInlineOffset += inline.cumulativeWidth; // in case of lineBreak mandatory if( line.length-1 === 1) { if ( line[ line.length - 2 ].width === 0 ) { // remove the offset of the character following the newline inline.offsetX -= inline.xoffset; lastInlineOffset -= inline.xoffset; } } } ); lastInlineOffset += inlineElement._margin._value.y + inlineElement._padding._value.y; } ); // Compute single line and combined lines dimensions const inlineCollapser = element._whiteSpace._inlineCollapser; let width = 0, height =0, lineOffsetY = 0; // calculates lines lines.forEach( ( line, i ) => { // starts by processing whitespace, it will return a collapsed left offset const whiteSpaceOffset = inlineCollapser( line ); // let lineHeight = 0; let lineBase = 0; line.forEach( ( inline ) => { lineHeight = Math.max( lineHeight, inline.lineHeight ); lineBase = Math.max( lineBase, inline.lineBase ); inline.offsetX -= whiteSpaceOffset; }); line.lineHeight = lineHeight; line.lineBase = lineBase; // const baseLineDelta = lineHeight - lineBase; if( i === 0 ){ lineOffsetY = -(lineHeight*INTERLINE - lineHeight) * 0.5; } else { lineOffsetY -= lines[i-1].lineHeight*INTERLINE; } line.y = lineOffsetY; line.x = 0; // process yoffset line.forEach( ( inline ) => { inline.offsetY = lineOffsetY - inline.anchor; if( inline.lineHeight < line.lineHeight ){ inline.offsetY -= line.lineBase- inline.lineBase; } }); height += ( line.lineHeight * INTERLINE ); // height += ( line.lineHeight); // line.width = 0; // if this line have inlines if ( line[ 0 ] ) { // compute its width: length from firstInline:LEFT to lastInline:RIGHT // only by the length of its extremities const lastInline = line[ line.length - 1 ]; // Right + Left ( left is negative ) line.width = ( lastInline.offsetX + lastInline.cumulativeWidth + lastInline.paddingRight + lastInline.marginRight ) + line[ 0 ].offsetX; width = Math.max( width, line.width); } } ); lines.height = height; lines.width = width; this._value = lines; if( INNER_WIDTH === Infinity ) { element._bounds.setChildrenWidth( element, lines.width ); } if( element._height._auto ) { element._bounds.setChildrenHeight( element, lines.height ); } const parent = element._parent._value; if( parent ) { parent._autoSize._needsProcess = true; parent._flexDirection._needsProcess = true; } element._inlineJustificator._needsProcess = true; element._textAlign._needsProcess = true; element._overflow._needsUpdate = true; } /** * * @param inlineElement * @protected */ _resetInlines ( inlineElement ) { // ensure no collapsed remains inlineElement._fontSize.process( inlineElement ); } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/TextAlignPropertyText.js class TextAlignPropertyText extends TextAlignProperty { constructor() { super(); // configure this._allowsInherit = false; this._needsUpdate = true; // // @TODO : strategies } /* eslint-disable no-unused-vars */computeOutputValue( element ) { /* eslint-enable no-unused-vars */ this._value = this._inheritedInput; this._needsProcess = true; } process( element ) { _process( element ); element._renderer._needsRender = true; } } function _process( element ) { const lines = element._layouter._value; const ALIGNMENT = element._textAlign._value; const INNER_WIDTH = element._bounds._innerWidth; // Start the alignment by sticking to directions : left, right, center for ( let i = 0; i < lines.length; i++ ) { const line = lines[ i ]; // compute the alignment offset of the line const offsetX = _computeLineOffset( element, line, i === lines.length - 1 ); const padding = element._padding._value; const border = element._borderWidth._value; // const paddingAmount = - ( padding.w + padding.y ) / 2 - ( border.w + border.y ) / 2; // const paddingAmount = - ( padding.w + padding.y ) / 2; const paddingAmount = ( - padding.w + padding.y ) / 2 + ( - border.w + border.y ) / 2; line.x += offsetX; // apply the offset to each characters of the line for ( let j = 0; j < line.length; j++ ) { line[ j ].offsetX += offsetX - paddingAmount; // line[ j ].offsetX += offsetX; } // line.x = line[ 0 ].offsetX; } // last operations for justifications alignments if ( ALIGNMENT.indexOf( 'justify' ) === 0 ) { for ( let i = 0; i < lines.length; i++ ) { const line = lines[ i ]; // do not process last line for justify-left or justify-right if ( ALIGNMENT.indexOf( '-' ) !== -1 && i === lines.length - 1 ) return; // can only justify is space is remaining const REMAINING_SPACE = INNER_WIDTH - line.width; if ( REMAINING_SPACE <= 0 ) return; // count the valid spaces to extend // Do not take the first nor the last space into account let validSpaces = 0; for ( let j = 1; j < line.length - 1; j++ ) { validSpaces += line[ j ].char === ' ' ? 1 : 0; } const additionalSpace = REMAINING_SPACE / validSpaces; // for right justification, process the loop in reverse let inverter = 1; if ( ALIGNMENT === 'justify-right' ) { line.reverse(); inverter = -1; } let incrementalOffsetX = 0; // start at ONE to avoid first space for ( let j = 1; j <= line.length - 1; j++ ) { // apply offset on each char const inlineCharacter = line[ j ]; inlineCharacter.offsetX += incrementalOffsetX * inverter; // and increase it when space incrementalOffsetX += inlineCharacter.char === ' ' ? additionalSpace : 0; } // for right justification, the loop was processed in reverse if ( ALIGNMENT === 'justify-right' ) { line.reverse(); } } } } function _computeLineOffset ( element, line, lastLine ) { switch ( element._textAlign._value ) { case 'justify-left': case 'justify': case 'left': return - element._bounds._innerWidth / 2; case 'justify-right': case 'right': return -line.width + ( element._bounds._innerWidth / 2 ); case 'center': return -line.width / 2; case 'justify-center': if ( lastLine ) { // center alignement return -line.width / 2; } // left alignment return - element._bounds._innerWidth / 2; default: console.warn( `textAlign: '${element._textAlign._value}' is not valid` ); } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/flex/FlexDirectionPropertyText.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class FlexDirectionPropertyText extends FlexDirectionProperty { constructor( ) { super(); this._value = this._input = 'column'; // Configure this._allowsInherit = false; this._needsUpdate = true; } /* eslint-disable no-unused-vars */computeOutputValue( element ) { /* eslint-enable no-unused-vars */ // @TODO : Evaluate the needs of this property. Could be empty this._value = this._inheritedInput; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/LineHeightPropertyInline.js class LineHeightPropertyInline extends LineHeightProperty { /** * */ constructor() { super(); // configure this._allowsInherit = false; this.computeOutputValue = this._computeFromInherited; } } ;// CONCATENATED MODULE: ./src/core/properties/style-properties/font/FontKerningPropertyText.js class FontKerningPropertyText extends FontKerningProperty { constructor() { super(); this._value = this._input = this.getDefaultValue(); // Configure this._allowsInherit = false; this.computeOutputValue = this._computeFromInherited; } _computeFromInherited( element ) { super._computeFromInherited(element); } } ;// CONCATENATED MODULE: ./src/core/properties/BoundsText.js class BoundsText extends BoundsBox { constructor() { super(); this._innerWidth = Infinity; this._innerHeight = 0; } } ;// CONCATENATED MODULE: ./src/core/properties/hierarchy/ChildrenText.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class ChildrenText extends BaseProperty { constructor() { super( 'children', null, false ); /** * * @type {Array.} * @internal */ this._uis = []; /** * * @type {Array.} * @internal */ this._inlines = []; /** * * @type {Array.} * @internal */ this._boxes = []; } /* eslint-disable no-unused-vars */ /** * Update requested when : * - New child has been added * - Existing child has been removed * * @param element * @param out */ update( element, out ) { /* eslint-enable no-unused-vars */ this._compute( element ); this._needsProcess = true; } /** * Process when : * - Existing child visibility changed * * @param element */ process( element ) { this._compute( element ); element._overflow._needsRender = true; } _compute( element ) { this._uis = element.children.filter( child => child.visible && child.isUI ); this._inlines = this._uis.filter( child => child.isInline ).sort( this._sortOrder ); } /** * */ dispose() { this._inlines = null; } /** * * Sort children according to their .style.order property or fallback on children index * * @param {HTMLElementVR} a * @param {HTMLElementVR} b * @return {number} * @private */ _sortOrder = ( a, b ) => { if( a._order._value < b._order._value ) return -1; if( a._order._value > b._order._value ) return 1; // if both children have the same order value, use their children index to order them if( this._uis.indexOf(a) < this._uis.indexOf(b) ) { return -1; } return 1; } } ;// CONCATENATED MODULE: ./src/core/properties/AutoSizePropertyText.js /** * Autosize are only trigger when natural size changed */ class AutoSizePropertyText extends BaseProperty { constructor() { super( 'autosize' ); } process( element ) { if( element._layouter._value && element._layouter._value.length ) { const lines = element._layouter._value; // as this is from children offsetWidth, it means parent innerWidth const padding = element._padding._value; const border = element._borderWidth._value; // has auto size get the height from children if ( element._width._auto ) { element._bounds.setOffsetWidth( element, lines.width + padding.w + padding.y + border.w + border.y ); } if ( element._height._auto ) { element._bounds.setOffsetHeight( element, lines.height + padding.x + padding.z + border.x + border.z ); } } } } ;// CONCATENATED MODULE: ./src/core/properties/rendering/RendererPropertyText.js class RendererPropertyText extends RendererPropertyBox{ constructor() { super( 'renderer' ); this._needsUpdate = false; } render( element ) { super.render( element ); for ( const inlineElement of element._children._inlines ) { inlineElement._renderer.render( inlineElement ); } element.performAfterUpdate(); } } ;// CONCATENATED MODULE: ./src/elements/basic/TextElement.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class TextElement extends BoxElement { /** * * @param {import('./../../core/elements/MeshUIBaseElement').Options} [values={}] * @param [properties={}] */ constructor( values = {}, properties = {}) { TextElement.definePropertiesValues( properties, values ); super( properties, values ); TextElement.init( this ); } /* eslint-disable no-unused-vars */ /** * A Text Element can only contains inline elements * @override * @param {...Object3D} object * @return {this} */ add( object ) { /* eslint-enable no-unused-vars */ /** * * @type {Array.} */ const validChildren = []; let updateLayout = false; for ( let i = 0; i < arguments.length; i++ ) { const argument = arguments[ i ]; if ( !argument.isUI || argument.isInline ) { if( argument.isInline ) { updateLayout = true; } validChildren.push( argument ); } else { console.warn( 'Block element can only contain Box elements.', argument ); } } if( validChildren.length > 0 ) { super.add( ...validChildren ) } if( updateLayout ) { this._children._needsUpdate = true; this._layouter._needsProcess = true; } return this; } set textContent ( value ) { this._textContent.value = value; } // Must redefine getter also, or issue. get textContent() { return super.textContent; } set invertAlpha( value ) { this._invertAlpha.value = value; } get invertAlpha () { return this._invertAlpha._value; } get lines() { return this._layouter._value; } /** * * @param {import('./../../core/elements/MeshUIBaseElement').Properties} properties * @param {import('./../../core/elements/MeshUIBaseElement').Options} values */ static definePropertiesValues( properties, values ) { properties.flexDirection = FlexDirectionPropertyText; properties.justifyContent = JustifyContentProperty; properties.alignItems = AlignItemsProperty; properties.bounds = BoundsText; properties.autoSize = AutoSizePropertyText; properties.renderer = RendererPropertyText; if( !properties.children ) properties.children = ChildrenText; if( !properties.textContent ) properties.textContent = TextContentText; if( !properties.layouter ) properties.layouter = TextLayouter; if( !properties.lineHeight ) properties.lineHeight = LineHeightPropertyInline; if( !properties.textAlign ) properties.textAlign = TextAlignPropertyText; if( !properties.whiteSpace ) properties.whiteSpace = WhiteSpacePropertyInline; if( !properties.fontKerning ) properties.fontKerning = FontKerningPropertyText; if( !properties.segments ) properties.segments = SegmentsPropertyText; // configure if ( !values.width ) values.width = '100%'; // break inheritance chains if ( !values.fontSide ) values.fontSide = 0; // FrontSide; } /** * * @param {MeshUIBaseElement} element */ static init( element ) { Object.defineProperties( element, { isText: { configurable: false, enumerable: true, value: true } } ); } } ;// CONCATENATED MODULE: ./src/core/properties/InlinesPropertyInlineBlock.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ /** * @property {Array.} value */ class InlinesPropertyInlineBlock extends BaseProperty{ constructor() { super( "inlines", null, false ); /** * * @type {Array.} * @internal */ this._value = []; } process( element ) { // First gets left side this._value[0].paddingLeft = element._padding._value.w; this._value[0].marginLeft = element._margin._value.w; // Last gets right side const lastIndex = this._value.length - 1; this._value[lastIndex].paddingRight = element._padding._value.y; this._value[lastIndex].marginRight = element._margin._value.y; } } ;// CONCATENATED MODULE: ./src/core/properties/rendering/RendererPropertyInlineBox.js class RendererPropertyInlineBox extends BaseProperty{ constructor() { super( 'renderer' ); } render( element ) { if( !element._backgroundMesh ) { element.setBackgroundMesh( new Frame(element) ); } element._backgroundMesh.position.x = element._inlines._value[0].offsetX + element._inlines._value[0].width/2; // element._backgroundMesh.position.y = element._inlines._value[0].offsetY + element._inlines._value[0].lineBase/4; element._backgroundMesh.position.y = element._inlines._value[0].offsetY + element._inlines._value[0].lineBase/2; element._bounds.render( element ); } } ;// CONCATENATED MODULE: ./src/core/properties/BoundsInlineBlock.js class BoundsInlineBlock extends BaseProperty { constructor() { super( 'bounds', null, false ); /** * * @type {Vector3} * @internal */ this._size = new external_THREE_namespaceObject.Vector3( 1, 1, 1 ); this._offsetWidth = 0; this._offsetHeight = 0; this._innerWidth = 0; this._innerHeight = 0; } /* eslint-disable no-unused-vars */ update( element, out ) { /* eslint-enable no-unused-vars */ this.output( out ); this._needsProcess = true; } process( element ) { this._offsetWidth = this._innerWidth = element._inlines._value[0].width; this._offsetHeight = this._innerHeight = element._inlines._value[0].height; this._needsRender = true; element._borderWidth._needsRender = true; element._borderRadius._needsRender = true; } /* eslint-disable no-unused-vars */ render( element ) { /* eslint-enable no-unused-vars */ this._size.x = this._offsetWidth; this._size.y = this._offsetHeight; } /** * * @param {Object.} out */ output( out ) { out[ 'size' ] = this._size; } } ;// CONCATENATED MODULE: ./src/elements/basic/InlineBlockElement.js class InlineBlockElement extends MeshUIBaseElement { /** * * @param {import('./../../core/elements/MeshUIBaseElement').Options} [values={}] */ constructor( values = {}) { const properties = {}; InlineBlockElement.definePropertiesValues( properties, values ); super( properties, values ); InlineBlockElement.init( this ); } clear() { // remove cross reference for ( const inline of this._inlines._value ) { inline.clear(); } return super.clear(); } /** * When the backgroundMesh has been set, bind properties * @override */ bindBackgroundMeshProperties () { this._backgroundMesh.raycast = ()=>{}; // bind the background scale with bounds this._bounds._size = this._backgroundMesh.scale; this._bounds._needsUpdate = true; } /** * When the backgroundMesh has been unset, unbind properties * @override */ unbindBackgroundMeshProperties () { // detach bounds size this._bounds._size = new external_THREE_namespaceObject.Vector3(1,1,1); this._bounds._needsUpdate = true; } /* eslint-disable no-unused-vars */ /** * * @override * @param {...Object3D} object * @return {this} */ add( object ) { /* eslint-enable no-unused-vars */ /** * * @type {Array.} */ const validChildren = []; for ( let i = 0; i < arguments.length; i++ ) { const argument = arguments[ i ]; if ( !argument.isUI ) { validChildren.push( argument ); argument.position.z = 0.005; } else { console.warn( 'ThreeMeshUI::InlineBlockElement cannot contains UI Elements.', argument ); } } return super.add( ...validChildren ); } /** * * @param {import('./../../core/elements/MeshUIBaseElement').Properties} properties * @param {import('./../../core/elements/MeshUIBaseElement').Options} values */ static definePropertiesValues( properties, values ) { if( !properties.children ) properties.children = ChildrenInline; if( !properties.bounds ) properties.bounds = BoundsInlineBlock; if( !properties.inlines ) properties.inlines = InlinesPropertyInlineBlock; if( !properties.layouter ) properties.layouter = InlineLayouter; if( !properties.renderer ) properties.renderer = RendererPropertyInlineBox; // reset inlineElement specificity if( !properties.fontFamily ) properties.fontFamily = FontFamilyPropertyInline; if( !properties.fontWeight ) properties.fontWeight = FontWeightPropertyInline; if( !properties.fontStyle ) properties.fontStyle = FontStylePropertyInline; if( !properties.fontSize ) properties.fontSize = FontSizePropertyInline; if( !properties.backgroundColor ) properties.backgroundColor = BackgroundColorProperty; if( !properties.lineBreak ) properties.lineBreak = LineBreakProperty; if( !properties.letterSpacing ) properties.letterSpacing = LetterSpacingPropertyInline; if( !properties.whiteSpace ) properties.whiteSpace = WhiteSpacePropertyInline; if( !properties.fontKerning ) properties.fontKerning = FontKerningProperty; if( !values.backgroundSize ) values.backgroundSize = 'cover'; if( !values.width ) values.width = '100%'; if( !values.height ) values.height = '100%'; if( !values.boxSizing ) values.boxSizing = 'border-box'; } /** * * @param {MeshUIBaseElement} element */ static init( element ) { Object.defineProperties( element, { isInline: { configurable: false, enumerable: true, value: true }, isInlineBlock: { configurable: false, enumerable: true, value: true } } ); element._inlines._value = [new InlineBlockInline(element)]; element.backgroundMaterial = new FrameMaterial(); element._renderer.render( element ); } } /** * InlineBlock has its own Inline implementation */ class InlineBlockInline extends Inline { /** * * @param {InlineBlockElement} parent */ constructor( parent ) { super(); /** * @TODO: This currently make a circular reference that should ideally be removed * @type {InlineBlockElement} * @private */ this._uiElement = parent; } /** * Rely on the parent for size computation * @override * @returns {number} */ get xadvance() { const padding = this._uiElement._padding._value; const width = this._uiElement._width; if( width._relative ) { return width._value * this._uiElement._fontSize.getInheritedInput( this._uiElement ); } return padding.w + padding.y + width.value ; } /** * Rely on the parent for size computation * @override * @returns {number} */ get width() { const width = this._uiElement._width; if( width._relative ) { return width._value * this._uiElement._fontSize.getInheritedInput( this._uiElement ); } return width.value; } /** * Rely on the parent for size computation * @override * @returns {number} */ get height() { const height = this._uiElement._height; if( height._relative ) { return height._value * this._uiElement._fontSize.getInheritedInput( this._uiElement ) ; } return height.value; } get anchor(){ return this.height; } /** * Rely on the parent for size computation * @override * @returns {number} */ get lineHeight() { const height = this._uiElement._height; if( height._relative ) { return height._value * this._uiElement._fontSize.getInheritedInput( this._uiElement ); } return height.value; } /** * Rely on the parent for size computation * @override * @returns {number} */ get lineBase() { const height = this._uiElement._height; if( height._relative ) { return height._value * this._uiElement._fontSize.getInheritedInput( this._uiElement ); } return height.value; } /** * */ clear() { this._uiElement = null; } } ;// CONCATENATED MODULE: ./src/utils/Behavior.js //JSDoc related imports /* eslint-disable no-unused-vars */ /* eslint-enable no-unused-vars */ class Behavior { /** * * @param {MeshUIBaseElement} subject */ constructor( subject ) { /** * * @type {MeshUIBaseElement} * @protected */ this._subject = subject; } /** * @abstract */ attach() { console.error(`Behavior::attach() - Is abstract and therefore should be overridden in ${this.constructor.name}`); } /** * @abstract * @returns {void} */ act() { throw new Error(`Behavior::act() - Is abstract and therefore should be overridden in ${this.constructor.name}`); } /** * @abstract */ detach() { console.error(`Behavior::detach() - Is abstract and therefore should be overridden in ${this.constructor.name}`); } /** * */ clear() { } } ;// CONCATENATED MODULE: ./src/three-mesh-ui.js /* global global */ const update = () => UpdateManager.update(); const ThreeMeshUI = { BaseProperty: BaseProperty, Block: BlockElement, Text : TextElement, Inline: InlineElement, InlineBlock : InlineBlockElement, // Keyboard : KeyboardElement, MeshUIBaseElement: MeshUIBaseElement, FontLibrary: font_FontLibrary, update, MSDFFontMaterialUtils: MSDFFontMaterialUtils, ShaderChunkUI: ShaderChunkUI, Behavior: Behavior, FontVariant: font_FontVariant }; if ( typeof __webpack_require__.g !== 'undefined' ) __webpack_require__.g.ThreeMeshUI = ThreeMeshUI; /* harmony default export */ const three_mesh_ui = ((/* unused pure expression or super */ null && (ThreeMeshUI))); // console.warn("ThreeMeshUI v7.1.x - Three "+window.__THREE__) /******/ })() ;