15484 lines
310 KiB
JavaScript
15484 lines
310 KiB
JavaScript
/******/ (() => { // 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.<string,any>} 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.<string,any>} 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.<string,any>}
|
|
*/
|
|
|
|
;// 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.<string,any> } 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>|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.<ShaderMaterial|Material>} v
|
|
* @abstract
|
|
*/
|
|
set fontMaterial( v ) {
|
|
throw Error( `FontVariant('${this.id}')::fontMaterial - is abstract.` );
|
|
}
|
|
|
|
/**
|
|
* @return {Function.<ShaderMaterial|Material>}
|
|
* @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.<BufferGeometry>}
|
|
*/
|
|
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.<string,Object.<string,number|string>>} 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.<string,number>} 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.<string>}
|
|
*/
|
|
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.<Plane>|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.<BorderRadiusMediator>}
|
|
* @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.<string,any>} 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.<string>} sideProperties
|
|
*/
|
|
constructor( borderRadiuses, sideProperties ) {
|
|
|
|
/**
|
|
*
|
|
* @type {Vector4}
|
|
* @private
|
|
*/
|
|
this._borderRadiuses = borderRadiuses;
|
|
|
|
/**
|
|
*
|
|
* @type {Array<string>}
|
|
* @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.<string,any>} out
|
|
*/
|
|
update( element , out ) { /* eslint-enable no-unused-vars */ }
|
|
|
|
/* eslint-disable no-unused-vars */
|
|
/**
|
|
* Output this property in a dictionnary
|
|
* @param {Object.<string,any>} 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.<string>}
|
|
*/
|
|
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.<string>}
|
|
*/
|
|
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.<string>}
|
|
*/
|
|
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.<string,any>} 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.<string,any>} 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 <uv_pars_vertex>',
|
|
'#include <uv_pars_vertex>\n' + msdf_alphaglyph_pars_vertex_glsl
|
|
);
|
|
|
|
// vertex chunks
|
|
shader.vertexShader = shader.vertexShader.replace(
|
|
'#include <uv_vertex>',
|
|
'#include <uv_vertex>\n' + msdf_alphaglyph_vertex_glsl
|
|
)
|
|
|
|
shader.vertexShader = shader.vertexShader.replace(
|
|
'#include <project_vertex>',
|
|
'#include <project_vertex>\n' + msdf_offsetglyph_vertex_glsl
|
|
)
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param shader
|
|
*/
|
|
static injectFragmentShaderChunks( shader ) {
|
|
shader.fragmentShader = shader.fragmentShader.replace(
|
|
'#include <uv_pars_fragment>',
|
|
'#include <uv_pars_fragment>\n' + msdf_alphaglyph_pars_fragment_glsl
|
|
)
|
|
|
|
// fragment chunks
|
|
shader.fragmentShader = shader.fragmentShader.replace(
|
|
'#include <alphamap_fragment>',
|
|
'#include <alphamap_fragment>\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 <clipping_planes_pars_vertex>
|
|
void main() {
|
|
${msdf_alphaglyph_vertex_glsl}
|
|
#include <begin_vertex>
|
|
#include <project_vertex>
|
|
${msdf_offsetglyph_vertex_glsl}
|
|
#include <clipping_planes_vertex>
|
|
}
|
|
`
|
|
|
|
/**
|
|
*
|
|
* @type {string}
|
|
*/
|
|
const fragmentShader = /* glsl */`
|
|
uniform vec3 diffuse;
|
|
uniform float opacity;
|
|
${msdf_alphaglyph_pars_fragment_glsl}
|
|
#include <alphatest_pars_fragment>
|
|
#include <clipping_planes_pars_fragment>
|
|
void main() {
|
|
// instead of <color_fragment> : vec4 diffuseColor
|
|
vec4 diffuseColor = vec4( diffuse, opacity );
|
|
${msdf_alphaglyph_fragment_glsl}
|
|
#include <alphatest_fragment>
|
|
// instead of <output_fragment>
|
|
gl_FragColor = diffuseColor;
|
|
#include <clipping_planes_fragment>
|
|
}
|
|
`
|
|
|
|
;// 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.<Material|ShaderMaterial>} v
|
|
* @override
|
|
*/
|
|
set fontMaterial( v ) {
|
|
|
|
this._defaultMaterialClass = v;
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @override
|
|
* @returns {Function.<Material|ShaderMaterial>}
|
|
*/
|
|
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.<MSDFJsonPage>} pages
|
|
* @property {Array.<MSDFJsonChar>} chars
|
|
* @property {{fieldType:string, distanceRange:number}} distanceField
|
|
* @property {Array.<MSDFJsonKerning>} 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.<number>} padding TThe padding for each character (up, right, down, left).
|
|
* @property {Array.<number>} 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.<FontVariant>}
|
|
* @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<unknown>}
|
|
*/
|
|
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.<string>}
|
|
*/
|
|
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.<BaseProperty>}
|
|
* @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.<Mesh>|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.<Mesh>|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.<string,Function>}
|
|
*/
|
|
|
|
/**
|
|
* @typedef Options
|
|
* @type {DocumentedOptions & Object.<string,any>}
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* @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>|number|string}
|
|
* @property [options.borderWidth] {Vector4|Array.<number>|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>|number|string}
|
|
* @property [options.margin] {Vector4|Array.<number>|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.<MeshUIBaseElement>}
|
|
*/
|
|
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 <clipping_planes_pars_vertex>
|
|
|
|
void main() {
|
|
|
|
#ifdef USE_MAP
|
|
vUv = uv;
|
|
#endif
|
|
|
|
${frame_border_vertex_glsl}
|
|
|
|
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
|
|
gl_Position = projectionMatrix * mvPosition;
|
|
|
|
#include <clipping_planes_vertex>
|
|
|
|
}
|
|
`
|
|
|
|
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 <clipping_planes_pars_fragment>
|
|
|
|
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 <clipping_planes_fragment>
|
|
}
|
|
`
|
|
|
|
;// 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.<string,any>} 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.<string,any>} 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 <uv_pars_vertex>',
|
|
'#include <uv_pars_vertex>\n' + frame_border_pars_vertex_glsl
|
|
);
|
|
|
|
// vertex chunks
|
|
shader.vertexShader = shader.vertexShader.replace(
|
|
'#include <uv_vertex>',
|
|
'#include <uv_vertex>\n' + frame_border_vertex_glsl
|
|
)
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param shader
|
|
*/
|
|
static injectFragmentShaderChunks( shader ) {
|
|
shader.fragmentShader = shader.fragmentShader.replace(
|
|
'#include <map_pars_fragment>',
|
|
'#include <map_pars_fragment>\n' + frame_background_pars_fragment_glsl
|
|
)
|
|
|
|
shader.fragmentShader = shader.fragmentShader.replace(
|
|
'#include <map_pars_fragment>',
|
|
'#include <map_pars_fragment>\n' + frame_border_pars_fragment_glsl
|
|
)
|
|
|
|
shader.fragmentShader = shader.fragmentShader.replace(
|
|
'#include <map_pars_fragment>',
|
|
'#include <map_pars_fragment>\n' + frame_common_pars_fragment_glsl
|
|
)
|
|
|
|
// fragment chunks
|
|
shader.fragmentShader = shader.fragmentShader.replace(
|
|
'#include <map_fragment>',
|
|
frame_background_fragment_glsl
|
|
)
|
|
|
|
shader.fragmentShader = shader.fragmentShader.replace(
|
|
'#include <alphamap_fragment>',
|
|
frame_border_fragment_glsl+'\n#include <alphamap_fragment>'
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @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.<MeshUIBaseElement>}
|
|
* @private
|
|
*/
|
|
this._uis = [];
|
|
|
|
/**
|
|
*
|
|
* @type {Array.<MeshUIBaseElement>}
|
|
* @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.<string,any>} 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.<number> }
|
|
* @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.<string,Vector3>}
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* @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.<Object3D>}
|
|
*/
|
|
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.<Inline>}
|
|
* @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.<Inline>}
|
|
*/
|
|
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.<TypographicGlyph>}
|
|
* @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.<TypographicGlyph>}
|
|
*/
|
|
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<string,any>) => 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.<MeshUIBaseElement>}
|
|
* @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<BufferGeometry>} 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<BufferAttribute>} 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<BufferAttribute>} attributes
|
|
* @return {Array<InterleavedBufferAttribute>}
|
|
*/
|
|
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.<Object3D>}
|
|
*/
|
|
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.<MeshUIBaseElement>}
|
|
* @internal
|
|
*/
|
|
this._uis = [];
|
|
|
|
/**
|
|
*
|
|
* @type {Array.<MeshUIBaseElement>}
|
|
* @internal
|
|
*/
|
|
this._inlines = [];
|
|
|
|
/**
|
|
*
|
|
* @type {Array.<MeshUIBaseElement>}
|
|
* @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.<Object3D>}
|
|
*/
|
|
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.<InlineGlyph>} value
|
|
*/
|
|
class InlinesPropertyInlineBlock extends BaseProperty{
|
|
|
|
constructor() {
|
|
|
|
super( "inlines", null, false );
|
|
|
|
/**
|
|
*
|
|
* @type {Array.<Inline>}
|
|
* @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.<string,any>} 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.<Object3D>}
|
|
*/
|
|
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__)
|
|
|
|
|
|
|
|
|
|
|
|
/******/ })()
|
|
; |