import { mat4 , vec3 } from 'gl-matrix' ; import { ChangeLookup } from '../changelookup' ; import { Color } from '../color' ; import { GLclampf4 } from '../tuples' ; import { FontFace } from './fontface' ; import { GlyphVertices } from './glyphvertices' ; import { Text } from './text' ; /**
 * Object comprising a text reference, a font face, and additional typographic information for type setting, rendering,
 * and interaction. Multiple labels might reference the same text, but could be placed at different locations or
 * rendered applying different font faces, styles etc.
 */ export abstract class Label { private static readonly DEFAULT_COLOR : GLclampf4 = [ 0.1098 , 0.4588 , 0.7373 , 1.0 ] ; private static readonly DEFAULT_ELLIPSIS = '...' ; see text protected _text : Text ; see alignment protected _alignment : Label . Alignment = Label . Alignment . Left ; see lineAnchor protected _lineAnchor : Label . LineAnchor = Label . LineAnchor . Baseline ; see lineWidth protected _lineWidth = NaN ; see fontSize protected _fontSize : number ; see fontSizeUnit protected _fontSizeUnit : Label . Unit = Label . Unit . World ; see fontFace protected _fontFace : FontFace | undefined ; see color protected _color : Color = new Color ( Label . DEFAULT_COLOR ) ; see background protected _backgroundColor : Color ; see type protected _type : Label . Type ; see staticTransform protected _staticTransform : mat4 ; see dynamicTransform protected _dynamicTransform : mat4 ; see extent protected _extent : [ number , number ] ; see altered protected readonly _altered = Object . assign ( new ChangeLookup ( ) , { any : false , color : false , resources : false , text : false , typesetting : false , static : false , dynamic : false , } ) ; see wrap protected _wrap = false ; see elide protected _elide : Label . Elide = Label . Elide . None ; see ellipsis protected _ellipsis : string = Label . DEFAULT_ELLIPSIS ; /**
     * Returns the window device pixel ratio. If this is not available/undefined 1.0 is returned.
     */ static devicePixelRatio ( ) { if ( window === undefined || typeof window . devicePixelRatio !== 'number' ) { return 1.0 ; } return window . devicePixelRatio as number ; } param text param type param fontFace constructor ( text , type , fontFace ? ) { this . _text = text ; this . _type = type ; this . _staticTransform = mat4 . create ( ) ; this . _dynamicTransform = mat4 . create ( ) ; this . _extent = [ 0 , 0 ] ; if ( fontFace ) { this . _fontFace = fontFace ; } } /**
     * Creates an Array of glyph vertices, ready to be used in the Typesetter.
     */ protected vertices ( ) { const vertices = new GlyphVertices ( this . length + this . ellipsis . length ) ; return vertices ; } /**
     * Interface intended to compute/update the label's static and dynamic transformations as well as invoking the
     * typesetter in order to create the glyph vertices. Returns undefined, if previous vertices can be reused since no
     * typesetting was required. Returns an empty GlyphVertices storage if label is invalid or cannot be rendered ...
     */ abstract typeset ( ) ; param index returns charAt ( index ) { return this . _text . charAt ( index ) ; } param index returns charCodeAt ( index ) { return this . _text . charCodeAt ( index ) ; } param index returns lineFeedAt ( index ) { return this . charAt ( index ) === this . lineFeed ; } param index returns kerningBefore ( index ) { if ( index < 1 || index > this . length ) { return NaN ; } return this . _fontFace ! . kerning ( this . charCodeAt ( index - 1 ) , this . charCodeAt ( index ) ) ; } param index returns kerningAfter ( index ) { if ( index < 0 || index > this . length - 1 ) { return NaN ; } return this . _fontFace ! . kerning ( this . charCodeAt ( index ) , this . charCodeAt ( index + 1 ) ) ; } param index returns advance ( index ) { if ( index < 0 || index > this . length ) { return NaN ; } return this . _fontFace ! . glyph ( this . charCodeAt ( index ) ) . advance ; } returns toString ( ) { if ( this . _text instanceof Text ) { return this . _text . text ; } return this . _text ; } /**
     * Intended for resetting alteration status.
     */ reset ( ) { this . _altered . reset ( ) ; } /**
     * Text that is to be rendered.
     */ set text ( text ) { this . _altered . alter ( 'text' ) ; this . _text = text ; } get text ( ) : Text { return this . _text ; } /**
     * Read-only access to this labels type specified at construction time. Static labels are baking as much
     * transformations as possible into the glyph vertices (used for GPU). This means, when the position or size
     * changes, the label must be typeset again and vertices are fully re-computed. For dynamic labels, only most
     * relevant transformations are applied and dynamic transformations such as rotation, translation, scale etc,
     * are applied during rendering without requiring re-typesetting or re-computation of vertices. The type,
     * however, does not relate to the text. Whenever the text changes, re-typesetting etc. have to be invoked.
     */ get type ( ) : Label . Type { return this . _type ; } /**
     * Length of the text, i.e., number of characters within the text.
     */ get length ( ) : number { return this . _text . length ; } /**
     * Character that is to be used for Line feed.
     */ get lineFeed ( ) : string { if ( this . _text instanceof Text ) { return this . _text . lineFeed ; } return Text . DEFAULT_LINE_FEED ; } /**
     * If enabled, breaks lines automatically at line width (while typesetting). Note that elide mode takes precedence.
     */ set wrap ( flag ) { this . _wrap = flag ; } get wrap ( ) : boolean { return this . _wrap ; } /**
     * If enabled, shrinks the label to line width. Depending on the elide mode, the ellipses is put left, middle, or
     * right. The ellipsis string can be adjusted (@see  ellipsis ). If the labels text does not exceed the line
     * width no elide will be applied.
      set elide ( elide ) { this . _elide = elide ; } get elide ( ) : Label . Elide { return this . _elide ; } /**
     * Allows to override/customize the ellipsis string used for text elide (@see  elide ).
      set ellipsis ( ellipsis ) { if ( this . _ellipsis === ellipsis ) { return ; } this . _ellipsis = ellipsis ; if ( this . _elide !== Label . Elide . None ) { this . _altered . alter ( 'typesetting' ) ; } } get ellipsis ( ) : string { return this . _ellipsis ; } /**
     * Line width used to either maximum length for elide or maximum length for line breaks due to word wrap. The line
     * width is expected in font size.
     */ set lineWidth ( lineWidth ) { if ( this . _lineWidth === lineWidth ) { return ; } this . _lineWidth = lineWidth ; this . _altered . alter ( 'typesetting' ) ; } /**
     * Width of a single line in typesetting space (the unit used while Typesetting, i.e., the unit as the font face's
     * glyph texture atlas). Since the font face needs to be defined in order to typeset, we assume here that the label
     * has a defined fontFace.
     */ get lineWidth ( ) : number { return this . _lineWidth * this . _fontFace ! . size / this . fontSize ; } /**
     * Horizontal text alignment for typesetting.
     */ set alignment ( alignment ) { if ( this . _alignment === alignment ) { return ; } this . _alignment = alignment ; this . _altered . alter ( 'typesetting' ) ; } get alignment ( ) : Label . Alignment { return this . _alignment ; } /**
     * Vertical text anchor point used for positional reference.
     */ set lineAnchor ( anchor ) { if ( this . _lineAnchor === anchor ) { return ; } this . _lineAnchor = anchor ; this . _altered . alter ( 'typesetting' ) ; } get lineAnchor ( ) : Label . LineAnchor { return this . _lineAnchor ; } /**
     * The currently used font size.
     * (@see  fontSizeUnit )
      set fontSize ( size ) { if ( this . _fontSize === size ) { return ; } this . _fontSize = size ; this . _altered . alter ( 'typesetting' ) ; } get fontSize ( ) : number { return this . _fontSize ; } /**
     * This unit is used for the font size.
     * (@see  fontSize )
      set fontSizeUnit ( unit ) { if ( this . _fontSizeUnit === unit ) { return ; } this . _fontSizeUnit = unit ; this . _altered . alter ( 'typesetting' ) ; } get fontSizeUnit ( ) : Label . Unit { return this . _fontSizeUnit ; } /**
     * Font face used for typesetting, transformation, and rendering. To avoid unnecessary state changes when rendering,
     * prefer to add labels of the same font face consecutively (since this specifies draw sequence and state change
     * occurs whenever font face changes between two subsequent labels).
     */ set fontFace ( fontFace ) { if ( this . _fontFace === fontFace ) { return ; } this . _fontFace = fontFace ; this . _altered . alter ( 'typesetting' ) ; this . _altered . alter ( 'resources' ) ; } get fontFace ( ) : FontFace | undefined { return this . _fontFace ; } /**
     * Color used for text rendering.
     */ set color ( color ) { if ( this . _color . equals ( color ) ) { return ; } this . _color = color ; this . _altered . alter ( 'color' ) ; } get color ( ) : Color { return this . _color ; } /**
     * Color used for background of text rendering.
     */ set backgroundColor ( color ) { if ( this . _backgroundColor . equals ( color ) ) { return ; } this . _backgroundColor = color ; this . _altered . alter ( 'color' ) ; } get backgroundColor ( ) : Color { return this . _backgroundColor ; } /**
     * Transformation used to move, scale, rotate, skew, etc. the label into an arbitrary coordinate space (e.g.,
     * screen space, world space, ...). This can be set either explicitly or implicitly using various transformation
     * utility functions. @todo review/refine this.
     */ set staticTransform ( transform ) { if ( mat4 . equals ( this . _staticTransform , transform ) ) { return ; } this . _staticTransform = transform ; this . _altered . alter ( 'static' ) ; } get staticTransform ( ) : mat4 { const s = this . fontSize / this . _fontFace ! . size ; const t = mat4 . create ( ) ; mat4 . scale ( t , this . _staticTransform , vec3 . fromValues ( s , s , s ) ) ; return t ; } /**
     * Stores the resulting dynamic transform. This is intended to be used when in dynamic mode.
     * (e.g., for calculations to the final transform).
     */ set dynamicTransform ( t ) { this . _altered . alter ( 'dynamic' ) ; this . _dynamicTransform = t ; } get dynamicTransform ( ) : mat4 { return this . _dynamicTransform ; } /**
     * The typesetter sets this extent after typesetting and applying the static transform. Don't set this manually
     * without typesetting.
     */ set extent ( e ) { this . _extent = e ; } /**
     * Returns the width and height of the typset label in fontSizeUnit. Both are zero if not typeset yet. The static
     * transform is already applied.
     */ get extent ( ) : [ number , number ] { return this . _extent ; } get altered ( ) : boolean { return this . _altered . any || this . _text . altered || this . _color . altered ; } /**
     * Returns whether or not this label is ready to be rendered (aka has a font face with valid texture
     * as well as a text with a length > 0).
     */ get valid ( ) : boolean { return this . _fontFace !== undefined && this . _fontFace . glyphTexture . valid && this . _text . length > 0 ; } } export namespace Label { export enum Type { Static = 'static' , Dynamic = 'dynamic' } export enum Elide { None = 'none' , Left = 'left' , Middle = 'middle' , Right = 'right' } export enum Alignment { Left = 'left' , Center = 'center' , Right = 'right' } export enum LineAnchor { Top = 'top' , Ascent = 'ascent' , Center = 'center' , Baseline = 'baseline' , Descent = 'descent' , Bottom = 'bottom' } /**
     * This unit is used for the font size and related calculations.
     */ export enum Unit { World = 'world' , Pixel = 'pixel' , Mixed = 'mixed' } }