From d3dfef30f074b1a7ad7ae257ca73845627a7e7cb Mon Sep 17 00:00:00 2001 From: Daniel Limberger Date: Fri, 6 Apr 2018 16:07:12 +0200 Subject: [PATCH 01/92] add font face --- source/fontface.ts | 337 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 source/fontface.ts diff --git a/source/fontface.ts b/source/fontface.ts new file mode 100644 index 00000000..382442f1 --- /dev/null +++ b/source/fontface.ts @@ -0,0 +1,337 @@ + +import { vec2, vec4 } from 'gl-matrix'; + +import { assert } from './auxiliaries'; +import { Context } from './context'; +import { Glyph, GlyphIndex } from './glyph'; +import { Texture2 } from './texture2'; +import { GLsizei2, GLsizei4 } from './tuples'; +import { Wizard } from './wizard'; + + +/** + * Font related data for glyph based text rendering. The glyph-based font face is described by, e.g., font-size, + * line spacing, a glyph catalogue, as well as kerning information. The glyph catalogue is based on a set of glyphs + * referring to a texture atlas (@see {@link Glyph}). All measures are provided in float even though most + * glyph-textures and associated font data is encoded via integer values. A font face explicitly relies on floating + * values to reduce the need of casting as well as to simplify the use for dpi aware text rendering. Most measures can + * be interpreted as points (by means of the unit pt), again, easing the use for arbitrary dpi. + * The font face interface is designed to access most basic font settings ascent, descent, and line gap (leading). + * Additional font settings are provided via interface but are derived from or mapped to the above mentioned three + * settings, e.g., font size is the sum of descent and ascent. This is to provide as much convenience measures for type + * setting/font rendering as possible. + * Note: This class does not provide dpi awareness, which has to be handled outside of this class, e.g., during + * layouting and rendering. + */ +export class FontFace { + + /** @see {@link base} */ + protected _base: number; + + /** @see {@link ascent} */ + protected _ascent = 0.0; + + /** @see {@link descent} */ + protected _descent = 0.0; + + /** @see {@link lineGap} */ + protected _lineGap = 0.0; + + /** @see {@link glyphTextureExtent} */ + protected _glyphTextureExtent: GLsizei2 = [0, 0]; + + /** @see {@link glyphTexturePadding} */ + protected _glyphTexturePadding: GLsizei4 = [0, 0, 0, 0]; + + /** @see {@link glyphTexturePadding} */ + protected _glyphTexture: Texture2; + + /** + * Map associating a glyph index to a glyph (sub image of a texture). + * @see {@link glyph} + * @see {@link hasGlyph} + * @see {@link addGlyph} + */ + protected _glyphs = new Map(); + + protected _context: Context; + + /** + * Constructs an unconfigured, empty font face specification. The appropriate setters should be used for configuring + * the font face. Alternatively, the font importer (@see {@link FontImporter}) provides the import of bitmap-font + * base configuration file ({@link http://www.angelcode.com/products/bmfont/}). + * @param context - Valid context to create the object for. + * @param identifier - Meaningful name for identification of this instances VAO and VBOs. + */ + constructor(context: Context, identifier?: string) { + this._context = context; + const gl = context.gl; + + identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; + this._glyphTexture = new Texture2(context, identifier + 'GlyphAtlas'); + const internalFormat = Wizard.queryInternalTextureFormat(context, gl.RGBA, 'byte'); + this._glyphTexture.initialize(1, 1, internalFormat[0], gl.RGBA, internalFormat[1]); + } + + /** + * The size of the font in pt. The font size is the measure from the tops of the tallest glyphs (ascenders) to the + * bottom of the lowest descenders in pt. It is derived via the sum of ascent and descent. + * @return - The font size in pt (ascent + descent). + */ + get size(): number { + // Note: this._descent is usually negative. + return this._ascent - this._descent; + } + + /** + * The font's base in pt. The base is the distance from the baseline to the top of the line in pt. + * @return - The distance from the baseline to the top of the line in pt. + */ + get base(): number { + return this._base; + } + + /** + * Set the font's base in pt. The base is the distance from the baseline to the top of the line in pt. + * @param base - The distance from the baseline to the top of the line in pt. + */ + set base(base: number) { + assert(base > 0.0, 'base should be larger than zero.'); + this._base = base; + } + + /** + * The font's ascent in pt. The ascent is the distance from the baseline to the tops of the tallest glyphs + * (ascenders) in pt. + * @return - The distance from the baseline to the topmost ascenders in pt. + */ + get ascent(): number { + return this._ascent; + } + + /** + * Set the font's ascent in pt. The ascent is the distance from the baseline to the tops of the tallest glyphs + * (ascenders) in pt. + * @param ascent - The distance from the baseline to the topmost ascenders in pt. + */ + set ascent(ascent: number) { + assert(ascent > 0.0, 'ascent should be larger than zero.'); + this._ascent = ascent; + } + + /** + * The font's descent in pt. The descent is the distance from the baseline to the lowest descenders in pt. Please + * note that this value is usually negative (if the fonts lowest descenders are below the baseline). + * @return - The distance from the baseline to the lowest descenders in pt. + */ + get descent(): number { + return this._descent; + } + + /** + * Set the font's descent in pt. The descent is the distance from the baseline to the lowest descenders in pt. + * Please note that this value is usually negative (if the fonts lowest descenders are below the baseline). + * @param descent - The distance from the baseline to the lowest descenders in pt. + */ + set descent(descent: number) { + /* No assert here: there might be fonts with their lowest descender above baseline. */ + // assert(descent < 0.f, ...); + this._descent = descent; + } + + /** + * The font's leading/line gap in pt. The leading is the distance from the lowest descenders to the topmost + * ascenders of a subsequent text line in pt. + * @return - The gap between two subsequent lines of text in pt. + */ + get lineGap(): number { + return this._lineGap; + } + + /** + * Set the font's leading/linegap in pt. The leading is the distance from the lowest descenders to the topmost + * ascenders of a subsequent text line in pt. + * @param lineGap - The gap between two subsequent lines of text in pt. + */ + set lineGap(lineGap: number) { + this._lineGap = lineGap; + } + + /** + * The baseline-to-baseline distance in pt. The line height is derived as follows: + * line_height = size + line_gap, or alternatively: line_height = size * line_space + * @return - The line height (baseline-to-baseline distance) in pt. + */ + get lineHeight(): number { + return this.size + this.lineGap; + } + + /** + * Set the baseline-to-baseline distance in pt. Negative values will result in negative linegap. + * @param lineHeight - The line height (baseline-to-baseline distance) in pt. + */ + set lineHeight(lineHeight: number) { + this._lineGap = lineHeight - this.size; + } + + /** + * The relative baseline-to-baseline distance w.r.t. the font's size. The relative line space is derived as follows: + * line_space = size / line_height; Note that the descent is usually a negative value. + * @return - The relative baseline-to-baseline distance w.r.t. the font's size. + */ + get lineSpace(): number { + if (this.lineHeight === 0.0) { + return this.lineHeight; + } + return this.size / this.lineHeight; + } + + /** + * Set the relative baseline-to-baseline distance w.r.t. the font's size. The line space is mapped to line gap as + * follows: line_gap = size * (line_space - 1). A space < 1.0 will result in a negative line gap. + * @param lineSpace - The relative baseline-to-baseline distance w.r.t. the font's size. + */ + set lineSpace(lineSpace: number) { + this._lineGap = this.size * (lineSpace - 1); + } + + /** + * The size/extent of the glyph texture in px. This can only be set via setGlyphTexture. + * @return - The size/extent of the glyph texture in px. + */ + get glyphTextureExtent(): GLsizei2 { + return this._glyphTextureExtent; + } + + // /** + // * Sets the glyph texture atlas extent. + // * @param extent - The texture extent in px + // */ + // set glyphTextureExtent(extent: GLsizei2) { + // assert(extent[0] > 0, 'expected extent.x to be larger than zero.'); + // assert(extent[1] > 0, 'expected extent.y to be larger than zero.'); + // this._glyphTextureExtent = extent; + // } + + /** + * The padding applied to every glyph in px. This can only be set via setGlyphTexture. + * @return - The CSS style (top, right, bottom, and left) padding applied to every glyph within the texture in px. + */ + get glyphTexturePadding(): GLsizei4 { + return this._glyphTexturePadding; + } + + // /** + // * Sets/updates the padding used for the glyph texture atlas. + // * @param padding The CSS style (top, right, bottom, left) padding applied to every glyph within the texture + // * in px. + // */ + // set glyphTexturePadding(padding: GLsizei4) { + // assert(padding[0] >= 0.0, 'expected padding[0] to be larger than zero.'); + // assert(padding[1] >= 0.0, 'expected padding[1] to be larger than zero.'); + // assert(padding[2] >= 0.0, 'expected padding[2] to be larger than zero.'); + // assert(padding[3] >= 0.0, 'expected padding[3] to be larger than zero.'); + // this._glyphTexturePadding = padding; + // } + + /** + * The font face's associated glyph atlas. All glyph data is associated to this texture atlas. + * @return - The texture object containing the texture atlas. + */ + get glyphTexture(): Texture2 { + return this._glyphTexture; + } + + /** + * Sets/updates the glyph texture atlas used for all comprised glyphs. + * @param texture - The new texture atlas for all glyphs + */ + set glyphTexture(texture: Texture2) { + this._glyphTexture = texture; + } + + /** + * Check if a glyph of a specific index is available. + * @return - True if a glyph for the provided index was added. + */ + hasGlyph(index: GlyphIndex): boolean { + return !!this._glyphs.get(index); + } + + /** + * Direct access to an indexed glyph. If the glyph does not exist, an empty glyph is returned without adding it to + * glyphs. The glyph atlas might be loaded asynchronously, thus, new glyphs are expected to be added via addGlyph. + * @param index - Index of the glyph to access. + * @return - Glyph with the matching index or an empty glyph, if index has not match + */ + glyph(index: GlyphIndex): Glyph { + const existingGlyph = this._glyphs.get(index); + if (existingGlyph) { + return existingGlyph; + } + const glyph = new Glyph(); + glyph.index = index; + return glyph; + } + + /** + * Add a glyph to the font face's set of glyphs. If the glyph already exists, the existing glyph remains. + * @param glyph - The glyph to add to the set of glyphs. + */ + addGlyph(glyph: Glyph) { + assert(!(this._glyphs.get(glyph.index)), 'expected glyph to not already exist'); + this._glyphs.set(glyph.index, glyph); + } + + /** + * Generates aan array of all comprised glyph indices. + * @return - An array of all glyph indices available to this font face. + */ + arrayOfGlyphIndices(): Array { + return Array.from(this._glyphs.keys()); + } + + /** + * Check if a glyph is depictable/renderable. If the glyph's sub-texture vertical or horizontal extent is zero the + * glyph does not need to be depicted/rendered. E.g., spaces, line feeds, other control sequences as well as + * unknown glyphs do not need to be processed for rendering. + * @param index - Index of the glyph to access. + * @return - Returns true if the glyph needs to be depicted/rendered. + */ + depictable(index: GlyphIndex): boolean { + return this.glyph(index).depictable(); + } + + /** + * Kerning for a glyph and a subsequent glyph in pt. If the glyph or the subsequent glyph are unknown to this font + * face (assertion), 0.f will be returned. For more details on kerning, refer to the Glyph class. + * @param index - The current glyph index (e.g., of the current pen-position). + * @param subsequentIndex - The glyph index of the subsequent/next glyph. + * @return - The kerning (usually negative) between the two glyphs in pt. If either on of the glyphs is unknown to + * this font face or no specific kerning for the glyph pair is available a zero kerning is returned. + */ + kerning(index: GlyphIndex, subsequentIndex: GlyphIndex): number { + const glyph = this._glyphs.get(index); + if (!glyph) { + return 0.0; + } + return glyph.kerning(subsequentIndex); + } + + /** + * Set the kerning for a glyph w.r.t. to a subsequent glyph in pt. If the glyph is known to this font face, the + * values are forwarded to the glyphs kerning setter (see Glyph for more information). + * @param index - The target glyph index. + * @param subsequentIndex - The glyph index of the respective subsequent/next glyph. + * @param kerning - Kerning of the two glyphs in pt. + */ + setKerning(index: GlyphIndex, subsequentIndex: GlyphIndex, kerning: number): void { + const glyph = this._glyphs.get(index); + if (!glyph || !this.hasGlyph(subsequentIndex)) { + assert(false, 'expected glyph or glyph of subsequent index to exist.'); + return; + } + glyph.setKerning(subsequentIndex, kerning); + } + +} From d34d48f3daab677100fc76857212a682c814d653 Mon Sep 17 00:00:00 2001 From: Daniel Limberger Date: Fri, 6 Apr 2018 18:01:27 +0200 Subject: [PATCH 02/92] refine font face, add glyph object --- source/fontface.ts | 148 ++++++++++++++-------------------------- source/glyph.ts | 165 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+), 96 deletions(-) create mode 100644 source/glyph.ts diff --git a/source/fontface.ts b/source/fontface.ts index 382442f1..9fbf1977 100644 --- a/source/fontface.ts +++ b/source/fontface.ts @@ -1,11 +1,10 @@ -import { vec2, vec4 } from 'gl-matrix'; - import { assert } from './auxiliaries'; + import { Context } from './context'; -import { Glyph, GlyphIndex } from './glyph'; +import { Glyph } from './glyph'; import { Texture2 } from './texture2'; -import { GLsizei2, GLsizei4 } from './tuples'; +import { GLfloat2, GLfloat4, GLsizei2 } from './tuples'; import { Wizard } from './wizard'; @@ -38,21 +37,19 @@ export class FontFace { protected _lineGap = 0.0; /** @see {@link glyphTextureExtent} */ - protected _glyphTextureExtent: GLsizei2 = [0, 0]; + protected _glyphTextureExtent: GLfloat2 = [0.0, 0.0]; /** @see {@link glyphTexturePadding} */ - protected _glyphTexturePadding: GLsizei4 = [0, 0, 0, 0]; + protected _glyphTexturePadding: GLfloat4 = [0.0, 0.0, 0.0, 0.0]; /** @see {@link glyphTexturePadding} */ protected _glyphTexture: Texture2; /** * Map associating a glyph index to a glyph (sub image of a texture). - * @see {@link glyph} - * @see {@link hasGlyph} - * @see {@link addGlyph} + * @see {@link glyph}, @see {@link hasGlyph}, @see {@link addGlyph} */ - protected _glyphs = new Map(); + protected _glyphs = new Map(); protected _context: Context; @@ -83,14 +80,6 @@ export class FontFace { return this._ascent - this._descent; } - /** - * The font's base in pt. The base is the distance from the baseline to the top of the line in pt. - * @return - The distance from the baseline to the top of the line in pt. - */ - get base(): number { - return this._base; - } - /** * Set the font's base in pt. The base is the distance from the baseline to the top of the line in pt. * @param base - The distance from the baseline to the top of the line in pt. @@ -99,14 +88,8 @@ export class FontFace { assert(base > 0.0, 'base should be larger than zero.'); this._base = base; } - - /** - * The font's ascent in pt. The ascent is the distance from the baseline to the tops of the tallest glyphs - * (ascenders) in pt. - * @return - The distance from the baseline to the topmost ascenders in pt. - */ - get ascent(): number { - return this._ascent; + get base(): number { + return this._base; } /** @@ -118,14 +101,8 @@ export class FontFace { assert(ascent > 0.0, 'ascent should be larger than zero.'); this._ascent = ascent; } - - /** - * The font's descent in pt. The descent is the distance from the baseline to the lowest descenders in pt. Please - * note that this value is usually negative (if the fonts lowest descenders are below the baseline). - * @return - The distance from the baseline to the lowest descenders in pt. - */ - get descent(): number { - return this._descent; + get ascent(): number { + return this._ascent; } /** @@ -138,14 +115,8 @@ export class FontFace { // assert(descent < 0.f, ...); this._descent = descent; } - - /** - * The font's leading/line gap in pt. The leading is the distance from the lowest descenders to the topmost - * ascenders of a subsequent text line in pt. - * @return - The gap between two subsequent lines of text in pt. - */ - get lineGap(): number { - return this._lineGap; + get descent(): number { + return this._descent; } /** @@ -156,24 +127,30 @@ export class FontFace { set lineGap(lineGap: number) { this._lineGap = lineGap; } + get lineGap(): number { + return this._lineGap; + } /** - * The baseline-to-baseline distance in pt. The line height is derived as follows: - * line_height = size + line_gap, or alternatively: line_height = size * line_space - * @return - The line height (baseline-to-baseline distance) in pt. + * Set the baseline-to-baseline distance in pt. Negative values will result in negative linegap. The line height is + * derived as follows: line_height = size + line_gap, or alternatively: line_height = size * line_space + * @param lineHeight - The line height (baseline-to-baseline distance) in pt. */ + set lineHeight(lineHeight: number) { + this._lineGap = lineHeight - this.size; + } get lineHeight(): number { return this.size + this.lineGap; } /** - * Set the baseline-to-baseline distance in pt. Negative values will result in negative linegap. - * @param lineHeight - The line height (baseline-to-baseline distance) in pt. + * Set the relative baseline-to-baseline distance w.r.t. the font's size. The line space is mapped to line gap as + * follows: line_gap = size * (line_space - 1). A space < 1.0 will result in a negative line gap. + * @param lineSpace - The relative baseline-to-baseline distance w.r.t. the font's size. */ - set lineHeight(lineHeight: number) { - this._lineGap = lineHeight - this.size; + set lineSpace(lineSpace: number) { + this._lineGap = this.size * (lineSpace - 1); } - /** * The relative baseline-to-baseline distance w.r.t. the font's size. The relative line space is derived as follows: * line_space = size / line_height; Note that the descent is usually a negative value. @@ -187,74 +164,53 @@ export class FontFace { } /** - * Set the relative baseline-to-baseline distance w.r.t. the font's size. The line space is mapped to line gap as - * follows: line_gap = size * (line_space - 1). A space < 1.0 will result in a negative line gap. - * @param lineSpace - The relative baseline-to-baseline distance w.r.t. the font's size. + * Sets the glyph texture atlas extent. + * @param extent - The texture extent in px */ - set lineSpace(lineSpace: number) { - this._lineGap = this.size * (lineSpace - 1); + set glyphTextureExtent(extent: GLsizei2) { + assert(extent[0] > 0, 'expected extent.x to be larger than zero.'); + assert(extent[1] > 0, 'expected extent.y to be larger than zero.'); + this._glyphTextureExtent = extent; } - /** - * The size/extent of the glyph texture in px. This can only be set via setGlyphTexture. + * The size/extent of the glyph texture in px. * @return - The size/extent of the glyph texture in px. */ get glyphTextureExtent(): GLsizei2 { return this._glyphTextureExtent; } - // /** - // * Sets the glyph texture atlas extent. - // * @param extent - The texture extent in px - // */ - // set glyphTextureExtent(extent: GLsizei2) { - // assert(extent[0] > 0, 'expected extent.x to be larger than zero.'); - // assert(extent[1] > 0, 'expected extent.y to be larger than zero.'); - // this._glyphTextureExtent = extent; - // } - /** * The padding applied to every glyph in px. This can only be set via setGlyphTexture. - * @return - The CSS style (top, right, bottom, and left) padding applied to every glyph within the texture in px. + * @param padding - CSS style (top, right, bottom, left) padding applied to every glyph within the texture in px. */ - get glyphTexturePadding(): GLsizei4 { + set glyphTexturePadding(padding: GLfloat4) { + assert(padding[0] >= 0.0, 'expected padding[0] to be larger than zero.'); + assert(padding[1] >= 0.0, 'expected padding[1] to be larger than zero.'); + assert(padding[2] >= 0.0, 'expected padding[2] to be larger than zero.'); + assert(padding[3] >= 0.0, 'expected padding[3] to be larger than zero.'); + this._glyphTexturePadding = padding; + } + get glyphTexturePadding(): GLfloat4 { return this._glyphTexturePadding; } - // /** - // * Sets/updates the padding used for the glyph texture atlas. - // * @param padding The CSS style (top, right, bottom, left) padding applied to every glyph within the texture - // * in px. - // */ - // set glyphTexturePadding(padding: GLsizei4) { - // assert(padding[0] >= 0.0, 'expected padding[0] to be larger than zero.'); - // assert(padding[1] >= 0.0, 'expected padding[1] to be larger than zero.'); - // assert(padding[2] >= 0.0, 'expected padding[2] to be larger than zero.'); - // assert(padding[3] >= 0.0, 'expected padding[3] to be larger than zero.'); - // this._glyphTexturePadding = padding; - // } - /** * The font face's associated glyph atlas. All glyph data is associated to this texture atlas. - * @return - The texture object containing the texture atlas. - */ - get glyphTexture(): Texture2 { - return this._glyphTexture; - } - - /** - * Sets/updates the glyph texture atlas used for all comprised glyphs. * @param texture - The new texture atlas for all glyphs */ set glyphTexture(texture: Texture2) { this._glyphTexture = texture; } + get glyphTexture(): Texture2 { + return this._glyphTexture; + } /** * Check if a glyph of a specific index is available. * @return - True if a glyph for the provided index was added. */ - hasGlyph(index: GlyphIndex): boolean { + hasGlyph(index: GLsizei): boolean { return !!this._glyphs.get(index); } @@ -264,7 +220,7 @@ export class FontFace { * @param index - Index of the glyph to access. * @return - Glyph with the matching index or an empty glyph, if index has not match */ - glyph(index: GlyphIndex): Glyph { + glyph(index: GLsizei): Glyph { const existingGlyph = this._glyphs.get(index); if (existingGlyph) { return existingGlyph; @@ -287,7 +243,7 @@ export class FontFace { * Generates aan array of all comprised glyph indices. * @return - An array of all glyph indices available to this font face. */ - arrayOfGlyphIndices(): Array { + arrayOfGlyphIndices(): Array { return Array.from(this._glyphs.keys()); } @@ -298,7 +254,7 @@ export class FontFace { * @param index - Index of the glyph to access. * @return - Returns true if the glyph needs to be depicted/rendered. */ - depictable(index: GlyphIndex): boolean { + depictable(index: GLsizei): boolean { return this.glyph(index).depictable(); } @@ -310,7 +266,7 @@ export class FontFace { * @return - The kerning (usually negative) between the two glyphs in pt. If either on of the glyphs is unknown to * this font face or no specific kerning for the glyph pair is available a zero kerning is returned. */ - kerning(index: GlyphIndex, subsequentIndex: GlyphIndex): number { + kerning(index: GLsizei, subsequentIndex: GLsizei): number { const glyph = this._glyphs.get(index); if (!glyph) { return 0.0; @@ -325,7 +281,7 @@ export class FontFace { * @param subsequentIndex - The glyph index of the respective subsequent/next glyph. * @param kerning - Kerning of the two glyphs in pt. */ - setKerning(index: GlyphIndex, subsequentIndex: GlyphIndex, kerning: number): void { + setKerning(index: GLsizei, subsequentIndex: GLsizei, kerning: number): void { const glyph = this._glyphs.get(index); if (!glyph || !this.hasGlyph(subsequentIndex)) { assert(false, 'expected glyph or glyph of subsequent index to exist.'); diff --git a/source/glyph.ts b/source/glyph.ts new file mode 100644 index 00000000..d6f8e6ea --- /dev/null +++ b/source/glyph.ts @@ -0,0 +1,165 @@ + +import { clampf2, GLclampf2, GLfloat2 } from './tuples'; + + +/** + * Glyph related data for glyph based text rendering. Most of the glyph data (except the advance) refers to the font + * face's glyph-texture. This class does not provide dpi awareness. This has to be handled outside of this class, e.g., + * during layouting and rendering. + */ +export class Glyph { + + /** @see {@link advance} */ + protected _advance: GLfloat; + + /** @see {@link bearing} */ + protected _bearing: GLfloat2 = [0.0, 0.0]; + + /** @see {@link extent} */ + protected _extent: GLfloat2 = [0.0, 0.0]; + + /** @see {@link index} */ + protected _index: GLsizei; + + /** @see {@link kernings} */ + protected _kernings = new Map(); + + /** @see {@link subTextureOrigin} */ + protected _subTextureOrigin: GLclampf2 = [0.0, 0.0]; + + /** @see {@link subTextureExtent} */ + protected _subTextureExtent: GLclampf2 = [0.0, 0.0]; + + constructor(index: GLsizei = 0, advance: GLfloat = 0) { + this._index = index; + this._advance = advance; + } + + /** + * Set the index of one single distinguishable character. + */ + set index(index: GLsizei) { + this._index = index; + } + get index(): GLsizei { + return this._index; + } + + /** + * Upper left position of the glyph's sub-texture. The upper left position refers to the glyph-texture that is + * specified by a font face (@see {@link FontFace}). It is the u and v coordinates pointing to the glyphs + * sub-texture within the texture atlas. The coordinates are normalized in [0;1]. + * @param origin - Normalized coordinates pointing to the upper left texel of the glyph's sub-texture. + */ + set subTextureOrigin(origin: GLclampf2) { + this._subTextureOrigin = clampf2(origin, 'texture origin'); + } + get subTextureOrigin(): GLclampf2 { + return this._subTextureOrigin; + } + + /** + * Width and height of the glyph's sub-texture. In combination with the sub-texture offset (subTextureOffset) the + * sub-texture rectangle is implicitly fully specified in normalized texture coordinates. Note: the extent + * comprises the font face's padding. + * @param extent - Normalized width and height of the glyph's sub-texture. + */ + set subTextureExtent(extent: GLclampf2) { + this._subTextureExtent = clampf2(extent, 'texture extent'); + } + get subTextureExtent(): GLclampf2 { + return this._subTextureExtent; + } + + /** + * Check if a glyph is depictable/renderable. If the glyph's sub texture vertical or horizontal extent is zero the + * glyph does not need to be depicted/rendered. E.g., spaces, line feeds, other control sequences as well as + * unknown glyphs do not need to be processed for rendering. + * @return - True if the glyph needs to be depicted/rendered. + */ + public depictable(): boolean { + return this._subTextureExtent[0] > 0 && this._subTextureExtent[1] > 0; + } + + /** + * The x and y offsets w.r.t. to the pen-position on the baseline. The horizontal bearing does not comprise the + * glyph-texture's padding provided by the owning font face (@see {@link FontFace}). The vertical bearing also does + * not comprises the glyph texture's padding and is the measured w.r.t. baseline. + * @param bearing - Horizontal and vertical bearing based on the glyph's origin/pen-position placed on the + * baseline in pt. + */ + set bearing(bearing: GLfloat2) { + this._bearing = bearing; + } + get bearing(): GLfloat2 { + return this._bearing; + } + + /** + * Convenience setter for the x and y bearings. The horizontal bearing does not comprise the glyph-texture's + * padding provided by the owning font face (see FontFace). The vertical bearing also does not comprise the glyph- + * texture's padding and is the measured w.r.t. baseline. + * The vertical bearing is computed as follows: bearingY = fontBase - (yOffset - top padding) + * The horizontal bearing equals the xOffset: bearingX = xOffset - left padding: + * @param fontBase - The font face's (FontFace) base-to-top distance in pt. + * @param xOffset - The glyphs horizontal offset without left padding. + * @param yOffset - The glyphs vertical offset w.r.t. the font's topmost descendends, without the font's top + * padding in pt. + */ + setBearing(fontBase: GLfloat, xOffset: GLfloat, yOffset: GLfloat) { + this._bearing[0] = xOffset; + this._bearing[1] = fontBase - yOffset; + } + + /** + * Width and height of the glyph in pt. + * @param extent - The glyph's extent by means of width and height in pt. + */ + set extent(extent: GLfloat2) { + this._extent = extent; + } + get extent(): GLfloat2 { + return this._extent; + } + + /** + * Set the glyph's horizontal overall advance in pt. The horizontal advance comprises the font face's left and + * right padding, the glyphs (inner) width as well as the horizontal bearing (and often a glyph specific gap). + * E.g., advance = subTextureExtent_width + xOffset (+ gap), or alternatively: + * advance = xOffset + padding_left + glyph_width + padding_right (+ gap) + * @param advance - The glyphs horizontal advance (along the baseline) in pt. + */ + set advance(advance: GLfloat) { + this._advance = advance; + } + get advance(): GLfloat { + return this._advance; + } + + /** + * The glyph's kernel w.r.t. a subsequent glyph in pt. The kerning provides a(usually negative) offset along the + * baseline that can be used to move the pen-position respectively, i.e., the subsequent pen-position is computed + * as follows: pen-position + advance + kerning + * @param subsequentIndex - The subsequent glyph's index. + * @return - The kerning w.r.t. to the subsequent glyph in pt. If no kerning data is available for the subsequent + * glyph, the return value is zero indicating no kerning. + */ + public kerning(subsequentIndex: GLsizei): GLfloat { + const kerning = this._kernings.get(subsequentIndex); + if (kerning !== undefined) { + return kerning; + } + return 0.0; + } + + /** + * Set the glyph's kernel w.r.t. a subsequent glyph in pt. @see {@link kerning} + * @param subsequentIndex - The subsequent glyph's index. + * @param kerning - The kerning value w.r.t. to the subsequent glyph in pt. Note that the kerning should be a + * negative value but is not enforced to be in terms of assertion or clamping. If kerning data for the subsequent + * glyph is already available it will be updated to the provided value. + */ + public setKerning(subsequentIndex: GLsizei, kerning: GLfloat) { + this._kernings.set(subsequentIndex, kerning); + } +} From 63258deaf322d84fc14ac4963be707ea58e485b5 Mon Sep 17 00:00:00 2001 From: Daniel Limberger Date: Thu, 12 Apr 2018 17:22:21 +0200 Subject: [PATCH 03/92] quicksave --- source/glyph.ts | 74 ++++----- source/glyphvertices.ts | 132 ++++++++++++++++ source/label.ts | 269 ++++++++++++++++++++++++++++++++ source/labelrenderer.ts | 93 +++++++++++ source/text.ts | 73 +++++++++ source/typesetter.ts | 337 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 942 insertions(+), 36 deletions(-) create mode 100644 source/glyphvertices.ts create mode 100644 source/label.ts create mode 100644 source/labelrenderer.ts create mode 100644 source/text.ts create mode 100644 source/typesetter.ts diff --git a/source/glyph.ts b/source/glyph.ts index d6f8e6ea..655144c9 100644 --- a/source/glyph.ts +++ b/source/glyph.ts @@ -35,6 +35,44 @@ export class Glyph { this._advance = advance; } + /** + * Check if a glyph is depictable/renderable. If the glyph's sub texture vertical or horizontal extent is zero the + * glyph does not need to be depicted/rendered. E.g., spaces, line feeds, other control sequences as well as + * unknown glyphs do not need to be processed for rendering. + * @return - True if the glyph needs to be depicted/rendered. + */ + depictable(): boolean { + return this._subTextureExtent[0] > 0 && this._subTextureExtent[1] > 0; + } + + /** + * The glyph's kernel w.r.t. a subsequent glyph in pt. The kerning provides a(usually negative) offset along the + * baseline that can be used to move the pen-position respectively, i.e., the subsequent pen-position is computed + * as follows: pen-position + advance + kerning + * @param subsequentIndex - The subsequent glyph's index. + * @return - The kerning w.r.t. to the subsequent glyph in pt. If no kerning data is available for the subsequent + * glyph, the return value is zero indicating no kerning. + */ + kerning(subsequentIndex: GLsizei): GLfloat { + const kerning = this._kernings.get(subsequentIndex); + if (kerning !== undefined) { + return kerning; + } + return 0.0; + } + + /** + * Set the glyph's kernel w.r.t. a subsequent glyph in pt. @see {@link kerning} + * @param subsequentIndex - The subsequent glyph's index. + * @param kerning - The kerning value w.r.t. to the subsequent glyph in pt. Note that the kerning should be a + * negative value but is not enforced to be in terms of assertion or clamping. If kerning data for the subsequent + * glyph is already available it will be updated to the provided value. + */ + setKerning(subsequentIndex: GLsizei, kerning: GLfloat) { + this._kernings.set(subsequentIndex, kerning); + } + + /** * Set the index of one single distinguishable character. */ @@ -71,16 +109,6 @@ export class Glyph { return this._subTextureExtent; } - /** - * Check if a glyph is depictable/renderable. If the glyph's sub texture vertical or horizontal extent is zero the - * glyph does not need to be depicted/rendered. E.g., spaces, line feeds, other control sequences as well as - * unknown glyphs do not need to be processed for rendering. - * @return - True if the glyph needs to be depicted/rendered. - */ - public depictable(): boolean { - return this._subTextureExtent[0] > 0 && this._subTextureExtent[1] > 0; - } - /** * The x and y offsets w.r.t. to the pen-position on the baseline. The horizontal bearing does not comprise the * glyph-texture's padding provided by the owning font face (@see {@link FontFace}). The vertical bearing also does @@ -136,30 +164,4 @@ export class Glyph { return this._advance; } - /** - * The glyph's kernel w.r.t. a subsequent glyph in pt. The kerning provides a(usually negative) offset along the - * baseline that can be used to move the pen-position respectively, i.e., the subsequent pen-position is computed - * as follows: pen-position + advance + kerning - * @param subsequentIndex - The subsequent glyph's index. - * @return - The kerning w.r.t. to the subsequent glyph in pt. If no kerning data is available for the subsequent - * glyph, the return value is zero indicating no kerning. - */ - public kerning(subsequentIndex: GLsizei): GLfloat { - const kerning = this._kernings.get(subsequentIndex); - if (kerning !== undefined) { - return kerning; - } - return 0.0; - } - - /** - * Set the glyph's kernel w.r.t. a subsequent glyph in pt. @see {@link kerning} - * @param subsequentIndex - The subsequent glyph's index. - * @param kerning - The kerning value w.r.t. to the subsequent glyph in pt. Note that the kerning should be a - * negative value but is not enforced to be in terms of assertion or clamping. If kerning data for the subsequent - * glyph is already available it will be updated to the provided value. - */ - public setKerning(subsequentIndex: GLsizei, kerning: GLfloat) { - this._kernings.set(subsequentIndex, kerning); - } } diff --git a/source/glyphvertices.ts b/source/glyphvertices.ts new file mode 100644 index 00000000..11141327 --- /dev/null +++ b/source/glyphvertices.ts @@ -0,0 +1,132 @@ + +import { vec3, vec4 } from 'gl-matrix'; + + +/** + * Information required for rendering a single glyph. Technical this could be denoted as a vertex of a vertex cloud. + */ +export interface GlyphVertex { + + /** + * Position of the glyph in normalized device coordinates. + */ + origin: vec3; + + /** + * Tangent vector (usually the label's baseline direction). The length of this vector is expected to be the advance + * of this glyphs geometry in baseline direction, i.e., it is used to derive the vertices using simple addition. + */ + tangent: vec3; + + /** + * Bitangent vector (orthogonal to the label's baseline). The length of this vector is expected to be the height of + * this glyphs geometry, i.e., it is used to derive the glyph vertices using simple addition. + */ + up: vec3; + + /** + * Sub image rect of the glyph in the glyph texture (uv-coordinates). + */ + uvRect: vec4; +} + +/** + * Vertex cloud that describes each glyph that is to be rendered on the screen. + */ +export class GlyphVertices extends Array { + + optimize() { + + } + +} + + +// protected _superSampling: SuperSampling; + + +// export enum Sampling { +// None = 'none', +// Grid2 = 'grid2', +// Grid3 = 'grid3', +// Grid4 = 'grid4', +// Quincunx = 'quincunx', +// RGSS = 'rgss', +// Rooks8 = 'rooks8', +// } + + +// get superSampling(): SuperSampling { +// return this._superSampling; +// } + +// set superSampling(superSampling: SuperSampling) { +// this._superSampling = superSampling; +// } + + + + +// // numDepictable +// // Extent(): number { +// // let count = 0; +// // for (let c of this._text) { + +// // /** +// // * let number = "h".charCodeAt(0); //(returns number = 104) +// // * let char = String.fromCharCode(number); //(returns char = "h") +// // */ + +// // if (this._fontFace.depictable(c.charCodeAt(0))) { +// // ++count; +// // } +// // } +// // return count; +// // } + + + +// protected _additionalTransform: mat4; +// protected _transformValid: boolean; +// protected _transform: mat4; + + + + +// // public setFromConfig(config: GlyphSequenceConfig) { +// // this.wordWrap = config.wordWrap; +// // this.lineWidth = config.lineWidth; +// // this.alignment = config.alignment; +// // this.lineAnchor = config.anchor; +// // this.fontColor = config.fontColor; +// // this.fontFace = config.fontFace; +// // this.fontSize = config.fontSize; +// // } + +// // get additionalTransform(): mat4 { +// // return this._additionalTransform; +// // } + +// // set additionalTransform(additionalTransform: mat4) { +// // this._transformValid = false; +// // this._additionalTransform = additionalTransform; +// // } + +// // get transform(): mat4 { +// // if (!this._transformValid) { +// // this.computeTransform(); +// // this._transformValid = true; +// // } +// // return this._transform; +// // } + +// // public computeTransform(): void { +// // //assert(this._fontFace); + +// // this._transform = mat4.create(); +// // mat4.multiply(this._transform, this._transform, this._additionalTransform); + +// // let s = this._fontSize / this._fontFace.size; + +// // mat4.scale(this._transform, this._transform, vec3.fromValues(s, s, s)) +// // } diff --git a/source/label.ts b/source/label.ts new file mode 100644 index 00000000..9179057c --- /dev/null +++ b/source/label.ts @@ -0,0 +1,269 @@ + +import { ChangeLookup } from './changelookup'; +import { Color } from './color'; +import { FontFace } from './fontface'; +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 class Label { + + /** @see {@link text} */ + protected _text: Text | string; + + /** @see {@link wordWrap} */ + protected _wordWrap = false; + + /** @see {@link alignment} */ + protected _alignment: Label.Alignment = Label.Alignment.Left; + + /** @see {@link lineAnchor} */ + protected _lineAnchor: Label.LineAnchor = Label.LineAnchor.Baseline; + + /** @see {@link lineWidth} */ + protected _lineWidth = 0.0; + + + /** @see {@link fontFace} */ + protected _fontFace: FontFace; + + /** @see {@link color} */ + protected _color: Color; + + /** @see {@link background} */ + protected _backgroundColor: Color; + + + /** @see {@link altered} */ + protected readonly _altered = Object.assign(new ChangeLookup(), { + any: false, color: false, resources: false, text: false, typesetting: false, transformation: false, + }); + + /** + * Returns the character at the specified index. + * @param pos - The zero-based index of the desired character. + */ + charAt(index: number): string { + if (this._text instanceof Text) { + return this._text.text.charAt(index); + } + return this._text.charAt(index); + } + + /** + * Returns the Unicode value of the character at the specified location. + * @param index - The zero-based index of the desired character. If there is no character at the specified index, + * NaN is returned. + */ + charCodeAt(index: number): number { + if (this._text instanceof Text) { + return this._text.text.charCodeAt(index); + } + return this._text.charCodeAt(index); + } + + /** + * Returns, whether or not the character at a given index is equal to the default or the text's line feed character. + * @param index - The zero-based index of the desired character. If there is no character at the specified index, + * NaN is returned. + */ + lineFeedAt(index: number): boolean { + return this.charAt(index) === this.lineFeed; + } + + + kerningBefore(index: number): number { + if (index < 1 || index > this.length) { + return NaN; + } + return this._fontFace.kerning(this.charCodeAt(index - 1), this.charCodeAt(index)); + } + + kerningAfter(index: number): number { + if (index < 0 || index > this.length - 1) { + return NaN; + } + return this._fontFace.kerning(this.charCodeAt(index), this.charCodeAt(index + 1)); + } + + /** + * Returns the advancement of a specified glyph. + * @param index - The zero-based index of the desired character. If there is no character at the specified index, + * NaN is returned. + */ + advance(index: number): number { + if (index < 0 || index > this.length) { + return NaN; + } + return this._fontFace.glyph(this.charCodeAt(index)).advance; + } + + + /** + * Text that is to be rendered. + */ + set text(text: Text | string) { + this._altered.alter('text'); + this._text = text; + } + get text(): Text | string { + return this._text; + } + + /** + * 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_LINEFEED; + } + + /** + * Whether or not words can be wrapped at the end of a line. + * @param wrap - `true` if word wrap is enabled, else `false` + */ + set wordWrap(wrap: boolean) { + if (this._wordWrap === wrap) { + return; + } + this._altered.alter('typesetting'); + this._wordWrap = wrap; + } + get wordWrap(): boolean { + return this._wordWrap; + } + + /** + * Horizontal text alignment for typesetting. + */ + set alignment(alignment: Label.Alignment) { + if (this._alignment === alignment) { + return; + } + this._altered.alter('typesetting'); + this._alignment = alignment; + } + get alignment(): Label.Alignment { + return this._alignment; + } + + /** + * Vertical text anchor point used for positional reference. + */ + set lineAnchor(anchor: Label.LineAnchor) { + if (this._lineAnchor === anchor) { + return; + } + this._altered.alter('typesetting'); + this._lineAnchor = anchor; + } + get lineAnchor(): Label.LineAnchor { + return this._lineAnchor; + } + + /** + * Width of a single line (in pt or w.r.t. font face scaling in world space respectively). The width of the line + * is not intended to be set explicitly, but implicitly vie transformations/label placement. + */ + get lineWidth(): number { + return this._lineWidth; + } + + /** + * Font face used for typesetting, transformation, and rendering. + */ + set fontFace(fontFace: FontFace) { + if (this._fontFace === fontFace) { + return; + } + this._altered.alter('typesetting'); + this._altered.alter('resources'); + this._fontFace = fontFace; + } + get fontFace(): FontFace { + return this._fontFace; + } + + /** + * Color used for text rendering. + */ + set color(color: Color) { + if (this._color.equals(color)) { + return; + } + this._altered.alter('color'); + this._color = color; + } + get color(): Color { + return this._color; + } + + /** + * Color used for background of text rendering. + */ + set backgroundColor(color: Color) { + if (this._backgroundColor.equals(color)) { + return; + } + this._altered.alter('color'); + this._backgroundColor = color; + } + get backgroundColor(): Color { + return this._backgroundColor; + } + + toString(): string { + if (this._text instanceof Text) { + return this._text.text; + } + return this._text; + } + + /* + * Whether or not any property or the referenced text has changed requiring, e.g., the new typesetting. + * The alteration status can be reset using `reset` (@see {@link reset}). + */ + get altered(): boolean { + return this._altered.any || (this._text instanceof Text ? this._text.altered : false); + } + + /** + * Intended for resetting alteration status. + */ + reset() { + this._altered.reset(); + } + +} + +export namespace Label { + + export enum Alignment { + Left = 'left', + Center = 'center', + Right = 'right', + } + + export enum LineAnchor { + Top = 'top', + Ascent = 'ascent', + Center = 'center', + Baseline = 'baseline', + Descent = 'descent', + Bottom = 'bottom', + } + +} diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts new file mode 100644 index 00000000..73c8e82e --- /dev/null +++ b/source/labelrenderer.ts @@ -0,0 +1,93 @@ + + + + + +// protected _superSampling: SuperSampling; + + +// export enum Sampling { +// None = 'none', +// Grid2 = 'grid2', +// Grid3 = 'grid3', +// Grid4 = 'grid4', +// Quincunx = 'quincunx', +// RGSS = 'rgss', +// Rooks8 = 'rooks8', +// } + + +// get superSampling(): SuperSampling { +// return this._superSampling; +// } + +// set superSampling(superSampling: SuperSampling) { +// this._superSampling = superSampling; +// } + + + + +// // numDepictable +// // Extent(): number { +// // let count = 0; +// // for (let c of this._text) { + +// // /** +// // * let number = "h".charCodeAt(0); //(returns number = 104) +// // * let char = String.fromCharCode(number); //(returns char = "h") +// // */ + +// // if (this._fontFace.depictable(c.charCodeAt(0))) { +// // ++count; +// // } +// // } +// // return count; +// // } + + + +// protected _additionalTransform: mat4; +// protected _transformValid: boolean; +// protected _transform: mat4; + + + + +// // public setFromConfig(config: GlyphSequenceConfig) { +// // this.wordWrap = config.wordWrap; +// // this.lineWidth = config.lineWidth; +// // this.alignment = config.alignment; +// // this.lineAnchor = config.anchor; +// // this.fontColor = config.fontColor; +// // this.fontFace = config.fontFace; +// // this.fontSize = config.fontSize; +// // } + +// // get additionalTransform(): mat4 { +// // return this._additionalTransform; +// // } + +// // set additionalTransform(additionalTransform: mat4) { +// // this._transformValid = false; +// // this._additionalTransform = additionalTransform; +// // } + +// // get transform(): mat4 { +// // if (!this._transformValid) { +// // this.computeTransform(); +// // this._transformValid = true; +// // } +// // return this._transform; +// // } + +// // public computeTransform(): void { +// // //assert(this._fontFace); + +// // this._transform = mat4.create(); +// // mat4.multiply(this._transform, this._transform, this._additionalTransform); + +// // let s = this._fontSize / this._fontFace.size; + +// // mat4.scale(this._transform, this._transform, vec3.fromValues(s, s, s)) +// // } diff --git a/source/text.ts b/source/text.ts new file mode 100644 index 00000000..e28ca013 --- /dev/null +++ b/source/text.ts @@ -0,0 +1,73 @@ + +/** + * The text object is intended as character sequence manipulation interface. A text can be referenced by multiple glyph + * sequences for rendering and interaction. E.g., a single text could be renderer multiple times at different locations + * or using different font faces, alignments, etc. The text object will probably increase in complexity when additional + * features such as text formatting (bold, italic, varying size), (multi)cursor, (multi)selection, etc. will be added. + */ +export class Text { + + static readonly DEFAULT_LINEFEED = '\x0A'; + + + /** @see {@link text} */ + protected _text: string; + + /** @see {@link lineFeed} */ + protected _lineFeed: string = Text.DEFAULT_LINEFEED; + + /** @see {@link altered} */ + protected _altered = false; + + + /** + * Length of the text, i.e., number of characters within the text. + */ + get length(): number { + return this._text.length; + } + + /** + * Text that is to be rendered. + */ + set text(text: string) { + if (this._text === text) { + return; + } + this._altered = true; + this._text = text; + } + get text(): string { + return this._text; + } + + /** + * Character that is to be used for Line feed. + */ + set lineFeed(lineFeed: string) { + if (this._lineFeed === lineFeed) { + return; + } + this._altered = true; + this._lineFeed = lineFeed; + return; + } + get lineFeed(): string { + return this._lineFeed; + } + + /** + * Intended for resetting alteration status. + */ + set altered(altered: boolean) { + this._altered = altered; + } + + /* + * Whether or not any other public property has changed. + */ + get altered(): boolean { + return this._altered; + } + +} diff --git a/source/typesetter.ts b/source/typesetter.ts new file mode 100644 index 00000000..d27d447e --- /dev/null +++ b/source/typesetter.ts @@ -0,0 +1,337 @@ + +import { vec2, vec3 } from 'gl-matrix'; + +import { assert } from './auxiliaries'; +import { FontFace } from './fontface'; +import { Glyph } from './glyph'; +import { GlyphVertex, GlyphVertices } from './glyphvertices'; +import { Label } from './label'; + + +/** + * The typesetter is responsible for layouting text on the screen or in a virtual space. It takes a glyph sequence, + * which defines where it wants to appear (@see {@link GlyphSequence}), and a font face that is used to display the + * text, and computes the actual position for each glyph. Its output is a vertex array, which describes the glyphs + * position and appearance on the screen/in the scene and which can be rendered using a GlyphRenderer. + */ +export class Typesetter { + + protected static readonly DELIMITERS: string = '\x0A ,.-/()[]<>'; + + /** + * This function internally performs the typesetting of a Label, but only returns its extent, disregarding the + * resulting vertex array. + */ + // extent(label: Label): vec2 { + // //return typeset(label, null, 0, true); + // } + + + // rectangle(label: Label, origin: vec3): [vec2, vec2] { + // let extent = Typesetter.extent(sequence); + // let offset = sequence.fontFace.lineHeight - sequence.fontFace.base; + + // switch (sequence.lineAnchor) { + // case LineAnchor.Ascent: + // offset += sequence.fontFace.ascent; + // break; + // case LineAnchor.Center: + // offset += sequence.fontFace.size * 0.5 + sequence.fontFace.descent; + // break; + // case LineAnchor.Descent: + // offset += sequence.fontFace.descent; + // break; + // case LineAnchor.Top: + // offset += sequence.fontFace.base; + // break; + // case LineAnchor.Bottom: + // offset += sequence.fontFace.base - sequence.fontFace.lineHeight; + // break; + // case LineAnchor.Baseline: + // default: + // break; + // } + // origin[1] -= offset; + + // let transformedOrigin = vec4.create(); + // vec4.transformMat4(transformedOrigin, vec4.fromValues(origin[0], origin[1], origin[2], 1.0), sequence.transform); + + + // switch (sequence.alignment) { + // case Alignment.LeftAligned: + // offset = 0.0; + // break; + // case Alignment.Centered: + // offset = 0.5 * extent[0]; + // break; + // case Alignment.RightAligned: + // offset = extent[0]; + // break; + // default: + // break; + // } + + // transformedOrigin[0] -= offset; + // return [vec2.fromValues(transformedOrigin[0], transformedOrigin[1]), extent]; + // } + + protected static wordWrap(label: Label, pen: vec2, glyph: Glyph, index: number, safeForwardIndex: number): boolean { + assert(label.wordWrap, `expected wordWrap to be enabled for label, given ${label}`); + const lineWidth = label.lineWidth; + + const penForward = pen[0] + glyph.advance + (index > 0 ? label.kerningBefore(index) : 0.0); + if (glyph.depictable() && penForward > lineWidth && (glyph.advance <= lineWidth || pen[0] > 0.0)) { + return true; + } + if (index < safeForwardIndex) { + return false; + } + // tslint:disable-next-line:prefer-const + let forwardWidth = 0.0; + safeForwardIndex = Typesetter.forward(label, index, forwardWidth); + return forwardWidth <= lineWidth && (pen[0] + forwardWidth) > lineWidth; + } + + /** + * Accumulate glyph advances (including kerning) up to the next delimiter or line feed occurrence starting at + * the given index for the given label. + * @param label - Label to compute accumulated advances for. + * @param begin - Index to start glyph advance accumulation at. + * @param width - Out parameter: the accumulated width up to the next delimiter (reset to 0). + * @returns - The index of the last character that was included in the forward accumulation. + * @todo Perhaps switch to another approach, i.e., calculate the all advances between delimiters once and create + * an index based lookup... + */ + protected static forward(label: Label, begin: number, width: number): number { + let index: number = begin; + const iEnd: number = label.length; + + width = 0.0; + while (index < iEnd && Typesetter.DELIMITERS.indexOf(label.charAt(index)) === -1) { + if (index > 0) { + width += label.kerningBefore(index); + } + width += label.advance(index); + ++index; + } + return index; + } + + /** + * Revert advance of preceding, not depictable glyphs. Intended to be used, e.g., on line feed. + * The given vertical extent is increased by the label's line height and the horizontal extent is set to either + * 0 or the extent of the non-depictable characters. + * @param label - Label to compute accumulated advances for. + * @param index - In/out parameter: index to backtrack the advance of non-depictable glyphs. + * @param begin - Index to start glyph advance accumulation at. + * @param pen - In/out parameter: pen (typesetting position) to be used and adjusted. + * @param extent - In/out parameter: extent to be adjusted. + */ + protected static backward(label: Label, index: number, begin: number, pen: vec2, extent: vec2): void { + while (index > begin) { + const precedingGlyph = label.fontFace.glyph(label.charCodeAt(index)); + if (precedingGlyph.depictable()) { + break; + } + pen[0] -= precedingGlyph.advance; + --index; + } + extent[0] = Math.max(pen[0], extent[1]); + extent[1] += label.fontFace.lineHeight; + } + + /** + * Adjusts the vertices for a line after typesetting (done due to line feed, word wrap, or end of line) w.r.t. + * the targeted line alignment. + * @param pen - Current typesetting position (probably the end of the line in typesetting space). + * @param alignment - Targeted alignment, e.g., left, center, or right. + * @param vertices - Glyph vertices for rendering to align the origins of (expected to be not yet transformed). + * @param begin - Vertex index to start alignment at. + * @param end - Vertex index to stop alignment at. + */ + protected static align(pen: vec2, alignment: Label.Alignment, + vertices: GlyphVertices | undefined, begin: number, end: number): void { + if (vertices === undefined || alignment === Label.Alignment.Left) { + return; + } + + let penOffset = -pen[0]; + if (alignment === Label.Alignment.Center) { + penOffset *= 0.5; + } + + /* Origin is expected to be in typesetting space (not transformed yet). */ + for (let i = begin; i < end; ++i) { + vertices[i].origin[0] += penOffset; + } + } + + /** + * Configuring the vertex for a given glyph to be renderer. If no vertex is given or the glyph is not depictable, + * this method immediately exits at the beginning. + * @param fontFace - Font face to be applied for setting up the vertex. + * @param pen - Typesetting position which is the not-yet-transformed position the glyph will be rendered at. + * @param glyph - Glyph that is to be rendered/configured. + * @param vertex - Associated vertex to store data required for rendering. + */ + protected static glyph(fontFace: FontFace, pen: vec2, glyph: Glyph, vertex: GlyphVertex | undefined): void { + if (vertex === undefined || glyph.depictable() === false) { + return; + } + + const padding = fontFace.glyphTexturePadding; + vertex.origin = vec3.fromValues(pen[0], pen[1], 0.0); + vertex.origin[0] += glyph.bearing[0] - padding[3]; + vertex.origin[1] += glyph.bearing[1] - glyph.extent[1] + padding[0]; + + vertex.tangent = vec3.fromValues(glyph.extent[0], 0.0, 0.0); + vertex.up = vec3.fromValues(0.0, glyph.extent[1], 0.0); + + vertex.uvRect[0] = glyph.subTextureOrigin[0]; + vertex.uvRect[1] = glyph.subTextureOrigin[1]; + + const upperRight = vec2.create(); + vec2.add(upperRight, glyph.subTextureOrigin, glyph.subTextureExtent); + + vertex.uvRect[2] = upperRight[0]; + vertex.uvRect[3] = upperRight[1]; + } + + + static typeset(label: Label, vertices?: GlyphVertices, begin?: number): vec2 { + /* Horizontal and vertical position at which typesetting takes place/arrived. */ + const pen = vec2.create(); + + let vertexIndex = begin !== undefined ? begin : 0; + const extent = vec2.create(); + + const iBegin = 0; + const iEnd: number = label.length; + + /* Index used to reduce the number of wordwrap forward passes. */ + // tslint:disable-next-line:prefer-const + let safeForwardIndex = iBegin; + let feedVertexIndex: number = vertexIndex; + + for (let index = iBegin; index !== iEnd; ++index) { + const glyph = label.fontFace.glyph(label.charCodeAt(index)); + + /* Handle line feeds as well as word wrap for next word (or next glyph if word exceeds the line width). */ + const feedLine = label.lineFeedAt(index) || (label.wordWrap && + Typesetter.wordWrap(label, pen, glyph, index, safeForwardIndex)); + + if (feedLine) { + /* Handle pen and extent w.r.t. non-depictable glyphs. */ + Typesetter.backward(label, index - 1, iBegin, pen, extent); + /* Handle alignment (does nothing if vertices are not required/undefined). */ + Typesetter.align(pen, label.alignment, vertices, feedVertexIndex, vertexIndex); + + pen[0] = 0.0; + pen[1] -= label.fontFace.lineHeight; + + feedVertexIndex = vertexIndex; + + } else if (index > iBegin) { + pen[0] += label.kerningBefore(index); + } + + /* Add and configure data for rendering the current character/glyph of the label. */ + Typesetter.glyph(label.fontFace, pen, glyph, vertices ? vertices[vertexIndex++] : undefined); + + /* ... todo */ + + // pen[0] += glyph.advance; + + // if (i + 1 == iEnd) // handle alignment (when last line of sequence is processed) + // { + // Typesetter.extent(fontFace, sequence, i, iBegin, pen, extent); + + // if (!dryrun) { + // typeset_align(pen, sequence.alignment, vertices, feedVertexIndex, vertexIndex); + // } + // } + // } + + // if (!dryRun) { + // anchor_transform(sequence, vertices, begin, vertexIndex); + // vertex_transform(sequence.transform, sequence.fontColor, sequence.superSampling, vertices, begin, vertexIndex); + // } + // return extent_transform(sequence, extent); + } + + + + +// function anchor_transform(sequence: GlyphSequence, vertices: Vertices, begin: number, end: number): void { + +// let offset: number = 0.0; + +// switch (sequence.lineAnchor) { +// case LineAnchor.Ascent: +// offset = sequence.fontFace.ascent; +// break; +// case LineAnchor.Center: +// offset = sequence.fontFace.size * 0.5 + sequence.fontFace.descent; +// break; +// case LineAnchor.Descent: +// offset = sequence.fontFace.descent; +// break; +// case LineAnchor.Top: +// offset = sequence.fontFace.base; +// break; +// case LineAnchor.Bottom: +// offset = sequence.fontFace.base - sequence.fontFace.lineHeight; +// break; +// case LineAnchor.Baseline: +// default: +// return; +// } + +// for (let i: number = begin; i < end; ++i) { +// let v = vertices[i]; +// v.origin[1] -= offset; +// } +// } + +// function vertex_transform(transform: mat4, fontColor: vec4, superSampling: SuperSampling | GLuint, vertices: Vertices, begin: number, end: number): void { +// for (let i: number = begin; i < end; ++i) { +// let v = vertices[i]; + +// let ll: vec4 = vec4.create(); +// vec4.transformMat4(ll, vec4.fromValues(v.origin[0], v.origin[1], v.origin[2], 1.0), transform); + +// let lr: vec4 = vec4.create(); +// vec4.transformMat4(lr, vec4.fromValues(v.origin[0] + v.vtan[0], v.origin[1] + v.vtan[1], v.origin[2] + v.vtan[2], 1.0), transform); + +// let ul: vec4 = vec4.create(); +// vec4.transformMat4(ul, vec4.fromValues(v.origin[0] + v.vbitan[0], v.origin[1] + v.vbitan[1], v.origin[2] + v.vbitan[2], 1.0), transform); + + +// v.origin = vec3.fromValues(ll[0], ll[1], ll[2]) + +// let vtan: vec4 = vec4.create(); +// vec4.sub(vtan, lr, ll); +// v.vtan = vec3.fromValues(vtan[0], vtan[1], vtan[2]) + +// let vbitan: vec4 = vec4.create(); +// vec4.sub(vbitan, ul, ll); +// v.vbitan = vec3.fromValues(vbitan[0], vbitan[1], vbitan[2]) + +// v.fontColor = fontColor; +// v.superSampling = superSampling; +// } +// } + +// function extent_transform(sequence: GlyphSequence, extent: vec2): vec2 { +// let ll = vec4.create(); +// vec4.transformMat4(ll, vec4.fromValues(0.0, 0.0, 0.0, 1.0), sequence.transform); + +// let lr = vec4.create(); +// vec4.transformMat4(lr, vec4.fromValues(extent[0], 0.0, 0.0, 1.0), sequence.transform); + +// let ul = vec4.create(); +// vec4.transformMat4(ul, vec4.fromValues(0.0, extent[1], 0.0, 1.0), sequence.transform); + +// return vec2.fromValues(vec4.distance(lr, ll), vec4.distance(ul, ll)); +// } +// } From 901f7c80c8175530f76fbf8a331759d6a2e01e3c Mon Sep 17 00:00:00 2001 From: Daniel Limberger Date: Thu, 10 May 2018 14:46:51 +0200 Subject: [PATCH 04/92] refine typesetter --- source/glyph.ts | 2 +- source/glyphvertices.ts | 5 - source/label.ts | 23 ++++ source/labelrenderer.ts | 8 -- source/renderer.ts | 2 +- source/typesetter.ts | 271 ++++++++++++++++------------------------ 6 files changed, 135 insertions(+), 176 deletions(-) diff --git a/source/glyph.ts b/source/glyph.ts index 655144c9..cb817d6e 100644 --- a/source/glyph.ts +++ b/source/glyph.ts @@ -62,7 +62,7 @@ export class Glyph { } /** - * Set the glyph's kernel w.r.t. a subsequent glyph in pt. @see {@link kerning} + * Set the glyph's kernel w.r.t. a subsequent glyph in pt. @see {@link kerning} * @param subsequentIndex - The subsequent glyph's index. * @param kerning - The kerning value w.r.t. to the subsequent glyph in pt. Note that the kerning should be a * negative value but is not enforced to be in terms of assertion or clamping. If kerning data for the subsequent diff --git a/source/glyphvertices.ts b/source/glyphvertices.ts index 11141327..4563c9ba 100644 --- a/source/glyphvertices.ts +++ b/source/glyphvertices.ts @@ -65,8 +65,6 @@ export class GlyphVertices extends Array { // } - - // // numDepictable // // Extent(): number { // // let count = 0; @@ -85,14 +83,11 @@ export class GlyphVertices extends Array { // // } - // protected _additionalTransform: mat4; // protected _transformValid: boolean; // protected _transform: mat4; - - // // public setFromConfig(config: GlyphSequenceConfig) { // // this.wordWrap = config.wordWrap; // // this.lineWidth = config.lineWidth; diff --git a/source/label.ts b/source/label.ts index 9179057c..6edc3427 100644 --- a/source/label.ts +++ b/source/label.ts @@ -1,4 +1,6 @@ +import { mat4 } from 'gl-matrix'; + import { ChangeLookup } from './changelookup'; import { Color } from './color'; import { FontFace } from './fontface'; @@ -37,6 +39,9 @@ export class Label { /** @see {@link background} */ protected _backgroundColor: Color; + /** @see {@link transform} */ + protected _transform: mat4; + /** @see {@link altered} */ protected readonly _altered = Object.assign(new ChangeLookup(), { @@ -225,6 +230,24 @@ export class Label { 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. + */ + set transform(transform: mat4) { + if (mat4.equals(this._transform, transform)) { + return; + } + this._altered.alter('transform'); + this._transform = transform; + } + get transform(): mat4 { + return this._transform; + } + + toString(): string { if (this._text instanceof Text) { return this._text.text; diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 73c8e82e..edc19e7f 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -1,8 +1,5 @@ - - - // protected _superSampling: SuperSampling; @@ -26,8 +23,6 @@ // } - - // // numDepictable // // Extent(): number { // // let count = 0; @@ -46,14 +41,11 @@ // // } - // protected _additionalTransform: mat4; // protected _transformValid: boolean; // protected _transform: mat4; - - // // public setFromConfig(config: GlyphSequenceConfig) { // // this.wordWrap = config.wordWrap; // // this.lineWidth = config.lineWidth; diff --git a/source/renderer.ts b/source/renderer.ts index 62d73c28..81fd6d0e 100644 --- a/source/renderer.ts +++ b/source/renderer.ts @@ -10,9 +10,9 @@ import { ChangeLookup } from './changelookup'; import { Context } from './context'; import { Controllable } from './controller'; import { Initializable } from './initializable'; +import { MouseEventProvider } from './mouseeventprovider'; import { GLclampf4, GLfloat2, GLsizei2, tuple2 } from './tuples'; import { FramePrecisionString } from './wizard'; -import { MouseEventProvider } from './mouseeventprovider'; // export interface IdCallback { (id: number, x?: number, y?: number): void; } diff --git a/source/typesetter.ts b/source/typesetter.ts index d27d447e..c083ade3 100644 --- a/source/typesetter.ts +++ b/source/typesetter.ts @@ -1,11 +1,13 @@ -import { vec2, vec3 } from 'gl-matrix'; +import { mat4, vec2, vec3, vec4 } from 'gl-matrix'; +import { fromVec4, v4 } from './gl-matrix-extensions'; import { assert } from './auxiliaries'; import { FontFace } from './fontface'; import { Glyph } from './glyph'; import { GlyphVertex, GlyphVertices } from './glyphvertices'; import { Label } from './label'; +import { GLfloat2 } from './tuples'; /** @@ -18,63 +20,6 @@ export class Typesetter { protected static readonly DELIMITERS: string = '\x0A ,.-/()[]<>'; - /** - * This function internally performs the typesetting of a Label, but only returns its extent, disregarding the - * resulting vertex array. - */ - // extent(label: Label): vec2 { - // //return typeset(label, null, 0, true); - // } - - - // rectangle(label: Label, origin: vec3): [vec2, vec2] { - // let extent = Typesetter.extent(sequence); - // let offset = sequence.fontFace.lineHeight - sequence.fontFace.base; - - // switch (sequence.lineAnchor) { - // case LineAnchor.Ascent: - // offset += sequence.fontFace.ascent; - // break; - // case LineAnchor.Center: - // offset += sequence.fontFace.size * 0.5 + sequence.fontFace.descent; - // break; - // case LineAnchor.Descent: - // offset += sequence.fontFace.descent; - // break; - // case LineAnchor.Top: - // offset += sequence.fontFace.base; - // break; - // case LineAnchor.Bottom: - // offset += sequence.fontFace.base - sequence.fontFace.lineHeight; - // break; - // case LineAnchor.Baseline: - // default: - // break; - // } - // origin[1] -= offset; - - // let transformedOrigin = vec4.create(); - // vec4.transformMat4(transformedOrigin, vec4.fromValues(origin[0], origin[1], origin[2], 1.0), sequence.transform); - - - // switch (sequence.alignment) { - // case Alignment.LeftAligned: - // offset = 0.0; - // break; - // case Alignment.Centered: - // offset = 0.5 * extent[0]; - // break; - // case Alignment.RightAligned: - // offset = extent[0]; - // break; - // default: - // break; - // } - - // transformedOrigin[0] -= offset; - // return [vec2.fromValues(transformedOrigin[0], transformedOrigin[1]), extent]; - // } - protected static wordWrap(label: Label, pen: vec2, glyph: Glyph, index: number, safeForwardIndex: number): boolean { assert(label.wordWrap, `expected wordWrap to be enabled for label, given ${label}`); const lineWidth = label.lineWidth; @@ -127,7 +72,7 @@ export class Typesetter { * @param pen - In/out parameter: pen (typesetting position) to be used and adjusted. * @param extent - In/out parameter: extent to be adjusted. */ - protected static backward(label: Label, index: number, begin: number, pen: vec2, extent: vec2): void { + protected static backward(label: Label, index: number, begin: number, pen: vec2, extent: vec2) { while (index > begin) { const precedingGlyph = label.fontFace.glyph(label.charCodeAt(index)); if (precedingGlyph.depictable()) { @@ -145,12 +90,12 @@ export class Typesetter { * the targeted line alignment. * @param pen - Current typesetting position (probably the end of the line in typesetting space). * @param alignment - Targeted alignment, e.g., left, center, or right. - * @param vertices - Glyph vertices for rendering to align the origins of (expected to be not yet transformed). + * @param vertices - Glyph vertices for rendering to align the origins' x-components of (expected untransformed). * @param begin - Vertex index to start alignment at. * @param end - Vertex index to stop alignment at. */ - protected static align(pen: vec2, alignment: Label.Alignment, - vertices: GlyphVertices | undefined, begin: number, end: number): void { + protected static transformAlignment(pen: vec2, alignment: Label.Alignment, + vertices: GlyphVertices | undefined, begin: number, end: number) { if (vertices === undefined || alignment === Label.Alignment.Left) { return; } @@ -167,14 +112,56 @@ export class Typesetter { } /** - * Configuring the vertex for a given glyph to be renderer. If no vertex is given or the glyph is not depictable, + * Adjusts the vertices for line anchor (done due after typesetting) w.r.t. the targeted anchoring. + * @param label - Label to adjust the y-positions for. + * @param vertices - Glyph vertices for rendering to align the origins' y-components of (expected untransformed). + * @param begin - Vertex index to start alignment at. + * @param end - Vertex index to stop alignment at. + * + * @todo Apply once at the beginning! Initial offset! + */ + protected static transformLineAnchor(label: Label, + vertices: GlyphVertices | undefined, begin: number, end: number) { + if (vertices === undefined) { + return; + } + + let offset = 0.0; + switch (label.lineAnchor) { + case Label.LineAnchor.Ascent: + offset = label.fontFace.ascent; + break; + case Label.LineAnchor.Center: + offset = label.fontFace.size * 0.5 + label.fontFace.descent; + break; + case Label.LineAnchor.Descent: + offset = label.fontFace.descent; + break; + case Label.LineAnchor.Top: + offset = label.fontFace.base; + break; + case Label.LineAnchor.Bottom: + offset = label.fontFace.base - label.fontFace.lineHeight; + break; + case Label.LineAnchor.Baseline: + default: + return; + } + + for (let i = begin; i < end; ++i) { + vertices[i].origin[1] -= offset; + } + } + + /** + * Configuring the vertex for a given glyph to be rendered. If no vertex is given or the glyph is not depictable, * this method immediately exits at the beginning. * @param fontFace - Font face to be applied for setting up the vertex. * @param pen - Typesetting position which is the not-yet-transformed position the glyph will be rendered at. * @param glyph - Glyph that is to be rendered/configured. * @param vertex - Associated vertex to store data required for rendering. */ - protected static glyph(fontFace: FontFace, pen: vec2, glyph: Glyph, vertex: GlyphVertex | undefined): void { + protected static transformGlyph(fontFace: FontFace, pen: vec2, glyph: Glyph, vertex: GlyphVertex | undefined) { if (vertex === undefined || glyph.depictable() === false) { return; } @@ -198,7 +185,51 @@ export class Typesetter { } - static typeset(label: Label, vertices?: GlyphVertices, begin?: number): vec2 { + /** + * Computes origin, tangent, and up vector for every vertex of in the given range. + * @param transform - Transformation to apply to every vertex. + * @param vertices - Glyph vertices to be transformed (expected untransformed, in typesetting space). + * @param begin - Vertex index to start alignment at. + * @param end - Vertex index to stop alignment at. + */ + protected static transformVertex(transform: mat4, + vertices: GlyphVertices | undefined, begin: number, end: number): void { + if (vertices === undefined || mat4.equals(transform, mat4.create())) { + return; + } + + for (let i: number = begin; i < end; ++i) { + const v = vertices[i]; + + const lowerLeft: vec4 = vec4.transformMat4(v4(), vec4.fromValues( + v.origin[0], v.origin[1], v.origin[2], 1.0), transform); + const lowerRight: vec4 = vec4.transformMat4(v4(), vec4.fromValues( + v.origin[0] + v.tangent[0], v.origin[1] + v.tangent[1], v.origin[2] + v.tangent[2], 1.0), transform); + const upperLeft: vec4 = vec4.transformMat4(v4(), vec4.fromValues( + v.origin[0] + v.up[0], v.origin[1] + v.up[1], v.origin[2] + v.up[2], 1.0), transform); + + v.origin = fromVec4(lowerLeft); + v.tangent = fromVec4(vec4.sub(v4(), lowerRight, lowerLeft)); + v.up = fromVec4(vec4.sub(v4(), upperLeft, lowerLeft)); + } + } + + + /** + * Transforms the labels extent (typesetting space to, e.g., world space or screen space). + * @param transform - Transformation that was applied to every vertex. + * @param extent - Untransformed label extent (in typesetting space) to be transformed. + * @returns - Transformed label extent (copy). + */ + protected static transformExtent(transform: mat4, extent: vec2): GLfloat2 { + const lowerLeft = vec4.transformMat4(v4(), vec4.fromValues(0.0, 0.0, 0.0, 1.0), transform); + const lowerRight = vec4.transformMat4(v4(), vec4.fromValues(extent[0], 0.0, 0.0, 1.0), transform); + const upperLeft = vec4.transformMat4(v4(), vec4.fromValues(0.0, extent[1], 0.0, 1.0), transform); + return [vec4.distance(lowerRight, lowerLeft), vec4.distance(upperLeft, lowerLeft)]; + } + + + static typeset(label: Label, vertices?: GlyphVertices, begin?: number): GLfloat2 { /* Horizontal and vertical position at which typesetting takes place/arrived. */ const pen = vec2.create(); @@ -213,7 +244,8 @@ export class Typesetter { let safeForwardIndex = iBegin; let feedVertexIndex: number = vertexIndex; - for (let index = iBegin; index !== iEnd; ++index) { + let index = iBegin; + for (; index !== iEnd; ++index) { const glyph = label.fontFace.glyph(label.charCodeAt(index)); /* Handle line feeds as well as word wrap for next word (or next glyph if word exceeds the line width). */ @@ -224,7 +256,7 @@ export class Typesetter { /* Handle pen and extent w.r.t. non-depictable glyphs. */ Typesetter.backward(label, index - 1, iBegin, pen, extent); /* Handle alignment (does nothing if vertices are not required/undefined). */ - Typesetter.align(pen, label.alignment, vertices, feedVertexIndex, vertexIndex); + Typesetter.transformAlignment(pen, label.alignment, vertices, feedVertexIndex, vertexIndex); pen[0] = 0.0; pen[1] -= label.fontFace.lineHeight; @@ -236,102 +268,19 @@ export class Typesetter { } /* Add and configure data for rendering the current character/glyph of the label. */ - Typesetter.glyph(label.fontFace, pen, glyph, vertices ? vertices[vertexIndex++] : undefined); - - /* ... todo */ - - // pen[0] += glyph.advance; - - // if (i + 1 == iEnd) // handle alignment (when last line of sequence is processed) - // { - // Typesetter.extent(fontFace, sequence, i, iBegin, pen, extent); + Typesetter.transformGlyph(label.fontFace, pen, glyph, vertices ? vertices[vertexIndex++] : undefined); - // if (!dryrun) { - // typeset_align(pen, sequence.alignment, vertices, feedVertexIndex, vertexIndex); - // } - // } - // } - - // if (!dryRun) { - // anchor_transform(sequence, vertices, begin, vertexIndex); - // vertex_transform(sequence.transform, sequence.fontColor, sequence.superSampling, vertices, begin, vertexIndex); - // } - // return extent_transform(sequence, extent); + pen[0] += glyph.advance; } + /* Handle alignment (when last line of sequence is processed). */ + Typesetter.backward(label, index - 1, iBegin, pen, extent); + /* Handle alignment and anchoring (does nothing if vertices are not required/undefined). */ + Typesetter.transformAlignment(pen, label.alignment, vertices, feedVertexIndex, iEnd - 1); + Typesetter.transformLineAnchor(label, vertices, iBegin, iEnd - 1); + Typesetter.transformVertex(label.transform, vertices, iBegin, vertexIndex); + return Typesetter.transformExtent(label.transform, extent); + } - -// function anchor_transform(sequence: GlyphSequence, vertices: Vertices, begin: number, end: number): void { - -// let offset: number = 0.0; - -// switch (sequence.lineAnchor) { -// case LineAnchor.Ascent: -// offset = sequence.fontFace.ascent; -// break; -// case LineAnchor.Center: -// offset = sequence.fontFace.size * 0.5 + sequence.fontFace.descent; -// break; -// case LineAnchor.Descent: -// offset = sequence.fontFace.descent; -// break; -// case LineAnchor.Top: -// offset = sequence.fontFace.base; -// break; -// case LineAnchor.Bottom: -// offset = sequence.fontFace.base - sequence.fontFace.lineHeight; -// break; -// case LineAnchor.Baseline: -// default: -// return; -// } - -// for (let i: number = begin; i < end; ++i) { -// let v = vertices[i]; -// v.origin[1] -= offset; -// } -// } - -// function vertex_transform(transform: mat4, fontColor: vec4, superSampling: SuperSampling | GLuint, vertices: Vertices, begin: number, end: number): void { -// for (let i: number = begin; i < end; ++i) { -// let v = vertices[i]; - -// let ll: vec4 = vec4.create(); -// vec4.transformMat4(ll, vec4.fromValues(v.origin[0], v.origin[1], v.origin[2], 1.0), transform); - -// let lr: vec4 = vec4.create(); -// vec4.transformMat4(lr, vec4.fromValues(v.origin[0] + v.vtan[0], v.origin[1] + v.vtan[1], v.origin[2] + v.vtan[2], 1.0), transform); - -// let ul: vec4 = vec4.create(); -// vec4.transformMat4(ul, vec4.fromValues(v.origin[0] + v.vbitan[0], v.origin[1] + v.vbitan[1], v.origin[2] + v.vbitan[2], 1.0), transform); - - -// v.origin = vec3.fromValues(ll[0], ll[1], ll[2]) - -// let vtan: vec4 = vec4.create(); -// vec4.sub(vtan, lr, ll); -// v.vtan = vec3.fromValues(vtan[0], vtan[1], vtan[2]) - -// let vbitan: vec4 = vec4.create(); -// vec4.sub(vbitan, ul, ll); -// v.vbitan = vec3.fromValues(vbitan[0], vbitan[1], vbitan[2]) - -// v.fontColor = fontColor; -// v.superSampling = superSampling; -// } -// } - -// function extent_transform(sequence: GlyphSequence, extent: vec2): vec2 { -// let ll = vec4.create(); -// vec4.transformMat4(ll, vec4.fromValues(0.0, 0.0, 0.0, 1.0), sequence.transform); - -// let lr = vec4.create(); -// vec4.transformMat4(lr, vec4.fromValues(extent[0], 0.0, 0.0, 1.0), sequence.transform); - -// let ul = vec4.create(); -// vec4.transformMat4(ul, vec4.fromValues(0.0, extent[1], 0.0, 1.0), sequence.transform); - -// return vec2.fromValues(vec4.distance(lr, ll), vec4.distance(ul, ll)); -// } -// } +} From 662fa62e645fc4623da9491879f248581ca60eec Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 1 Jun 2018 14:31:49 +0200 Subject: [PATCH 05/92] GlyphSequence was renamed to Label, so update comments --- source/labelrenderer.ts | 2 +- source/text.ts | 8 ++++---- source/typesetter.ts | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index edc19e7f..f7018a45 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -45,7 +45,7 @@ // protected _transformValid: boolean; // protected _transform: mat4; - +/** TODO GlyphSequenceConfig --> LabelConfig */ // // public setFromConfig(config: GlyphSequenceConfig) { // // this.wordWrap = config.wordWrap; // // this.lineWidth = config.lineWidth; diff --git a/source/text.ts b/source/text.ts index e28ca013..3f7d2a10 100644 --- a/source/text.ts +++ b/source/text.ts @@ -1,9 +1,9 @@ /** - * The text object is intended as character sequence manipulation interface. A text can be referenced by multiple glyph - * sequences for rendering and interaction. E.g., a single text could be renderer multiple times at different locations - * or using different font faces, alignments, etc. The text object will probably increase in complexity when additional - * features such as text formatting (bold, italic, varying size), (multi)cursor, (multi)selection, etc. will be added. + * The text object is intended as character sequence manipulation interface. A text can be referenced by multiple labels + * for rendering and interaction. E.g., a single text could be rendered multiple times at different locations or using + * different font faces, alignments, etc. The text object will probably increase in complexity when additional features + * such as text formatting (bold, italic, varying size), (multi)cursor, (multi)selection, etc. will be added. */ export class Text { diff --git a/source/typesetter.ts b/source/typesetter.ts index c083ade3..bd931a3d 100644 --- a/source/typesetter.ts +++ b/source/typesetter.ts @@ -11,10 +11,10 @@ import { GLfloat2 } from './tuples'; /** - * The typesetter is responsible for layouting text on the screen or in a virtual space. It takes a glyph sequence, - * which defines where it wants to appear (@see {@link GlyphSequence}), and a font face that is used to display the + * The typesetter is responsible for layouting text on the screen or in a virtual space. It takes a label, + * which defines where it wants to appear (@see {@link Label}), and a font face that is used to display the * text, and computes the actual position for each glyph. Its output is a vertex array, which describes the glyphs - * position and appearance on the screen/in the scene and which can be rendered using a GlyphRenderer. + * position and appearance on the screen/in the scene and which can be rendered using a LabelRenderer. */ export class Typesetter { From 49734267547b135e18eaf6922a211d23c35e379a Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 4 Jun 2018 11:48:57 +0200 Subject: [PATCH 06/92] initial commit: hijacked the testrenderer (now using LabelRender) --- source/glyphvertices.ts | 2 +- source/labelrenderer.ts | 201 ++++++++++++++++++++++++++++++++++ source/shaders/glyphquad.frag | 45 ++++++++ source/shaders/glyphquad.vert | 71 ++++++++++++ source/webgl-operate.slim.ts | 1 + website/js/website.js | 2 +- 6 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 source/shaders/glyphquad.frag create mode 100644 source/shaders/glyphquad.vert diff --git a/source/glyphvertices.ts b/source/glyphvertices.ts index 4563c9ba..c62be1e0 100644 --- a/source/glyphvertices.ts +++ b/source/glyphvertices.ts @@ -87,7 +87,7 @@ export class GlyphVertices extends Array { // protected _transformValid: boolean; // protected _transform: mat4; - +/** TODO GlyphSequenceConfig --> LabelConfig ( if at all ) */ // // public setFromConfig(config: GlyphSequenceConfig) { // // this.wordWrap = config.wordWrap; // // this.lineWidth = config.lineWidth; diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index f7018a45..e1c9ce24 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -1,4 +1,205 @@ +import { assert } from './auxiliaries'; + +import { AccumulatePass } from './accumulatepass'; +import { AntiAliasingKernel } from './antialiasingkernel'; +import { BlitPass } from './blitpass'; +import { Context } from './context'; +import { DefaultFramebuffer } from './defaultframebuffer'; +import { Framebuffer } from './framebuffer'; +import { MouseEventProvider } from './mouseeventprovider'; +import { NdcFillingTriangle } from './ndcfillingtriangle'; +import { Program } from './program'; +import { Renderbuffer } from './renderbuffer'; +import { Invalidate, Renderer } from './renderer'; +import { Shader } from './shader'; +import { Texture2 } from './texture2'; + +import { TestNavigation } from './debug/testnavigation'; + + +export class LabelRenderer extends Renderer { + + protected _extensions = false; + protected _program: Program; + + protected _ndcOffsetKernel: AntiAliasingKernel; + protected _uNdcOffset: WebGLUniformLocation; + protected _uFrameNumber: WebGLUniformLocation; + protected _ndcTriangle: NdcFillingTriangle; + + protected _accumulate: AccumulatePass; + protected _blit: BlitPass; + + protected _defaultFBO: DefaultFramebuffer; + protected _colorRenderTexture: Texture2; + protected _depthRenderbuffer: Renderbuffer; + protected _intermediateFBO: Framebuffer; + + protected _testNavigation: TestNavigation; + + + protected onInitialize(context: Context, callback: Invalidate, + mouseEventProvider: MouseEventProvider, + /* keyEventProvider: KeyEventProvider, */ + /* touchEventProvider: TouchEventProvider */): boolean { + + const gl = this._context.gl; + const gl2facade = this._context.gl2facade; + + /* Enable required extensions. */ + + if (this._extensions === false && this._context.isWebGL1) { + assert(this._context.supportsStandardDerivatives, `expected OES_standard_derivatives support`); + /* tslint:disable-next-line:no-unused-expression */ + this._context.standardDerivatives; + this._extensions = true; + } + + /* Create and configure program and geometry. */ + + // const vert = new Shader(this._context, gl.VERTEX_SHADER, 'glyphquad.vert'); + // vert.initialize(require('./shaders/glyphquad.vert')); + // const frag = new Shader(this._context, gl.FRAGMENT_SHADER, 'glyphquad.frag'); + // frag.initialize(require('./shaders/glyphquad.frag')); + const vert = new Shader(this._context, gl.VERTEX_SHADER, 'testrenderer.vert'); + vert.initialize(require('./debug/testrenderer.vert')); + const frag = new Shader(this._context, gl.FRAGMENT_SHADER, 'testrenderer.frag'); + frag.initialize(require('./debug/testrenderer.frag')); + + this._program = new Program(this._context); + this._program.initialize([vert, frag]); + + this._uNdcOffset = this._program.uniform('u_ndcOffset'); + this._uFrameNumber = this._program.uniform('u_frameNumber'); + + this._ndcTriangle = new NdcFillingTriangle(this._context); + const aVertex = this._program.attribute('a_vertex', 0); + this._ndcTriangle.initialize(aVertex); + + this._ndcOffsetKernel = new AntiAliasingKernel(this._multiFrameNumber); + + /* Create framebuffers, textures, and render buffers. */ + + this._defaultFBO = new DefaultFramebuffer(this._context, 'DefaultFBO'); + this._defaultFBO.initialize(); + + this._colorRenderTexture = new Texture2(this._context, 'ColorRenderTexture'); + this._depthRenderbuffer = new Renderbuffer(this._context, 'DepthRenderbuffer'); + + this._intermediateFBO = new Framebuffer(this._context, 'IntermediateFBO'); + + /* Create and configure accumulation pass. */ + + this._accumulate = new AccumulatePass(this._context); + this._accumulate.initialize(this._ndcTriangle); + this._accumulate.precision = this._framePrecision; + this._accumulate.texture = this._colorRenderTexture; + // this._accumulate.depthStencilAttachment = this._depthRenderbuffer; + + /* Create and configure blit pass. */ + + this._blit = new BlitPass(this._context); + this._blit.initialize(this._ndcTriangle); + this._blit.readBuffer = gl2facade.COLOR_ATTACHMENT0; + this._blit.drawBuffer = gl.BACK; + this._blit.target = this._defaultFBO; + + /* Create and configure test navigation. */ + + this._testNavigation = new TestNavigation(() => this.invalidate(), mouseEventProvider); + + return true; + } + + protected onUninitialize(): void { + super.uninitialize(); + + this._uNdcOffset = -1; + this._uFrameNumber = -1; + this._program.uninitialize(); + + this._ndcTriangle.uninitialize(); + + this._intermediateFBO.uninitialize(); + this._defaultFBO.uninitialize(); + this._colorRenderTexture.uninitialize(); + this._depthRenderbuffer.uninitialize(); + + this._blit.uninitialize(); + } + + + protected onUpdate(): boolean { + this._testNavigation.update(); + + const redraw = this._testNavigation.altered; + this._testNavigation.reset(); + + if (!redraw && !this._altered.any) { + return false; + } + + if (this._altered.multiFrameNumber) { + this._ndcOffsetKernel.width = this._multiFrameNumber; + } + + return redraw; + } + + protected onPrepare(): void { + const gl = this._context.gl; + const gl2facade = this._context.gl2facade; + + if (!this._intermediateFBO.initialized) { + this._colorRenderTexture.initialize(this._frameSize[0], this._frameSize[1], + this._context.isWebGL2 ? gl.RGBA8 : gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE); + this._depthRenderbuffer.initialize(this._frameSize[0], this._frameSize[1], gl.DEPTH_COMPONENT16); + this._intermediateFBO.initialize([[gl2facade.COLOR_ATTACHMENT0, this._colorRenderTexture] + , [gl.DEPTH_ATTACHMENT, this._depthRenderbuffer]]); + + } else if (this._altered.frameSize) { + this._intermediateFBO.resize(this._frameSize[0], this._frameSize[1]); + } + + if (this._altered.clearColor) { + this._intermediateFBO.clearColor(this._clearColor); + } + + this._accumulate.update(); + + this._altered.reset(); + } + + protected onFrame(frameNumber: number): void { + const gl = this._context.gl; + + gl.viewport(0, 0, this._frameSize[0], this._frameSize[1]); + + this._program.bind(); + + const ndcOffset = this._ndcOffsetKernel.get(frameNumber); + ndcOffset[0] = 2.0 * ndcOffset[0] / this._frameSize[0]; + ndcOffset[1] = 2.0 * ndcOffset[1] / this._frameSize[1]; + gl.uniform2fv(this._uNdcOffset, ndcOffset); + gl.uniform1i(this._uFrameNumber, frameNumber); + + this._intermediateFBO.clear(gl.COLOR_BUFFER_BIT, true, false); + this._ndcTriangle.bind(); + this._ndcTriangle.draw(); + this._intermediateFBO.unbind(); + + this._accumulate.frame(frameNumber); + } + + protected onSwap(): void { + this._blit.framebuffer = this._accumulate.framebuffer ? + this._accumulate.framebuffer : this._blit.framebuffer = this._intermediateFBO; + this._blit.frame(); + } + +} + // protected _superSampling: SuperSampling; diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag new file mode 100644 index 00000000..268ee969 --- /dev/null +++ b/source/shaders/glyphquad.frag @@ -0,0 +1,45 @@ + +#if __VERSION__ == 100 + #extension GL_EXT_draw_buffers : enable + #define texture(sampler, coord) texture2D(sampler, coord) + #ifdef GL_EXT_draw_buffers + #define fragColor gl_FragData[0].rgba + #else + #define COLOR_ATTACHMENT0_RESTRICTED + #define fragColor gl_FragColor.rgba + #endif +#endif + +@import ./facade.frag; + +precision mediump float; + +uniform sampler2D u_glyphs; + +varying vec2 v_texture_coord; + +#if __VERSION__ == 300 +layout(location = 0) out vec4 fragColor; +#endif + +void main(void) +{ + //requires blend: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + float d = texture(u_glyphs, v_texture_coord).r; + /** + * using if-statement and discard can slow down performance: + * it's bad for IMR, TBR, TBDR and early-Z optimization + * https://stackoverflow.com/questions/8509051/is-discard-bad-for-program-performance-in-opengl + * But it is necessary: overlapping glyphs (like in 'ft') should not fight each other + */ + if(d < 0.45) + discard; + + vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green + + //TODO mipmap access? + float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur + + fragColor = vec4(fc.rgb, fc.a * a); +} diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert new file mode 100644 index 00000000..59654407 --- /dev/null +++ b/source/shaders/glyphquad.vert @@ -0,0 +1,71 @@ + +#if __VERSION__ == 100 + #extension GL_EXT_draw_buffers : enable +#endif + +@import ./facade.vert; + +precision mediump float; +precision lowp int; + +#if __VERSION__ == 100 +attribute mediump vec2 a_vertex; +attribute vec3 a_id; // encoded uint24 id in byte3 +attribute mediump vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] +attribute mediump vec3 a_origin; +attribute mediump vec3 a_tan; +attribute mediump vec3 a_bitan; + +attribute mediump vec4 a_transform1; +attribute mediump vec4 a_transform2; +attribute mediump vec4 a_transform3; +attribute mediump vec4 a_transform4; +#else +layout(location = 0) in mediump vec2 a_vertex; +layout(location = 1) in vec3 a_id; // encoded uint24 id in byte3 +layout(location = 2) in mediump vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] +layout(location = 3) in mediump vec3 a_origin; +layout(location = 4) in mediump vec3 a_tan; +layout(location = 5) in mediump vec3 a_bitan; + +layout(location = 7) in mediump vec4 a_transform1; +layout(location = 8) in mediump vec4 a_transform2; +layout(location = 9) in mediump vec4 a_transform3; +layout(location = 10) in mediump vec4 a_transform4; +#endif + +uniform mat4 u_viewProjection; +uniform vec2 u_ndcOffset; + +varying vec2 v_texture_coord; + + +@import ./ndcoffset; + +void main(void) +{ + + //TEXTURE COORDS + + float posX = a_texture_coord[0]; + float posY = a_texture_coord[1]; + + float pos2X = a_texture_coord[2]; + float pos2Y = a_texture_coord[3]; + vec2 texExt = vec2(pos2X-posX, pos2Y-posY); + + v_texture_coord = a_vertex * texExt + a_texture_coord.xy; + + //POSITIONING + + //quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_vertex) + + vec4 vertex = vec4(a_vertex, 0.0, 1.0) * (vec4(a_tan, 1.0) + vec4(a_bitan, 1.0)) + vec4(a_origin, 0.0); + + mat4 transform = mat4(a_transform1, a_transform2, a_transform3, a_transform4); + + vertex = u_viewProjection * transform * vertex; + + ndcOffset(vertex, u_ndcOffset); + gl_Position = vertex; +} diff --git a/source/webgl-operate.slim.ts b/source/webgl-operate.slim.ts index 7c0fbfe6..81c797e0 100644 --- a/source/webgl-operate.slim.ts +++ b/source/webgl-operate.slim.ts @@ -44,6 +44,7 @@ export { AccumulatePass } from './accumulatepass'; export { BlitPass } from './blitpass'; export { ReadbackPass } from './readbackpass'; +export { LabelRenderer } from './labelrenderer'; import * as root_auxiliaries from './auxiliaries'; export import auxiliaries = root_auxiliaries; diff --git a/website/js/website.js b/website/js/website.js index 5604ec94..c202c04e 100644 --- a/website/js/website.js +++ b/website/js/website.js @@ -11,6 +11,6 @@ window.onload = function () { context = canvas.context; canvas.controller.multiFrameNumber = 1024; canvas.frameScale = [0.2, 0.2]; - renderer = new gloperate.debug.TestRenderer(); + renderer = new gloperate.LabelRenderer(); canvas.renderer = renderer; }; From b4a834aff8cab816d26522c4cfcececd5d6e2ab2 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 4 Jun 2018 12:21:44 +0200 Subject: [PATCH 07/92] stripped-down glyph shaders --- source/labelrenderer.ts | 15 ++++---- source/shaders/glyphquad.frag | 31 +++++++--------- source/shaders/glyphquad.vert | 67 ++++++++++++++++++----------------- 3 files changed, 53 insertions(+), 60 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index e1c9ce24..caf8f913 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -58,14 +58,13 @@ export class LabelRenderer extends Renderer { /* Create and configure program and geometry. */ - // const vert = new Shader(this._context, gl.VERTEX_SHADER, 'glyphquad.vert'); - // vert.initialize(require('./shaders/glyphquad.vert')); - // const frag = new Shader(this._context, gl.FRAGMENT_SHADER, 'glyphquad.frag'); - // frag.initialize(require('./shaders/glyphquad.frag')); - const vert = new Shader(this._context, gl.VERTEX_SHADER, 'testrenderer.vert'); - vert.initialize(require('./debug/testrenderer.vert')); - const frag = new Shader(this._context, gl.FRAGMENT_SHADER, 'testrenderer.frag'); - frag.initialize(require('./debug/testrenderer.frag')); + const vert = new Shader(this._context, gl.VERTEX_SHADER, 'glyphquad.vert'); + vert.initialize(require('./shaders/glyphquad.vert')); + + + const frag = new Shader(this._context, gl.FRAGMENT_SHADER, 'glyphquad.frag'); + frag.initialize(require('./shaders/glyphquad.frag')); + this._program = new Program(this._context); this._program.initialize([vert, frag]); diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index 268ee969..e0bac2bd 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -1,45 +1,38 @@ #if __VERSION__ == 100 - #extension GL_EXT_draw_buffers : enable - #define texture(sampler, coord) texture2D(sampler, coord) - #ifdef GL_EXT_draw_buffers - #define fragColor gl_FragData[0].rgba - #else - #define COLOR_ATTACHMENT0_RESTRICTED - #define fragColor gl_FragColor.rgba - #endif + #define fragColor gl_FragColor + #extension GL_OES_standard_derivatives : enable +#else + layout(location = 0) out vec4 fragColor; #endif @import ./facade.frag; precision mediump float; -uniform sampler2D u_glyphs; +// uniform sampler2D u_glyphs; -varying vec2 v_texture_coord; - -#if __VERSION__ == 300 -layout(location = 0) out vec4 fragColor; -#endif +// varying vec2 v_texture_coord; void main(void) { //requires blend: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - float d = texture(u_glyphs, v_texture_coord).r; + // float d = texture(u_glyphs, v_texture_coord).r; /** * using if-statement and discard can slow down performance: * it's bad for IMR, TBR, TBDR and early-Z optimization * https://stackoverflow.com/questions/8509051/is-discard-bad-for-program-performance-in-opengl * But it is necessary: overlapping glyphs (like in 'ft') should not fight each other */ - if(d < 0.45) - discard; + //if(d < 0.45) + // discard; vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green //TODO mipmap access? - float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur + // float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur - fragColor = vec4(fc.rgb, fc.a * a); + //fragColor = vec4(fc.rgb, fc.a * a); + fragColor = vec4(fc.rgb, 1.0); } diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 59654407..69de32d9 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -9,32 +9,32 @@ precision mediump float; precision lowp int; #if __VERSION__ == 100 -attribute mediump vec2 a_vertex; -attribute vec3 a_id; // encoded uint24 id in byte3 -attribute mediump vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] -attribute mediump vec3 a_origin; -attribute mediump vec3 a_tan; -attribute mediump vec3 a_bitan; - -attribute mediump vec4 a_transform1; -attribute mediump vec4 a_transform2; -attribute mediump vec4 a_transform3; -attribute mediump vec4 a_transform4; +attribute vec2 a_vertex; +// attribute vec3 a_id; // encoded uint24 id in byte3 +// attribute vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] +// attribute vec3 a_origin; +// attribute vec3 a_tan; +// attribute vec3 a_bitan; + +// attribute vec4 a_transform1; +// attribute vec4 a_transform2; +// attribute vec4 a_transform3; +// attribute vec4 a_transform4; #else -layout(location = 0) in mediump vec2 a_vertex; -layout(location = 1) in vec3 a_id; // encoded uint24 id in byte3 -layout(location = 2) in mediump vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] -layout(location = 3) in mediump vec3 a_origin; -layout(location = 4) in mediump vec3 a_tan; -layout(location = 5) in mediump vec3 a_bitan; - -layout(location = 7) in mediump vec4 a_transform1; -layout(location = 8) in mediump vec4 a_transform2; -layout(location = 9) in mediump vec4 a_transform3; -layout(location = 10) in mediump vec4 a_transform4; +layout(location = 0) in vec2 a_vertex; +// layout(location = 1) in vec3 a_id; // encoded uint24 id in byte3 +// layout(location = 2) in vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] +// layout(location = 3) in vec3 a_origin; +// layout(location = 4) in vec3 a_tan; +// layout(location = 5) in vec3 a_bitan; + +// layout(location = 7) in vec4 a_transform1; +// layout(location = 8) in vec4 a_transform2; +// layout(location = 9) in vec4 a_transform3; +// layout(location = 10) in vec4 a_transform4; #endif -uniform mat4 u_viewProjection; +// uniform mat4 u_viewProjection; uniform vec2 u_ndcOffset; varying vec2 v_texture_coord; @@ -47,24 +47,25 @@ void main(void) //TEXTURE COORDS - float posX = a_texture_coord[0]; - float posY = a_texture_coord[1]; + // float posX = a_texture_coord[0]; + // float posY = a_texture_coord[1]; - float pos2X = a_texture_coord[2]; - float pos2Y = a_texture_coord[3]; - vec2 texExt = vec2(pos2X-posX, pos2Y-posY); + // float pos2X = a_texture_coord[2]; + // float pos2Y = a_texture_coord[3]; + // vec2 texExt = vec2(pos2X-posX, pos2Y-posY); - v_texture_coord = a_vertex * texExt + a_texture_coord.xy; + // v_texture_coord = a_vertex * texExt + a_texture_coord.xy; //POSITIONING - //quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_vertex) + //quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_vertex) TODO: is this up-to-date? - vec4 vertex = vec4(a_vertex, 0.0, 1.0) * (vec4(a_tan, 1.0) + vec4(a_bitan, 1.0)) + vec4(a_origin, 0.0); + //vec4 vertex = vec4(a_vertex, 0.0, 1.0) * (vec4(a_tan, 1.0) + vec4(a_bitan, 1.0)) + vec4(a_origin, 0.0); + vec4 vertex = vec4(a_vertex, 0.0, 1.0); - mat4 transform = mat4(a_transform1, a_transform2, a_transform3, a_transform4); + // mat4 transform = mat4(a_transform1, a_transform2, a_transform3, a_transform4); - vertex = u_viewProjection * transform * vertex; + // vertex = u_viewProjection * transform * vertex; ndcOffset(vertex, u_ndcOffset); gl_Position = vertex; From 634f9b71c6ffce8b36d0ad239cd614b220e32b6d Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 4 Jun 2018 15:25:56 +0200 Subject: [PATCH 08/92] fix: precision for float --- source/shaders/glyphquad.frag | 6 +++--- source/shaders/glyphquad.vert | 10 +++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index e0bac2bd..c6817d85 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -1,3 +1,6 @@ +precision mediump float; + +@import ./facade.frag; #if __VERSION__ == 100 #define fragColor gl_FragColor @@ -6,9 +9,6 @@ layout(location = 0) out vec4 fragColor; #endif -@import ./facade.frag; - -precision mediump float; // uniform sampler2D u_glyphs; diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 69de32d9..7f9614bc 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -1,14 +1,10 @@ - -#if __VERSION__ == 100 - #extension GL_EXT_draw_buffers : enable -#endif - -@import ./facade.vert; - precision mediump float; precision lowp int; +@import ./facade.vert; + #if __VERSION__ == 100 +#extension GL_EXT_draw_buffers : enable attribute vec2 a_vertex; // attribute vec3 a_id; // encoded uint24 id in byte3 // attribute vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] From 8485777813b1e2fa60f5c0b1d7e4d42a897627e8 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Wed, 6 Jun 2018 15:39:46 +0200 Subject: [PATCH 09/92] added fontloader; untested --- source/fontloader.ts | 260 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 source/fontloader.ts diff --git a/source/fontloader.ts b/source/fontloader.ts new file mode 100644 index 00000000..5ba7e9df --- /dev/null +++ b/source/fontloader.ts @@ -0,0 +1,260 @@ + +import { assert } from './auxiliaries'; + +import { Context } from './context'; +import { FontFace } from './fontface'; +import { Glyph } from './glyph'; +import { GLfloat2, GLfloat4 } from './tuples'; + + +type StringPairs = Map; + +function directoryPath(path: string): string { + const pos = path.lastIndexOf('/'); + + if (pos < 0) { + return ''; + } + + return path.substr(0, pos + 1); // Add trailing slash +} + +function stripped(input: string, blacklist: Array): string { + let result: string = input; + + // first param for erase: + blacklist.forEach((element) => { + if (result.includes(element)) { + result = result.replace(RegExp(element, 'g'), ''); + } + }); + return result; +} + + +/** + * Loads the png image that displays the glyph atlas, that was prepared using a Distance Field Transform. It also + * loads all needed data to use that image from a fnt-file with the same name as the png image. It thus creates + * a font face (@see {@link FontFace}). + * + * Example: + * ``` + * let loader = new FontLoader(); + * let fontFace: FontFace = loader.load(this.context, 'font/opensansr144/opensansr144.fnt', false, callbackFunction); + * ``` + */ +export class FontLoader { + + constructor() { + } + + // fontface: Objects and arrays are passed by reference + protected handleInfo(stream: Array, fontFace: FontFace): void { + const pairs = this.readKeyValuePairs(stream, ['padding']); + + const values = pairs.get('padding')!.split(','); + + assert(values.length === 4, `handleInfo: expected 4 values for padding`); + + const padding: GLfloat4 = [ + parseFloat(values[2]), // top + parseFloat(values[1]), // right + parseFloat(values[3]), // bottom + parseFloat(values[0]), // left + ]; + + fontFace.glyphTexturePadding = padding; + } + + protected handleCommon(stream: Array, fontFace: FontFace): void { + const pairs = this.readKeyValuePairs(stream, ['lineHeight', 'base', 'ascent', 'descent', 'scaleW', 'scaleH']); + + fontFace.base = parseFloat(pairs.get('base')!); + fontFace.ascent = parseFloat(pairs.get('ascent')!); + fontFace.descent = parseFloat(pairs.get('descent')!); + + assert(fontFace.size > 0.0, `fontFace.size is not greater than 0.`); + fontFace.lineHeight = parseFloat(pairs.get('lineHeight')!); + + fontFace.glyphTextureExtent = [ + parseFloat(pairs.get('scaleW')!), + parseFloat(pairs.get('scaleH')!), + ]; + } + + protected handlePage( + stream: Array, fontFace: FontFace, filename: string, context: Context + , onImageLoad: (() => void)): void { + + const pairs = this.readKeyValuePairs(stream, ['file']); + + const path = directoryPath(filename); + const file = stripped(pairs.get('file')!, ['"', '\r']); + + const pngPath: string = path + file.split('.')[0] + '.png'; + + const img = new Image(); + img.src = pngPath; + + img.onload = () => { + onImageLoad(); + + const gl = context.gl; + + fontFace.glyphTexture.bind(); + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + + const w: number = img.naturalWidth; + const h: number = img.naturalHeight; + + fontFace.glyphTexture.reformat(gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE); + fontFace.glyphTexture.resize(w, h); + fontFace.glyphTexture.data(img); + + /** + * mipmaps are usually a good idea, but: using Fonts based on Distance Fields and super sampling + * (accumulation frames), the rendered Glyph looks more readable when not using mipmaps. + * Maybe manually provide mipmaps; smaller than 8x8 could be blank (can't render glyphs that small?) + */ + fontFace.glyphTexture.filter(gl.LINEAR, gl.LINEAR, true); + fontFace.glyphTexture.wrap(gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE, true); + + fontFace.glyphTexture.unbind(); + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + }; + } + + protected handleChar(stream: Array, fontFace: FontFace): void { + const pairs = this.readKeyValuePairs(stream, + ['id', 'x', 'y', 'width', 'height', 'xoffset', 'yoffset', 'xadvance']); + + const index: number = parseInt(pairs.get('id')!, 10); + assert(index > 0, `handleChar: glyph index is not greater than 0.`); + + const glyph = new Glyph(); + + glyph.index = index; + + const extentScale: GLfloat2 = [ + 1.0 / fontFace.glyphTextureExtent[0], + 1.0 / fontFace.glyphTextureExtent[1], + ]; + const extent: GLfloat2 = [ + parseFloat(pairs.get('width')!), + parseFloat(pairs.get('height')!), + ]; + + glyph.subTextureOrigin = [ + parseFloat(pairs.get('x')!) * extentScale[0], + 1.0 - (parseFloat(pairs.get('y')!) + extent[1]) * extentScale[1], + ]; + + glyph.extent = extent; + + glyph.subTextureExtent[0] = extent[0] * extentScale[0]; + glyph.subTextureExtent[1] = extent[1] * extentScale[1]; + + glyph.setBearing(fontFace.base, + parseFloat(pairs.get('xoffset')!), + parseFloat(pairs.get('yoffset')!), + ); + + glyph.advance = parseFloat(pairs.get('xadvance')!); + + fontFace.addGlyph(glyph); + } + + protected handleKerning(stream: Array, fontFace: FontFace): void { + const pairs = this.readKeyValuePairs(stream, ['first', 'second', 'amount']); + + const first: number = parseInt(pairs.get('first')!, 10); + assert(first > 0, `handleKerning: first is not greater than 0.`); + + const second: number = parseInt(pairs.get('second')!, 10); + assert(second > 0, `handleKerning: second is not greater than 0.`); + + const kerning: number = parseFloat(pairs.get('amount')!); + + fontFace.setKerning(first, second, kerning); + } + + protected readKeyValuePairs(stream: Array, mandatoryKeys: Array): StringPairs { + + let key: string; + let value: string; + + const pairs: StringPairs = new Map(); + + for (const s of stream) { + const pair = s.split('='); + key = pair[0]; + value = pair[1]; + pairs.set(key, value); + } + + // check if all required keys are provided + let valid = true; + for (const mandatoryKey of mandatoryKeys) { + valid = valid && pairs.has(mandatoryKey); + } + + if (!valid) { + return new Map(); // typescript does not allow writing `new StringPairs()` + } else { + return pairs; + } + } + + /** + * @param context + * @param filename path to a .fnt file + * @param headless + * @param onImageLoad Callback is called when the glyph atlas is loaded. + */ + load(context: Context, filename: string, headless: boolean, onImageLoad: (() => void)): FontFace { + const xmlhttp = new XMLHttpRequest(); + + // fontface: Objects and arrays are passed by reference (JS) + const fontFace = new FontFace(context); + + // asynchronous loading + xmlhttp.open('GET', filename, true); + xmlhttp.onreadystatechange = () => { + if (xmlhttp.readyState === 4) { + if (xmlhttp.status === 200 || xmlhttp.status === 0) { + const text = xmlhttp.responseText; + + const lines = text.split('\n'); + + for (const l of lines) { + let line = l.split(' '); + const identifier = line[0]; + line = line.slice(1); + + if (identifier === 'info') { + this.handleInfo(line, fontFace); + } else if (identifier === 'common') { + this.handleCommon(line, fontFace); + } else if (identifier === 'page' && !headless) { + this.handlePage(line, fontFace, filename, context, onImageLoad); + } else if (identifier === 'char') { + this.handleChar(line, fontFace); + } else if (identifier === 'kerning') { + this.handleKerning(line, fontFace); + } + } + } + } + }; + xmlhttp.send(); + + // TODO: assert? throw exception? + // if (headless || fontFace.glyphTexture) { + return fontFace; + // } else { + // return null; + // } + } +} From 61b69ff7a278ffd60fda4185d436d6c41d137afd Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Thu, 7 Jun 2018 15:47:19 +0200 Subject: [PATCH 10/92] use fontloader; ToDo: copy source/data to build/data when installing --- source/data/opensansr144/opensansr144.fnt | 1845 +++++++++++++++++++++ source/data/opensansr144/opensansr144.png | Bin 0 -> 542566 bytes source/labelrenderer.ts | 13 + 3 files changed, 1858 insertions(+) create mode 100644 source/data/opensansr144/opensansr144.fnt create mode 100644 source/data/opensansr144/opensansr144.png diff --git a/source/data/opensansr144/opensansr144.fnt b/source/data/opensansr144/opensansr144.fnt new file mode 100644 index 00000000..84785432 --- /dev/null +++ b/source/data/opensansr144/opensansr144.fnt @@ -0,0 +1,1845 @@ +info face=opensansr144 size=144 bold=0 italic=0 charset= unicode= stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=0,0 outline=0 +common lineHeight=196.1 base=135 ascent=110 descent=-34 scaleW=2048 scaleH=2048 pages=1 packed=0 +page id=0 file="opensansr144.2048.2048.r.ub.raw" +chars count=217 +char id=33 x=0 y=0 width=25 height=112 xoffset=14.69 yoffset=31.29 xadvance=38.46 page=0 chnl=15 +char id=34 x=25 y=0 width=47 height=45 xoffset=13.35 yoffset=31.29 xadvance=57.73 page=0 chnl=15 +char id=35 x=72 y=0 width=93 height=110 xoffset=7.59 yoffset=31.29 xadvance=93.02 page=0 chnl=15 +char id=36 x=165 y=0 width=71 height=125 xoffset=13.21 yoffset=24.82 xadvance=82.34 page=0 chnl=15 +char id=37 x=236 y=0 width=111 height=113 xoffset=11.31 yoffset=29.81 xadvance=118.55 page=0 chnl=15 +char id=38 x=347 y=0 width=104 height=113 xoffset=11.95 yoffset=29.67 xadvance=105.12 page=0 chnl=15 +char id=39 x=451 y=0 width=21 height=45 xoffset=13.35 yoffset=31.29 xadvance=31.85 page=0 chnl=15 +char id=40 x=472 y=0 width=40 height=133 xoffset=9.77 yoffset=31.29 xadvance=42.61 page=0 chnl=15 +char id=41 x=512 y=0 width=40 height=133 xoffset=8.29 yoffset=31.29 xadvance=42.61 page=0 chnl=15 +char id=42 x=552 y=0 width=75 height=72 xoffset=10.05 yoffset=24.68 xadvance=79.45 page=0 chnl=15 +char id=43 x=627 y=0 width=75 height=77 xoffset=11.31 yoffset=48.38 xadvance=82.34 page=0 chnl=15 +char id=44 x=702 y=0 width=29 height=43 xoffset=8.43 yoffset=117.35 xadvance=35.3 page=0 chnl=15 +char id=45 x=731 y=0 width=42 height=18 xoffset=9.91 yoffset=90.14 xadvance=46.34 page=0 chnl=15 +char id=46 x=773 y=0 width=25 height=27 xoffset=14.69 yoffset=117.07 xadvance=38.32 page=0 chnl=15 +char id=47 x=798 y=0 width=58 height=110 xoffset=5.41 yoffset=31.29 xadvance=52.88 page=0 chnl=15 +char id=48 x=856 y=0 width=76 height=113 xoffset=11.17 yoffset=29.67 xadvance=82.34 page=0 chnl=15 +char id=49 x=932 y=0 width=45 height=110 xoffset=17.22 yoffset=31.29 xadvance=82.34 page=0 chnl=15 +char id=50 x=977 y=0 width=75 height=112 xoffset=11.03 yoffset=29.81 xadvance=82.34 page=0 chnl=15 +char id=51 x=1052 y=0 width=75 height=113 xoffset=10.61 yoffset=29.81 xadvance=82.34 page=0 chnl=15 +char id=52 x=1127 y=0 width=84 height=111 xoffset=7.02 yoffset=30.73 xadvance=82.34 page=0 chnl=15 +char id=53 x=1211 y=0 width=72 height=112 xoffset=13.35 yoffset=31.29 xadvance=82.34 page=0 chnl=15 +char id=54 x=1283 y=0 width=75 height=113 xoffset=12.23 yoffset=29.81 xadvance=82.34 page=0 chnl=15 +char id=55 x=1358 y=0 width=76 height=110 xoffset=10.61 yoffset=31.29 xadvance=82.34 page=0 chnl=15 +char id=56 x=1434 y=0 width=75 height=113 xoffset=11.31 yoffset=29.81 xadvance=82.34 page=0 chnl=15 +char id=57 x=1509 y=0 width=75 height=113 xoffset=11.45 yoffset=29.81 xadvance=82.34 page=0 chnl=15 +char id=58 x=1584 y=0 width=25 height=89 xoffset=14.69 yoffset=55.05 xadvance=38.32 page=0 chnl=15 +char id=59 x=1609 y=0 width=31 height=105 xoffset=8.43 yoffset=55.05 xadvance=38.32 page=0 chnl=15 +char id=60 x=1640 y=0 width=75 height=78 xoffset=11.31 yoffset=46.83 xadvance=82.34 page=0 chnl=15 +char id=61 x=1715 y=0 width=73 height=46 xoffset=12.37 yoffset=64.13 xadvance=82.34 page=0 chnl=15 +char id=62 x=1788 y=0 width=75 height=78 xoffset=11.31 yoffset=46.83 xadvance=82.34 page=0 chnl=15 +char id=63 x=1863 y=0 width=64 height=114 xoffset=5.9 yoffset=29.81 xadvance=61.8 page=0 chnl=15 +char id=64 x=1927 y=0 width=120 height=123 xoffset=12.51 yoffset=31.43 xadvance=129.45 page=0 chnl=15 +char id=65 x=552 y=77 width=99 height=111 xoffset=4 yoffset=30.87 xadvance=91.13 page=0 chnl=15 +char id=66 x=702 y=43 width=79 height=110 xoffset=18.13 yoffset=31.29 xadvance=93.3 page=0 chnl=15 +char id=67 x=1640 y=78 width=85 height=113 xoffset=12.79 yoffset=29.81 xadvance=90.84 page=0 chnl=15 +char id=68 x=1725 y=78 width=90 height=110 xoffset=18.13 yoffset=31.29 xadvance=104.98 page=0 chnl=15 +char id=69 x=25 y=110 width=65 height=110 xoffset=18.13 yoffset=31.29 xadvance=80.09 page=0 chnl=15 +char id=70 x=90 y=110 width=65 height=110 xoffset=18.13 yoffset=31.29 xadvance=74.32 page=0 chnl=15 +char id=71 x=932 y=112 width=93 height=113 xoffset=12.79 yoffset=29.81 xadvance=104.84 page=0 chnl=15 +char id=72 x=1127 y=112 width=86 height=110 xoffset=18.13 yoffset=31.29 xadvance=106.24 page=0 chnl=15 +char id=73 x=451 y=45 width=20 height=110 xoffset=18.13 yoffset=31.29 xadvance=40.15 page=0 chnl=15 +char id=74 x=651 y=77 width=44 height=137 xoffset=-7.25 yoffset=31.29 xadvance=38.46 page=0 chnl=15 +char id=75 x=236 y=113 width=82 height=110 xoffset=18.13 yoffset=31.29 xadvance=88.38 page=0 chnl=15 +char id=76 x=781 y=110 width=65 height=110 xoffset=18.13 yoffset=31.29 xadvance=74.74 page=0 chnl=15 +char id=77 x=318 y=113 width=109 height=110 xoffset=18.13 yoffset=31.29 xadvance=130.01 page=0 chnl=15 +char id=78 x=1025 y=113 width=88 height=110 xoffset=18.13 yoffset=31.29 xadvance=108.56 page=0 chnl=15 +char id=79 x=1213 y=113 width=102 height=113 xoffset=12.79 yoffset=29.67 xadvance=112.15 page=0 chnl=15 +char id=80 x=1358 y=110 width=73 height=110 xoffset=18.13 yoffset=31.29 xadvance=86.7 page=0 chnl=15 +char id=81 x=1431 y=113 width=102 height=136 xoffset=12.79 yoffset=29.67 xadvance=112.15 page=0 chnl=15 +char id=82 x=846 y=113 width=80 height=110 xoffset=18.13 yoffset=31.29 xadvance=89.02 page=0 chnl=15 +char id=83 x=1533 y=113 width=72 height=113 xoffset=11.45 yoffset=29.81 xadvance=79.03 page=0 chnl=15 +char id=84 x=1815 y=114 width=85 height=110 xoffset=5.27 yoffset=31.29 xadvance=79.66 page=0 chnl=15 +char id=85 x=1900 y=123 width=86 height=112 xoffset=17.08 yoffset=31.29 xadvance=104.84 page=0 chnl=15 +char id=86 x=427 y=155 width=93 height=110 xoffset=4 yoffset=31.29 xadvance=85.71 page=0 chnl=15 +char id=87 x=1605 y=191 width=137 height=110 xoffset=5.9 yoffset=31.29 xadvance=133.31 page=0 chnl=15 +char id=88 x=520 y=188 width=89 height=110 xoffset=4.56 yoffset=31.29 xadvance=83.11 page=0 chnl=15 +char id=89 x=609 y=214 width=88 height=110 xoffset=4 yoffset=31.29 xadvance=80.65 page=0 chnl=15 +char id=90 x=155 y=125 width=78 height=110 xoffset=9.77 yoffset=31.29 xadvance=82.2 page=0 chnl=15 +char id=91 x=1315 y=113 width=40 height=133 xoffset=15.67 yoffset=31.29 xadvance=47.39 page=0 chnl=15 +char id=93 x=1986 y=123 width=40 height=133 xoffset=7.59 yoffset=31.29 xadvance=47.39 page=0 chnl=15 +char id=94 x=697 y=153 width=78 height=72 xoffset=7.45 yoffset=30.52 xadvance=78.05 page=0 chnl=15 +char id=95 x=1715 y=46 width=73 height=17 xoffset=3.72 yoffset=147.02 xadvance=64.55 page=0 chnl=15 +char id=96 x=25 y=45 width=35 height=31 xoffset=31.63 yoffset=23.77 xadvance=83.11 page=0 chnl=15 +char id=97 x=1742 y=188 width=69 height=87 xoffset=10.61 yoffset=55.76 xadvance=80.09 page=0 chnl=15 +char id=98 x=0 y=220 width=75 height=118 xoffset=16.38 yoffset=24.68 xadvance=88.24 page=0 chnl=15 +char id=99 x=75 y=220 width=63 height=87 xoffset=12.09 yoffset=55.62 xadvance=68.55 page=0 chnl=15 +char id=100 x=1355 y=220 width=75 height=118 xoffset=12.09 yoffset=24.68 xadvance=88.24 page=0 chnl=15 +char id=101 x=1113 y=222 width=73 height=87 xoffset=12.09 yoffset=55.62 xadvance=80.79 page=0 chnl=15 +char id=102 x=775 y=220 width=61 height=118 xoffset=6.04 yoffset=23.91 xadvance=48.8 page=0 chnl=15 +char id=103 x=233 y=223 width=80 height=121 xoffset=6.74 yoffset=55.62 xadvance=78.89 page=0 chnl=15 +char id=104 x=313 y=223 width=72 height=117 xoffset=16.38 yoffset=24.68 xadvance=88.38 page=0 chnl=15 +char id=105 x=2026 y=123 width=21 height=113 xoffset=15.39 yoffset=28.41 xadvance=36.42 page=0 chnl=15 +char id=106 x=385 y=223 width=41 height=148 xoffset=-3.8 yoffset=28.41 xadvance=36.42 page=0 chnl=15 +char id=107 x=836 y=223 width=69 height=117 xoffset=16.38 yoffset=24.68 xadvance=75.59 page=0 chnl=15 +char id=108 x=1186 y=222 width=19 height=117 xoffset=16.38 yoffset=24.68 xadvance=36.42 page=0 chnl=15 +char id=109 x=905 y=225 width=117 height=86 xoffset=16.38 yoffset=55.62 xadvance=133.95 page=0 chnl=15 +char id=110 x=1025 y=223 width=72 height=86 xoffset=16.38 yoffset=55.62 xadvance=88.38 page=0 chnl=15 +char id=111 x=1811 y=224 width=78 height=87 xoffset=12.09 yoffset=55.62 xadvance=86.98 page=0 chnl=15 +char id=112 x=697 y=225 width=75 height=121 xoffset=16.38 yoffset=55.62 xadvance=88.24 page=0 chnl=15 +char id=113 x=1205 y=226 width=75 height=121 xoffset=12.09 yoffset=55.62 xadvance=88.24 page=0 chnl=15 +char id=114 x=1533 y=226 width=52 height=86 xoffset=16.38 yoffset=55.62 xadvance=58.78 page=0 chnl=15 +char id=115 x=138 y=235 width=62 height=87 xoffset=11.45 yoffset=55.62 xadvance=68.7 page=0 chnl=15 +char id=116 x=1889 y=235 width=53 height=104 xoffset=6.18 yoffset=39.16 xadvance=50.84 page=0 chnl=15 +char id=117 x=1280 y=246 width=72 height=86 xoffset=15.53 yoffset=57.02 xadvance=88.38 page=0 chnl=15 +char id=118 x=1430 y=249 width=80 height=85 xoffset=4 yoffset=57.02 xadvance=72.14 page=0 chnl=15 +char id=119 x=426 y=298 width=116 height=85 xoffset=5.62 yoffset=57.02 xadvance=112.01 page=0 chnl=15 +char id=120 x=1942 y=256 width=77 height=85 xoffset=6.74 yoffset=57.02 xadvance=75.45 page=0 chnl=15 +char id=121 x=1585 y=301 width=80 height=119 xoffset=4.14 yoffset=57.02 xadvance=72.56 page=0 chnl=15 +char id=122 x=1742 y=275 width=63 height=85 xoffset=9.77 yoffset=57.02 xadvance=67.36 page=0 chnl=15 +char id=123 x=542 y=298 width=53 height=133 xoffset=8.29 yoffset=31.29 xadvance=54.56 page=0 chnl=15 +char id=124 x=200 y=235 width=18 height=152 xoffset=38.73 yoffset=24.68 xadvance=79.31 page=0 chnl=15 +char id=125 x=1665 y=301 width=53 height=133 xoffset=9.06 yoffset=31.29 xadvance=54.56 page=0 chnl=15 +char id=126 x=426 y=265 width=75 height=26 xoffset=11.31 yoffset=74.18 xadvance=82.34 page=0 chnl=15 +char id=127 x=1022 y=309 width=67 height=110 xoffset=17.57 yoffset=31.29 xadvance=86.41 page=0 chnl=15 +char id=8364 x=1089 y=309 width=85 height=113 xoffset=8.43 yoffset=29.81 xadvance=84.94 page=0 chnl=15 +char id=8218 x=1605 y=105 width=29 height=43 xoffset=8.43 yoffset=117.35 xadvance=35.3 page=0 chnl=15 +char id=402 x=905 y=311 width=67 height=146 xoffset=17.71 yoffset=29.81 xadvance=83.11 page=0 chnl=15 +char id=8222 x=75 y=307 width=54 height=43 xoffset=5.76 yoffset=117.35 xadvance=58.29 page=0 chnl=15 +char id=8230 x=595 y=324 width=99 height=27 xoffset=14.69 yoffset=117.07 xadvance=112.92 page=0 chnl=15 +char id=8224 x=1805 y=311 width=63 height=117 xoffset=12.65 yoffset=24.68 xadvance=72.28 page=0 chnl=15 +char id=8225 x=1510 y=312 width=64 height=117 xoffset=12.65 yoffset=24.68 xadvance=73.41 page=0 chnl=15 +char id=710 x=129 y=322 width=55 height=31 xoffset=22.84 yoffset=23.77 xadvance=85.22 page=0 chnl=15 +char id=8240 x=1280 y=338 width=167 height=113 xoffset=11.03 yoffset=29.81 xadvance=173.11 page=0 chnl=15 +char id=352 x=0 y=338 width=72 height=143 xoffset=11.45 yoffset=0 xadvance=79.03 page=0 chnl=15 +char id=8249 x=972 y=311 width=40 height=67 xoffset=9.77 yoffset=66.73 xadvance=43.8 page=0 chnl=15 +char id=338 x=772 y=340 width=123 height=113 xoffset=12.79 yoffset=29.67 xadvance=132.89 page=0 chnl=15 +char id=381 x=1868 y=341 width=78 height=142 xoffset=9.77 yoffset=0 xadvance=82.2 page=0 chnl=15 +char id=8216 x=520 y=133 width=29 height=43 xoffset=5.76 yoffset=31.29 xadvance=24.47 page=0 chnl=15 +char id=8217 x=1605 y=148 width=29 height=43 xoffset=5.76 yoffset=31.29 xadvance=24.47 page=0 chnl=15 +char id=8220 x=1447 y=334 width=54 height=43 xoffset=5.76 yoffset=31.29 xadvance=50.41 page=0 chnl=15 +char id=8221 x=313 y=340 width=54 height=43 xoffset=5.76 yoffset=31.29 xadvance=50.41 page=0 chnl=15 +char id=8226 x=1946 y=341 width=39 height=42 xoffset=15.53 yoffset=64.13 xadvance=54.14 page=0 chnl=15 +char id=8211 x=218 y=344 width=68 height=18 xoffset=9.77 yoffset=90.14 xadvance=72 page=0 chnl=15 +char id=8212 x=595 y=351 width=140 height=18 xoffset=9.77 yoffset=90.14 xadvance=144 page=0 chnl=15 +char id=732 x=1985 y=341 width=60 height=26 xoffset=22.56 yoffset=28.55 xadvance=85.22 page=0 chnl=15 +char id=8482 x=1174 y=347 width=104 height=58 xoffset=6.6 yoffset=31.29 xadvance=111.73 page=0 chnl=15 +char id=353 x=72 y=353 width=62 height=119 xoffset=11.45 yoffset=23.77 xadvance=68.7 page=0 chnl=15 +char id=8250 x=134 y=353 width=40 height=67 xoffset=9.63 yoffset=66.73 xadvance=43.8 page=0 chnl=15 +char id=339 x=595 y=369 width=128 height=87 xoffset=11.95 yoffset=55.76 xadvance=135.63 page=0 chnl=15 +char id=382 x=1718 y=360 width=63 height=118 xoffset=9.77 yoffset=23.77 xadvance=67.36 page=0 chnl=15 +char id=376 x=218 y=362 width=88 height=136 xoffset=4 yoffset=5.48 xadvance=80.65 page=0 chnl=15 +char id=32 x=2047 y=0 width=0 height=0 xoffset=0 yoffset=130.09 xadvance=37.41 page=0 chnl=15 +char id=161 x=735 y=346 width=25 height=112 xoffset=14.69 yoffset=55.48 xadvance=38.46 page=0 chnl=15 +char id=162 x=306 y=383 width=64 height=113 xoffset=17.36 yoffset=29.81 xadvance=82.34 page=0 chnl=15 +char id=163 x=370 y=383 width=80 height=112 xoffset=8.43 yoffset=29.95 xadvance=82.34 page=0 chnl=15 +char id=164 x=450 y=383 width=73 height=72 xoffset=12.65 yoffset=50.84 xadvance=82.34 page=0 chnl=15 +char id=165 x=1946 y=383 width=85 height=110 xoffset=6.18 yoffset=31.29 xadvance=82.34 page=0 chnl=15 +char id=166 x=174 y=353 width=18 height=152 xoffset=38.73 yoffset=24.68 xadvance=79.31 page=0 chnl=15 +char id=167 x=1447 y=377 width=63 height=118 xoffset=12.65 yoffset=24.05 xadvance=74.32 page=0 chnl=15 +char id=168 x=25 y=76 width=47 height=21 xoffset=25.73 yoffset=29.25 xadvance=83.11 page=0 chnl=15 +char id=169 x=972 y=419 width=113 height=113 xoffset=11.03 yoffset=29.81 xadvance=119.81 page=0 chnl=15 +char id=170 x=1174 y=405 width=47 height=56 xoffset=8.92 yoffset=30.09 xadvance=50.98 page=0 chnl=15 +char id=171 x=1574 y=420 width=68 height=67 xoffset=9.77 yoffset=66.73 xadvance=71.58 page=0 chnl=15 +char id=172 x=1085 y=422 width=75 height=45 xoffset=11.31 yoffset=78.47 xadvance=82.34 page=0 chnl=15 +char id=173 x=731 y=18 width=42 height=18 xoffset=9.91 yoffset=90.14 xadvance=46.34 page=0 chnl=15 +char id=174 x=1221 y=451 width=113 height=113 xoffset=11.03 yoffset=29.81 xadvance=119.81 page=0 chnl=15 +char id=175 x=1781 y=428 width=80 height=17 xoffset=3.58 yoffset=15.75 xadvance=72 page=0 chnl=15 +char id=176 x=1510 y=429 width=51 height=51 xoffset=12.93 yoffset=29.81 xadvance=61.66 page=0 chnl=15 +char id=177 x=1642 y=434 width=75 height=93 xoffset=11.31 yoffset=48.38 xadvance=82.34 page=0 chnl=15 +char id=178 x=523 y=431 width=50 height=71 xoffset=7.45 yoffset=29.95 xadvance=49.99 page=0 chnl=15 +char id=179 x=1781 y=445 width=51 height=72 xoffset=6.32 yoffset=29.95 xadvance=49.99 page=0 chnl=15 +char id=180 x=1815 y=78 width=35 height=31 xoffset=31.63 yoffset=23.77 xadvance=83.11 page=0 chnl=15 +char id=181 x=1334 y=451 width=72 height=119 xoffset=16.38 yoffset=57.02 xadvance=89.16 page=0 chnl=15 +char id=182 x=760 y=453 width=78 height=135 xoffset=11.95 yoffset=24.68 xadvance=94.29 page=0 chnl=15 +char id=183 x=0 y=112 width=25 height=27 xoffset=14.69 yoffset=73.76 xadvance=38.32 page=0 chnl=15 +char id=184 x=1221 y=405 width=36 height=42 xoffset=6.6 yoffset=134.09 xadvance=32.7 page=0 chnl=15 +char id=185 x=134 y=420 width=36 height=69 xoffset=9.34 yoffset=31.29 xadvance=49.99 page=0 chnl=15 +char id=186 x=838 y=453 width=52 height=56 xoffset=8.64 yoffset=30.09 xadvance=54 page=0 chnl=15 +char id=187 x=450 y=455 width=68 height=67 xoffset=9.63 yoffset=66.73 xadvance=71.58 page=0 chnl=15 +char id=188 x=573 y=456 width=107 height=110 xoffset=9.27 yoffset=31.29 xadvance=112.29 page=0 chnl=15 +char id=189 x=1085 y=467 width=110 height=110 xoffset=7.23 yoffset=31.29 xadvance=112.29 page=0 chnl=15 +char id=190 x=0 y=481 width=116 height=112 xoffset=5.83 yoffset=29.95 xadvance=112.29 page=0 chnl=15 +char id=191 x=890 y=457 width=64 height=114 xoffset=7.59 yoffset=55.48 xadvance=61.8 page=0 chnl=15 +char id=192 x=1832 y=483 width=99 height=142 xoffset=4 yoffset=0 xadvance=91.13 page=0 chnl=15 +char id=193 x=1510 y=487 width=99 height=142 xoffset=4 yoffset=0 xadvance=91.13 page=0 chnl=15 +char id=194 x=1931 y=493 width=99 height=142 xoffset=4 yoffset=0 xadvance=91.13 page=0 chnl=15 +char id=195 x=1406 y=495 width=99 height=137 xoffset=4 yoffset=4.78 xadvance=91.13 page=0 chnl=15 +char id=196 x=306 y=496 width=99 height=136 xoffset=4 yoffset=5.48 xadvance=91.13 page=0 chnl=15 +char id=197 x=192 y=498 width=99 height=134 xoffset=4 yoffset=7.66 xadvance=91.13 page=0 chnl=15 +char id=198 x=405 y=522 width=125 height=110 xoffset=3.86 yoffset=31.29 xadvance=125.72 page=0 chnl=15 +char id=199 x=1717 y=517 width=85 height=146 xoffset=12.79 yoffset=29.81 xadvance=90.84 page=0 chnl=15 +char id=200 x=680 y=458 width=65 height=142 xoffset=18.13 yoffset=0 xadvance=80.09 page=0 chnl=15 +char id=201 x=116 y=505 width=65 height=142 xoffset=18.13 yoffset=0 xadvance=80.09 page=0 chnl=15 +char id=202 x=1609 y=527 width=65 height=142 xoffset=18.13 yoffset=0 xadvance=80.09 page=0 chnl=15 +char id=203 x=954 y=532 width=65 height=136 xoffset=18.13 yoffset=5.48 xadvance=80.09 page=0 chnl=15 +char id=204 x=530 y=502 width=35 height=142 xoffset=4.35 yoffset=0 xadvance=40.15 page=0 chnl=15 +char id=205 x=838 y=509 width=35 height=142 xoffset=16.59 yoffset=0 xadvance=40.15 page=0 chnl=15 +char id=206 x=1019 y=532 width=55 height=142 xoffset=-0.01 yoffset=0 xadvance=40.15 page=0 chnl=15 +char id=207 x=1195 y=564 width=47 height=136 xoffset=4.35 yoffset=5.48 xadvance=40.15 page=0 chnl=15 +char id=208 x=565 y=566 width=99 height=110 xoffset=7.3 yoffset=31.29 xadvance=103.99 page=0 chnl=15 +char id=209 x=1242 y=564 width=88 height=137 xoffset=18.13 yoffset=4.78 xadvance=108.56 page=0 chnl=15 +char id=210 x=1074 y=577 width=102 height=143 xoffset=12.79 yoffset=0 xadvance=112.15 page=0 chnl=15 +char id=211 x=0 y=593 width=102 height=143 xoffset=12.79 yoffset=0 xadvance=112.15 page=0 chnl=15 +char id=212 x=664 y=600 width=102 height=143 xoffset=12.79 yoffset=0 xadvance=112.15 page=0 chnl=15 +char id=213 x=1802 y=625 width=102 height=138 xoffset=12.79 yoffset=4.78 xadvance=112.15 page=0 chnl=15 +char id=214 x=1505 y=629 width=102 height=138 xoffset=12.79 yoffset=5.48 xadvance=112.15 page=0 chnl=15 +char id=215 x=1330 y=570 width=71 height=71 xoffset=13.35 yoffset=51.4 xadvance=82.34 page=0 chnl=15 +char id=216 x=181 y=632 width=102 height=119 xoffset=12.79 yoffset=26.79 xadvance=112.15 page=0 chnl=15 +char id=217 x=283 y=632 width=86 height=143 xoffset=17.08 yoffset=0 xadvance=104.84 page=0 chnl=15 +char id=218 x=369 y=632 width=86 height=143 xoffset=17.08 yoffset=0 xadvance=104.84 page=0 chnl=15 +char id=219 x=1401 y=632 width=86 height=143 xoffset=17.08 yoffset=0 xadvance=104.84 page=0 chnl=15 +char id=220 x=1904 y=635 width=86 height=138 xoffset=17.08 yoffset=5.48 xadvance=104.84 page=0 chnl=15 +char id=221 x=455 y=644 width=88 height=142 xoffset=4 yoffset=0 xadvance=80.65 page=0 chnl=15 +char id=222 x=873 y=571 width=74 height=110 xoffset=18.13 yoffset=31.29 xadvance=87.96 page=0 chnl=15 +char id=223 x=102 y=647 width=78 height=119 xoffset=16.38 yoffset=23.91 xadvance=89.58 page=0 chnl=15 +char id=224 x=766 y=588 width=69 height=119 xoffset=10.61 yoffset=23.77 xadvance=80.09 page=0 chnl=15 +char id=225 x=1330 y=641 width=69 height=119 xoffset=10.61 yoffset=23.77 xadvance=80.09 page=0 chnl=15 +char id=226 x=1674 y=663 width=69 height=119 xoffset=10.61 yoffset=23.77 xadvance=80.09 page=0 chnl=15 +char id=227 x=947 y=668 width=69 height=115 xoffset=10.61 yoffset=28.55 xadvance=80.09 page=0 chnl=15 +char id=228 x=543 y=676 width=69 height=114 xoffset=10.61 yoffset=29.25 xadvance=80.09 page=0 chnl=15 +char id=229 x=835 y=681 width=69 height=126 xoffset=10.61 yoffset=16.73 xadvance=80.09 page=0 chnl=15 +char id=230 x=1176 y=701 width=117 height=87 xoffset=10.61 yoffset=55.62 xadvance=123.54 page=0 chnl=15 +char id=231 x=1607 y=669 width=63 height=121 xoffset=12.09 yoffset=55.62 xadvance=68.55 page=0 chnl=15 +char id=232 x=1016 y=720 width=73 height=119 xoffset=12.09 yoffset=23.77 xadvance=80.79 page=0 chnl=15 +char id=233 x=1089 y=720 width=73 height=119 xoffset=12.09 yoffset=23.77 xadvance=80.79 page=0 chnl=15 +char id=234 x=0 y=736 width=73 height=119 xoffset=12.09 yoffset=23.77 xadvance=80.79 page=0 chnl=15 +char id=235 x=612 y=743 width=73 height=114 xoffset=12.09 yoffset=29.25 xadvance=80.79 page=0 chnl=15 +char id=236 x=1674 y=527 width=35 height=118 xoffset=1.33 yoffset=23.77 xadvance=36.42 page=0 chnl=15 +char id=237 x=1990 y=635 width=35 height=118 xoffset=15.88 yoffset=23.77 xadvance=36.42 page=0 chnl=15 +char id=238 x=1743 y=663 width=55 height=118 xoffset=-1.41 yoffset=23.77 xadvance=36.42 page=0 chnl=15 +char id=239 x=766 y=707 width=47 height=112 xoffset=2.59 yoffset=29.25 xadvance=36.42 page=0 chnl=15 +char id=240 x=685 y=743 width=79 height=119 xoffset=11.95 yoffset=23.77 xadvance=85.85 page=0 chnl=15 +char id=241 x=180 y=751 width=72 height=113 xoffset=16.38 yoffset=28.55 xadvance=88.38 page=0 chnl=15 +char id=242 x=1293 y=760 width=78 height=119 xoffset=12.09 yoffset=23.77 xadvance=86.98 page=0 chnl=15 +char id=243 x=1798 y=763 width=78 height=119 xoffset=12.09 yoffset=23.77 xadvance=86.98 page=0 chnl=15 +char id=244 x=73 y=766 width=78 height=119 xoffset=12.09 yoffset=23.77 xadvance=86.98 page=0 chnl=15 +char id=245 x=1487 y=767 width=78 height=115 xoffset=12.09 yoffset=28.55 xadvance=86.98 page=0 chnl=15 +char id=246 x=1876 y=773 width=78 height=114 xoffset=12.09 yoffset=29.25 xadvance=86.98 page=0 chnl=15 +char id=247 x=1954 y=773 width=75 height=74 xoffset=11.31 yoffset=50.27 xadvance=82.34 page=0 chnl=15 +char id=248 x=252 y=775 width=78 height=94 xoffset=12.09 yoffset=52.59 xadvance=86.98 page=0 chnl=15 +char id=249 x=330 y=775 width=72 height=119 xoffset=15.53 yoffset=23.77 xadvance=88.38 page=0 chnl=15 +char id=250 x=1371 y=775 width=72 height=119 xoffset=15.53 yoffset=23.77 xadvance=88.38 page=0 chnl=15 +char id=251 x=1670 y=782 width=72 height=119 xoffset=15.53 yoffset=23.77 xadvance=88.38 page=0 chnl=15 +char id=252 x=904 y=783 width=72 height=114 xoffset=15.53 yoffset=29.25 xadvance=88.38 page=0 chnl=15 +char id=253 x=402 y=786 width=80 height=153 xoffset=4.14 yoffset=23.77 xadvance=72.56 page=0 chnl=15 +char id=254 x=1162 y=788 width=75 height=152 xoffset=16.38 yoffset=24.68 xadvance=88.24 page=0 chnl=15 +char id=255 x=482 y=790 width=80 height=147 xoffset=4.14 yoffset=29.25 xadvance=72.56 page=0 chnl=15 +kernings count=1623 +kerning first=34 second=65 amount=-10.05 +kerning first=34 second=84 amount=2.88 +kerning first=34 second=86 amount=2.88 +kerning first=34 second=87 amount=2.88 +kerning first=34 second=89 amount=1.41 +kerning first=34 second=97 amount=-5.77 +kerning first=34 second=99 amount=-8.65 +kerning first=34 second=100 amount=-8.65 +kerning first=34 second=101 amount=-8.65 +kerning first=34 second=103 amount=-4.29 +kerning first=34 second=109 amount=-4.29 +kerning first=34 second=110 amount=-4.29 +kerning first=34 second=111 amount=-8.65 +kerning first=34 second=112 amount=-4.29 +kerning first=34 second=113 amount=-8.65 +kerning first=34 second=114 amount=-4.29 +kerning first=34 second=115 amount=-4.29 +kerning first=34 second=117 amount=-4.29 +kerning first=34 second=192 amount=-10.05 +kerning first=34 second=193 amount=-10.05 +kerning first=34 second=194 amount=-10.05 +kerning first=34 second=195 amount=-10.05 +kerning first=34 second=196 amount=-10.05 +kerning first=34 second=197 amount=-10.05 +kerning first=34 second=221 amount=1.41 +kerning first=34 second=224 amount=-8.65 +kerning first=34 second=225 amount=-5.77 +kerning first=34 second=226 amount=-5.77 +kerning first=34 second=227 amount=-5.77 +kerning first=34 second=228 amount=-5.77 +kerning first=34 second=229 amount=-5.77 +kerning first=34 second=230 amount=-5.77 +kerning first=34 second=231 amount=-8.65 +kerning first=34 second=232 amount=-8.65 +kerning first=34 second=233 amount=-8.65 +kerning first=34 second=234 amount=-8.65 +kerning first=34 second=235 amount=-8.65 +kerning first=34 second=242 amount=-8.65 +kerning first=34 second=243 amount=-8.65 +kerning first=34 second=244 amount=-8.65 +kerning first=34 second=245 amount=-8.65 +kerning first=34 second=246 amount=-8.65 +kerning first=34 second=248 amount=-8.65 +kerning first=34 second=249 amount=-4.29 +kerning first=34 second=250 amount=-4.29 +kerning first=34 second=251 amount=-4.29 +kerning first=34 second=252 amount=-4.29 +kerning first=34 second=339 amount=-8.65 +kerning first=34 second=376 amount=1.41 +kerning first=39 second=65 amount=-10.05 +kerning first=39 second=84 amount=2.88 +kerning first=39 second=86 amount=2.88 +kerning first=39 second=87 amount=2.88 +kerning first=39 second=89 amount=1.41 +kerning first=39 second=97 amount=-5.77 +kerning first=39 second=99 amount=-8.65 +kerning first=39 second=100 amount=-8.65 +kerning first=39 second=101 amount=-8.65 +kerning first=39 second=103 amount=-4.29 +kerning first=39 second=109 amount=-4.29 +kerning first=39 second=110 amount=-4.29 +kerning first=39 second=111 amount=-8.65 +kerning first=39 second=112 amount=-4.29 +kerning first=39 second=113 amount=-8.65 +kerning first=39 second=114 amount=-4.29 +kerning first=39 second=115 amount=-4.29 +kerning first=39 second=117 amount=-4.29 +kerning first=39 second=192 amount=-10.05 +kerning first=39 second=193 amount=-10.05 +kerning first=39 second=194 amount=-10.05 +kerning first=39 second=195 amount=-10.05 +kerning first=39 second=196 amount=-10.05 +kerning first=39 second=197 amount=-10.05 +kerning first=39 second=221 amount=1.41 +kerning first=39 second=224 amount=-8.65 +kerning first=39 second=225 amount=-5.77 +kerning first=39 second=226 amount=-5.77 +kerning first=39 second=227 amount=-5.77 +kerning first=39 second=228 amount=-5.77 +kerning first=39 second=229 amount=-5.77 +kerning first=39 second=230 amount=-5.77 +kerning first=39 second=231 amount=-8.65 +kerning first=39 second=232 amount=-8.65 +kerning first=39 second=233 amount=-8.65 +kerning first=39 second=234 amount=-8.65 +kerning first=39 second=235 amount=-8.65 +kerning first=39 second=242 amount=-8.65 +kerning first=39 second=243 amount=-8.65 +kerning first=39 second=244 amount=-8.65 +kerning first=39 second=245 amount=-8.65 +kerning first=39 second=246 amount=-8.65 +kerning first=39 second=248 amount=-8.65 +kerning first=39 second=249 amount=-4.29 +kerning first=39 second=250 amount=-4.29 +kerning first=39 second=251 amount=-4.29 +kerning first=39 second=252 amount=-4.29 +kerning first=39 second=339 amount=-8.65 +kerning first=39 second=376 amount=1.41 +kerning first=40 second=74 amount=12.94 +kerning first=44 second=67 amount=-7.17 +kerning first=44 second=71 amount=-7.17 +kerning first=44 second=79 amount=-7.17 +kerning first=44 second=81 amount=-7.17 +kerning first=44 second=84 amount=-10.05 +kerning first=44 second=85 amount=-2.88 +kerning first=44 second=86 amount=-8.65 +kerning first=44 second=87 amount=-8.65 +kerning first=44 second=89 amount=-8.65 +kerning first=44 second=199 amount=-7.17 +kerning first=44 second=210 amount=-7.17 +kerning first=44 second=211 amount=-7.17 +kerning first=44 second=212 amount=-7.17 +kerning first=44 second=213 amount=-7.17 +kerning first=44 second=214 amount=-7.17 +kerning first=44 second=216 amount=-7.17 +kerning first=44 second=217 amount=-2.88 +kerning first=44 second=218 amount=-2.88 +kerning first=44 second=219 amount=-2.88 +kerning first=44 second=220 amount=-2.88 +kerning first=44 second=221 amount=-8.65 +kerning first=44 second=338 amount=-7.17 +kerning first=44 second=376 amount=-8.65 +kerning first=45 second=84 amount=-5.77 +kerning first=46 second=67 amount=-7.17 +kerning first=46 second=71 amount=-7.17 +kerning first=46 second=79 amount=-7.17 +kerning first=46 second=81 amount=-7.17 +kerning first=46 second=84 amount=-10.05 +kerning first=46 second=85 amount=-2.88 +kerning first=46 second=86 amount=-8.65 +kerning first=46 second=87 amount=-8.65 +kerning first=46 second=89 amount=-8.65 +kerning first=46 second=199 amount=-7.17 +kerning first=46 second=210 amount=-7.17 +kerning first=46 second=211 amount=-7.17 +kerning first=46 second=212 amount=-7.17 +kerning first=46 second=213 amount=-7.17 +kerning first=46 second=214 amount=-7.17 +kerning first=46 second=216 amount=-7.17 +kerning first=46 second=217 amount=-2.88 +kerning first=46 second=218 amount=-2.88 +kerning first=46 second=219 amount=-2.88 +kerning first=46 second=220 amount=-2.88 +kerning first=46 second=221 amount=-8.65 +kerning first=46 second=338 amount=-7.17 +kerning first=46 second=376 amount=-8.65 +kerning first=65 second=34 amount=-10.05 +kerning first=65 second=39 amount=-10.05 +kerning first=65 second=67 amount=-2.88 +kerning first=65 second=71 amount=-2.88 +kerning first=65 second=74 amount=18.7 +kerning first=65 second=79 amount=-2.88 +kerning first=65 second=81 amount=-2.88 +kerning first=65 second=84 amount=-10.05 +kerning first=65 second=86 amount=-5.77 +kerning first=65 second=87 amount=-5.77 +kerning first=65 second=89 amount=-8.65 +kerning first=65 second=199 amount=-2.88 +kerning first=65 second=210 amount=-2.88 +kerning first=65 second=211 amount=-2.88 +kerning first=65 second=212 amount=-2.88 +kerning first=65 second=213 amount=-2.88 +kerning first=65 second=214 amount=-2.88 +kerning first=65 second=216 amount=-2.88 +kerning first=65 second=221 amount=-8.65 +kerning first=65 second=338 amount=-2.88 +kerning first=65 second=376 amount=-8.65 +kerning first=65 second=8217 amount=-10.05 +kerning first=65 second=8221 amount=-10.05 +kerning first=66 second=44 amount=-5.77 +kerning first=66 second=46 amount=-5.77 +kerning first=66 second=65 amount=-2.88 +kerning first=66 second=84 amount=-4.29 +kerning first=66 second=86 amount=-1.41 +kerning first=66 second=87 amount=-1.41 +kerning first=66 second=88 amount=-2.88 +kerning first=66 second=89 amount=-1.41 +kerning first=66 second=90 amount=-1.41 +kerning first=66 second=192 amount=-2.88 +kerning first=66 second=193 amount=-2.88 +kerning first=66 second=194 amount=-2.88 +kerning first=66 second=195 amount=-2.88 +kerning first=66 second=196 amount=-2.88 +kerning first=66 second=197 amount=-2.88 +kerning first=66 second=221 amount=-1.41 +kerning first=66 second=376 amount=-1.41 +kerning first=66 second=381 amount=-1.41 +kerning first=66 second=8218 amount=-5.77 +kerning first=66 second=8222 amount=-5.77 +kerning first=67 second=67 amount=-2.88 +kerning first=67 second=71 amount=-2.88 +kerning first=67 second=79 amount=-2.88 +kerning first=67 second=81 amount=-2.88 +kerning first=67 second=199 amount=-2.88 +kerning first=67 second=210 amount=-2.88 +kerning first=67 second=211 amount=-2.88 +kerning first=67 second=212 amount=-2.88 +kerning first=67 second=213 amount=-2.88 +kerning first=67 second=214 amount=-2.88 +kerning first=67 second=216 amount=-2.88 +kerning first=67 second=338 amount=-2.88 +kerning first=68 second=44 amount=-5.77 +kerning first=68 second=46 amount=-5.77 +kerning first=68 second=65 amount=-2.88 +kerning first=68 second=84 amount=-4.29 +kerning first=68 second=86 amount=-1.41 +kerning first=68 second=87 amount=-1.41 +kerning first=68 second=88 amount=-2.88 +kerning first=68 second=89 amount=-1.41 +kerning first=68 second=90 amount=-1.41 +kerning first=68 second=192 amount=-2.88 +kerning first=68 second=193 amount=-2.88 +kerning first=68 second=194 amount=-2.88 +kerning first=68 second=195 amount=-2.88 +kerning first=68 second=196 amount=-2.88 +kerning first=68 second=197 amount=-2.88 +kerning first=68 second=221 amount=-1.41 +kerning first=68 second=376 amount=-1.41 +kerning first=68 second=381 amount=-1.41 +kerning first=68 second=8218 amount=-5.77 +kerning first=68 second=8222 amount=-5.77 +kerning first=69 second=74 amount=8.65 +kerning first=70 second=44 amount=-8.65 +kerning first=70 second=46 amount=-8.65 +kerning first=70 second=63 amount=2.88 +kerning first=70 second=65 amount=-2.88 +kerning first=70 second=192 amount=-2.88 +kerning first=70 second=193 amount=-2.88 +kerning first=70 second=194 amount=-2.88 +kerning first=70 second=195 amount=-2.88 +kerning first=70 second=196 amount=-2.88 +kerning first=70 second=197 amount=-2.88 +kerning first=70 second=8218 amount=-8.65 +kerning first=70 second=8222 amount=-8.65 +kerning first=75 second=67 amount=-2.88 +kerning first=75 second=71 amount=-2.88 +kerning first=75 second=79 amount=-2.88 +kerning first=75 second=81 amount=-2.88 +kerning first=75 second=199 amount=-2.88 +kerning first=75 second=210 amount=-2.88 +kerning first=75 second=211 amount=-2.88 +kerning first=75 second=212 amount=-2.88 +kerning first=75 second=213 amount=-2.88 +kerning first=75 second=214 amount=-2.88 +kerning first=75 second=216 amount=-2.88 +kerning first=75 second=338 amount=-2.88 +kerning first=76 second=34 amount=-11.53 +kerning first=76 second=39 amount=-11.53 +kerning first=76 second=67 amount=-2.88 +kerning first=76 second=71 amount=-2.88 +kerning first=76 second=79 amount=-2.88 +kerning first=76 second=81 amount=-2.88 +kerning first=76 second=84 amount=-2.88 +kerning first=76 second=85 amount=-1.41 +kerning first=76 second=86 amount=-2.88 +kerning first=76 second=87 amount=-2.88 +kerning first=76 second=89 amount=-4.29 +kerning first=76 second=199 amount=-2.88 +kerning first=76 second=210 amount=-2.88 +kerning first=76 second=211 amount=-2.88 +kerning first=76 second=212 amount=-2.88 +kerning first=76 second=213 amount=-2.88 +kerning first=76 second=214 amount=-2.88 +kerning first=76 second=216 amount=-2.88 +kerning first=76 second=217 amount=-1.41 +kerning first=76 second=218 amount=-1.41 +kerning first=76 second=219 amount=-1.41 +kerning first=76 second=220 amount=-1.41 +kerning first=76 second=221 amount=-4.29 +kerning first=76 second=338 amount=-2.88 +kerning first=76 second=376 amount=-4.29 +kerning first=76 second=8217 amount=-11.53 +kerning first=76 second=8221 amount=-11.53 +kerning first=79 second=44 amount=-5.77 +kerning first=79 second=46 amount=-5.77 +kerning first=79 second=65 amount=-2.88 +kerning first=79 second=84 amount=-4.29 +kerning first=79 second=86 amount=-1.41 +kerning first=79 second=87 amount=-1.41 +kerning first=79 second=88 amount=-2.88 +kerning first=79 second=89 amount=-1.41 +kerning first=79 second=90 amount=-1.41 +kerning first=79 second=192 amount=-2.88 +kerning first=79 second=193 amount=-2.88 +kerning first=79 second=194 amount=-2.88 +kerning first=79 second=195 amount=-2.88 +kerning first=79 second=196 amount=-2.88 +kerning first=79 second=197 amount=-2.88 +kerning first=79 second=221 amount=-1.41 +kerning first=79 second=376 amount=-1.41 +kerning first=79 second=381 amount=-1.41 +kerning first=79 second=8218 amount=-5.77 +kerning first=79 second=8222 amount=-5.77 +kerning first=80 second=44 amount=-18.7 +kerning first=80 second=46 amount=-18.7 +kerning first=80 second=65 amount=-7.17 +kerning first=80 second=88 amount=-2.88 +kerning first=80 second=90 amount=-1.41 +kerning first=80 second=192 amount=-7.17 +kerning first=80 second=193 amount=-7.17 +kerning first=80 second=194 amount=-7.17 +kerning first=80 second=195 amount=-7.17 +kerning first=80 second=196 amount=-7.17 +kerning first=80 second=197 amount=-7.17 +kerning first=80 second=381 amount=-1.41 +kerning first=80 second=8218 amount=-18.7 +kerning first=80 second=8222 amount=-18.7 +kerning first=81 second=44 amount=-5.77 +kerning first=81 second=46 amount=-5.77 +kerning first=81 second=65 amount=-2.88 +kerning first=81 second=84 amount=-4.29 +kerning first=81 second=86 amount=-1.41 +kerning first=81 second=87 amount=-1.41 +kerning first=81 second=88 amount=-2.88 +kerning first=81 second=89 amount=-1.41 +kerning first=81 second=90 amount=-1.41 +kerning first=81 second=192 amount=-2.88 +kerning first=81 second=193 amount=-2.88 +kerning first=81 second=194 amount=-2.88 +kerning first=81 second=195 amount=-2.88 +kerning first=81 second=196 amount=-2.88 +kerning first=81 second=197 amount=-2.88 +kerning first=81 second=221 amount=-1.41 +kerning first=81 second=376 amount=-1.41 +kerning first=81 second=381 amount=-1.41 +kerning first=81 second=8218 amount=-5.77 +kerning first=81 second=8222 amount=-5.77 +kerning first=84 second=44 amount=-8.65 +kerning first=84 second=45 amount=-5.77 +kerning first=84 second=46 amount=-8.65 +kerning first=84 second=63 amount=2.88 +kerning first=84 second=65 amount=-10.05 +kerning first=84 second=67 amount=-2.88 +kerning first=84 second=71 amount=-2.88 +kerning first=84 second=79 amount=-2.88 +kerning first=84 second=81 amount=-2.88 +kerning first=84 second=84 amount=2.88 +kerning first=84 second=97 amount=-11.53 +kerning first=84 second=99 amount=-10.05 +kerning first=84 second=100 amount=-10.05 +kerning first=84 second=101 amount=-10.05 +kerning first=84 second=103 amount=-10.05 +kerning first=84 second=109 amount=-7.17 +kerning first=84 second=110 amount=-7.17 +kerning first=84 second=111 amount=-10.05 +kerning first=84 second=112 amount=-7.17 +kerning first=84 second=113 amount=-10.05 +kerning first=84 second=114 amount=-7.17 +kerning first=84 second=115 amount=-8.65 +kerning first=84 second=117 amount=-7.17 +kerning first=84 second=118 amount=-2.88 +kerning first=84 second=119 amount=-2.88 +kerning first=84 second=120 amount=-2.88 +kerning first=84 second=121 amount=-2.88 +kerning first=84 second=122 amount=-5.77 +kerning first=84 second=192 amount=-10.05 +kerning first=84 second=193 amount=-10.05 +kerning first=84 second=194 amount=-10.05 +kerning first=84 second=195 amount=-10.05 +kerning first=84 second=196 amount=-10.05 +kerning first=84 second=197 amount=-10.05 +kerning first=84 second=199 amount=-2.88 +kerning first=84 second=210 amount=-2.88 +kerning first=84 second=211 amount=-2.88 +kerning first=84 second=212 amount=-2.88 +kerning first=84 second=213 amount=-2.88 +kerning first=84 second=214 amount=-2.88 +kerning first=84 second=216 amount=-2.88 +kerning first=84 second=224 amount=-10.05 +kerning first=84 second=225 amount=-11.53 +kerning first=84 second=226 amount=-11.53 +kerning first=84 second=227 amount=-11.53 +kerning first=84 second=228 amount=-11.53 +kerning first=84 second=229 amount=-11.53 +kerning first=84 second=230 amount=-11.53 +kerning first=84 second=231 amount=-10.05 +kerning first=84 second=232 amount=-10.05 +kerning first=84 second=233 amount=-10.05 +kerning first=84 second=234 amount=-10.05 +kerning first=84 second=235 amount=-10.05 +kerning first=84 second=242 amount=-10.05 +kerning first=84 second=243 amount=-10.05 +kerning first=84 second=244 amount=-10.05 +kerning first=84 second=245 amount=-10.05 +kerning first=84 second=246 amount=-10.05 +kerning first=84 second=248 amount=-10.05 +kerning first=84 second=249 amount=-7.17 +kerning first=84 second=250 amount=-7.17 +kerning first=84 second=251 amount=-7.17 +kerning first=84 second=252 amount=-7.17 +kerning first=84 second=253 amount=-2.88 +kerning first=84 second=338 amount=-2.88 +kerning first=84 second=339 amount=-10.05 +kerning first=84 second=382 amount=-5.77 +kerning first=84 second=8211 amount=-5.77 +kerning first=84 second=8212 amount=-5.77 +kerning first=84 second=8218 amount=-8.65 +kerning first=84 second=8222 amount=-8.65 +kerning first=85 second=44 amount=-2.88 +kerning first=85 second=46 amount=-2.88 +kerning first=85 second=65 amount=-1.41 +kerning first=85 second=192 amount=-1.41 +kerning first=85 second=193 amount=-1.41 +kerning first=85 second=194 amount=-1.41 +kerning first=85 second=195 amount=-1.41 +kerning first=85 second=196 amount=-1.41 +kerning first=85 second=197 amount=-1.41 +kerning first=85 second=8218 amount=-2.88 +kerning first=85 second=8222 amount=-2.88 +kerning first=86 second=44 amount=-7.17 +kerning first=86 second=46 amount=-7.17 +kerning first=86 second=63 amount=2.88 +kerning first=86 second=65 amount=-5.77 +kerning first=86 second=67 amount=-1.41 +kerning first=86 second=71 amount=-1.41 +kerning first=86 second=79 amount=-1.41 +kerning first=86 second=81 amount=-1.41 +kerning first=86 second=97 amount=-2.88 +kerning first=86 second=99 amount=-2.88 +kerning first=86 second=100 amount=-2.88 +kerning first=86 second=101 amount=-2.88 +kerning first=86 second=103 amount=-1.41 +kerning first=86 second=109 amount=-1.41 +kerning first=86 second=110 amount=-1.41 +kerning first=86 second=111 amount=-2.88 +kerning first=86 second=112 amount=-1.41 +kerning first=86 second=113 amount=-2.88 +kerning first=86 second=114 amount=-1.41 +kerning first=86 second=115 amount=-1.41 +kerning first=86 second=117 amount=-1.41 +kerning first=86 second=192 amount=-5.77 +kerning first=86 second=193 amount=-5.77 +kerning first=86 second=194 amount=-5.77 +kerning first=86 second=195 amount=-5.77 +kerning first=86 second=196 amount=-5.77 +kerning first=86 second=197 amount=-5.77 +kerning first=86 second=199 amount=-1.41 +kerning first=86 second=210 amount=-1.41 +kerning first=86 second=211 amount=-1.41 +kerning first=86 second=212 amount=-1.41 +kerning first=86 second=213 amount=-1.41 +kerning first=86 second=214 amount=-1.41 +kerning first=86 second=216 amount=-1.41 +kerning first=86 second=224 amount=-2.88 +kerning first=86 second=225 amount=-2.88 +kerning first=86 second=226 amount=-2.88 +kerning first=86 second=227 amount=-2.88 +kerning first=86 second=228 amount=-2.88 +kerning first=86 second=229 amount=-2.88 +kerning first=86 second=230 amount=-2.88 +kerning first=86 second=231 amount=-2.88 +kerning first=86 second=232 amount=-2.88 +kerning first=86 second=233 amount=-2.88 +kerning first=86 second=234 amount=-2.88 +kerning first=86 second=235 amount=-2.88 +kerning first=86 second=242 amount=-2.88 +kerning first=86 second=243 amount=-2.88 +kerning first=86 second=244 amount=-2.88 +kerning first=86 second=245 amount=-2.88 +kerning first=86 second=246 amount=-2.88 +kerning first=86 second=248 amount=-2.88 +kerning first=86 second=249 amount=-1.41 +kerning first=86 second=250 amount=-1.41 +kerning first=86 second=251 amount=-1.41 +kerning first=86 second=252 amount=-1.41 +kerning first=86 second=338 amount=-1.41 +kerning first=86 second=339 amount=-2.88 +kerning first=86 second=8218 amount=-7.17 +kerning first=86 second=8222 amount=-7.17 +kerning first=87 second=44 amount=-7.17 +kerning first=87 second=46 amount=-7.17 +kerning first=87 second=63 amount=2.88 +kerning first=87 second=65 amount=-5.77 +kerning first=87 second=67 amount=-1.41 +kerning first=87 second=71 amount=-1.41 +kerning first=87 second=79 amount=-1.41 +kerning first=87 second=81 amount=-1.41 +kerning first=87 second=97 amount=-2.88 +kerning first=87 second=99 amount=-2.88 +kerning first=87 second=100 amount=-2.88 +kerning first=87 second=101 amount=-2.88 +kerning first=87 second=103 amount=-1.41 +kerning first=87 second=109 amount=-1.41 +kerning first=87 second=110 amount=-1.41 +kerning first=87 second=111 amount=-2.88 +kerning first=87 second=112 amount=-1.41 +kerning first=87 second=113 amount=-2.88 +kerning first=87 second=114 amount=-1.41 +kerning first=87 second=115 amount=-1.41 +kerning first=87 second=117 amount=-1.41 +kerning first=87 second=192 amount=-5.77 +kerning first=87 second=193 amount=-5.77 +kerning first=87 second=194 amount=-5.77 +kerning first=87 second=195 amount=-5.77 +kerning first=87 second=196 amount=-5.77 +kerning first=87 second=197 amount=-5.77 +kerning first=87 second=199 amount=-1.41 +kerning first=87 second=210 amount=-1.41 +kerning first=87 second=211 amount=-1.41 +kerning first=87 second=212 amount=-1.41 +kerning first=87 second=213 amount=-1.41 +kerning first=87 second=214 amount=-1.41 +kerning first=87 second=216 amount=-1.41 +kerning first=87 second=224 amount=-2.88 +kerning first=87 second=225 amount=-2.88 +kerning first=87 second=226 amount=-2.88 +kerning first=87 second=227 amount=-2.88 +kerning first=87 second=228 amount=-2.88 +kerning first=87 second=229 amount=-2.88 +kerning first=87 second=230 amount=-2.88 +kerning first=87 second=231 amount=-2.88 +kerning first=87 second=232 amount=-2.88 +kerning first=87 second=233 amount=-2.88 +kerning first=87 second=234 amount=-2.88 +kerning first=87 second=235 amount=-2.88 +kerning first=87 second=242 amount=-2.88 +kerning first=87 second=243 amount=-2.88 +kerning first=87 second=244 amount=-2.88 +kerning first=87 second=245 amount=-2.88 +kerning first=87 second=246 amount=-2.88 +kerning first=87 second=248 amount=-2.88 +kerning first=87 second=249 amount=-1.41 +kerning first=87 second=250 amount=-1.41 +kerning first=87 second=251 amount=-1.41 +kerning first=87 second=252 amount=-1.41 +kerning first=87 second=338 amount=-1.41 +kerning first=87 second=339 amount=-2.88 +kerning first=87 second=8218 amount=-7.17 +kerning first=87 second=8222 amount=-7.17 +kerning first=88 second=67 amount=-2.88 +kerning first=88 second=71 amount=-2.88 +kerning first=88 second=79 amount=-2.88 +kerning first=88 second=81 amount=-2.88 +kerning first=88 second=199 amount=-2.88 +kerning first=88 second=210 amount=-2.88 +kerning first=88 second=211 amount=-2.88 +kerning first=88 second=212 amount=-2.88 +kerning first=88 second=213 amount=-2.88 +kerning first=88 second=214 amount=-2.88 +kerning first=88 second=216 amount=-2.88 +kerning first=88 second=338 amount=-2.88 +kerning first=89 second=44 amount=-8.65 +kerning first=89 second=46 amount=-8.65 +kerning first=89 second=63 amount=2.88 +kerning first=89 second=65 amount=-8.65 +kerning first=89 second=67 amount=-2.88 +kerning first=89 second=71 amount=-2.88 +kerning first=89 second=79 amount=-2.88 +kerning first=89 second=81 amount=-2.88 +kerning first=89 second=97 amount=-7.17 +kerning first=89 second=99 amount=-7.17 +kerning first=89 second=100 amount=-7.17 +kerning first=89 second=101 amount=-7.17 +kerning first=89 second=103 amount=-2.88 +kerning first=89 second=109 amount=-4.29 +kerning first=89 second=110 amount=-4.29 +kerning first=89 second=111 amount=-7.17 +kerning first=89 second=112 amount=-4.29 +kerning first=89 second=113 amount=-7.17 +kerning first=89 second=114 amount=-4.29 +kerning first=89 second=115 amount=-5.77 +kerning first=89 second=117 amount=-4.29 +kerning first=89 second=122 amount=-2.88 +kerning first=89 second=192 amount=-8.65 +kerning first=89 second=193 amount=-8.65 +kerning first=89 second=194 amount=-8.65 +kerning first=89 second=195 amount=-8.65 +kerning first=89 second=196 amount=-8.65 +kerning first=89 second=197 amount=-8.65 +kerning first=89 second=199 amount=-2.88 +kerning first=89 second=210 amount=-2.88 +kerning first=89 second=211 amount=-2.88 +kerning first=89 second=212 amount=-2.88 +kerning first=89 second=213 amount=-2.88 +kerning first=89 second=214 amount=-2.88 +kerning first=89 second=216 amount=-2.88 +kerning first=89 second=224 amount=-7.17 +kerning first=89 second=225 amount=-7.17 +kerning first=89 second=226 amount=-7.17 +kerning first=89 second=227 amount=-7.17 +kerning first=89 second=228 amount=-7.17 +kerning first=89 second=229 amount=-7.17 +kerning first=89 second=230 amount=-7.17 +kerning first=89 second=231 amount=-7.17 +kerning first=89 second=232 amount=-7.17 +kerning first=89 second=233 amount=-7.17 +kerning first=89 second=234 amount=-7.17 +kerning first=89 second=235 amount=-7.17 +kerning first=89 second=242 amount=-7.17 +kerning first=89 second=243 amount=-7.17 +kerning first=89 second=244 amount=-7.17 +kerning first=89 second=245 amount=-7.17 +kerning first=89 second=246 amount=-7.17 +kerning first=89 second=248 amount=-7.17 +kerning first=89 second=249 amount=-4.29 +kerning first=89 second=250 amount=-4.29 +kerning first=89 second=251 amount=-4.29 +kerning first=89 second=252 amount=-4.29 +kerning first=89 second=338 amount=-2.88 +kerning first=89 second=339 amount=-7.17 +kerning first=89 second=382 amount=-2.88 +kerning first=89 second=8218 amount=-8.65 +kerning first=89 second=8222 amount=-8.65 +kerning first=90 second=67 amount=-1.41 +kerning first=90 second=71 amount=-1.41 +kerning first=90 second=79 amount=-1.41 +kerning first=90 second=81 amount=-1.41 +kerning first=90 second=199 amount=-1.41 +kerning first=90 second=210 amount=-1.41 +kerning first=90 second=211 amount=-1.41 +kerning first=90 second=212 amount=-1.41 +kerning first=90 second=213 amount=-1.41 +kerning first=90 second=214 amount=-1.41 +kerning first=90 second=216 amount=-1.41 +kerning first=90 second=338 amount=-1.41 +kerning first=91 second=74 amount=12.94 +kerning first=97 second=34 amount=-1.41 +kerning first=97 second=39 amount=-1.41 +kerning first=97 second=8217 amount=-1.41 +kerning first=97 second=8221 amount=-1.41 +kerning first=98 second=34 amount=-1.41 +kerning first=98 second=39 amount=-1.41 +kerning first=98 second=118 amount=-2.88 +kerning first=98 second=119 amount=-2.88 +kerning first=98 second=120 amount=-2.88 +kerning first=98 second=121 amount=-2.88 +kerning first=98 second=122 amount=-1.41 +kerning first=98 second=253 amount=-2.88 +kerning first=98 second=382 amount=-1.41 +kerning first=98 second=8217 amount=-1.41 +kerning first=98 second=8221 amount=-1.41 +kerning first=99 second=34 amount=2.88 +kerning first=99 second=39 amount=2.88 +kerning first=99 second=8217 amount=2.88 +kerning first=99 second=8221 amount=2.88 +kerning first=101 second=34 amount=-1.41 +kerning first=101 second=39 amount=-1.41 +kerning first=101 second=118 amount=-2.88 +kerning first=101 second=119 amount=-2.88 +kerning first=101 second=120 amount=-2.88 +kerning first=101 second=121 amount=-2.88 +kerning first=101 second=122 amount=-1.41 +kerning first=101 second=253 amount=-2.88 +kerning first=101 second=382 amount=-1.41 +kerning first=101 second=8217 amount=-1.41 +kerning first=101 second=8221 amount=-1.41 +kerning first=102 second=34 amount=8.65 +kerning first=102 second=39 amount=8.65 +kerning first=102 second=8217 amount=8.65 +kerning first=102 second=8221 amount=8.65 +kerning first=104 second=34 amount=-1.41 +kerning first=104 second=39 amount=-1.41 +kerning first=104 second=8217 amount=-1.41 +kerning first=104 second=8221 amount=-1.41 +kerning first=107 second=99 amount=-2.88 +kerning first=107 second=100 amount=-2.88 +kerning first=107 second=101 amount=-2.88 +kerning first=107 second=111 amount=-2.88 +kerning first=107 second=113 amount=-2.88 +kerning first=107 second=224 amount=-2.88 +kerning first=107 second=231 amount=-2.88 +kerning first=107 second=232 amount=-2.88 +kerning first=107 second=233 amount=-2.88 +kerning first=107 second=234 amount=-2.88 +kerning first=107 second=235 amount=-2.88 +kerning first=107 second=242 amount=-2.88 +kerning first=107 second=243 amount=-2.88 +kerning first=107 second=244 amount=-2.88 +kerning first=107 second=245 amount=-2.88 +kerning first=107 second=246 amount=-2.88 +kerning first=107 second=248 amount=-2.88 +kerning first=107 second=339 amount=-2.88 +kerning first=109 second=34 amount=-1.41 +kerning first=109 second=39 amount=-1.41 +kerning first=109 second=8217 amount=-1.41 +kerning first=109 second=8221 amount=-1.41 +kerning first=110 second=34 amount=-1.41 +kerning first=110 second=39 amount=-1.41 +kerning first=110 second=8217 amount=-1.41 +kerning first=110 second=8221 amount=-1.41 +kerning first=111 second=34 amount=-1.41 +kerning first=111 second=39 amount=-1.41 +kerning first=111 second=118 amount=-2.88 +kerning first=111 second=119 amount=-2.88 +kerning first=111 second=120 amount=-2.88 +kerning first=111 second=121 amount=-2.88 +kerning first=111 second=122 amount=-1.41 +kerning first=111 second=253 amount=-2.88 +kerning first=111 second=382 amount=-1.41 +kerning first=111 second=8217 amount=-1.41 +kerning first=111 second=8221 amount=-1.41 +kerning first=112 second=34 amount=-1.41 +kerning first=112 second=39 amount=-1.41 +kerning first=112 second=118 amount=-2.88 +kerning first=112 second=119 amount=-2.88 +kerning first=112 second=120 amount=-2.88 +kerning first=112 second=121 amount=-2.88 +kerning first=112 second=122 amount=-1.41 +kerning first=112 second=253 amount=-2.88 +kerning first=112 second=382 amount=-1.41 +kerning first=112 second=8217 amount=-1.41 +kerning first=112 second=8221 amount=-1.41 +kerning first=114 second=34 amount=5.77 +kerning first=114 second=39 amount=5.77 +kerning first=114 second=97 amount=-2.88 +kerning first=114 second=99 amount=-2.88 +kerning first=114 second=100 amount=-2.88 +kerning first=114 second=101 amount=-2.88 +kerning first=114 second=103 amount=-1.41 +kerning first=114 second=111 amount=-2.88 +kerning first=114 second=113 amount=-2.88 +kerning first=114 second=224 amount=-2.88 +kerning first=114 second=225 amount=-2.88 +kerning first=114 second=226 amount=-2.88 +kerning first=114 second=227 amount=-2.88 +kerning first=114 second=228 amount=-2.88 +kerning first=114 second=229 amount=-2.88 +kerning first=114 second=230 amount=-2.88 +kerning first=114 second=231 amount=-2.88 +kerning first=114 second=232 amount=-2.88 +kerning first=114 second=233 amount=-2.88 +kerning first=114 second=234 amount=-2.88 +kerning first=114 second=235 amount=-2.88 +kerning first=114 second=242 amount=-2.88 +kerning first=114 second=243 amount=-2.88 +kerning first=114 second=244 amount=-2.88 +kerning first=114 second=245 amount=-2.88 +kerning first=114 second=246 amount=-2.88 +kerning first=114 second=248 amount=-2.88 +kerning first=114 second=339 amount=-2.88 +kerning first=114 second=8217 amount=5.77 +kerning first=114 second=8221 amount=5.77 +kerning first=116 second=34 amount=2.88 +kerning first=116 second=39 amount=2.88 +kerning first=116 second=8217 amount=2.88 +kerning first=116 second=8221 amount=2.88 +kerning first=118 second=34 amount=5.77 +kerning first=118 second=39 amount=5.77 +kerning first=118 second=44 amount=-5.77 +kerning first=118 second=46 amount=-5.77 +kerning first=118 second=63 amount=2.88 +kerning first=118 second=8217 amount=5.77 +kerning first=118 second=8218 amount=-5.77 +kerning first=118 second=8221 amount=5.77 +kerning first=118 second=8222 amount=-5.77 +kerning first=119 second=34 amount=5.77 +kerning first=119 second=39 amount=5.77 +kerning first=119 second=44 amount=-5.77 +kerning first=119 second=46 amount=-5.77 +kerning first=119 second=63 amount=2.88 +kerning first=119 second=8217 amount=5.77 +kerning first=119 second=8218 amount=-5.77 +kerning first=119 second=8221 amount=5.77 +kerning first=119 second=8222 amount=-5.77 +kerning first=120 second=99 amount=-2.88 +kerning first=120 second=100 amount=-2.88 +kerning first=120 second=101 amount=-2.88 +kerning first=120 second=111 amount=-2.88 +kerning first=120 second=113 amount=-2.88 +kerning first=120 second=224 amount=-2.88 +kerning first=120 second=231 amount=-2.88 +kerning first=120 second=232 amount=-2.88 +kerning first=120 second=233 amount=-2.88 +kerning first=120 second=234 amount=-2.88 +kerning first=120 second=235 amount=-2.88 +kerning first=120 second=242 amount=-2.88 +kerning first=120 second=243 amount=-2.88 +kerning first=120 second=244 amount=-2.88 +kerning first=120 second=245 amount=-2.88 +kerning first=120 second=246 amount=-2.88 +kerning first=120 second=248 amount=-2.88 +kerning first=120 second=339 amount=-2.88 +kerning first=121 second=34 amount=5.77 +kerning first=121 second=39 amount=5.77 +kerning first=121 second=44 amount=-5.77 +kerning first=121 second=46 amount=-5.77 +kerning first=121 second=63 amount=2.88 +kerning first=121 second=8217 amount=5.77 +kerning first=121 second=8218 amount=-5.77 +kerning first=121 second=8221 amount=5.77 +kerning first=121 second=8222 amount=-5.77 +kerning first=123 second=74 amount=12.94 +kerning first=192 second=34 amount=-10.05 +kerning first=192 second=39 amount=-10.05 +kerning first=192 second=67 amount=-2.88 +kerning first=192 second=71 amount=-2.88 +kerning first=192 second=74 amount=18.7 +kerning first=192 second=79 amount=-2.88 +kerning first=192 second=81 amount=-2.88 +kerning first=192 second=84 amount=-10.05 +kerning first=192 second=86 amount=-5.77 +kerning first=192 second=87 amount=-5.77 +kerning first=192 second=89 amount=-8.65 +kerning first=192 second=199 amount=-2.88 +kerning first=192 second=210 amount=-2.88 +kerning first=192 second=211 amount=-2.88 +kerning first=192 second=212 amount=-2.88 +kerning first=192 second=213 amount=-2.88 +kerning first=192 second=214 amount=-2.88 +kerning first=192 second=216 amount=-2.88 +kerning first=192 second=221 amount=-8.65 +kerning first=192 second=338 amount=-2.88 +kerning first=192 second=376 amount=-8.65 +kerning first=192 second=8217 amount=-10.05 +kerning first=192 second=8221 amount=-10.05 +kerning first=193 second=34 amount=-10.05 +kerning first=193 second=39 amount=-10.05 +kerning first=193 second=67 amount=-2.88 +kerning first=193 second=71 amount=-2.88 +kerning first=193 second=74 amount=18.7 +kerning first=193 second=79 amount=-2.88 +kerning first=193 second=81 amount=-2.88 +kerning first=193 second=84 amount=-10.05 +kerning first=193 second=86 amount=-5.77 +kerning first=193 second=87 amount=-5.77 +kerning first=193 second=89 amount=-8.65 +kerning first=193 second=199 amount=-2.88 +kerning first=193 second=210 amount=-2.88 +kerning first=193 second=211 amount=-2.88 +kerning first=193 second=212 amount=-2.88 +kerning first=193 second=213 amount=-2.88 +kerning first=193 second=214 amount=-2.88 +kerning first=193 second=216 amount=-2.88 +kerning first=193 second=221 amount=-8.65 +kerning first=193 second=338 amount=-2.88 +kerning first=193 second=376 amount=-8.65 +kerning first=193 second=8217 amount=-10.05 +kerning first=193 second=8221 amount=-10.05 +kerning first=194 second=34 amount=-10.05 +kerning first=194 second=39 amount=-10.05 +kerning first=194 second=67 amount=-2.88 +kerning first=194 second=71 amount=-2.88 +kerning first=194 second=74 amount=18.7 +kerning first=194 second=79 amount=-2.88 +kerning first=194 second=81 amount=-2.88 +kerning first=194 second=84 amount=-10.05 +kerning first=194 second=86 amount=-5.77 +kerning first=194 second=87 amount=-5.77 +kerning first=194 second=89 amount=-8.65 +kerning first=194 second=199 amount=-2.88 +kerning first=194 second=210 amount=-2.88 +kerning first=194 second=211 amount=-2.88 +kerning first=194 second=212 amount=-2.88 +kerning first=194 second=213 amount=-2.88 +kerning first=194 second=214 amount=-2.88 +kerning first=194 second=216 amount=-2.88 +kerning first=194 second=221 amount=-8.65 +kerning first=194 second=338 amount=-2.88 +kerning first=194 second=376 amount=-8.65 +kerning first=194 second=8217 amount=-10.05 +kerning first=194 second=8221 amount=-10.05 +kerning first=195 second=34 amount=-10.05 +kerning first=195 second=39 amount=-10.05 +kerning first=195 second=67 amount=-2.88 +kerning first=195 second=71 amount=-2.88 +kerning first=195 second=74 amount=18.7 +kerning first=195 second=79 amount=-2.88 +kerning first=195 second=81 amount=-2.88 +kerning first=195 second=84 amount=-10.05 +kerning first=195 second=86 amount=-5.77 +kerning first=195 second=87 amount=-5.77 +kerning first=195 second=89 amount=-8.65 +kerning first=195 second=199 amount=-2.88 +kerning first=195 second=210 amount=-2.88 +kerning first=195 second=211 amount=-2.88 +kerning first=195 second=212 amount=-2.88 +kerning first=195 second=213 amount=-2.88 +kerning first=195 second=214 amount=-2.88 +kerning first=195 second=216 amount=-2.88 +kerning first=195 second=221 amount=-8.65 +kerning first=195 second=338 amount=-2.88 +kerning first=195 second=376 amount=-8.65 +kerning first=195 second=8217 amount=-10.05 +kerning first=195 second=8221 amount=-10.05 +kerning first=196 second=34 amount=-10.05 +kerning first=196 second=39 amount=-10.05 +kerning first=196 second=67 amount=-2.88 +kerning first=196 second=71 amount=-2.88 +kerning first=196 second=74 amount=18.7 +kerning first=196 second=79 amount=-2.88 +kerning first=196 second=81 amount=-2.88 +kerning first=196 second=84 amount=-10.05 +kerning first=196 second=86 amount=-5.77 +kerning first=196 second=87 amount=-5.77 +kerning first=196 second=89 amount=-8.65 +kerning first=196 second=199 amount=-2.88 +kerning first=196 second=210 amount=-2.88 +kerning first=196 second=211 amount=-2.88 +kerning first=196 second=212 amount=-2.88 +kerning first=196 second=213 amount=-2.88 +kerning first=196 second=214 amount=-2.88 +kerning first=196 second=216 amount=-2.88 +kerning first=196 second=221 amount=-8.65 +kerning first=196 second=338 amount=-2.88 +kerning first=196 second=376 amount=-8.65 +kerning first=196 second=8217 amount=-10.05 +kerning first=196 second=8221 amount=-10.05 +kerning first=197 second=34 amount=-10.05 +kerning first=197 second=39 amount=-10.05 +kerning first=197 second=67 amount=-2.88 +kerning first=197 second=71 amount=-2.88 +kerning first=197 second=74 amount=18.7 +kerning first=197 second=79 amount=-2.88 +kerning first=197 second=81 amount=-2.88 +kerning first=197 second=84 amount=-10.05 +kerning first=197 second=86 amount=-5.77 +kerning first=197 second=87 amount=-5.77 +kerning first=197 second=89 amount=-8.65 +kerning first=197 second=199 amount=-2.88 +kerning first=197 second=210 amount=-2.88 +kerning first=197 second=211 amount=-2.88 +kerning first=197 second=212 amount=-2.88 +kerning first=197 second=213 amount=-2.88 +kerning first=197 second=214 amount=-2.88 +kerning first=197 second=216 amount=-2.88 +kerning first=197 second=221 amount=-8.65 +kerning first=197 second=338 amount=-2.88 +kerning first=197 second=376 amount=-8.65 +kerning first=197 second=8217 amount=-10.05 +kerning first=197 second=8221 amount=-10.05 +kerning first=198 second=74 amount=8.65 +kerning first=199 second=67 amount=-2.88 +kerning first=199 second=71 amount=-2.88 +kerning first=199 second=79 amount=-2.88 +kerning first=199 second=81 amount=-2.88 +kerning first=199 second=199 amount=-2.88 +kerning first=199 second=210 amount=-2.88 +kerning first=199 second=211 amount=-2.88 +kerning first=199 second=212 amount=-2.88 +kerning first=199 second=213 amount=-2.88 +kerning first=199 second=214 amount=-2.88 +kerning first=199 second=216 amount=-2.88 +kerning first=199 second=338 amount=-2.88 +kerning first=200 second=74 amount=8.65 +kerning first=201 second=74 amount=8.65 +kerning first=202 second=74 amount=8.65 +kerning first=203 second=74 amount=8.65 +kerning first=208 second=44 amount=-5.77 +kerning first=208 second=46 amount=-5.77 +kerning first=208 second=65 amount=-2.88 +kerning first=208 second=84 amount=-4.29 +kerning first=208 second=86 amount=-1.41 +kerning first=208 second=87 amount=-1.41 +kerning first=208 second=88 amount=-2.88 +kerning first=208 second=89 amount=-1.41 +kerning first=208 second=90 amount=-1.41 +kerning first=208 second=192 amount=-2.88 +kerning first=208 second=193 amount=-2.88 +kerning first=208 second=194 amount=-2.88 +kerning first=208 second=195 amount=-2.88 +kerning first=208 second=196 amount=-2.88 +kerning first=208 second=197 amount=-2.88 +kerning first=208 second=221 amount=-1.41 +kerning first=208 second=376 amount=-1.41 +kerning first=208 second=381 amount=-1.41 +kerning first=208 second=8218 amount=-5.77 +kerning first=208 second=8222 amount=-5.77 +kerning first=210 second=44 amount=-5.77 +kerning first=210 second=46 amount=-5.77 +kerning first=210 second=65 amount=-2.88 +kerning first=210 second=84 amount=-4.29 +kerning first=210 second=86 amount=-1.41 +kerning first=210 second=87 amount=-1.41 +kerning first=210 second=88 amount=-2.88 +kerning first=210 second=89 amount=-1.41 +kerning first=210 second=90 amount=-1.41 +kerning first=210 second=192 amount=-2.88 +kerning first=210 second=193 amount=-2.88 +kerning first=210 second=194 amount=-2.88 +kerning first=210 second=195 amount=-2.88 +kerning first=210 second=196 amount=-2.88 +kerning first=210 second=197 amount=-2.88 +kerning first=210 second=221 amount=-1.41 +kerning first=210 second=376 amount=-1.41 +kerning first=210 second=381 amount=-1.41 +kerning first=210 second=8218 amount=-5.77 +kerning first=210 second=8222 amount=-5.77 +kerning first=211 second=44 amount=-5.77 +kerning first=211 second=46 amount=-5.77 +kerning first=211 second=65 amount=-2.88 +kerning first=211 second=84 amount=-4.29 +kerning first=211 second=86 amount=-1.41 +kerning first=211 second=87 amount=-1.41 +kerning first=211 second=88 amount=-2.88 +kerning first=211 second=89 amount=-1.41 +kerning first=211 second=90 amount=-1.41 +kerning first=211 second=192 amount=-2.88 +kerning first=211 second=193 amount=-2.88 +kerning first=211 second=194 amount=-2.88 +kerning first=211 second=195 amount=-2.88 +kerning first=211 second=196 amount=-2.88 +kerning first=211 second=197 amount=-2.88 +kerning first=211 second=221 amount=-1.41 +kerning first=211 second=376 amount=-1.41 +kerning first=211 second=381 amount=-1.41 +kerning first=211 second=8218 amount=-5.77 +kerning first=211 second=8222 amount=-5.77 +kerning first=212 second=44 amount=-5.77 +kerning first=212 second=46 amount=-5.77 +kerning first=212 second=65 amount=-2.88 +kerning first=212 second=84 amount=-4.29 +kerning first=212 second=86 amount=-1.41 +kerning first=212 second=87 amount=-1.41 +kerning first=212 second=88 amount=-2.88 +kerning first=212 second=89 amount=-1.41 +kerning first=212 second=90 amount=-1.41 +kerning first=212 second=192 amount=-2.88 +kerning first=212 second=193 amount=-2.88 +kerning first=212 second=194 amount=-2.88 +kerning first=212 second=195 amount=-2.88 +kerning first=212 second=196 amount=-2.88 +kerning first=212 second=197 amount=-2.88 +kerning first=212 second=221 amount=-1.41 +kerning first=212 second=376 amount=-1.41 +kerning first=212 second=381 amount=-1.41 +kerning first=212 second=8218 amount=-5.77 +kerning first=212 second=8222 amount=-5.77 +kerning first=213 second=44 amount=-5.77 +kerning first=213 second=46 amount=-5.77 +kerning first=213 second=65 amount=-2.88 +kerning first=213 second=84 amount=-4.29 +kerning first=213 second=86 amount=-1.41 +kerning first=213 second=87 amount=-1.41 +kerning first=213 second=88 amount=-2.88 +kerning first=213 second=89 amount=-1.41 +kerning first=213 second=90 amount=-1.41 +kerning first=213 second=192 amount=-2.88 +kerning first=213 second=193 amount=-2.88 +kerning first=213 second=194 amount=-2.88 +kerning first=213 second=195 amount=-2.88 +kerning first=213 second=196 amount=-2.88 +kerning first=213 second=197 amount=-2.88 +kerning first=213 second=221 amount=-1.41 +kerning first=213 second=376 amount=-1.41 +kerning first=213 second=381 amount=-1.41 +kerning first=213 second=8218 amount=-5.77 +kerning first=213 second=8222 amount=-5.77 +kerning first=214 second=44 amount=-5.77 +kerning first=214 second=46 amount=-5.77 +kerning first=214 second=65 amount=-2.88 +kerning first=214 second=84 amount=-4.29 +kerning first=214 second=86 amount=-1.41 +kerning first=214 second=87 amount=-1.41 +kerning first=214 second=88 amount=-2.88 +kerning first=214 second=89 amount=-1.41 +kerning first=214 second=90 amount=-1.41 +kerning first=214 second=192 amount=-2.88 +kerning first=214 second=193 amount=-2.88 +kerning first=214 second=194 amount=-2.88 +kerning first=214 second=195 amount=-2.88 +kerning first=214 second=196 amount=-2.88 +kerning first=214 second=197 amount=-2.88 +kerning first=214 second=221 amount=-1.41 +kerning first=214 second=376 amount=-1.41 +kerning first=214 second=381 amount=-1.41 +kerning first=214 second=8218 amount=-5.77 +kerning first=214 second=8222 amount=-5.77 +kerning first=216 second=44 amount=-5.77 +kerning first=216 second=46 amount=-5.77 +kerning first=216 second=65 amount=-2.88 +kerning first=216 second=84 amount=-4.29 +kerning first=216 second=86 amount=-1.41 +kerning first=216 second=87 amount=-1.41 +kerning first=216 second=88 amount=-2.88 +kerning first=216 second=89 amount=-1.41 +kerning first=216 second=90 amount=-1.41 +kerning first=216 second=192 amount=-2.88 +kerning first=216 second=193 amount=-2.88 +kerning first=216 second=194 amount=-2.88 +kerning first=216 second=195 amount=-2.88 +kerning first=216 second=196 amount=-2.88 +kerning first=216 second=197 amount=-2.88 +kerning first=216 second=221 amount=-1.41 +kerning first=216 second=376 amount=-1.41 +kerning first=216 second=381 amount=-1.41 +kerning first=216 second=8218 amount=-5.77 +kerning first=216 second=8222 amount=-5.77 +kerning first=217 second=44 amount=-2.88 +kerning first=217 second=46 amount=-2.88 +kerning first=217 second=65 amount=-1.41 +kerning first=217 second=192 amount=-1.41 +kerning first=217 second=193 amount=-1.41 +kerning first=217 second=194 amount=-1.41 +kerning first=217 second=195 amount=-1.41 +kerning first=217 second=196 amount=-1.41 +kerning first=217 second=197 amount=-1.41 +kerning first=217 second=8218 amount=-2.88 +kerning first=217 second=8222 amount=-2.88 +kerning first=218 second=44 amount=-2.88 +kerning first=218 second=46 amount=-2.88 +kerning first=218 second=65 amount=-1.41 +kerning first=218 second=192 amount=-1.41 +kerning first=218 second=193 amount=-1.41 +kerning first=218 second=194 amount=-1.41 +kerning first=218 second=195 amount=-1.41 +kerning first=218 second=196 amount=-1.41 +kerning first=218 second=197 amount=-1.41 +kerning first=218 second=8218 amount=-2.88 +kerning first=218 second=8222 amount=-2.88 +kerning first=219 second=44 amount=-2.88 +kerning first=219 second=46 amount=-2.88 +kerning first=219 second=65 amount=-1.41 +kerning first=219 second=192 amount=-1.41 +kerning first=219 second=193 amount=-1.41 +kerning first=219 second=194 amount=-1.41 +kerning first=219 second=195 amount=-1.41 +kerning first=219 second=196 amount=-1.41 +kerning first=219 second=197 amount=-1.41 +kerning first=219 second=8218 amount=-2.88 +kerning first=219 second=8222 amount=-2.88 +kerning first=220 second=44 amount=-2.88 +kerning first=220 second=46 amount=-2.88 +kerning first=220 second=65 amount=-1.41 +kerning first=220 second=192 amount=-1.41 +kerning first=220 second=193 amount=-1.41 +kerning first=220 second=194 amount=-1.41 +kerning first=220 second=195 amount=-1.41 +kerning first=220 second=196 amount=-1.41 +kerning first=220 second=197 amount=-1.41 +kerning first=220 second=8218 amount=-2.88 +kerning first=220 second=8222 amount=-2.88 +kerning first=221 second=44 amount=-8.65 +kerning first=221 second=46 amount=-8.65 +kerning first=221 second=63 amount=2.88 +kerning first=221 second=65 amount=-8.65 +kerning first=221 second=67 amount=-2.88 +kerning first=221 second=71 amount=-2.88 +kerning first=221 second=79 amount=-2.88 +kerning first=221 second=81 amount=-2.88 +kerning first=221 second=97 amount=-7.17 +kerning first=221 second=99 amount=-7.17 +kerning first=221 second=100 amount=-7.17 +kerning first=221 second=101 amount=-7.17 +kerning first=221 second=103 amount=-2.88 +kerning first=221 second=109 amount=-4.29 +kerning first=221 second=110 amount=-4.29 +kerning first=221 second=111 amount=-7.17 +kerning first=221 second=112 amount=-4.29 +kerning first=221 second=113 amount=-7.17 +kerning first=221 second=114 amount=-4.29 +kerning first=221 second=115 amount=-5.77 +kerning first=221 second=117 amount=-4.29 +kerning first=221 second=122 amount=-2.88 +kerning first=221 second=192 amount=-8.65 +kerning first=221 second=193 amount=-8.65 +kerning first=221 second=194 amount=-8.65 +kerning first=221 second=195 amount=-8.65 +kerning first=221 second=196 amount=-8.65 +kerning first=221 second=197 amount=-8.65 +kerning first=221 second=199 amount=-2.88 +kerning first=221 second=210 amount=-2.88 +kerning first=221 second=211 amount=-2.88 +kerning first=221 second=212 amount=-2.88 +kerning first=221 second=213 amount=-2.88 +kerning first=221 second=214 amount=-2.88 +kerning first=221 second=216 amount=-2.88 +kerning first=221 second=224 amount=-7.17 +kerning first=221 second=225 amount=-7.17 +kerning first=221 second=226 amount=-7.17 +kerning first=221 second=227 amount=-7.17 +kerning first=221 second=228 amount=-7.17 +kerning first=221 second=229 amount=-7.17 +kerning first=221 second=230 amount=-7.17 +kerning first=221 second=231 amount=-7.17 +kerning first=221 second=232 amount=-7.17 +kerning first=221 second=233 amount=-7.17 +kerning first=221 second=234 amount=-7.17 +kerning first=221 second=235 amount=-7.17 +kerning first=221 second=242 amount=-7.17 +kerning first=221 second=243 amount=-7.17 +kerning first=221 second=244 amount=-7.17 +kerning first=221 second=245 amount=-7.17 +kerning first=221 second=246 amount=-7.17 +kerning first=221 second=248 amount=-7.17 +kerning first=221 second=249 amount=-4.29 +kerning first=221 second=250 amount=-4.29 +kerning first=221 second=251 amount=-4.29 +kerning first=221 second=252 amount=-4.29 +kerning first=221 second=338 amount=-2.88 +kerning first=221 second=339 amount=-7.17 +kerning first=221 second=382 amount=-2.88 +kerning first=221 second=8218 amount=-8.65 +kerning first=221 second=8222 amount=-8.65 +kerning first=222 second=44 amount=-18.7 +kerning first=222 second=46 amount=-18.7 +kerning first=222 second=65 amount=-7.17 +kerning first=222 second=88 amount=-2.88 +kerning first=222 second=90 amount=-1.41 +kerning first=222 second=192 amount=-7.17 +kerning first=222 second=193 amount=-7.17 +kerning first=222 second=194 amount=-7.17 +kerning first=222 second=195 amount=-7.17 +kerning first=222 second=196 amount=-7.17 +kerning first=222 second=197 amount=-7.17 +kerning first=222 second=381 amount=-1.41 +kerning first=222 second=8218 amount=-18.7 +kerning first=222 second=8222 amount=-18.7 +kerning first=224 second=34 amount=-1.41 +kerning first=224 second=39 amount=-1.41 +kerning first=224 second=8217 amount=-1.41 +kerning first=224 second=8221 amount=-1.41 +kerning first=225 second=34 amount=-1.41 +kerning first=225 second=39 amount=-1.41 +kerning first=225 second=8217 amount=-1.41 +kerning first=225 second=8221 amount=-1.41 +kerning first=226 second=34 amount=-1.41 +kerning first=226 second=39 amount=-1.41 +kerning first=226 second=8217 amount=-1.41 +kerning first=226 second=8221 amount=-1.41 +kerning first=227 second=34 amount=-1.41 +kerning first=227 second=39 amount=-1.41 +kerning first=227 second=8217 amount=-1.41 +kerning first=227 second=8221 amount=-1.41 +kerning first=228 second=34 amount=-1.41 +kerning first=228 second=39 amount=-1.41 +kerning first=228 second=8217 amount=-1.41 +kerning first=228 second=8221 amount=-1.41 +kerning first=229 second=34 amount=-1.41 +kerning first=229 second=39 amount=-1.41 +kerning first=229 second=8217 amount=-1.41 +kerning first=229 second=8221 amount=-1.41 +kerning first=232 second=34 amount=-1.41 +kerning first=232 second=39 amount=-1.41 +kerning first=232 second=118 amount=-2.88 +kerning first=232 second=119 amount=-2.88 +kerning first=232 second=120 amount=-2.88 +kerning first=232 second=121 amount=-2.88 +kerning first=232 second=122 amount=-1.41 +kerning first=232 second=253 amount=-2.88 +kerning first=232 second=382 amount=-1.41 +kerning first=232 second=8217 amount=-1.41 +kerning first=232 second=8221 amount=-1.41 +kerning first=233 second=34 amount=-1.41 +kerning first=233 second=39 amount=-1.41 +kerning first=233 second=118 amount=-2.88 +kerning first=233 second=119 amount=-2.88 +kerning first=233 second=120 amount=-2.88 +kerning first=233 second=121 amount=-2.88 +kerning first=233 second=122 amount=-1.41 +kerning first=233 second=253 amount=-2.88 +kerning first=233 second=382 amount=-1.41 +kerning first=233 second=8217 amount=-1.41 +kerning first=233 second=8221 amount=-1.41 +kerning first=234 second=34 amount=-1.41 +kerning first=234 second=39 amount=-1.41 +kerning first=234 second=118 amount=-2.88 +kerning first=234 second=119 amount=-2.88 +kerning first=234 second=120 amount=-2.88 +kerning first=234 second=121 amount=-2.88 +kerning first=234 second=122 amount=-1.41 +kerning first=234 second=253 amount=-2.88 +kerning first=234 second=382 amount=-1.41 +kerning first=234 second=8217 amount=-1.41 +kerning first=234 second=8221 amount=-1.41 +kerning first=235 second=34 amount=-1.41 +kerning first=235 second=39 amount=-1.41 +kerning first=235 second=118 amount=-2.88 +kerning first=235 second=119 amount=-2.88 +kerning first=235 second=120 amount=-2.88 +kerning first=235 second=121 amount=-2.88 +kerning first=235 second=122 amount=-1.41 +kerning first=235 second=253 amount=-2.88 +kerning first=235 second=382 amount=-1.41 +kerning first=235 second=8217 amount=-1.41 +kerning first=235 second=8221 amount=-1.41 +kerning first=240 second=34 amount=-1.41 +kerning first=240 second=39 amount=-1.41 +kerning first=240 second=118 amount=-2.88 +kerning first=240 second=119 amount=-2.88 +kerning first=240 second=120 amount=-2.88 +kerning first=240 second=121 amount=-2.88 +kerning first=240 second=122 amount=-1.41 +kerning first=240 second=253 amount=-2.88 +kerning first=240 second=382 amount=-1.41 +kerning first=240 second=8217 amount=-1.41 +kerning first=240 second=8221 amount=-1.41 +kerning first=242 second=34 amount=-1.41 +kerning first=242 second=39 amount=-1.41 +kerning first=242 second=118 amount=-2.88 +kerning first=242 second=119 amount=-2.88 +kerning first=242 second=120 amount=-2.88 +kerning first=242 second=121 amount=-2.88 +kerning first=242 second=122 amount=-1.41 +kerning first=242 second=253 amount=-2.88 +kerning first=242 second=382 amount=-1.41 +kerning first=242 second=8217 amount=-1.41 +kerning first=242 second=8221 amount=-1.41 +kerning first=243 second=34 amount=-1.41 +kerning first=243 second=39 amount=-1.41 +kerning first=243 second=118 amount=-2.88 +kerning first=243 second=119 amount=-2.88 +kerning first=243 second=120 amount=-2.88 +kerning first=243 second=121 amount=-2.88 +kerning first=243 second=122 amount=-1.41 +kerning first=243 second=253 amount=-2.88 +kerning first=243 second=382 amount=-1.41 +kerning first=243 second=8217 amount=-1.41 +kerning first=243 second=8221 amount=-1.41 +kerning first=244 second=34 amount=-1.41 +kerning first=244 second=39 amount=-1.41 +kerning first=244 second=118 amount=-2.88 +kerning first=244 second=119 amount=-2.88 +kerning first=244 second=120 amount=-2.88 +kerning first=244 second=121 amount=-2.88 +kerning first=244 second=122 amount=-1.41 +kerning first=244 second=253 amount=-2.88 +kerning first=244 second=382 amount=-1.41 +kerning first=244 second=8217 amount=-1.41 +kerning first=244 second=8221 amount=-1.41 +kerning first=246 second=34 amount=-2.88 +kerning first=246 second=39 amount=-2.88 +kerning first=246 second=8217 amount=-2.88 +kerning first=246 second=8221 amount=-2.88 +kerning first=248 second=34 amount=-1.41 +kerning first=248 second=39 amount=-1.41 +kerning first=248 second=118 amount=-2.88 +kerning first=248 second=119 amount=-2.88 +kerning first=248 second=120 amount=-2.88 +kerning first=248 second=121 amount=-2.88 +kerning first=248 second=122 amount=-1.41 +kerning first=248 second=253 amount=-2.88 +kerning first=248 second=382 amount=-1.41 +kerning first=248 second=8217 amount=-1.41 +kerning first=248 second=8221 amount=-1.41 +kerning first=253 second=34 amount=5.77 +kerning first=253 second=39 amount=5.77 +kerning first=253 second=44 amount=-5.77 +kerning first=253 second=46 amount=-5.77 +kerning first=253 second=63 amount=2.88 +kerning first=253 second=8217 amount=5.77 +kerning first=253 second=8218 amount=-5.77 +kerning first=253 second=8221 amount=5.77 +kerning first=253 second=8222 amount=-5.77 +kerning first=254 second=34 amount=-1.41 +kerning first=254 second=39 amount=-1.41 +kerning first=254 second=118 amount=-2.88 +kerning first=254 second=119 amount=-2.88 +kerning first=254 second=120 amount=-2.88 +kerning first=254 second=121 amount=-2.88 +kerning first=254 second=122 amount=-1.41 +kerning first=254 second=253 amount=-2.88 +kerning first=254 second=382 amount=-1.41 +kerning first=254 second=8217 amount=-1.41 +kerning first=254 second=8221 amount=-1.41 +kerning first=255 second=34 amount=5.77 +kerning first=255 second=39 amount=5.77 +kerning first=255 second=44 amount=-5.77 +kerning first=255 second=46 amount=-5.77 +kerning first=255 second=63 amount=2.88 +kerning first=255 second=8217 amount=5.77 +kerning first=255 second=8218 amount=-5.77 +kerning first=255 second=8221 amount=5.77 +kerning first=255 second=8222 amount=-5.77 +kerning first=338 second=74 amount=8.65 +kerning first=376 second=44 amount=-8.65 +kerning first=376 second=46 amount=-8.65 +kerning first=376 second=63 amount=2.88 +kerning first=376 second=65 amount=-8.65 +kerning first=376 second=67 amount=-2.88 +kerning first=376 second=71 amount=-2.88 +kerning first=376 second=79 amount=-2.88 +kerning first=376 second=81 amount=-2.88 +kerning first=376 second=97 amount=-7.17 +kerning first=376 second=99 amount=-7.17 +kerning first=376 second=100 amount=-7.17 +kerning first=376 second=101 amount=-7.17 +kerning first=376 second=103 amount=-2.88 +kerning first=376 second=109 amount=-4.29 +kerning first=376 second=110 amount=-4.29 +kerning first=376 second=111 amount=-7.17 +kerning first=376 second=112 amount=-4.29 +kerning first=376 second=113 amount=-7.17 +kerning first=376 second=114 amount=-4.29 +kerning first=376 second=115 amount=-5.77 +kerning first=376 second=117 amount=-4.29 +kerning first=376 second=122 amount=-2.88 +kerning first=376 second=192 amount=-8.65 +kerning first=376 second=193 amount=-8.65 +kerning first=376 second=194 amount=-8.65 +kerning first=376 second=195 amount=-8.65 +kerning first=376 second=196 amount=-8.65 +kerning first=376 second=197 amount=-8.65 +kerning first=376 second=199 amount=-2.88 +kerning first=376 second=210 amount=-2.88 +kerning first=376 second=211 amount=-2.88 +kerning first=376 second=212 amount=-2.88 +kerning first=376 second=213 amount=-2.88 +kerning first=376 second=214 amount=-2.88 +kerning first=376 second=216 amount=-2.88 +kerning first=376 second=224 amount=-7.17 +kerning first=376 second=225 amount=-7.17 +kerning first=376 second=226 amount=-7.17 +kerning first=376 second=227 amount=-7.17 +kerning first=376 second=228 amount=-7.17 +kerning first=376 second=229 amount=-7.17 +kerning first=376 second=230 amount=-7.17 +kerning first=376 second=231 amount=-7.17 +kerning first=376 second=232 amount=-7.17 +kerning first=376 second=233 amount=-7.17 +kerning first=376 second=234 amount=-7.17 +kerning first=376 second=235 amount=-7.17 +kerning first=376 second=242 amount=-7.17 +kerning first=376 second=243 amount=-7.17 +kerning first=376 second=244 amount=-7.17 +kerning first=376 second=245 amount=-7.17 +kerning first=376 second=246 amount=-7.17 +kerning first=376 second=248 amount=-7.17 +kerning first=376 second=249 amount=-4.29 +kerning first=376 second=250 amount=-4.29 +kerning first=376 second=251 amount=-4.29 +kerning first=376 second=252 amount=-4.29 +kerning first=376 second=338 amount=-2.88 +kerning first=376 second=339 amount=-7.17 +kerning first=376 second=382 amount=-2.88 +kerning first=376 second=8218 amount=-8.65 +kerning first=376 second=8222 amount=-8.65 +kerning first=381 second=67 amount=-1.41 +kerning first=381 second=71 amount=-1.41 +kerning first=381 second=79 amount=-1.41 +kerning first=381 second=81 amount=-1.41 +kerning first=381 second=199 amount=-1.41 +kerning first=381 second=210 amount=-1.41 +kerning first=381 second=211 amount=-1.41 +kerning first=381 second=212 amount=-1.41 +kerning first=381 second=213 amount=-1.41 +kerning first=381 second=214 amount=-1.41 +kerning first=381 second=216 amount=-1.41 +kerning first=381 second=338 amount=-1.41 +kerning first=8211 second=84 amount=-5.77 +kerning first=8212 second=84 amount=-5.77 +kerning first=8216 second=65 amount=-10.05 +kerning first=8216 second=84 amount=2.88 +kerning first=8216 second=86 amount=2.88 +kerning first=8216 second=87 amount=2.88 +kerning first=8216 second=89 amount=1.41 +kerning first=8216 second=97 amount=-5.77 +kerning first=8216 second=99 amount=-8.65 +kerning first=8216 second=100 amount=-8.65 +kerning first=8216 second=101 amount=-8.65 +kerning first=8216 second=103 amount=-4.29 +kerning first=8216 second=109 amount=-4.29 +kerning first=8216 second=110 amount=-4.29 +kerning first=8216 second=111 amount=-8.65 +kerning first=8216 second=112 amount=-4.29 +kerning first=8216 second=113 amount=-8.65 +kerning first=8216 second=114 amount=-4.29 +kerning first=8216 second=115 amount=-4.29 +kerning first=8216 second=117 amount=-4.29 +kerning first=8216 second=192 amount=-10.05 +kerning first=8216 second=193 amount=-10.05 +kerning first=8216 second=194 amount=-10.05 +kerning first=8216 second=195 amount=-10.05 +kerning first=8216 second=196 amount=-10.05 +kerning first=8216 second=197 amount=-10.05 +kerning first=8216 second=221 amount=1.41 +kerning first=8216 second=224 amount=-8.65 +kerning first=8216 second=225 amount=-5.77 +kerning first=8216 second=226 amount=-5.77 +kerning first=8216 second=227 amount=-5.77 +kerning first=8216 second=228 amount=-5.77 +kerning first=8216 second=229 amount=-5.77 +kerning first=8216 second=230 amount=-5.77 +kerning first=8216 second=231 amount=-8.65 +kerning first=8216 second=232 amount=-8.65 +kerning first=8216 second=233 amount=-8.65 +kerning first=8216 second=234 amount=-8.65 +kerning first=8216 second=235 amount=-8.65 +kerning first=8216 second=242 amount=-8.65 +kerning first=8216 second=243 amount=-8.65 +kerning first=8216 second=244 amount=-8.65 +kerning first=8216 second=245 amount=-8.65 +kerning first=8216 second=246 amount=-8.65 +kerning first=8216 second=248 amount=-8.65 +kerning first=8216 second=249 amount=-4.29 +kerning first=8216 second=250 amount=-4.29 +kerning first=8216 second=251 amount=-4.29 +kerning first=8216 second=252 amount=-4.29 +kerning first=8216 second=339 amount=-8.65 +kerning first=8216 second=376 amount=1.41 +kerning first=8217 second=65 amount=-10.05 +kerning first=8217 second=84 amount=2.88 +kerning first=8217 second=86 amount=2.88 +kerning first=8217 second=87 amount=2.88 +kerning first=8217 second=89 amount=1.41 +kerning first=8217 second=97 amount=-5.77 +kerning first=8217 second=99 amount=-8.65 +kerning first=8217 second=100 amount=-8.65 +kerning first=8217 second=101 amount=-8.65 +kerning first=8217 second=103 amount=-4.29 +kerning first=8217 second=109 amount=-4.29 +kerning first=8217 second=110 amount=-4.29 +kerning first=8217 second=111 amount=-8.65 +kerning first=8217 second=112 amount=-4.29 +kerning first=8217 second=113 amount=-8.65 +kerning first=8217 second=114 amount=-4.29 +kerning first=8217 second=115 amount=-4.29 +kerning first=8217 second=117 amount=-4.29 +kerning first=8217 second=192 amount=-10.05 +kerning first=8217 second=193 amount=-10.05 +kerning first=8217 second=194 amount=-10.05 +kerning first=8217 second=195 amount=-10.05 +kerning first=8217 second=196 amount=-10.05 +kerning first=8217 second=197 amount=-10.05 +kerning first=8217 second=221 amount=1.41 +kerning first=8217 second=224 amount=-8.65 +kerning first=8217 second=225 amount=-5.77 +kerning first=8217 second=226 amount=-5.77 +kerning first=8217 second=227 amount=-5.77 +kerning first=8217 second=228 amount=-5.77 +kerning first=8217 second=229 amount=-5.77 +kerning first=8217 second=230 amount=-5.77 +kerning first=8217 second=231 amount=-8.65 +kerning first=8217 second=232 amount=-8.65 +kerning first=8217 second=233 amount=-8.65 +kerning first=8217 second=234 amount=-8.65 +kerning first=8217 second=235 amount=-8.65 +kerning first=8217 second=242 amount=-8.65 +kerning first=8217 second=243 amount=-8.65 +kerning first=8217 second=244 amount=-8.65 +kerning first=8217 second=245 amount=-8.65 +kerning first=8217 second=246 amount=-8.65 +kerning first=8217 second=248 amount=-8.65 +kerning first=8217 second=249 amount=-4.29 +kerning first=8217 second=250 amount=-4.29 +kerning first=8217 second=251 amount=-4.29 +kerning first=8217 second=252 amount=-4.29 +kerning first=8217 second=339 amount=-8.65 +kerning first=8217 second=376 amount=1.41 +kerning first=8218 second=67 amount=-7.17 +kerning first=8218 second=71 amount=-7.17 +kerning first=8218 second=79 amount=-7.17 +kerning first=8218 second=81 amount=-7.17 +kerning first=8218 second=84 amount=-10.05 +kerning first=8218 second=85 amount=-2.88 +kerning first=8218 second=86 amount=-8.65 +kerning first=8218 second=87 amount=-8.65 +kerning first=8218 second=89 amount=-8.65 +kerning first=8218 second=199 amount=-7.17 +kerning first=8218 second=210 amount=-7.17 +kerning first=8218 second=211 amount=-7.17 +kerning first=8218 second=212 amount=-7.17 +kerning first=8218 second=213 amount=-7.17 +kerning first=8218 second=214 amount=-7.17 +kerning first=8218 second=216 amount=-7.17 +kerning first=8218 second=217 amount=-2.88 +kerning first=8218 second=218 amount=-2.88 +kerning first=8218 second=219 amount=-2.88 +kerning first=8218 second=220 amount=-2.88 +kerning first=8218 second=221 amount=-8.65 +kerning first=8218 second=338 amount=-7.17 +kerning first=8218 second=376 amount=-8.65 +kerning first=8220 second=65 amount=-10.05 +kerning first=8220 second=84 amount=2.88 +kerning first=8220 second=86 amount=2.88 +kerning first=8220 second=87 amount=2.88 +kerning first=8220 second=89 amount=1.41 +kerning first=8220 second=97 amount=-5.77 +kerning first=8220 second=99 amount=-8.65 +kerning first=8220 second=100 amount=-8.65 +kerning first=8220 second=101 amount=-8.65 +kerning first=8220 second=103 amount=-4.29 +kerning first=8220 second=109 amount=-4.29 +kerning first=8220 second=110 amount=-4.29 +kerning first=8220 second=111 amount=-8.65 +kerning first=8220 second=112 amount=-4.29 +kerning first=8220 second=113 amount=-8.65 +kerning first=8220 second=114 amount=-4.29 +kerning first=8220 second=115 amount=-4.29 +kerning first=8220 second=117 amount=-4.29 +kerning first=8220 second=192 amount=-10.05 +kerning first=8220 second=193 amount=-10.05 +kerning first=8220 second=194 amount=-10.05 +kerning first=8220 second=195 amount=-10.05 +kerning first=8220 second=196 amount=-10.05 +kerning first=8220 second=197 amount=-10.05 +kerning first=8220 second=221 amount=1.41 +kerning first=8220 second=224 amount=-8.65 +kerning first=8220 second=225 amount=-5.77 +kerning first=8220 second=226 amount=-5.77 +kerning first=8220 second=227 amount=-5.77 +kerning first=8220 second=228 amount=-5.77 +kerning first=8220 second=229 amount=-5.77 +kerning first=8220 second=230 amount=-5.77 +kerning first=8220 second=231 amount=-8.65 +kerning first=8220 second=232 amount=-8.65 +kerning first=8220 second=233 amount=-8.65 +kerning first=8220 second=234 amount=-8.65 +kerning first=8220 second=235 amount=-8.65 +kerning first=8220 second=242 amount=-8.65 +kerning first=8220 second=243 amount=-8.65 +kerning first=8220 second=244 amount=-8.65 +kerning first=8220 second=245 amount=-8.65 +kerning first=8220 second=246 amount=-8.65 +kerning first=8220 second=248 amount=-8.65 +kerning first=8220 second=249 amount=-4.29 +kerning first=8220 second=250 amount=-4.29 +kerning first=8220 second=251 amount=-4.29 +kerning first=8220 second=252 amount=-4.29 +kerning first=8220 second=339 amount=-8.65 +kerning first=8220 second=376 amount=1.41 +kerning first=8222 second=67 amount=-7.17 +kerning first=8222 second=71 amount=-7.17 +kerning first=8222 second=79 amount=-7.17 +kerning first=8222 second=81 amount=-7.17 +kerning first=8222 second=84 amount=-10.05 +kerning first=8222 second=85 amount=-2.88 +kerning first=8222 second=86 amount=-8.65 +kerning first=8222 second=87 amount=-8.65 +kerning first=8222 second=89 amount=-8.65 +kerning first=8222 second=199 amount=-7.17 +kerning first=8222 second=210 amount=-7.17 +kerning first=8222 second=211 amount=-7.17 +kerning first=8222 second=212 amount=-7.17 +kerning first=8222 second=213 amount=-7.17 +kerning first=8222 second=214 amount=-7.17 +kerning first=8222 second=216 amount=-7.17 +kerning first=8222 second=217 amount=-2.88 +kerning first=8222 second=218 amount=-2.88 +kerning first=8222 second=219 amount=-2.88 +kerning first=8222 second=220 amount=-2.88 +kerning first=8222 second=221 amount=-8.65 +kerning first=8222 second=338 amount=-7.17 +kerning first=8222 second=376 amount=-8.65 diff --git a/source/data/opensansr144/opensansr144.png b/source/data/opensansr144/opensansr144.png new file mode 100644 index 0000000000000000000000000000000000000000..376ad15f627dea7bb562674fb20ec22a035f6046 GIT binary patch literal 542566 zcmY(KWn7aF`0k&L4rv6G?vzrb1_KERC6q=$Y6?jAXi&OKBn1(ryCjv44Ny9zd*nuQ z=I?*b=bZCud%fqn?(2Kq&%H?P=c;#z?-2t4a7XQ_k}d!cV!sjsg!tHx>Z0Ew0HMl> znv%jx@0s1~*}=eh>03I*Y6oHs37sQ~X<7DKP95IF(ym}_GG}(4f!w^TJ|Z$2SEw-+ zUrOpKQKAiX)fYkm5}V`Fu#xK>k6T~)o$J=NNqV;!sopHNA>@YJ=K6eSc*=AZ zbK16W^7V1+mfFho;_Y>=-0m;A-7OBeWe)v#^6L?D|0Da0vpn*Pydv`319EeZS2@ue z0F{>R1=RHdYAaO!EHrnq&^_(^lEANn!1U%7 z%Zh(>RsDKf`}rI}z!Jg28sjU4z*2vNre|!+#JFlab0h71gNI;$a>;&X9XznWFnPm|8AkM z$u)N)2ltq{$et-Owua~xJNm{YV;^ZY@|$b|ejng-wfF^6xQq=0#F`ARDd{qV>o z-mP}y0=5$nR`HWrdS?MsZ7_;Qo-yYn|28hJwt~4}_g7ud3d{M&L(fi9&(+8kf3g`K z|1lnj{{+l`;`c_t{zf@-?jb|&;fV_i#zpuUSu%z%_lQp`_gE^oX1iII4%uM+lG!7- zAbVq>j^UKKlw+sA+02JbwSA??f2IiHf1(;pazE^Q{_Mh9|Z3Y6|I$Q9F6|BQgefKOGL=BcyLP;o#`0H>H%wJP*r13tZd2ZAg#Cg))Ns z6Is0llZKtryTa2hrFw%iM`jm{uw<0$or#a)z8dzL&{$03@Zde=Os_4?!HvV?4!_nl zAtxUW?_0u-Jtr2yt*7 z^IqjVw2Q9y-|($hab8Yy82vmP$WvW-=CrltBre!ar-tpx-2p}96zWOQbck!v>{8LfI?L~z- zq;0#TZQG}M?%+wKh>`gbm-*3&hdIWhq7RRYk{L+|OM(_KQc= zlpFXs;ghwcIPO66IQlAx5401){I8YGPn3~ji5^!26;}h>pL*+Vqbkm#9#7mJ%ikX7 zGC{2c2WorJu6jjh#22BD7LKwW{HSjepS&1CUJY$Pyhwt#%^=Pm^N$rPD$}P3>@iC* zsP|War{e|&C_DrejF?BObB*%Irb5SrJd1Z7XZ{w?`~c5s>dqn48Pmmk%;hffW;Z=V z+!J?D}x4QVC6L973IZx`$~(yjTSR-ce*01z>>uP2S>zUEcKJX|EsEz zDo5dqtE|Jgu3g-@_4(SHY2)KIS$QPFNM+w4+uWKEWBc5uAa#Fu;hqNyu6AYbSZAqHYS-S3fg(lQF(a#SP&nicq-GjB9qgGfwy~|o~1dRr)>~%JO7Q~ zAOon+<`=K$|Buw(D#kY>M^$D&{og6$@o&mt{E!$~|CPr9D>cFWY`OEB@hW^jcQf11 zGjrRnXWOs0WZI8qa@%%(wC$W{FT;=E`2R`HV@=vLey8nx*eU1Y+J%e=sPA9x90QnyGbjEb8+ zCAduVkK8pn&eHLG=L0M6ZnKbYIT{d1*F?V?_b{0BHz9yWw07n2V-sLY3qemt+k9lw zerjT{hl8Lx8l!&Cu{y|HP|sW(gG<`!ckp}>{caImdVg8wKn5)@27)L!uiK4><31R* z2jnNt6jieVsSa^TQ%~Z9pRcnpd{{_hhShHRPUzR?WSN%7TMb@2ezuHc6(ur-@^kV{ zT|zcoDjw+vne9ZyzL#4z>mx^7#=r817eyd9@qr^4ELrcTKAvr;=%)B*`w3F-fW zycV=z9q$%qOq-C@@66Fp-?n+Dnc)12!!H&KFGDNLweR|$tec_N`pzaDR*0*J*B6r; z!3UBf30@ZDpL1m$6Q|i8G0-FHR$1+ljIVcUfqC1ywf)zN-}S=jYFVv}PpqHe*YyRQ zR9UiY<_d5Q`IUbWcw``Lpo0x9_bd+g(%EP^c~$Y?&rXKamR?h+mQ26Y#{KGW?*J~f zNb2%aJ$EPckNvV%gXYJ_63Yvil(We>vA|6LA$@r#^OP+)eXmGTl{7n;fWm^5B6cdV zL$As(M0~L%b50d``mW~gv+hA(Hy==cd?^yXlb(JA?9(xd} z!gm?rwe*Ui)M(B?{_2KS#5vR}6&P0; z?xTv}5u-YP8uF&ZZ`wX(0w;SvH8F_ITE-deZ)Pp*>^SmxlX*3yxT73Q#-0#%91&m@ z>s`To3)2FCel4yWH8YacA}Eg=LcB(93}ejG6MxkgFSzaNZ_u2x^^WQI~Z|3yaW7>JUZQ_oN%sTu4M&caE-ezICsSy-CfQWP!2ON%n-{((@cVBavu1 zFI$1tNbyAPpQ=PRpN3673G`Pc?U{TM&RLu2sN}Ftb??15s~ak%YM%di6pJgDM!iGz zX)kQxR0aU^*28pvNPq)34lUAI>RTiJM#hrMAVbc0+K)B=4K(=<4T-V3@7TM%F*Bp2 z>s?B8z9kFsQH_(Hzf>mbesU4`U}C*kLThu_J8sVGd-#{%Qf7Xj3&lifQZZYG0Qa_!zEfR-e1f=ps=|&H@d(xdOXBYs++? zUoGv>gGgv<$jT-E7}aVIKm9!?^X8>=oK%F|=MLJ!|6ig}*$1UlE!1^=Lri(Omrc3w zi&XRT)b&Gg`D1Zq0IDhGd~JUA-uzATKJxYo=>!I@j)ZSrCy$Z(og1{CzC{xr7Avo_ z%F8T!@=pCbzur#gUQa*S_qSaR$MCkUXkB$_Y36ol-&&HYFOa;Zj~!#VtYU#*VBxKQ z!)r~j=lI)V)q-Cfa?n|iHU)f@B^5T~zzR&@f8GIoOLM^sB0!`7%<5Mq(!W8>x^}y> zSm4VIFmtlDt6z878wL60oZkN9K^TEHegQhDgSC%0=RK_1F1MxNF0`?R-w7--TI2jzXK3=B(&Mc&aPT0}+5UzEP+t z#ne-pu$o)~f|lhDQQ?-IF-9?hDb-J^3v@+U?bkUhShG41DbkdEb!f;utyaS*;<ztv$u&< zJBgx7rmWqUE6sf?Ur{KTI|%I-U;VvpN_$e^5OB}37v$88mzkBk=&W_m5mKe6$iQkEK7 zbc(4z2EFj%4K2pB7=ZxIHz$Jw1U0{G*3E-5={L={x1LKOsjka`%CR zJ^#R7BWilws%i0U^C36ayZJg%+AIFvNNoR;_cr@2e_10BUwL)@IJEs(imU0YEyf>> zYd_qy9v~hJ{E>e05J3#(^VYscj4xUDr}Q0@Hapncd)XWyPf3m;o;Y|Mc>Fkb@)#3% zi&-=@H+RFoomdROcm-l&PjBnYFY90g2~vuYcI182D-7OubKa_m^v$T}K~Db6;8OIH zR2hAfPQARDaJtsNFb%xw4ZLbWtN$q(cwe*aO41tntFT7#;EvUYV+3N=@Um38hADZr`|u|gC4CGdzRu)2{>l5W$0i@3G8lqT;@ zRTYN5?$jC0=q@X!aI|g^cxsUQE?_l(J?vJs!ui>1h;X3a7h&{DC43qN&EORhMk+c@OOfL z){_Mi$eIgD!xK9(?L*S@+8E8R4MASEHEWI@i)`}eZRnU;`Kmm;bCj>{p-@9`^+(l` z-S{f<=_jzA0bQM^X|l9IlN;(A{M-HBUq>r{M%6fl)USTVgP_P%!*vD zXmDqM&QY#--c2_zUfAdRg{5q$E*V#RDVDacSC{5DYvwt(h>bH@kZxN%sOPk?yf_Mm z10qa~wp!O4rn++RrezT$#S6{PElcW!$M!SmA1EQR0cKtvYe9R=KQU`zO&ECGl5=|& ze|y%_*0z4{$|MkugfATTT-Uv<~u^j&>cVj`uY#@WT`kT)m^kyh6#D{S~B`cuui zTna83pVcdW{afB&uEducA-r=M=hd=u-jpBOKhq^1#b`AzqABvtn@+DC%z1#>xY*@E zhZJWg;?QRqs8pq&FkO^c-wrv*XAuDr=ziK0*jDxkiNI)7sidl3S)Fe7Q8)WW>sMHUyu6PF`{FeP`71c@5MMmRnccoxQCj}<8DrXJcTiq< z%KUez3f5u(IWo~Cor~X~?$Zi>?oN9ddHSyv&BCs`YHH+e(N4GeWxhLhfaCyUC^(sgo^NXYQz(ap5c>i`u1kOkqBC|J830IeY^Q^uP{Aw9Tk zBRg0-ikF$2n&87;{O=rV8^34u<4r7Whk&~7o=}YEb2d}o@8T>6uZ_L!lS8nfK4hUA@oiBG#WG^0$t ztPK7o-3p#iU*KeNlvC54ly2EHYkAio$hWERkSojXA$P8eou?LGW~0n#sFI6@XaTca zf$WlR|5DHKQcAI50E>B%niivGt8+0i_#pT+?HCg3nmaeSegR+La8wC*PrPrMfV%l(XtA3w#NPnl`hYA-1nqf(K;7G6!y`_X>L!@5Mz1IxSsGoV zqNhSkY-t^bk*Yb#mcm5n>4p)FOr(&Q#>V!N=iorvgLUd{KwASrFZPPi;yJ>r?Z6>RFUc)S$KBlCwT-56tu+DO=f`L`DfK7; zgbI9*7KQ{bl}wYvZ~))EE+hO6z`gv<(sj1Fbno~wehDlR)|A~NiTlgT0E;#u2f@ZO*?;) z@DcK=IuSb@N6HY(d8{95BzZG}5j`JPnrh3ZOwTH&c3oJ?&;LT|dZx%qt2(mhGTj6+ z<7$v@{N5GnNuvtH4tT>l`v0s$xuoO#tb-8mk9T{SL`A@XN6q>20t5X-#;7$xt`4Pd z8blkMuQxr_$E-H>gQu#yIMMH^lEGyfMQoQeZubsr^u8jk z2j`dDtQM8#v!}7?W%DH`Ww*B;M$V7^%-RmV(lN+yh{IK}t+%wketGoL?S1(l(|1w^ z)pY&=Sww;}(VNQM;ZMCC#-sU@+yaNK{5N-JW_k>XU`?slzcZ3)VJ~B{q_|b8CtIC_ z4uwS=gbuZa60hs-0?y>}_8`EL)E@wuA-*f=6BBTV%d5+hH1e`mcj!6(s+6<$Kj!tq z8%WB1z=D|6>afl+dS{$yRCCF(>KuWc-#+`~Vi?MY19Rg4oB%kaywW!QKOUGOm%U#f zGPRPW(1)ajQ8#&8?7Ig7Q2qWk_v=T&9=MbsTwd=rKlB3ZYlr*y!eD;g(@?hX(KY{w zY+p}%`Ilu31hK`q1djjpaiM71P^ zH8C$dh~*cpl|p|zAsG55R~aX+6jZj6>ZiCyRk<>K`a~qEW_3J@&G3cJuaz(`uez|8BvhM&nb zfVF3>gM1t2-_Bv&euYpdn9cUxe9huA-|mN*C+t$jmf3l-cW5l3pIkS>iQvbA?+WJjK1K~ zZ!8!w(yY=%hbA%LN=eCN?6pqDhsrcPzBC5R@dR+utb`Juwg!{KhWBxjz`A0qK+!$<=1p*T8-~>g(0sx?=-=Zq}X}k~BV{UGukNlA` z;MyAiFm+pGQ%y+M@v3<~BVHXySc|z^>k#Xo=j3gGwtr^8+!@tmEf9ro-v45FJm;8$ zyE3V@&o}CmZ;6>8?K{D9`_5~y5`&Se@52lv&F$A|A$GwB%l5l=Neknlz0W%>Gt zgH*TtQ@#Lz1_2ho#oEbPJEF^!57eqx#bI}EbA2qe2E44u`lDm{Ee^&f=4yK?L^^GVD zq|xSy6wyq+l1Xe^S!AldRrQ8k4Z(qZ>znzAXuWS77Zj=zcoO_SA0TY>K|8e#6qHT6Tz5e_hZ@Cs?wLJ=V$1}OfSXN+NIkg9d8zL&w9g?^D zp8uTUr}X3Je+OlK+AR@tn*Kw|AeQ9Y?MK-0oYeVfM=T3AbNB&wcy>q}S4NSQUXACp!Ol8~Vm<^i9Va1gOIQG>Q zoL2S9-fIs}A1Q6%0M4%fXgxs+p~{1_fXd`;Y4(lyCT7)B>Yw>Bu5cqxV?|W>aE)mJM zc3vCJ_vK&0ZToL@DCFt`c<=aseeV+D|7lD8WL&3AG@I-7ciu3JU1IwXOo%d{e}b!t zcj=8asa{Nw2h(RdKIejwJD2?Svr|W*OoZRGT{`s5xdwps*1N*=J}48-D-zPoE&RRt zIv{AKslBcW+pwEd${|63kAk+QT=ctiAKhh!+rNaY#?OS`UMB=+apXKo{;?TnM1Q#| ze5{!WdSKQ6!X9r~e8~UK!)=LzhDZALR_HeT>K_kB3NEr{*1ZKl?pB_GXq>>M$A81ulwJWnKVmv7ozI`bhc?!sQS4jRbf6-%Dn&wIYe8{AV%Nuf5C+uP_ zQ!>=RJJLIR|8wcwA95ZTutC$NxXA7f6SHK;YvH*aw=Y$By`$%+>ndrpd+6{jzIYSn z@;f)^?ktuBS0Dk}hz%py+iAWz|G=LO+0Z>OOXU=;Qb_&z?VpCzis?)~mOa!`zwnOl z`KMrVRNUUIm^t{DwD>*vGuqFh)1#K*uijOXCj@et)IDMlr^Yh$rEcoZs5CsxQ9kPX zbNAH;p^!)cidkLDF@f(&W&A96nM{vf*MNc&QQzrM*k^2n=}zkmFjr@Fn)fm~lK(xA z3c&n`MlP48<^Q$Gt|O>cq`Tv9W`AB#MOw zLD%Ll_a^|r4tAs8YYafsp0*jVLWhFC6YBTFs`kXRgxbE$^e<@*1Sox|50X~05q6## zyKbb*-C!`1sY+Jc=T{YfQlfvw=BI$>g7b=X*a-UA`FtFH?*}bfiT5GTrndCl4ZO44 zId*_NWaLj;^Bs#_sOSF;+B6X*>~`Y{)DgPhmGtBW_q_gvtW7p`ij@6!Xa<8ny*wwa zad|tJXa#N@X7z0oG6r-t^MjYLnl2)kS?Pp*StM+ zq0wgd*4I{ki^U1!81}4=#(n8YTm?&@xwa_%+pWp|@TCG@w^@Y0sb~<<5XIE2FN$Gn zQG=8sO9q7#pj3GQ0;arL{=L!mjQJy{LMB#S_A(=ScaZ95Rk~IG`96_lJfl57pD~U} z;e>V>2+g5d>w$M~h@Kqx;;5L(dOo7wfOE7Z(DTn}-_*Skt?@ofPu*K7yfC{^q_?~W zBW%@IKaoKpP~CF?y>hNf(2{P9@vdv_pvLk=i;CA_z?4Q=f8Qe@=pLa$rw`1<*|tpg zQTPN6Lst&XKVwWC73SeJtAR*R&%Q^crY%7ni2UU1FMsy%NRE~)W{>itz0gv2WoFj$ z%gM_-Z*o#jA}`w|447ASoQq&>T`@h)((n)-nMxra%(V5A{=_3R=u)97DyfWrA}Wbg z21`@a;y>3p^7^D?HJqGA_f+e@yGgsov*kUuW0gpUFKDEq*u!PDO|UKre!d~o2V@_P z0zny=F04&pLh4oc2oHPFDP2Vw?pqW0hA76Y@uXIEAFbsNkPf+_s8?sL=twl-qQTXZ zUD-lN^qk_Yq=;+9#K!F(`*|br6`X~v|XIQt-K)BRIJN?1(iHF4B zv@}t5rNPIE*L{BVfb@YCV-|OyH4#N@o63GZIhPkxDN?!&&n5n3RvCk2PLgxOZ|qB= z!+iIHVp8?qq9h8An5%I*pxo2f!&9VFCxo~Xi%%(@Xok6Hi;zwKnHeg? z?bG9DrG(}`m;(c>Z&}yWe`eY<>s^NdrweuYH+1hmlj_`*thUnxzfl#D)^To0#t)gg zEKf;75SpF-T0^M^B|s_n&PE4jF4JosTaAsq9lZ9@tBL{1M6{E#t1!bRFTb@`f0K?bfJ3A#;b!t4a@wnZ9&Wlkmr{IiZF-r5KrGGh z`&E%YEhp4`k?+RUmsv&b%nZx_WQ>C^P^UOR^@P4kn~Isp?;j%;glzb@uudIwsgFFp zHCHEuA-uEzczl(CuyR(W5Ur2OOKxkkDPj%mWp3-pKXqn)&sp z7~%48r6+NuX>GmJ%C-XZFYmR4i^~z6g|}_YrR|sGmGhHwVz5pz+5Fm+ZZInllxHpG zNr3`uKLB%nFSkemua^NpkQ;85I0YC!u=mXv9J3f*6^Z1RM8Yj)LjLvrd3cMW3oGk_?fBX3|XgLD_G)fb} zfr(vXQdvHXZ|%p8C!gX+?|u2AWvNZmRD@mHG>qA3$T{>^j5DY*vpOYE763pEDqJY^ z8qtPXE_j3T5v1Y zu(Sxz%PPE~P9GQNdpq6Y-?)s`){7-$ki2rcWyX5neBABDtW8d43)*){ox2vr&X<(Q z!BNfRhBXWxDv|_p37syL=o^NH``!z0#Jj-iAi8|{!6w&B<$o#j%b!+@((0E5ev>}{ zj2cBxBB=&AvN~qqO20hTiQ6XRO3nOxU@6X_&DMLLdsv-5|3r*Li}OGU?;e?Q?h>6G z$I%c;|Ae9w8!L)QJtR-Z+uzs zC#_LZkBY!!uDGcb{l)R|wx9?ijj-wqEp}5bE1C`-wY}h`8k)blHqRzzJ}ZG(bOIGk zqPG;(wbbLTbMD-5bJ3MlPdzF1=K}`ho-n(Zb3IKVYy*>Y==c`f%u5A=1BrY-mC5Yv z!KJI6OQ(N>|9H#XK`Tr=f8zF;m;z0_q4n~G0kJoxRxzp>b9dwtLkb*NS2}=nI#-5H z$8LMWoZZ{tA(QYpeE=GZ_gU$kngyki$j)>GM9JoH3RJ!&UHm?8M>XC_VON!5R*F*c z^%(f%3Aa@sTW%F)!nmuak@>2ZySq4SA~`2(`BfSrGEl>DZ@o_llJ@Xn=LCjnRq}#m7H=< zh0%#uyraC>Im&(Y(SbK#lXeoE`I|B#?aGivmO2*}-z@v`z*7KNB8I;n?3;_TC<~N&8yG4zvEttv|JL#6Jg%cmk1t#xi zM68W%46L|vndm^}xNb)4#?n7k?E0OLv@`|?PDgVE73?U-D~qA*Sl&N>i#L|DRT6_O z;oVx&0*9#GLL;OD0mJ9;gU1Il5hk#Ws|EaHR{5Co{)-v=S1t1>9Kr*+SrvzGIV4s& zEl}W`zrE}F@$US`h`S$)@t+ES{>-r`1aLSR8>aS+5|*M^_r-}>x|38*C68wdHPlI+ z&)$QpeMUq5I=C`LXFz8UvIy?v@R8mAuqL@QIj2Iy)r5(myd5g|4yp@k@&ZnwEZs8B zuY!B4SSIQRx9|Z$J-Jg!dxk@W>^Y%LoRX@AVEgFA$ixuKWhNuH^(H!CNnQVEu)WK(@@G^*t$l49Z9^nI z@&GQ`!GzQRRo6m^NhhC{uYiK^A0cG_BW*+$(;eK0{ZA@vqh2n_$#2UumuyoS4qfZ0 z&+rZE5YqD%Kh$B3g6x3&q-<5IXitKiEy49zi{Ts<54^~WLe;1?rll|*cVApCd0pL+ z61q!R}C?)&lAo$$ZhN_V9h|d_j1itued|nigOGfb+v|>?E-OAXh)Fhe|m#X>V=s^rdO;LsSY?O$3$ly49q^M+1)0uaO#xpr z_3I!K5ma?KnsEwmax&QLdg&z{@qjKROI)@T@IzwHWqXpo&epD}FH5qh-j@96?y15h zJhUFEabJZ_2et_1vD>4#W|n3$Y+1g|r~9=n=*v4=9m-h>S>>cWbX23NHUqctE^$Mx0}3vGBLcT zsHXvAEhzv2Twl92H)?VYr)@|7mNomw=SS%t$&V$%f|$X-H&b=R6d*cM;VtAFx`%hO zXE%S@hMum~WchE&68ymQlwMjjQI^g(3iFcm3+_-%ndmEn@^|9jJrg)jx?I0E6^km08Dxpp z9VyTqYL2Dn(7R-^Z>wfAZS^K$3Gwb!)rqXJO!xDbwIhR!kA50Ny6j>%1~@>M1oq7I zQlI*E_YN@$M?v`P&m4=1fKeaIMQ4=})qoz9hY0ylI3hWeb6L8{Kl9YM9b2(tD0%64 zY+r-)9+InH)W8yJj{3Rm*D_gA*1rWoQjA$&PZcNX227pCeQkHF+&|W=elD1IqMMg@ zi$omxhhKk#@UY%@WAexHqfh%Aaefq=3}F5euZ0vr($-4;&$;K228NMn(YR$m@#Mh{ zrv=VoP@moE(6FDr#htBQp*`1PAV?SW``#xZw+MBFF}bm$hNam_h&@VN127Nvkf6_Pstny+Cbv8R}qLC--Qk3%_pL&0C7eIN2fBv*=ai~K$~JUbLA;{%QPwVf)4 zWh52`{|FLKQoH@=(8!UctV2!N`__+5di&`*wRhyj;!yZD&!nDn+=_AH+^nkz$^|~@rRuL%kY$=StZ#I|D3U8sI8DnN+iU}_kL za(^wS&kMbs96r>si&P{~+^MW_GHc=1`;+Y{t8@8q>@r0P5kShFT5M&qb=!)R!6Y1u z3_=5|orpG7S(PIFizkw0G*Xb3_k9l-{L?>Qs?FI1@5iirlJr$|p+jVOEKI;T-l-Wl z*VsKn(+FG3ec$w&_2;Gd^*~@Uhrm)U=I^hC<2J|0zofM;NJ;b%fOO4}N?r3_azuFqsl|`~s-z`26#HL(PMc~LQ#=IgnThi*v!}E*) z-yNcbD+Cj-56!~A>o(wnwSNd#*kSbL%Sg3E3BN0S_}z19%EwEE6RU2?yYx%;_2s{= zh9%C5^!0&IS>pYa^+5~+tQH?kA(Q-(8bR2+*WH#_#&*ecsYU8y+* zj93eDFv)qx4Qcx_s~Y-e6uyAq;J(#xU(MI(EqdLal0?u4;c}6RkIMDVlenmywx~EV zLdYRYzElLsXZ4zOoRrqjeWQFEZ&XAG#hfJ-e}vE6bl*)IU3{g^+UuFd?@CcpX!*`^ z&04q;);je*&_4G~`~jp}ekw3A=i}uAy*XI3tI(Ood(Z-zu7h5gTQ|co^DomUtVF|f z8;TOjnqeZs>KQb0ddRJE?_fta~6%{?VeYQ`I4^iLaRHZj%KsDkk_7; z!hKq6?^--!(e$~r3B%FM`4vN1%9%PoKWf z4fUN>C?NXII}viL6K*})Za8=i(d|=k9QXa0#d$3v{F%W z-P?jm4Vjdbb42_s$>%g=XIcFAo*TjK zXs(&@7+>88VLp<$N>43&bKw_dr^A0Y=lUPEkJ+dMp?>QqBe9fb3JIF6ts$$WdIi zzF#zi^}a^u`_-HMsiK19lWC-4Bnxw{V8|-`QkEX+sQ5Z)zYT~jpm(uRK36#{N_P-` z?6Pl_8EKeGhDQ|zsSX{QBfsH==#_lXmft4aj&#e$Gw@hf*?XHU%>T;R5Ia&`ZDkS? zhh(^x=Bdv1nUiiC@B!Xn3k~-q$jL2p-!ICaZT3W_ERPHc`b$gsb;~HSjJ{1w>gI8- zJ1gb;Rn#!!c-P$oh>pxZahrW{&0SB|@p9S}=5I@D{Tjt?pJAV)!TMq%9p~_3eQXw}BAIfl`(y}Z_uCarIIE{Wy5!>Se z9i4E7)G8cq9|@`RO%(m^%aI7Lmm@Kc)-JN1WgFoMSFS+bbV4WjEjpQCeL=LF|0N#XKJ~1+NjD)}Jb&rA zgn14jd&U?T3{A6W&!%*rEzye$`ixt^9*rZdlPbgj_3#9s6^u|v#Fs()W`ALPLQz!r zY9Jq9f0OM4+NGCNk2UUAB43KI;VYB>sSkQLTIZKm6VKQ|rXW%2X*b_j2lxAp{i8(L zFMsX$BG&K%az+z=Un(~uio4hIeIglKtk$J7k&R@k_%3&7r6fxVSFd%IA6-6$he3Kw zD}|>5^ZCfK%qYleb>4EIu84Bc7EXzbT#sqxKh`t$`%U%2(b{5u zWuYPIthYjcd2#k*lhAHQj$p8?%!{9;E)UFA>n>xV2D`PTt_)2mq8Sr>u(d-=6p4(< z{r2Mt@tK;wnvW4Gj(zbjN;bz1Jjczj%b~1((`mD+X4!hXSn0P~>u$l%EguBKYy(rT zJaZ;ZCL{L3A#0=(qXa1xfp5<`f(D?+8|?@P^-rZ+`4~97jIFJ25lrf6C*~x*5Bz2Yf_T`!SyY-PKS5Y zJXiU6xNWIv8qOFnk=WER5?jkBD;#wP)Ynpt^d?_Ov}g;-iW|@&rwya{`63i&2t#ig zYEQ|kkEA1ex7)j&;+g+-)rW~Rh|P?;XWC~VWRlF)C2Bbbm+j}Bzf8$NqkTQRtUA8d zYYm%j@1q`k9;f_TxNss#Acb zQ8`hUE0!On^eryDr$~4^CM&EX97lX{&R}8*GPENQlOciDvZ$ANsjKZxCuNZX&`YRl zf4}|PP`$A-4K!Unmn%276_Qcsw!2ANgDL41=L3y*os}fYa3<=ozVQrxvumdL@5l}_ zF4ncX&4|M72HkFN&6^U{te}8R`&B`=`R!dxH^Q^t%LCIXIP*yyYfvMBUH{**poa(& z}j^&mrSOAItnOw%%H<>a3VNfJ(W zr^9SnHQq{+<616B#*IO0h3ZfyTy4vpg9$bhYK59jKd9uwJ$@X#oz{W}+Ojw-cb2*` zfFNdg?y-n~)v6#VMbwM&q}%872p?y_B#jaJJmyWt$Mc3wSu|}`nn1$}J;OJo-$>~z zRsECCxQ@|n@qlK#fvvgs{d-lCzR;}Jh%8pmpLGq~_|xS4yo%3jc*ei-fhX)Nh6!(| z=2DZXU-jS)<8hJna7->yFwVOR} zdf}tEZZ8JRS&@k^GFoSrORQa_G0BZy{aQ1=s1lwVz0v4r`;oU(JwyMZu+3Qbd^&^C zdQo~r?|qWdnwXr}(R+VCa%fOedDP|jHPyLOS12x(9{at;FB=fN=PzXJS3SAi@W2!# z2HcnV&u4zTs^Pg^lj(<1*n?bIFZL{Zll`>2+e6#&tY2VDCKg%at*ty-Hvz>7wezPS zP**vx#AgRG8m+;TMktV#dFQ<35C|G#+g31k+A1^2uW)KY3ex(OwZ18MQp`E44>M{N z)XVs*;Lq^M=*ZrQM(3HN8{x}CVZ8N-`{DEx|m~!lZuRYKUh}YlcvQShifqiRuq^F@8Vx-=;JVb;9$N0*l2{(L5I)axpNYQ`z ze{)E_<^GV6NUOjB??^9Zll)CTmZCGCW8ky5uHEM;3 zT~)o;9GPp-XlW7dG9y(q7gCA!-sPeR$DyN!rZ6O+T-?@Xd=&Sm-f2Zp5kG_M6yK$J z%(DVy>%*$y zng}Zk=C+H>69D0lhkrUQh5WjVsOvI%uq;y(U^YUr!Tr?G_`zkG8J?A6XFnoB}5}X=2=ZVHqo~o7nZZHS7Len{>E719Ls@e2UOfB0DC$G z1=+F^<1975AG$x*6KRi(j&kcZ+Ky(eO5}<<=wxzMLyv4qgI#U9 zELt{Sr}=XLpMm=WT9zBqzwtLZjVRci1#2>E7?A@WB{+ckNfCCVYt){F%^NtCWWGo4 z9bjZ8x_&*u@jF#y@F-~+cBfU>{aPna_d^qn{%%w7QK#fzio{wKfNE+3G8IV3O1i~0 z@GZtS`#($kdL()zSu6Q;E?!+Bj6pFS_BTW1lakG{1#sFbaQc(#=wZl1%R2TD4*TDg zU)dL99S91${3!+6AtI7_=KUl;$KU!`+Ef!1+50=$s;igo38kHKqcQjYu14;QqQBA! zxhv%fD0tT0*kZ5Fat27&M`P{DKb%c3{0`1~kJnLBgt6FrvqzMPSFBoz5@fip(G!@* zUM7R()$<0qf?hmpkY1D7V7zop3oiS+#OUQ+Xb$-sHP@T=liP-sr3x?XkG`^aGkC*Y zA}_mPDlzue_BCUtz9R|^<}+FIFJ)A`iN2WVq*cFpkr-{t#77O;++zhm3e`&SNxxWU zCST&I7{wej&Qg3I3qE&+s^cqJA-?rcdE|umW6)Qa6>B?cDR%Oj10= zo=Fxfg8N3T(2k&U8>Ggt#2D?nB0uzj{<$IuP8)v-e2NUf0wbmp;dgZo} ztmMtF*uL!r-GQ0D5E}q-a21aV0D!t{o;9k%CaB3ZwXs3^?OPH9V$14qNmX#A%U$Nk z+v8$JtC*to&_ZW^i9z>k3nyF4rTGClDaCAz5N- z+nS)TyGV%pt?YU{7#M|Nu8Zvgm(fQWDm>Te*4UK;%PTx%quHF8CRjY!%w03>%F9{* zWVZkFp^UYfb?@=SBhwcS(_J(ZT^4Zs&pRgVhoW$rz7)BGGQvKDG}koAh@Z z*1ouDW;x*j-g@@yxQ^e4obD{a6t;}0USbo0$k7LHhA{l0m3H5H#l-Cy;lLd-ny9EH zJy}gzab&lfj7@Ap9%06U((Me({;%t>t1e*ONV2b#X{R_xl??w|l~f8sSF=avNf_hT zx`(@QWbs23`;|WT0X*la-dz~_v~S7%!$quKKZClz=fyKU^ZJ*Ck_m5LIxXMj88it^ zfm%{pJkp3xeY8tbaQY{0Aex>uQs^$)nblqp=&R+`!dQSJ#((c}P;t@Ew$glP1sWS~ zGP3S7edB)G17~~eqm^U-u6>LEu#h@4vb#>mZr9bW_-evY8GSh<;*Hi+9SmL>l3tUp zCW7Nl|6+U_@wYFz6qkwUqa&0TMYS|wyc;3KyIoDX+le{5xd(sz<2I=U6IPPMGkQMD zLDZdtJraH1k`z!F#)Ed8pljrr{@#I~2XYrZUcHPT;9DgM-Y?~P4U z=i+Ih%?k8)-$lm>Zr0OD`c{CZ#e&xV}uZwam!Ur$z@ z-QLz|j;PBxl%Z-b%8xoY8H2CxZ7Q+t)*n&&O0g&5_aHc7RMSle%~K_M&Sz=mVaw)} z9_ci1r2&g9Ps)9Gv0$KfC2&5-aM0B*zQlhhj1~wwiSN4gfdX8Dt*%BwsYZ{j6LCO& zu#?^Q|BIxr{-^tY|9>9cHQjZz8OBQ;F*R&znwhC%j_w*q%rMuXN_?=k?PGbm$v?Pe67RE{j#y2*sn2bZ zV#3W(J#*?9{MtuXl?|I3@}=ISfg6q&MQ1ibW@^ncZZDi^#VLSOu5Ee|EG7Gz+HjA> zD~vy9Mv_uhpD5OE_9Xd{iPqx;qV-AUR9Xq6D!M9stJ|xV3-g3fH(xB3UwTCf@-Y*M z2*#wZN!r-zp#VXoYi-$!#?;$b67ji zW+y|m7ib@KcjWKdHHy#(4Si>MIh3L|eIpJ1XDG45=M4tiKYeVY|1$bQh%apOeF+!NS~_*gqlL<^*N@kgNiqkFcvDD6im#-1+7%) zJ*5Rr$}_I8t39>>=}pL!J%5oiX4J^d$?Cx)T;hrJH<+oM?{9M~RgiEM)W}(?ue1tZ zgDuQ|m=@nO=IzKwlh}wEp3(tRg(4Z4@NiSffHvtt9iCH~Uit-&ckXri)e4hE;d;44 zmpN4)WdZV6o>~vK9#dPm(4JzBRT!=*E_2tZVp!={RhB5yXA~S^Lh4w+%%;3nrC?oE zc?fQzR3H>?`<*v%m>-drIC9;XpzQ#wmTRrFD!8UObt=Ee?D0`~9AIr9O}YQv?aZ>k zHk4JatXO_#;`_G23Z)kUryRlvQ2e8t;B_b6V47;_;w)aUl!w21fRa!oZL}l%40@Ki z0-NLq>tAjNCvBL{FD}VrxzWJmLesfOXiN|@hrc)MVs7)6@huR zeo1Sl=g?FzTl|kRRNXjJxsQFlLb77z|MN8Ba+E8YfmXGZ zfoC`JVn7Xg9tB<(7V1J%wsTlJ;(|xTX9n9_B8HZ!RQl%EdafEd{y$}Ts+%vODns)k z7>6|0SNwU9u~(DxpnxrHq$POo4b*r6%cUnvsk+`}nXWa$X%mvkyZXrr4+Gylg!(V< z6#Fhaa;0I93>57>=-PdNxJWnxX z58XLY3`-utagYwLR1zHaAO+a9Dq*W~7zjT!{P`{rI3-BD( ziWlt0&}6Po9dm{{F}E{}?{X33689Qvv%=XkCCz3~%%FHNM&p#p6+WJH z-KC-)UsFUYWO@=J>9&p_1Idbg=H`LTZVxS}2c|SZx<4~6jpxD9*^}`dKKY2WlDdzi zQ%lKBmrEtwZ6-zWsjvPAR-q=X(mJ4VPu)6voWTd@Ch0EcN`MvPS1u6_)L@5$72YKT zPNrV&xCTYO&j$NKjzx@kCwjP!6%;4{*~GgTVfs*D!LksDZHQ-a6I29Tm1Q2CcWe-e zR_n!ZB?wrJ1+C5}E3Vg=ov6>nh9JkPo1$=LuI|HsApJHJp$0_o%&5Z+wP9jLKN}EAHF(L(nfRnZG za2%r<1ukXEv8;8Vj!tDLA=x_QD31h}KG(?LV|-U2!1C?G+S`L#H4EaXqyngQWt@t7ooB^F;%Y%29UAq7w?*41p447f&v-5*Hgp5w*xY2-Bln2K`Rteo!3g zZ}eoq+Tcw_*gv8@)>)CKJaQRu;s6(@u)|+*`cO+EfkFj0KhsaoS@>k5)@)pDLvaq| zFL*#^+m-mXt+YTvf14TK5lLlh@*6o~sBJouo|3*;k`HV(x*H)0n_N(YHb@d({Fwh* z(H~`499G@*Pqy59mGLv1^bQDOcv?DCdb4=&Xrh#=Q+sxW%3KD+GDLS|-|1UhbkG5C zrw}UGW44@Yh0&KyVQ=$GaX$qesh@;AZriCun2+$de;&5^R^|1tihUnUbXQETu65yN z`@3@FUcxDAYRCJ1PTgbSsa-Z_=j)thS3Bkbe+#vxoBzJDweGS~U1 z!7xQQ!}<4?SyO?Z`@-voZ`Dq3b9ftUZZa}&(MUfsK;aV$ir`5h_E3i|dl(?cHq!B1 zVIV!R64I%!Zc$1G@JTb zJcU8R(ZUz!VRdFTiATQ|S5>)$91BOOw_zZ-fHF6#hy$Kry#bVoJi1Y#E_Uxkl&}2t zTqq7Ax5KR6X$Q$~Vjx3u?!wIUAS}1o75E9Pg`3(@iCFsg!u{&#w5}!~GG&wS;+7M8 z9=qeyD~cF-<5BR%c-+&!4Tj)Dj69Lc3TeiYFMTKuejI3Cdr!*)B&;>u%z49I`g6t%&30GXsY z<(sU;toF95#y22uS-4oFV@Y`FyDg}GK9euhvgyr7+mLIb$5mK@h%wurZ2{cVe#hTC zolSot3V6Rg-&Tv3q0FT;}V_jo`zk8v8mjOeO5;+R0}SlY>5Z_}SW)T%M4os|hj40M|_ z^3hi{(>H*d#h+M{5;UnlV|pDO(tBNNB#w{9QH$@c(-%x?2->M~g6#@Ty&k*Jmq-{B zn&5y_Oe6)FLce8G0u*-)hV%6`lh$VKM5-Np_6?4f)=&0~_g+v~zx}-|m;B&q_@Hj3 zp8kc#)6q%&%Y}SZ-LG`cs&v0;Q<+A(ryXDIEMiZkwVuhjnq>%AlBi?q>RzvQH_`+87VUgKtmjmxgMJFZDA<{pJ?|I5#{vD ziD#@41a#Y=l4S5Wb(EJE=c$Hb#`~Vz0Eu_+)`sBHuws?C&K*LgRNB+NrYG091Xhm; zW2pFkE1o(v;h3iDIuM!xVo^PFlsE0T7O%8!T`0GSbOX-wUQv`|nf83#{lco0a*vV1 zumNtVdHR5~Q8?1USIerfcz4~9a8Im&Hgh=7|0Puwx9@imF*5&&WmW&f1E7-khMWZp ziw#@c27lPg4F{N^V%vR#4BL`>7_7FQ6a@7LL~myn5S)e{^FYElgziG`|J=<#s+;zR zTtCJ5k*rHz#!WO(7dTD{Y5{PrkmhRqK<0do_=TIx3*D9g;*|OJCj1VA;s@7~_&6X! z96nl(e)5leani&@w?y91FcbIj2T$_z%x(D~%TOHKDl9@)2L&pi zz)=dBceU9ij-`vZv(OSu7367DPoN#_m{*%vIBmvTiFY98XqGT^6DZi3l`FcR#L^Gy zN_*{L6teyNYm@1pXM^|2;AjLRLzd8bb|!m&@5%EocdzV_A{2?pf%d5y^vR?rf zRjHUCxRDR@aBlL58Cg`{c<_M(mxT+f&s^Lw@PaqQ-9>c-0HAn&%dy)e%HV(cpPzt= z{+d5r@7MeXe(;ZMSS-qU9hDjiSWTW_MKP`isOIh4?Q{+B34#j)F^j9UZ-}V~FO%CoIZ6r6{)%z6VOb|0(11 zaY^$Z$t1l8tM&$ZIOrNRz0b!TPwS_AEj|~O`U4BsmXIZtJ@Co?&TCEXbQ4BLVNUL4 zb#9`D2Jp3osK|k#@bCBtzW)pnJJb=TM{jY(_NK6`{;akK5W_KJq#B2L1zuXp8aC}B zEV#dSYSgUugIfEbTyHkJ@;ue&uWD%p@chCrlHZPH zhib?brl{_t?g(ts6i$l-9;mFwQQuUy(t}@Pj4K``arp8%cz&d+e=sRAF5vlp|BQ;l z4@P8Ac0XDz#dWgoy~u_HXUuq$~+H6b(#((mmem{&#yejJ5 zY@MGRUNL_2{(ONqMsf|Kj;@E^nWzCfS`LToM^$$%tKPL zhZ>-$qt#FmmW8&)b4M_sTa*3v^hS5U=wqgW==a<@+evRGO8G7-sA`q})pV46&U_W9 z$O5U`hLpxYMP_#2g2&8AF57ZO9NNDQ^e?EAwahfz*>2>k2=A<6)Gz-bscz&j1&IzF z#0^Dx*0`@8XK+yfHxzeD%)df`enJo+2M2~Oxr&1*LpwB`dxyLH15KJD1;7u-3nSoF z&OagK1OP+ZmT%b~v*^EV9CJ+*d}CozS{l2R(*iCNmS46Oxvw#Zf#{VmAli!|EqHFQ zUghWO;Y5&kb~xUPTq@>F6@>TYQw>UeII3`49*K#%aR)Mzh^X zFA~vigY+8|(9{V7Z$grvi<7dj*`&Is>~3g+r*(s?yiaBO0?)Mizf&M$WhpDGjPs94 zL^U=IJ&jA{cK_-Ga!c3=DnI`eDk@H}NdTd#a7-`f*V4X6HnW!Uw>fO|_+&sMTw%9t}FHpySuJ+>%LNaAuc0 zHWql#SNV4bT-*2`3Gj0e+rl;mgdH;;>zIz5mnv+yXsei>mp6xjlAOo;GN|vDw(~uE z+cicuj*DGb;lm`~*&G&yS{R?15{aHp^99O#&mDSe{dmSRyCI6an|ZW(2x$`!kW)l$ zux14X0l=c-I>Y5ZTBi=g_P!ASO?AK^>b$A3YmY#Z92>A8yzNk{KCex-i}ixs zpC>usDY>yr)^9-03U$g4^W(Fw(HCn4_ zwPWi1`}7uMGPuIk^n zzOY;P#gBu|w-oltsG^~N+LO|wtONpK@7CrnWKIVceoGS`)sqzk8l8W`*lF*|R1(c7 z8x02S0k}{m=QoX+y5b987*(+mM|*mJQK0yARL==TIJpdy=%pL3O#yCqD%e=PKD2Ro zHkQM7l2utfj_YhDo%?!0`<}{=125iMx3M*|Ad`f)zf$K*=3Y@#P@4BevJARMk8g}5wX&^pu z2G4G8t}h*-L86D6b0|p>yb8WAC-~T2f)-%eSCU z`h6Z<@>~?DXj2yGE9%TV%zu?v$FlrE0ProOp-H^{I=|X>_GCU7v&dcebSt`8{*bHZ zPB=gH>)ufW=*|-Uc7X#l)>6p4GQhESif;LrHnV;^R!uQ4FxaexYA}AWpL99-=s~k% z;}jDNR1*#&ei(Kh1Le!CNuFdV?{DT6+e}e{Cz4ES3=K%qT2C6hwFYf z?RFqZu4wg;CEEhlFJgAiQqFD2Z2Cx6s4P5d2Me%Xcj*7>B#l#-?SZE*N2~^=(OhV7 zB_*-<@-qcn2vgR5X8w`o8=K^eNG~~wob=ghM#8@MS!^ng3P_Owf6G54d zyQujXyPE!5-DQR+Z_Cg(FJ|7u&W0QyG?e#Rh7zXWKUvzr$j$;az3<>cWFV_tBK(DZ zZ_15$*MWv@Kb0(SVsbL*{cJES1$>Kp7}I!ff$tw(`Al2vO^5o|F~``)61fSF_mrBe zS-1c|T@E@T<%%mI+_dK4{#eV0|K@kByAA-49W=T7`|}kSNUJ32oY?s03;oOw_>VF;QRj%(tBbk@TuSAwuUk@)^E!pHN7PjE@V!HozN)ZDl ztD$Vll@jO{=PTH$LUIcx8M^FyHiI^$a7(B#y5IWxr|Jwxd(QzyT37ym=Yh`NQ%hgR zZCad%NBmfW<&3%8NiTf%Gv$ybQ9>i&<-`{9Jw0EIv(Er6MDUh zg_StQfyW#QT)OwKOQFbc92WA)*%jX$pFV%1^5Sg_E}v`W)NJw4Z1%!?+*_ZE0oIX_ z5w+?c*A4jomJ@h4&s5v0uvxyK{>HjPHfkN&#t^_NMYstukriuwGm(7D;YYiwEXJNJ zyW$r!@mW)Kcz|xjR!W~l@06p4zFP4#X{-@QiAu{MS+6g@ojNw&tNZ@I{BY&jM`O(c zfvirSCkdkbnGA6fyaQ8D_7uKtDFi+m=zE)%ROHQy3k;V}JTby0I9mO=ED{F#zODbP zuqoSGWU&!RRH%pcRW~FM&xLM9)KET4>{cO>r%>GPgff)C1l+`=%ZRqbueDYaKGPSacm4Yw=d0 zS#OeWQ;rAE@J9!Z<0w@xLaA8@qwkXOT*iC^(I_!g?UZB2F>T;5G)dacH!x^rCHE)X zubh%lajvHP8OlU#iNkLE!m4$<`}BX9)n7O+}3cam?E;7?I23 zPDhN`M*LsBMSKRPqC@V-q{pZ#@-Cq)QO3oakmCdrE4F;XBmEadZ$cr1a?3ZmvW*OI z@nJ_>V3H~n7_KBC_=#=c4}h+%OU&O>LO)WyqDOP}un{kq2;Smf5joEBz5y{vPRHA0 z^yAMZ?PHe@Wb2xm$qr0A`+U6zzq5K6|Fdb$$Q+>YrGBJN-W_@chVHhCKszs%=kRFZ z9#0x+sZD?Fw1dAt`9HY1ywok1QFTa8>MN6Iu<|1LS|R4pk7($x&$-t}*^neup$)B9 z=cKAPlT)aYo~(wDfuFx2y|Bk&JitH=;Ocm^rbI|c(#?@}w9Ww2SVB6+AOHX%Owhcl z;AtRgKb!pwPl*~Xdif-UYesxXcs9n=yJ_MN-@5W(e-eLDIwpRouL8fPWw$K$zN@(t z%xG7~pJO+S3=2190?4<{xWnp47pRWDoPX;lgC8n({eDrPoG3Bg8f&)NQ_*uJI~i`N zpQfP14#z#fgdxMqQM2W@Folw4?BG19opG8jF6p^&rxh8D$u&Mm9#8Sf#HZNbLQxr^ z+05gOU=C*QmToXK!5km3opmXf9|eq?I?rKYE8zSuMiOk{tE@n7=t$|HJv_g5M)vzh z1RfJbJH5GfC>}0_?UK02Wep^a7&DAvGC`mX z{x&5*LRO9Ybwj&k_vI z(zJK=y~g4}b0YS!n~A4nJ^c@=PfA+EEv4aF111eEA7OQ&TK|w@_JtMw3DxPBJ8QR= z2T`V+vyUG7bE9wrHbNvNc?{FnZZavQ!VkRIfq`L^LuAm_&EffHq$k1?|EpYIvb zo(b_5NJ*k!F)PkI2;6(T359yUc^l!>&tv6mYBQpC+c2MP=#FziFu5uwXaAK0&%{;) zO>q>77!0D~RdOx7@vCP2ta>o#h^q_hNvi{%)*8kCeK(_Ds896Rk7j~Ey#gFXS+N`V zD0T7nr*W*B^MDHeGo9#g{jP6G#i=F}Ij4GPYSJiF?(>v?QB%8LQXK+j-$W=I$l$1h z0M1ckD*%W6rb$(*5$}}?2w0Dr*ewlUqmBj63%!2hJ0GMM+`YJqfe{iL8q10g4I}Ya zA3e3%7*0m$t2La9tkw@<^ahM~A%jP{<#=|kDQB6{tc~e2W+?;$hROs&q2s^XhUEtt z72yroR&d>%ol;@n~`|(0*NZ zPKnj3$K*h%Ii4N@z>D)q{povns+8ow&YYQL#w`Sw%3-*z6Tx|JqvY?#;d@D`=S}y; zhfJ}y=Fu}jO?%(Q72M$qB-EuiZxPwBGGVPa=}KH09XqkpA$iqiQMP$stRL1(=z@KW zBwZA<5Ot}x-ucJi=9zy-l(tp3sN%yfcGQG1tjg0JzOjC=dZfWSI;Ym6N^w((QKGfY zROT)agj_qGJxJVG$_QQu89R3Ql5;hDlylRO17i!@b;maWnPw%{v93q1BM~O+ZnU-B zvP=?eokRC!f7gW$TaDW%r^2!Lyhz-?fRPcE^9KSX$;QubJ-^BwPwrd>@w;Pss3>Xv zKoT0~nGCv9?)-*REvvj24vY{@!$S8}^s{(78c5)(?B|E! z=f9sWm|BC2&mr906XV+QE@p&VuM$Lde4`8|K$UfPctR< zffi&#bt*&pFFh_S{1Xw$U|sP?`QAZdKI%5gUm?sVi z*E}TyLQz%DlEqFQKHa808W4Ev3*%9;E>#6P!bWk-7%w~#eAch9wt1szHK4-Z?GL_( z0_^DiLI0sZCS%8u4>~Wg?WHGtF6@iguN=(4+qQHdEQZMr>tdgePxXNpV zF~a9))xD7ITPgoS7!#Yg~e^~22 zR5sxFeMTYQM?wmE3mJ*K^*^sXtE?C1qY7VyF*n6}NbndnJB^mGrUmC$5w2Xa7EhTc z`A8Lq`*4r$9$&O{-IWlPAr4=g?Jz=IT|LU|8*@kjdR22jEdU@yL_OJjy`mR)Cbs%f zOia__-R>QP@Lv^qQA6s~nZ}g8QY#XtChU-iBtYk9CAn>M%(mu!-M2geLzax)hnC?c zapr5wCw=)7B4w2-Ga{KhFd*`BxwtK1Vpy{Va)2Wi|E^o&{SJ>`+&y&3pm+wQ9GyA_ighulTLfbsh?QM73XEIm0ymSC70Sl zX3brlVc#&Ckd>}dQP~2N)o4-`#(53b|I{|kFuce!k@#GVt!}$0E0txat?M)w74-Xc zZS?aq0lb2S*jVn%^z)MrBA|dL1$z9DKr)(9?rpyNp#JyA5Pkl0?&zp-$7n%6_W^<% zUw%_PYrAq9bnJ7^%C{;yQkhY@>0Mv-@NPMQpRj7GfCZ{SA4Kg zz!B^_(5wx%Um!8a07;sgdOOZOi1M<3eS#Fwe`uI6H>Eq0FcCqLesyrt{zrq%Z1Pzs zHL115YE~1X5G1969kVFFcI|8*&lw%0BfgplpMHv6na<<20Syy9YmjUw_PLZ%IWtoi z)~ftImBAnmE6sBx%o(R#BLdd1i}frppyE7&ynPZ-%YzD-YtOR`xjTQ2daX&3QJSyS zlcxW((GDdC0H+|pV%?5FT-wy5I>!?41M7{hr86JlO3aMN57p>&&v)I~1zTMXz9qX2 zc?F_VbQKQ+gKlZ(^IAIR`uHyY^5s&dO!sSECzqJ6d7&2i@4k~MH)>=3jIMr1Y6U;H zS`}}ObBcR@QQ-`)F1!FpkUHp8clviFoTb_*@Y8n($~18!$u15`E%A&Pz4(XN`^;8b z?S!DbA-s`6%X@NFP6+I;2 zhr@Z*IHZZb=3!4U8rMMBN5)sGNKKSw`Psa(ys?(>UVw9cakA;EeH^s+t3C+uiSv?v z7(n+9Dn-V;BazWT=6o^18*b$UtfJ$uksyHKsClNQUF{)6q)M@Rc&~dQZCpTG_>WGrgR{Ne7O;G#l%hnGwnHGeo}!Wwu&Y`Hb zO0^v`&7xexvI_uce04TxkIa>72C&ztS=EeBWvFiaQl}QuuXRC?u!fq>YsaaJTpB>P;W#7jov`2EQN;rNqUyP?A+esOxHf;?|K)`oW0NzsJ&JJ{Pwh_O_^fFt5YeK}j zBH&?wwb2ZK?13#8VXt8iT!w~9;PXWFG}gRVL681<$RN)6VqHA7U;Oj?3dJO--mHR{ zc0DDcH@n@*tKIPUx$A%#<2<(`$LA~Gx(`D6&_ngD-;TJ2Q#_O1-)Xvk%YgQjC>d;o z!@g4c9hX;8KHpFF+(C~-*j8|@CO-|nO$|w>>Tgov%D(S1)ZNN^WxZ$2zfT^Ddj(4+&`q0R-XXDO22n0na+@$x)~@`>3~r1HlM z&4r`NWOg-YA24Z9l~gkoJmM7idM=+~Hcxv2*RO;fwu#2cFfIN5K3E_wtk^e#dt?Zm^4GJ5pwZGIa4;wLo$Z2FZ zdC2q`zm2mN8(XSu4GBdg;Bx_=WZixVYC4Y9BvH*W zVH+o%GORQzu1#TVz-;rQ@w*S+Y8@@d>im9{`T8nojk|bRE3E)JF4eB{1KT;|jC*#|HQc!!hB&iG}cLBA}Z; z2D0aN!P&c*mWef5w2$EYphI4-Q~Ewk)HL5?J8w1jfY@;F$$xxWw)~L>B*;Po!fV}; z@MQZCPX&&6$W{J%;prax8}h@E(>FifE-C8_(9bgtKf_zIL(t0+dw!WBrl%lv!SMD!J#9pY_5Gf$p+YIY272b5zu*R) zRpPupXFcFWuqmUe?&l0(Z8?~&;v%+NBnLa0$*z`uSlY zM0i14e7gM26#_E`W8l)zxh4S#hC%!ZpFKA0u}v$`OwL;v)ewT^bY+hBZXZj+H2EmD z`p6aI_pS(KIN9DYNEV5qN&z2a?-%O65wrSbF#vc%1dBO*RpM8bJ$@6fR(@!H+*c!? zqw&L(Mk{T^}ODXYgvkg zel-LrMJ&ET5*c7#cisMD`u^zPi;vOl-bto-Q4tOeJKu2vGrM-!i|+v&U3S%n&dUNO zlxzc`z&v07?9Z2!1O?5@rX0!GplVK8tr0h`Xrhap0WC$DRS6i)@)U39{Ik-Tbd!NB zfCNH_ppoN4)@RJZ4o*yRv_LI2b#y4*x869LPe+S4@`25O+g|b|HdX$Q_ju=+gHIUECo@YB zN*FM!7`ea#Y&FL9(C8R+rFw47M|6=F_^SZAf~i5vvo~-^F_%}obbbSM7*r%At;2n# zv6yUP=t1+1wPdu7ih_EDo+u(v82q4QHi*GeLdALbigF11$3*qvl6i5-&vWzk>S+?t zDV`SYFauI!8`v@WWV9z8ZQWJGM32*y>54~)kBse+nD>lj?S?b`Jg{*Ev1>v|1o0kf z3bIP83F2i>akVAYG7kF|_wR?@AaqcjN#bE86ev>#HPk&n*N)a#e>`O#$B4sb{U`pF zS_4~rKzjB>x`o~QpCx1@{G_(UfI&jRiT9cPm)!}hRnw3|y`Diqx9IE)$Pw={e>C8N8giG&KXD2%iUlVb zihlZL2f9Ua_oQRtyJ{uUNcnwMMzWgnWKa!bvUL6T;3RpT)Hp8-8-B zslDbbPUdEwK$Zp$^fZabpfDdcO$s#o8O%#hSffM`1T-71yH+u~q*206sE-_Jf|?wC z>8k7gdu{NfiolH|TB!Dk`91(uP2BJX=Zjje^Th;DRQM!%lVP#{9uC-8<`is|&$oH= z`#aScmqWs{H=6ei$U2!)D8&Br=~RHg#S44VB;(;Kl9AWtsrgm)gEwU@24n%K_3VQ4Cgai5`Xt2e(=KKK^qP5xfVHY)vn4o8HO9&8rjb8q8gzQ$($XSI9)aga0cJ zct&ZJ8W|PtxR%%V8BQF3@2%4rFG(0&o;wlsg1DsTO3hNf{9%VpqP#7(NOl_Io%)%U z{G)`HKdVZbX%|MSWenr|2Lhxr?%&L2O5AJ-9HXyih;#A}IeAM|>Lu(t_9rIH^BWnf)uw{{WlDN;!69HG_?Q`@%HAa#*wf;%z zn*sB4;y?OJTi`%ptbQ%w2=0&^hIol*NGJY0CHcC9DV7N{BWvF6m19J$d(^-6kUHK> zF7T~?NS;Wub)2%att;CYdI2VJsI8<%hhY?^fOoVcx`oSf=rt8su~ z>(@9)f?7(4)6+@uw`eZ)L{#01fs?eMHBC%a!3Di-zT+&;d=*D z5$LJewDHN1#v-v)PrIYMmT-slvUa}GVW}PMTrZmv8OL08&J5%0JCWuDMw@89Fo*{G zLMQ%B+J2tN`g&bHBLN&Sg!WT75UL(H_3-g~V1@h${w*7naVbhjYt1;W{Nnx*@A(#$ zJ5}gF5*-#VI}D^-pqvhrls7uPOjgH>M|7WYnd?55#CVvlhU9zRw3Sj2KrW7#V9msDR zqP3TTT5jGu{t{r5*jDUV$Astj|MmwzU@n+w95C9#oCX*_E}hQnIWo0>=~F}1ATKOf z8%+3EEq17#3q5VIZJwu7x#dM|dcq!IJ##}Ia&U_hPlq!aO`B++4JSztXB9UM4P(7! zushvNDl~$H{)%!+uGgjt4eeyjqB- zt>3V=RzE-H?Bse)K_D4sTNv#dd{r0p;PsCvyNmm&NeL{#t=~;^-K_oN zkVA!E#kr>Wv&w{`_J5#ymv?sFXS*0c|6Re1c69g`pRn!IcOTHUM<|6lFo7C8RPb>&_-`o>BZs=|;}B-U=1fi8_4atcS)<5EvMv!` z5pSkekXQ}%ba!iH^8Us@c+p1ZhSB~%1RF2O)(oE*-^;?rr62iiu$ps}xmFrKq~HwG zYvn8q@Q0Zv;}jD!&qP+G_EQo73t%_NHRWpe^zXE&mln~GTI#Of((?_;=%5VP(fv46 zeKOQh<9^?N8GlRKEU4SkzJxI|Sn{TS3AeeHDYe_U1sPnX?#l_2&L1Z}rnz`4H?*sF zfg1u{JM!P4>d%?aD2E#c0$yY%8IXh*Jlh)6Y3FT3pGW@>;WM;<9LzljhY8LYJ0_PRRm@B6>FoUZ zpj#OKW=Lu{E%$4I4%LLl&WGirsRLfs4!8vUapBHV34-A+36G8CW_`j>`Zw_`k6$eD~za8PQ z)l(-$8EP6PGoMrj*l)e3>F(*{oOM)ENDyGD}OuH{H@2c#beox2Y(R2m9e5Mq`- zOZJnWafQ{-cE+*3xz}76)g8+b%_8{6k_Imw%+55D_!SAMw%r*gZaMkXt0-wrGf0(It~-wIn8r4yzJ0m?AhQbQL1_ zG?wcuRo0`tcQ{j)*_N_GU;X@xd{Ky7*?)TWe46?EfANNQ34HKzb2?TCpblNC2LZ6!C<)kf@jb9nNQMGKzhTAwSoTg3YhaYhaPI5AW| z7b`v>;S5k5z;@E0-dG%f^P&y=*AdU&r|$aKk>7ce8QGSGCEUE79HFyQ2x!_K?w7oX zpc;JqORLJ0GqHsl%6GLne_OuwYSunS=i*(?ulWYU(}8RPX2tGTkecI{2Bg6{itu?O z_pv=8w#tV3&={_V906RGxo_4_nftELj#=$xaSRYQ%n8ywLMZ}yRJ?y6RtQV z6#3opn--_0$zOu#5Y-JuTl71)S+T;ySvztTgo+ZyIw9K>z#-A>gEg-H6pf5|J$){m zh5H6Dg};om;3gZA1}e>D@aMg-`hgs!Z%2@C5&0~DuGW2qz;7E1u`vI-Q?tdh!Dd}7 z7gb#|9H+8c81Yip4#fJik*%36fj$Rq2pv;}V(9$OdfZyi0V>sl#;q2CkCn9b!@aae zwc-5g+oa}R4u4lQ?yZ;stFr$lk&YGO*M+mOPtQMEi#C&y7ujiCCMW&3v8rWQ25$aH ziAGwn7Kd6;$&`FCRT25*qIXBs8f@SHKh#59o8M>--&TdD(#kAF$(xm*>7P?ZLe=!X zm51w#yv$J}!Koo`6U1U~d2TKb%Dv80z1JO#j(McDbYE!vt%^ENkI>}LY|mI}p4)xr z`Xq(r4gfk(9pCT%Ay^uW9&1zngC8ZuVnVnAdwu%WRMLxzAsS`nA%>yWXludL$GJBD02dr?P|aT6CbY5t#>6rKEc7s)8x@B3?E`Zg-@WjrVx=hawHw1Y6s-5;C?^{1ZG!#s8u% zYl19KOCv_3F$_P!)98TqWUkEDCXbqUDgOjlItkH$*S}G= zE84rK8~kCexTn6I%ZgU4;X|nk{xJQ+5LL)72x;vXSLMOdx%YfMziqau3brpn`p7j5 z5(O!!`6TJ*A7bQ+D!K|{DV{xf-L!A-6F=0QWI^`2Uv8&gvh3i5sAJkEH{T6DQ>bS* zCQ5~-4zXbV`^Vs(@Amg+FWBp`et+r?h?uaSb)Yu#B zmBoK6vh$gY3m1Q2Y}|JhRA)T%%w0vfreHLp)g zp|*aBLCE0Hq`1h2t^Z&5g?;GT0K~)%0Y$fbm-^^ZY`Hct65A`KT$%8t1a(|?Ve693 zkz|>S4Xs_oa9$!8Z?@kr->dmzbh%LXVZNd(`JKa3$YSpS9sY@KsNRHRAOMLT-wOJz zUQ0)4!tutY`!$XFIH}Lm=CDcfdess3pc#EJf}8E<3pwYL2G(&qUY~hwL^n>c%vubH zUxP3K8KaMzbGh!*k;%a>fpL2;kt>tKSVV;k>CXtE4Tp+I=@Q!{v%;vpl zPi_)u{rUZe)io@kGSM_U>XjtO&?da!6Zm57+JMr!|9-1`*e45;yWHJg5(pVM&iwZ4NZthsa$IX8 z$q$H|$`9Kucwc=f_JxG|YbBO{V3(4M`eBA8MI&>?qYB8yLa_ZWt?>D8L zj@l6F(=FyFYh(qSksqoWeVMW`$paQzJ*GX3a!is^7rwT*)*;0W3v0uKUjDWS_TMqA z%7?P^A0E`zkN!K5yl6QC`GlfS^Y^`A!gY|?ukF5$e&^{Ee8tq=ic0Lv zs=l1p*WyE0>Ai$BDol3EU$ectfr;>6O1g_OAbESJm{QFO;f@Kw5MM6h*?tWnY zYGnq`Xjqf#yp2k!?`K)hp!~b=lZ{q?ZF0;PxzFrWdZLJ;zOIWIz3>?s$X_tT{M^M) zCx?VzBpi<3x}#~_I34%)$y0G2T5ib5u_pg;FpA*@9$I$!V~q`ch3kA$14=T=P#OAB+^Zm{johj4=3)I2}yt&~4h<(TN<6muo2HW)j_t|sJ&EHr8x(uKO`%uPj|BZI3; zB1UJi@(`FHe*p~VBsIC}zW677@4?~YC(dhv+^>$A^8zJy1eMWiW7m(oRP2j>P2mz% z3tsM~wsW7^iHQG~7ge~oR-t^$8 zb~_`gw>(HA4d_)I_lqksk8oS58>}_rtU{&U59k=|HO>4U=yp3Mr%V*T|Ly-{>Z<~x zYQL`!-Q6M~-4cRys5F9fcMQ@n^w22M-7O*AAsx~+gv8LLGz=kK-@O0d&3Ah)&eh)g zdG=cCS*s-u88%FJ^vNrMd28JQcW1FdO~3?cd_ z1HAJ^hHHG6^fdcv5Oj+_Ih;8h2qF;kMmUTMSbPJsK!Gzl*^+F&SA48%I@5vuo;y!a zox3$|u5VNmd}nyMd7VVo-x`o=DN2VIXkF-oLS?dJ-u-t2(@tlAxBj>f8>hp<{Ko{+ zbR|NQYVYuo}V^+TqMPo#G1Bl_U77 zA=`RBw*nj3+t<~9|HLzmQd1R6oC7(!_WwKm4$t@?rN0K#r-bs=d6Q@8L+``xSZ+ODW54 zHs$L~L8@ITd7fMNuzLTmJnaYTa|-G8nxiU3e-q}1hAvbS@!EW8T=3BD%U9I*W9+y7 z=$O**s57Rvj?`QqN_0w`j_wehjEjL*5bYm@sPjAs<|js0uOhdch`}yhsgx_9ieFCC zH4vR4XBtC&{Hr75QUG;wsEaI9>zri<)H?2ZJL1DhRrKT!&A`=ww}0)crtSGj=-t5# zNJo@kmu@EayhzxYFAMHu_;ghVAazij4{{1kt^dSD-9w8O^*CVbzw1N+_~|U{4_gpp zuUq8w09)HL-fF+>DUGJQ&JkBl^}tnAv_Lr*Re z@J`t?pALe{Qmd(OL6(fv%gKNAFP@OEBoE3w33eaHZ5ycSfj4SeMqOVmLO;|mMmRKA3tSZk+yKRsXiF+e?txHVtJbSMzADC@_8ha$W z<}=}UPBZ_`z9@IWxw4Us}^0AhQJjed2)KMh-Djz8O!t8 z<*Cvp+DVkt=v^`8Sm@F`{3tb~xS*&o!=7E3Oqel5ui)kUT1*vp?Kg%)a!8sUHH4xw zzmv1p3f*FJO&mp(*tp>L?zQoBDZ-axmm{{ z)i;rLj&%S+FB>^pSyCbOGZbJVO)ia+*!&L)B-9tgj#t<2FWv|W)i$7Fe7Q=--sEcK zcjDUe>8PXdcU$R!LgQNQ!><1#>%nRmQo=OaTmbKzFYz&tf1BB66I99EeTs8^>Izx~ zZ~Db>R-qy!DY;OB>vwg;r2Qij45wlcZI=m=i90)+U5~1O9GdY|Gy-K`M^xDv4d9*c z2MHIx`iVXcW4chofhw*Csi|RzEkbR)>>9Kr>XA>-EXT91x;R;IUWcAJ4j+%x>(X2ywuW@ z(cFcDznH}yFkKShZ;@5%tVd_0d9Z8Q<;W0tHS3PFR^gE)Gy9w_MV?WB%n>(-)bEU6 z{i0rS=cdPVelg0A0zR*=B+b@sD@qMYo4#F=0RVI4F)^@lME77ny;N=>Fq1tbF?4%^ zmXZ3$BDJWZiR#aj__ynFX2Q+^I{2;nqNeMUWq?23IO)FE$Bh&+J| zQzXhyHz9MGZrF0fbKlE~?v8W0q%H=nK4oP&T9yb#4`YCJ)a55Ma8cVSpLY-OM2n2# z{+aYg!=F5t>>PKQ4%4^b9fd`q8qu!KyjmI-*WH<%-C8#E&(NTDxi)W`(n+|3~AW69HbDd@RzqhqrM3QmfH<*SJNQH#L$mmKBmJLzpwp5Nr5T- zNZA>(__^m(ye`7)k|R1i{3Y9Ea0EEz($n=rEol^EyH>=QSkyck z+DYTue=OCz$O%|J&H{y| zH`kIt04{LZNcw>#(-)t zKq~*{ zu&nhmHQ1(}3G?iuHjr0WGuPernx4E1o6xYS8V$EZ^jdN6_ z*bc(C){gX>^7~BQ)6&#k+c+G0?Fb)sl+wHwBKKkNb8}Z5)C!&mF#-og>IK8n>W&H9 zl+N{l6wHIIHX|v1Z9v*gtzbKD)VS*7lYGvQ{`O<-q)1dp;H=j;Ufp=Y$n*Tm;$45a zem5H6XTKR~#WUlLqkC)txtNPsV;yqf&l}mo*K<3RJ{EOv^AELG_-(Wtr9+&``0dE; z$|aJ$5ZXn`H=6twQJ-F;u@H5s;=q)41HQc9zL) zoBbQD%+&1V5gFREHyxJ8Wm*)rC+f$Y7rykrxJNi zr<7x;OkY)o?c}PeoGeq^l{uaMqW8v)TtG3VnfHB6Y-&ZKU@`DvFtN8m^zdV)`*@Es zGdTdS81s#c2y4Km4h|8}trBn32702Ul!WL7NV$fvED1vYkNrLhDKmJvhd~1YOT*ne zvdR38VA%4$FGLu{#4R^P%OhC~f5L>UKV!Bi5{C=QJ2Q$UU>q4<0V4r_xMYOTsOREx z;AIlZ^M<5`F=o`eThUJysLOTiwcH(@C{Iewg9w|cZD(XJHoXDK#Z1ypWI$S?yR%$x zawqvAw@8Ot@D_R49dufTcPx1PcFX?2V|{dqB@IWT8`S*9%Wktr-;=OAod*@Ut@TWPJ=Bz~ zUW0DS&HQdk9&~K4_SCMzw*MV*zk@h?sI?JO^pm?rdRUOzdGuxz3&_zP9QD6rcLsmS z+jq1L=Nl~IJbbT0ArZ9x>*=piDcWkTNGR3vU5+4B__alGk1lhUbil7(wqfOCnRz)C z!y_4I^@KWPbI#H^a>$gSR(~@OzMRa}DHl^+L=N;NJbWY&)TyqDa4C6dJx=qG+YJ_y zXL=Su@6&MV{oA+8J}l%*0f&3Z47+Db+;vv(1|lD1-!|Chy&RYh z`rVe?GER16jv0d!2Wo#T{`reQZ>G7zD)!&!`SJ}Nv~?w_SqqgI+>jG>eV7H;{84zq z9a?B+Lr6B)evG%IOMnjxRB~5=f1H!1(_aw>2jEAc3P~vcQiuV%N~J=)q;f=r5nka5 zQAc2Dig4i0XQTi1X4BIh|MFJKr`8Q)z1t-LIk5Xgtr zAP9ck!+{GN6>dY?jD+AaG_#~mPKjeKBT$W@{Z>k_pH<)%0mp6Rk(PEd&~1UNF~{4h z#QaG159%AmpnF!6ekc&+Km6`G$^wF6YU!-m-_G8};DT{yen^|wi53)6X`;=wBYYJ` zJgaodsTgB=?C2v*S4B@+_mLO%-Dajyp8tq{LW;5(pv}hF-1~w=*O2v)mHR4E;0XWuX3g8}tKq^0SJ{i%p*3iauk=V=OaCzLuI_pq?9F0@`U z8Q`z8W&x92|0Z5u#7#-$@ZRv5U8jC5O#cSumdv4CwICK)#sK6{+eReeXRJTJ&)G4C ze3oCH)}Q`}XM4g01UW(NMC4U6FSi2tZr2T3E)lz9iScd+VS{Cyo6VHRc%H5)BelCH z>@9MiFizMQcGLGwFx7F}wc&2Go1EV3Dw z&iYxn;UoM!%bi!UvzWGQa@ zNP($yH+zn2Z*%pT2+vRGczf3P-VwjbKAiQ)cjct725e{f0 z2|YT+xeX%yf2a;ANnFnsE%U^Zt>q4n6(Jabr5G>a+kN}>{IfY61ve7cnkl@aN^sF7 z*sro*@kr`1u6=D$s_a*KR(bqs9bkgaBF-~Cs#m`KhMsM{NmPh1ZsyVa;=^jeA5e$S znmF_}xB`qMCgTu?xH{6H({h%&A2+Q!(%orjJt)Vt*_%t0dzHszBz4!Afb`@s!kpy8 zYa{J>pMkKSU?Pp&3wpUUfjdq3;>Em-4$PO2D*jS=0(yhU0nUXY3>}{6An;U1i5jEj z6Ld;pXY>`EYr4}cu}yT!<;e;(jJ}?pTzm|6c1;Q!9dhZ)K>w7KL^giPG%B!3$-tP8 z<))^H;J){v*dddHFgzD|V{&cT<|-DW;N-bfz_gxs@P?sFN%74( zpCpEHJv*Gf&j5{O>*7KjMD|V>6<{I^^BEP61Z(7Ihsg%uJdl?y5>!e-GmBPN1=8&$ zC)YXN%RE4@!90pz+qI{v70nX=Fy1Y2(}9_|TM!Ol#wt{zJ5#WX`@?^*h`i!to0C<` zlLjZNZ0P#wPfrqluYM93*Tek@EMw~#s0>KzJ@Eig=GEiZH!ZWQkDy@|KtCAt~MUtSnW(?IG3TbrID#Y-oc8O8+)h&PU%e(iM)g+ecrbZN^B>J5%#Ux(0FgHfa|w@oo2Ma~DUPdO=0A5G|4JF8HI|c!0l*uYTAWshhmam||SS-qQZ& zmdoy<8fNJ^f`wk29|M;OcK+A5GN*SP9;Z*$;jIc)fGR=9B|Q0%SVJQJWwEtfINuEG zvnhT0IJkXu=5zzuoxTO19NDG}n9u4Z&ls`$pEWw&QSmD@WKV@3k3Z%)fGQ;Nawddi ztoQ`lN%XVePeplp#f>dI3u$-H987#NC4z`}Q3wqdD@5X07*CWVA zy|EXK%2_W74!^JnS(B|_rgTNj!MXA-#~%}qh>yTq*8Z$lZ7Pk(^$rD=+#}^*==#=5 zbUs~oRB}}^pTq93l|C{Vf|txrkf=u_%tLjY5)U#dnj@e2jTSn;J*%Fr|0u9K%t-T! ze3pmV+Xuw8gfobo4O3O3FiJJF90iEolZa-+JYG5e z$6YV{^;N=w%+fJg&YW%=bGwD2UHNkFqm#7wJJ{^WnbJZ=)?ryJU%TjUJvV1mmzU&~ zudo|d?#c1=JV!S>N|Dw*)V!!8AU(sbgympVi-YPS80N7to;B*tY zjX$-@RxI;jx!4j_f=Wz^i^sOwzP2nqIuFUrg>OE3ViD1(uf2jy^*)0FU4uM&mHBbf zrIq7DHXn2dG zd|jI1s+}$hFk@cYe3j&p|5Mtr0oC7{O{7mTvu?oUweW;7g9m!**1cWO5;F)@z#J@p zKUUNp5Zydcw5GSZ3g?}@5m@8m?=%5}e?8>t%^ zEVE|Kgt$erW-*|rGT-3X0*%bIra$wK?r9b(!U!m_Ev@O`rJLh};}t<2Ekxqm8C@Qq z4m^@zWUGhvgoz=C^8vMex7_KD^)K0Pg7OJ6<4X1cf5&=y6PxFn8LeVeK!j|*=6;q1 z6Z}f4ojKHwH7$kzwIM%Jr7^QvIj;05qY3lMYm>}fYwRehnJQzGFzk`l&&cUOU4Gcy!NDCB`ufp=AOF*jXI>0KY@jn6*C zhYu<3aX0Z^Qx9%*DpEq)4`w2cfruxq+19L4hfO_}zf!=8$t0v-+O$5eK!WgHP8>)M(^oLNp!( z{m?}%csJ4_?B8s~)Rt6AzS^@n{AE{tSFJ!L*%A3YZxjuTW!@w0P1(}`ZELa$v?yB< z?TvQiRdub!Ov2?gzQr{L(DK72!$E;O1WN1YY`3};JnXA=m@3jQq(D%qb4q&)JiLgM z%+?j$^!HrYO;S-{>}*NsAEHV$_+RhXWsfXvij$V=GV5}MXlKPt!p+ky?v1<|g-z%RPTpVV8$)dRkjanb%X8WP zcIrDE`DkK$#0owG+Ajw%H6kbC@Pa$L78Vjf$R_yC3^qdD+oJ{*2TlBVOA8E;>9QHI)H_x`ZoU z0ALcsslVUy3Vnl1loYS!!NPo_n%$xbXZZY6*01RIo#M!tUvPm3UUQ;v*ZY=^3T*=q z39bX;ac1L8MF@#>YfxU1oH8Mx+E>ZxOW?7VOBdy)QKjIhD%$U4?GtI;NpI? zXCVv2j9`QeRv~_o&ldwT-;zs&|0-8amsEhiY^M$3B|2-t4u1NKh;e_eos<2i`s*!K z*ouJu5v9kA+B^@IBy$CkkZI98kJV^BAkjc1DVAkpa%K&|YFBd_DQ!Y_(m${Ax5x)0 zX>)of$L|woW}H^HO=gSKc$$gxainY0AKN~ggT+RLA2vBfGXsJD=IV&BB5$@CL<|#T z&~X!!Bk5TmCULn^cztD8pDbX?VR1#<7}MdL2>U}v3DXX!`G;d4+}d}w^?D=9Qtgzw zw#1W5|Bp)f9w!VaS26Uo5O}L?psw?#*MWtgb&?YZN93>&f9Mr=BZryugr@&*$%*LJ zPs*lQp<7qS0eQICoasLeTD*WnUdnWgU!tPueG<4*{?VuZAau^Bx!&pdYVJoeHxQ)a z@2PO15C1s&tfH~}HM-`N@E}6CnQ%)_yZ)PI0CeNvNx%K=Vl8pt70E%)xME3`$wg!y zFq{&@brI1ZTx1X9g&HCKlja!|k9L2q=f5*K{xmpfo%VSrMyyrM0Y^j|d#uANVBJH| zOTz#Q^;6u^iI$_pH79ismCp>v+n>5n9vMJfz+McVcI`;f98C?QbKt%&Lr$ype^8ww zt~(14?n5WLBE1R~NtKyEmy{A~;L}w^eHwgjxD|q6?U+f#cT=giS#BTZ9`;aIN?m^} z`MXBuV?8^3;&eU)Ige(P+I}6YYl-+pYsu(AuYP*ZhYR9SvR0PHG9|g$j>cxv_WsNs zFSC#Ci9}fVy^*`B<({u-aO zA3nqhq)+oh+zj*~LTV_dH0pPc;5jKVN8ExpO0pDd{S^l5Dx2JgabO+J6; z;OCa%Q2c6I*1(X#mk%6qxzHQR~G#?e|biNCz|# zfb{#qE0DGWX8nEouZpHAAb)TTCV z^^5?}uiSFe;P{D;WnfNj-sV*MIw8Hu6k5F)2@?M7U>~7LKdQ;fWV9s%cm-Z>(A_ka zP1VIzuwV6$XvE8m03e7geTM7FP?#Qn&rb?k+=!&bns+tSu8@sAy}*bNX*Y- zO>k8irr`%^BRtcrr5mR>M9K%c`*Kh9pQ;OaK9<{xeeih=QmUHjTpMXzjK~T25%kwe zR+)-2avFp}Dv1V|nJ-qqy?X1)3ARXBM}A(^5E&Oib96)1cw;yPt=^5*fRJ_<$9*=> zx_q^)784Bm$jBePu$N|! z6&>zA6>~#)kQ^abuWpUMy~4Wh%hqqG|~Uruaz?Q-4mkg z=yh?}v^UnqL|!4C{V&B$f&+_@7wyw1tkEFOz}mp8q!87k#TbN0%;*eJsPE$5?VXmHxvY@>mKZd6(Pp{G^N z6lu)r%ruKl40|#$Gt9UL^bVa>OpwAj>DFIf1{ep=;H{z|@AYWFnDG~;Dff{lh`qd! z0UO08HFRKn#|2eKgmH6B9EZQ$*ySK0R?m#sHZ5YNQ}u|G3pn9MW~I3+A5q=%A)|rP z0r~5PBbgV_p0^5BhM$A?J=G6W1<>-i_k%g{ICeup|4Iwl+x^-bt~B)kIWy_Uy@T~R zu&9irZm_}sola(G8I7oY=8UI{_HQd`La~U+&0c%l{9diS?Ci{9(sn|-AIaTbX->+x z>@gyer+hn)%33GtigQ2zjxEsAO(1x_AK{r$H>EGx@TLHEtonodFAXuOiT-pgpKdU| z6hc#t;M1>2TA32_$s0AV#WV2jALbXA@L{5srPQ4z89&;-sykqL&UmW_DYBGBP#nkZ zELO9Xk8K4>K}`gP{^pjtg%);LXlx}p>t!TV?C3^Ayi>>bi*wSf`Sf?{&9_IO3R|?3 z)#Jg#CnK3m&0AEp?Vsaroipj`PxEA*sXd54D62EJXSTzugp~AUt9u`{3@82b*wcn3 ztxZHfw^P|ypK;&UL$Tl{gPB$WWTc-k7XEl)eP|Zwm-)W^aDJ~*&_+5+7ljykIrGgiq0qQ%*EVjLj-|dr4)0E*#x54B z0u3XvpaXgIsJaf2Z6G9uk!J?1(X8ia#Va}&G4_d4Oox$x<${Nxy7;6hCIqBO(l5|( z`cZ$O{|Cc}*tmR{QF!D0k&E#bsi=5-V` z#4&jz(u!xVnw87~bQCuXxHrd#=EC1BhHkG+Q`*F{_JnpTPsp|_%PSU9VfMf$`cG-- z0is~iej_QYREy{NX}+{0^N;#S0Pl?8ofgoe6C~xP5v$>5_(d&V-Lu!@F3)Fv#EfBz zM_<0#;a>$8026m;$dONpz1BugKwQGself3Ko&Z`I20w2EDkbL07UF#yeq%$wL4#k%@n7 z&oKM+u-#y#YvaZV8;TWb%RTHz8g<%KW72M7Ba!SF z8!K524XFG;HYTe0qwhtxcK%TKkHDz!C#Br*p52@F5y~`0yWrQLgTlDPMi|Fdu+_j6 zYYlD~WGjeM(0bAq7`aLBoDewWS_N-ex(Cn+?T=6r%y2nb>k5E62CBH}gYaaQd{1640vbGLxCS=h&nZHpG618_a4cD`#mgsN z5a~5kyp}~A<8XLoW6vS^$?z)JpoN&eu0&>Ilws8Q=ywn+`hX|MwWTlw)NSsik?J%{ z1a19*pL4Cu)AT!Na?7Rup#G_W(}>Q9g*4!`;eaWfXBbF4Zc;J2hnC@rc@2ZfO!buv z?Z`m_+>_-oKA;w!>UX4YhxoL@DdGz?^h=tfHZzf^shG^IbQsaFi*>Pjf3{lYvjxw_ z(^<`G55wcOCYwYC9LQjk9Vd%m{!yRYIfr`Iz-d9(Zd^ct^mGx62*;E%h{t?77{Ik@ zIK0wySE-m^3OBH9>{*v-IrVz*=P^tAVN}DyAfVsoyMS|uiP5UD%S=eG?%+U~E7mS% zqi{_`DgBYU>l?h{pWOa16C;3hqhHG1wj_^M8;L*9cO8)NjY#6hKU;BA7CcSUN=}n9 z=8V-kn*VBlBkNFy3h5d=)2DS6`9V_K;LHp8wlf-KH9$neNa60Br=@Q`Ci33{b+GA7 zheu|Vcy}Tv;yf6k1BaiHJE-4l=2-ci-8A zrgcu*Yuu%5xisPU>9QheF1*FRqRNqy*9TN+T}_3b7Wur%e@`Mx_x7uD;tQDc{3|fC zM6E$DjEsa|=`vnAW5m`Y$7}@IY^dkJ9g-qD*6$T^anPWnm|sZW*)YFYTH%Hc=&4_v z?nx@y{6O?g=2kcTOmc6=z>H@)6Ct-p?d37_heV(tdsW_plN)Kb(kHI_X!DZo$}#mN zTSUrB(;cb1WI|T*9U~@I>VEB@Gh^8| zs*4FW+5ilo2afh)n8#d|Cu!Dek?^Q~JX|SyNzdx!dtv#>3k;pBZ=zVkivpe#*qppl z)@ONsA<57ClS@Fas9G&foz{*N*Bk3iI@35GR>p*1wJO>FcP#$4PFCJ!q6T*&ZF#R-fb543f_#9i{algT!b2L+SSCg)`y={ z-(Rx?SkLrXI-lk>pC5+2b7-uP@{DMpy?vRd#$If760e;m7~gifpH<)8G1&mSmGp^+ zR0^W|Nd$nN^1FsmKQLDek++97#gBerCRFzPb|Ntvt73G8brcH9UAQ}<;cc(~@&oTL z?grO}zez$jEUW#*jT}Phwa#6e=d;RrzO1_=zWBR|y?*Dl-I~Qz|6?HjVUTeGp}B_W z=r8rHUqSB=&X)K(lzv%o(pQ7CV-4-1@|t=o8FTU3*rJ`iXwa@r+kzG8iRae-6Of4QV#)YvY4}glW7!zUz|&M5Pe|wU6SOCY6@!PXL;SEWdsk9`x;pZ z0d4X2G9A+1XVGY~7YcIV&N|i3h8X)FQ-v!Qrg0eC;7D z>l$<6Ei8RN0!&^%fn3dW9w=1uXjtyfZ^B{Z(B70tQ*qYAzpH=PT(RwgKz}-Rk8}>b ze}JHtO!J3vY=e@t8PK!UhXi2QgzmhLZpEPtZJ^v7^xzOI)8 zL)yd)f3fo{{0}0ZM}?RQwI{}i#Fj4AxKeHjvgo)T8qiNK^$7YSZ(<*w)pfxbs6An^ zMNJOnWgjUA!MVktKk{)A^Xgpjy^HC61Z=1gXTh z!14sa{4ecMkywXuDu-~`%XwXEBgO#X<@G-X*@f~$B~w}8ygzE~2ttxo5@%RwNs+>I zTVMhIkR4}uM8i;Q$QoAKoN;dzm(x3by&uZpcSRP*@4;IY3WA4Q0v>*yZ903vZK24Yxepm+{TzG>RDH*{&>PTZ^Yn+2iUOlOlQRX zsry&Ffk;Sy#a{Cd)F#Tu%l8_YL*ju7DPbNJPVR>Vj z2suwA^I94IPe%T4&r(PaQbIoe@vc(&DpUaN+e{;preU0~2wz`XHUD0!QTOsz(U%eV zqtFjydUD^Eu01m${=sao3)Oh>Lk0>O2g0NXl41w9+>P~1PSD@v zkiJs-$=;Re$!VnC%uisxBn40?0L66!8Rr(_k3yYj_Rl*|fZEHPKs>-Dwa^Om1uI7t z`G=>6nV0Ay5lMi2&LI&_ki#oQ^%4Ktz{CO#(Km~t)iZw($dn`nFtsm8j9Y7>DKO{s zcIRc5aQ$xm-;HZ;r<_gD+VOi5*4l0?Z%jYYcy(k0?n=`#il1+D%VoNyzpCYD)dX#+ zB?#XL0X?N`^x^JIp@lYY@4G$IvyaDeI&0QG#{Mep7ws9cdURboSB>D)qlQUuq0f7k zMA!v~d-T1mZ?4F5O5h;z2?3&AYh~nj^m=&j2S03u%O&6U4)tPenp`dG5H<}dzpX>0yzRK6eq<7MWU>5ok?MyZ2ztYIWsY+o0&=QV;;sFL zYN^*!9+2i1E4Y4xPhAjW)?vy#>t^;IEi_w3Fc$C9Kpx||m-Z$Z*>*1WZFg);RlWNO z9a7Q}4kDnLwhdX6%|=^iwVdWz@jRUqxw(mIsl`uT01LzI8nN;d@6Zr2YGYyazUV&y z2!=htPMhEcX9ThUmK*;S@ao6#kFO^kgCz`;PNCo!s%oAmx-*PtV!$jFKJ4#SoD44| zYUUq?J2VSFAy*fC%L@jCrH?FzEldy6O~_W1Uf1v(QZeF0BJd)7*W5SATtD0c63K5? z?Y(A+1h6mNJD}cIv&XGT|8)Q|?GABh!YU1Urw+C+!=9A7nlEJZS2g#9pzt*b`NwK($8K z@r5mkZstN?wNHUcYcfn0!@G1lb>^+0ns#DNWY66F_dMQknA(0_IbH`cs*7yAm|ObM zd1Yo=&dvmpV;|z40Y=y=eknnXmJZ4s?@jB$_iWsFIOa6_ZiS@KOt|0KVxplnI%{4M z>26z4pv%?q004(%NamrxoarO7_zhsz^DlN4WYc!ltTTPfIq$=zpigEsdqY16j3=vN z|MnF<&CTA)A8(fIPfP*>2(IJBK%h*~oN4*j&p4rr=I0;O_Y}oO@3ox1dat+C&Bz^U z>bY!2=LLHl8sRkdNTC&t-K}eW*#r9gC<6G_jX!@t)8(wX`jFI*nDI{mZ*7jSyI1e1 z$(l*>5%2t^dnD@2>_*$G^BNNl-j#^M#&!34LcBhWb@4mY)@J$QU>1xQoVhB=95qSl zX7}wt>=86zviSRnK6a@Qu#Qj4cp&dvA*pDK)+4)}h6g;)E@Y%5TbmO=e=%BUY@(L6 zjU}Vu>g%qgeHL6By)xuCYRC3&++Mrs?WV-3KRHsXDr@fs_)yd81hV6GJj?rJx?6Z_ zLPe{Gqy6+PP!m6-rEDyTW<>0U_GYax^gdqD!`gFIKY(UATq>d7y*1FzOXN!MVye){ z(t7SM{N`T!95W9eFf(~vgBiMRj)#EhPR6{9*#C+p+LQ{85k)~V@)nQ)kFCl4Z#DElj%Nly zOw^XPc4BO0Z1>|d8x#|((V;9#rBz2X+2TabCVCJ)dP;BbP`O*(CgF8Rxp}(zWfVKV z!9t_t{cl(fvvJq(5)rp59!G}e+UtwacGdoR(PQ|*yAv4>x(Nr*0ifYo}E}mC~bR6YVS?gc?AFJp3)^|a3VbvvJhC@ z{iCS8K6>jHtNkycHEzl5dyV{xNmNJIbF#jQ6dWr*t}V?J}V zG(w_js&OH(xuO*XnXL-Vd(fs)HI?4cnKem2gKrHj&rK)+^lIS;t{$R-k>gn{Rna;% zU-nS+>A>cskkO}(`r)_PiAWEf`JPch8PqCR=v2qyBb*+tY4?()<_}U(yU4xlxJ97n z>8}aw9{(xkx}22rV(5|P(uX*D-0Q6Kzi}kXsO{q zIEDjT$R#Ipc$$Km-t!HE(OutvI}2YdL9jDMPF?0buH6xhp z05^Vs^w7f$Gnkk#>qts2zFGiAnm&e{WE6Fkq}L#@NY&edT$PjmK^`!K{6rc571 zGhvsFLl5EsIYjt$S2OGHv@GxpU6#}3+c4Q+eAVZa&svreozvJ@+Uhr7Um1MZ>P%_)v*Lq%g zTr7EjjeU+&@EzO5I5?WXN=Ynfc-BY~`_aNo*L#k^_=9Hg`6!JVNiL*cp?f_oNSvYi3&2MmvvGd1XvPe<#CveR!3_Jm6)Xk2JD)a2gXs zxx~VaP`iKa?_OP0nb_-A_O^f}*!B>zNMaiKTretf=^H;r!X7rZKnhfsc7fEmF5ubraw?^2d(TzvCCajp!tf7#_8f!eYtgSB?|6XBMln#Hh2J0d}_=NFH(w9awL2S5+bB0XnJzI3=;~W486Upt{ zdCFaGaGo?*F9qm^kTjh`7*9%fr=6?KZT(31yd8%8fHp5G3#%py_ZWa9(2QO94Ww(x z>3JN_ZIz?9N5Hc+xEkp=KkV!|URGTxL?qR7ZTAnAgDt~udj7-ci;nA@=)Lso*kqKu zW^in4-8B9I`1;dCH|8gE?$ZDmXTQTy){-7#5*Wdw%!)ZssN+QD(74 z6+?0_$W5;(76LB&1M4ic_g1k`RKfFRD|%=G)JrJFg|@$s~YHU&qyob1=jY~M0# z+i-k(zu+Jf&gsekT)4zET(MlS`?e945JCEVA*2OKdxYCFjTasgPPnY+j$mHfRr$9o zT0q)EoGV`$)Gwzg4DsQufq(`Nkb2GCJY7Jo6+N-E?DNBs;q8YQxd!sx+2Dq zP;J4GP(j=0+72htvN21$`$GGYVJ4}m4}V1!;_l4wh-r7@P`=FA_S!gsmzh}fMJ3TN zd(?A{Fs5LJ_{j#>{-^Xz0A6LdiI!y(Nt_-;knKYj= zs9Lb|t6?Uj2e0ZnuSP@r+#g~kr^Tmb!^M?MpUoV^FM|ExFzj`bk?nT9cpCsc3HC=o zxV<*&k4T#ZZ@$o)ewt~m`OZ;86bq@Js@v9hE8zC|>N*H$9AY1^2-u=&ZRMT{8Taxu z1v@cO=6eoaa2Yca_D!DpWc?Z^IPWA!n!xis;|@@+Pj&cbFg4hK{To+-N-9W(U^Aa%;;=*E!25G#0*LC=dp{Q4tRFsWtdS zrvH}XS7!rOdYk|{4&_i0gGn(?KVhCn6>ClB`S(X`AZU^$hv-mUv=9|}_!Hlqdem>L zk8_{JL*>*ASS~Y)tbbuy8gGZ6skQ}cPJ&<%)E*`r;l1`PG6>nflvr=PBO_2{4SoVt zl1AdfgkzPlbhCjNlQxWTHKlD{Q^%@C=OT4{q+;MXQ_JS5;%Ky;9$4d4oawA2$9gx$ zpi`3eX68E+y@b;LmE~)oXUld;23dW+%`^6-jr+wKBNtFlarTI=WTflvnLKwZ@KHtaV_jvH%9{jbl=e_sbbI);h(1an|(}@z5m=1;gO|ibu z*jin%g)_U#bSrf)L4V6|vIlx>=aZpN{`6$u?T)cB6*5r&g{O>-uAwL)T}LXu&5wJd zmD&1T2$@V1F$z~nO6Soxl9fvu&RR&0O+BFRr+cK|gV0xoY~#1d37909c{(;^twL`pher+VZIHxRXHf;AXFP zVt>Mh7P2P4K)N}uiQ6A7SOx~#5)FvslJGdLGgde*Rgn40UIf&zzKA+fMR!8SqYyra zl)QQ-3&O1qn=q^9B#_Uz;OB*U4HLt-bnh+n`yvgZ`NaoUFlU^x@0D|Iw0iKP5`AeT~&p<>&}mxQF$$@3S2ka}>1r-IesS z4}Qdw37MiJe(gU4vj3bam+DEZIWBHQv&-{6wL5OO60Yrr@9=SG`KeFdX_HoO?H)d# z{ga6Xsr*Oq2R~2~EZo4MiD{BDH-O9q)GeE>wK!7F^x{uGq~IEt9yVYGuZYyX$qzmh z`?|s-zJ7LFpINp8DFp$wjyAdqwtpzUJQ7c*Gw?Dj%G?2 zkD<8y$nwg<-^x8j`Hig9u}7NJyVbnNrlH{}MayIXgY& zyhzTcxkwqByHSj&n&pJMxkS2L*u$LeqaW%;>K0T@$OJ@9n4Q3b8X5Cf{o7j|@a_Kl z%TYTekX#~w8G8=N9v$_B!j)65_Z61M{(}$zZOf!))M|T&KHg~~*+A4oqnqoHo<2X^ zMGUC#(n;B|yi{t|r{BAt%*WC-?$_Mo!Jid2Y-WHvsiRO6?#bfReCnpger@rXXn6Bs zT_r@E_z7d9hMyN762mBiJ2dnU{QC5f*UwKbL13t{%mz<5IAbE}E1_OK3sCldfI;>L zb>aBC|3HSre_UGW;=>QhY1w_=Q~iYiQB*RxLb?K0u_y4MZ}tNT44zzHu6t2_)QoGI zw}`^wrS7vBNaRm9D=w!?G+DZ**e`l$N2fpXDu$uE+bwgy{JZ;SMDU)+wKUK-Ka91* zK*85vB~2|>m@h+``y_N`$9Q?mEvr3HT4G-U8rqetsDxG|J`2HKt3YJ> z3Zhv#tZ*Cb{5%YI>IjHeB^}At`96H3aVxhTZwM+G7G}&dIZt43!06c-+_=}GdarzC zk~E+Nbz*Bc4@r)x!dOP;jz6@?7~Q@h9*Pgw<7&COUNN|;Ve=gu4d5u$fm7h8zfah2 zfV;*fgl>D|W-c5Sm!ytbnYDM5|5wtYe>Jpv3%|5l)wBRo86^vu-InR7=liHY=2~48 zJ96|IerdEaV2UHN@vM9zweomjcfztjdcaI!;0J~OFy}G_{!YvaAzqa5uqprBbw*O| zPOS1)@Mi_UWUzxktLw+p5E9&rHCG1MnmYrxf{?3j+iwzs}E{XK*=1;;5CJoC{{ zd>cTF^Hyg5R;bk=Zeb6`Shr=~;SMoH_V4#P?VIf}-|_-|wHH{Hm%uEWS9I3-jC9~j zhh-&2{YtS~4c$X05mjGIFcEOH*St1;8>Z_hAL)90{PV%3z~wWZx*-AOJlXF~th)dg zWNxeyanX3vss&KK;c3TCh0lndJi?wlN^9^4;s2dHqM(iZTytUVCqK)Wb=qdUkr33c zfRz6bSC`hP$sJ~{GP-@)S!^Gd@wQ@lSccPxLJ?zkB?7tMK@@lxHPHAS(hZpVg*3`l z{<=J0Iwa<=l@2b0KSupuZTWlx4Egn1{|pJa?;(Euz8ThAuWHQGWwuGcFXT(ii4P)Xiy(aPwJy#l7{jVnJB?rTT=#1* z@~npeKztK~&|GW2Cy&Xtlq=n@+&?)7sI||ivM=fS4d_4+wlTw0XfLm zwhm9fevCQ($69P}Koz^Ckem!&Bg1}b8jj>!J5Ue=p&k?$g^8iq&(tx?L(0Bfjo-f- z`B85F*4w_;Vfpn1w{c?tD$4z3{@L_M*l@A|7mZHC8Wk#0Ua= z7b)S;B+vM&>g8AN1c8*F*$Upp&m>Nd54qKsh)(D`1Zx~9aXmtYd$HZHCXbKLxlAtu zke)}+4UlT~+%My!C}TFDmG%z}>PR2QKN5m|gM>tRJmar;gKqp@yh>C8PF%umIicq6 zAUr_0%`NFp^-c8QcZ$4dAh;*;x-~K{29RTJRRd5A$al@tibZOJ&FB7Dfl}`V?Jn}v zwj#j`?T;5`Os1V@%EdYzjXWEVZ83u@jrlktueyKx;)ZS*&PC9NRbF9f)A`2Jxuf{c zZ&AJ_=J0I{(;m(ulH6>c5dL(#b9AAwn&ILAh{E-qjhdx3`3xPcfJ9f`63bay)^z*IC z2~$fhmz%FDOKd?RTgUBVN0*&7UDRlh(L1{mm zSI7K4vq*b3me(nP8K3{;@RW1ojOl_an)lCyArp}33Jt3aZHu`>UMflMRq~x4 zBEXxse>Q+@wc1GuAI}?r;Nc@I?#}G6XT1c91wudnH6CJ>P(K!?8S{p4E&v(#E}Q{1 z4KL5>KMa=Iy*}&d&4WNV8gY8FS~a+VJ6K_C?>1u#CQ>7~qX#|Ri#PpuTIHul|BtSt zRjcAjB_o2N{9Gi_z={Z#t;Ubjx5>^nY+Ohi^0*%;_}Y)Xz;lmbLE(8ol0AE&&6JO7l}%(&qU$TfSTL^l(*NOv_Y74@o_ z$Op2F{}H*EhfWeH&LPZ`)4WM(DpSEaQMR1ugl(`fT{?d zPTFmD_YW(avdQwBkYCUKkeh`g~PZs z{DgO)N8p`?bL?jL`{B;NEaZ-N%-IF#s~nC@>h0aYf}T50DS&K=0CTnN&`K&=6Wqzf zfj!bstM@vqzcB~^TB0JfZZJ(rBmYFM4-0X&bGf3fQ^Acd>dg9&%`ViW5(j>%S_KxF%(CNqLLz~7bXtwBbZzou`EZx3e%?w@Nk!QEN9OK!-^pV;0) zFV|WLbQRH>;^mwGYN@Ods9XCG&$6XkU(wr!U|Dx^S)aeI>8oz?F&jGP85yNIlB*P> z(Zc>xz5S(o=2(ivqccA(KNBm+3(kw&R4QXELIt=4>#|IA%j#a@MZMUv=!&<%^xn&` zzK>u>6pXVjFE0HI@cSQ88OoH>9vjO<5b)lpsIL4w<$6;8slrVwEne7nktaV6Mk(wi zBn8l^7G|c~$@xChJc}=!_i`3psm(X6l{KCQ3MfzvZZ1n`cW7L$@x>h$g+)F-s)(*2 zBgoK3e{Jf)!_84rryd+RQffe#*CE!18rXl=t52)Y2m%{< zzq-l6(dF@dZvHE^yu4bL+NV9O17S zv}t#JiL)Ft8oi?d)Sw0~M-xLL8cR9)cJgM&;{b3{(&ePiVl@@~C@@BBIA(6VxQdmD z|LHuZ_aN!TSqbm5Fb-8=pD)vDp4+nhJAW>En5~h+@Yhc{uW@-||7HgZb9#nzV&8`K zcs72JVOGt>Zil7KFI9HPExow;c)w6Ww|lnS+=UJ_tH9AI?IU$86+s6&&9LNY;poj} z!nwk<2i+nF6@IVji*33(M z{iWNY0`~FKSf6x;_JgL>y{0gnTgBd^?p*FLVME?sSWl@HmV+9ARSs##0MA(XCk|Dy zr>a)D#yvc{_b#tF;#YYz`gsz{ezImRA%rIWnZ4m`rOm9JMOSP3kebk|Ji&d_#JEW$ zVhoEt@TyNsn<{nk(&~GROXyk_2n|h24E>wRi`O=un}xGnP=bN9?KXFLe^)g&^kh*P zVVgJ+1&-TQ`)?P-t_Z1vHGn>r;mGhGcE^$TcC&F01@7IK3X<4=vO4(Bn69?w&qKRo zq4q?h(L+(HedpkUBTO(?extN5@9loowH zc$iuH{Aj<5Eb$3(-`b0wq!nW(F7|B2-xP9RWUP~YLyEf&?TT=>vs2lBLI_tdr$K+) zL}PkqaoIQN*~`bDlTNkR+-%{))eA7lpkav1~xSc(b%r}Gbma^+T*PpXDT5h1$q3Z z-oJc4)D3OSD{|OEN9y~1`1TJ{XS>Y5Iv2@1x0^BY(d{;0cy+E^;;g;A6sUou*y%p! z9)*_j`4-wpw5`?l$COvK%H&A@uC?|+4vztoL@e$iTk>criT?H-FEJ*@$04mDP58;= zSJ1P0=$&fO>c4wH)FF$jrH>4=Wwav=+ucLbue#=^91-{9zszA}ekS}E;NJS_h|V(0_0ihmLeLYyCRfMxJ- z-J^X`XJB4hW=5rZrm6UeXulajP1vWhfCuiNzf}}&goL(agqnznkj~F8j{e`3OPT&R zyH9PXM$F*`GXemo^;pNn+}776`%qhwEUqt zi8}%Q@W<|`SRxoxB z$Ay+8;y7rEr`^+Y~Tz&HZ82zHj)=hh^%Jy6avNfPFV~8x!UU({wU6mK$I} z)ZyZOS%Mwdi*6p%6+B8SIkf%ZeV<>ZQvPd>w{803hJtNU7xP?_HV#D9>RZlRmJgxk9=LdZPt!i{=GM2AOcB4me;+$4y)#-hh;~2{iP!5 zj{_4IVt27AbZ_z*w2)aabZHIN6o>*6b9V}i8q)+%3tw!cb;JX7fvkl*6Eb&qyC9a` zuc;nRX5x0clwYC~6+b1+1NU%2TF{4gA{GBc?o`cCZLC1l^u-*WcO|awSa1BVS$Kp8 zbQ~_SP)u^Fg&;*-FN+L-`-i(~TmdNP#zp7Nht%5-F>*c2&!05>8nlVVd*eFUvAuJC zY-$w9w^Usnyip4m^5Zg@Z2uip>=|}DWaL@a@CYuXQ~y59h$Ivle?zNriJDo#XHWK3 z7={~xASrm17G^)gjjDmIb7 zC;u&8enL+??=a93{Uzh&=*NA|&Z0zdxI%G9=T}I1D(_shU2qJRCu!qUp+8ed!d`U3 zeUq+)-mFT5Z9W=|nhtw&oh^unfTP<-n;~7`!$H=NX`&rt@Any}nF)PTC}8uK(%02J z9Gt+i<}WQ=`ah5jS-ALqG2~AhV!kixf=@S$>_5t)UrA#BMi9fqQ!Pz5glT-va`PC= zZ~0DtbUj}`TV^)8Mte!*wWQ)Z=lGo-{C=Dirb{!aJJg$77~))N6XI3~xEU5|JPjc> zxn2W&7c#u#d$w^zqn?r~#CnMUAF)<10agR5xJHuIqt1Bqo^f`uuZ$&ji6nWO7D%+!DCxNIxN%_N~>fu!@yF-D+-ZSmO66o$AvBi8Rf+V5tg&|4FAU?-$;7& z<6a2>|Y%i!D4b#rfB~EYYc>cC#LAEdXT3xuo5lt+B&syceIVyp~Zu zE#}w}8R8pn_=3{Hc?6KBHAm@$g?)T`Y{rdMUe5tOdEmV0W5R3WPRu%wn`4)RbVm6@ z9(d6C>EA_%L;GAaJ{NgE5zXKzCY#{Gqw4rxBW5~E9>rI1`R+T_o|tw@3Wr0@u~|s% zMQ&Bd8lC%>OZ&MPB%`;g>VA;;#fu)rq|KpwG5F?oL(~i3&gbHPgbil!$5xVHvizcN zq3sCKu?PI<=r8F3gxs;=Paf_XmlDjnQcrd%q-o`0jD-2W3%c0 zDaV^2wXAHSsC@y3NEZX%z>732?yp9JM)lMpOqs2_S>}xs^Y}AZx8HjN#xkq!(;tk} ze%3-^v#H-J$r*&y=I4+F^WQDw%9ys6U)P8=+{T(IeZ8_PQ1;oOwIYm;M(H4 zyIse26H)Bao#4jVJ()Xv+}8M8Xn{XUPRmz`C*Ayc1x1}1|&(8nxS}m7oqzJ%eYy^KmHt6_vNww1x2#C>9Y-pEtTSi)Gtr=Bpz(Y0wS8<)s01c#3*>cuXsP_5>CM6!PUh+DqWc1qZ4 z`M>Rm_>SeUARSq$pcwoQ=r3A_Y1dm3$AhkCnVz*Zk}b#wcywgs+}k4-(bb?{!y6~; z0g*=4TpzbztL$cWi1p9!8)haU$xHTPwKHMA>pur&W^#<}uD&03-k)i)QbbzPZ;GT} zaA7Y-BMWQcA%|IVF*l}?Q`w7H$C>aFZldN`|b@E`3Jv8uu7 zZPt|Rq>m!_z?a(U%bR^5N|fLAtMoo|qmv43$N6{YEPArUHZZ4mR=J71&GnUeg->5O zg%5+35e38whBw39S3gB5QkSHB?|(!m7PiRb-1<6tNo2Ky_Q(!a{_xFNg40NpN%%le z&FM6bsX@y-I*b>co2Cjl9f*1d0FQcuhW+-kdC!G?#>;~z6vvcwNuu<`w}mO4K+l1y zt~7VlHBQONlQXj{_{z`bMkOE_gQPgFCQ{v`s0S&z!|$Uwpce7m5;p=hanl3$jq!s& zd2#(cO{=S_UY?2WcD;9U_`#$uM>_?wTERFS%)g*`I=(cp@=|T`vOq>-YdhE==HlXE zPos`31-boUh!@=d|B#kr27PpLh??A^Nb~sl>#3=m7Cv_l#kq!vv6l=a@1<5v3ZvbYUWX9fh_+3tS+l&B^ZW3#y$AEY@ac>AFZUyJZr7so zn;}D~DMhx>uDO10)IX59Ud25*A();v;fGdU&{_e%=qv=-ouiiz}m)g4>Z6*Bd{I z5JtD-BBi2-fK}j~B9Rfo6%>&Jz)1(P^$420gM z5}$){Qo_s9fK}|p06pzxx5~K=nNv-nbc&S?G6s|uc)xl@-i~5hWJW|cGI#i+sDI%2 z4NLkTp&t_hcmR&Q!K6Hd{w^YtHT~&2A5hxOeLQSi*_2%<*8*LBhzvQiyqF5~kBCvd zB$wG6!{lEs=WwHam9%}jNx~j0mKA(j)2@4#-OsK~mck1^wAA9b@G({5z~fHSDXZb_ zH@v+n+|vg)DLOoW_j(be!kY$ z!VTixKufuu$M#4|@^0TkNN^EB_ALWj4@FTNVO#f_dz=nprSS!}}@C<4jH&O+` zA~ic1(u{6qlesJp2J>5~2U#&H6<^k#foEaFcvX8>AATtwd_8 zZO9MUYj({+^E@FbXVP(}Nc6ux9VC`~`mI+Mmz)^}3)PB6@Zw{gIo*!6I+&(EI z07_o-9&w8gAOnY=scFuSlsoa;5SvxTmPzTn*D3@v)z zR#w;Wpu53_^-PqF!C2wy7em+=+|+^tU{GqYkF8eDn^Xd}KVaVDVfnboKD0^*O5R-& z03vbaRLIpMz&~Pn4Buxq6&eOBwr|L|d9nEX7sMD{ce{!=cR&j-Zg*5&uUNDR@S+oh zGZ?((oc&aV3q_wq1P{s9NNb7~+Mup0Xd|`G*+RJ2&h+@3u0D`yM#O2n3y5AVvica* ze`WCFiv6O7QCo-+{G!j=n?LXQgdN0&UU6TvA}(^_fnRJjy;{Z`obkrkGWu-Fa+K;1 zT3k*5{+rSfO4SEj`U7I#^1220B%zz+UWg{?Q`6gHL@}kwvG%~i{nO;t5l?#qvE;Wn z6y96Vff8*SLW#h9LUtzvmdE8k}MkbYIS1(W$&4!RLO+|M(-(spWRI!Sb2yfM-`O`$a_KO*Lla3m)T7jjI}r?9;gtm(4veq4CgL(U)iMOV6!6WH0q*)TcfPQY;j5WFUU@w@hEHL)d#`YP+*}GjD7h~!$IpTxO2W}Q zKU!Q9B`7k|JLQ!U4iYUawnjhRvKk*nY|eR(z2Nts6EDHu{Rm@{*Npy&06m`$)t38(mVJN}*f%Wi>~zTStT7o|2IkMB21Kd~9SgEcD~5AMF168=rjh z82Oewrr-Tte*g2xN_aHQ+Q7!y87=mawNtYGuv)A7=&6Hb(ib@6Nim#ufw_lwo6ucM zohE?Onb6(~lA{`qnVLinh+RGj*I3|omy}t`e$RKmoBVpcER8&rwf-)^R{-?a@yJ+_ z8d!8dH^!GvjlQTeP}pr|`7I8MfjHRE_&M2qn3jiCTI(^cxYQl3O_4dNTI8Xo2?eK4 z2H#v?h@Ocdb3mIcA*QE+UW`~U0IaHdu=D2)(m0LBW{CTw_u<)0I6OabaDATYW><(? z_|5*=FUPJw}f9}sxNA8AG zGs5{Sbc~F3>`S^uW^i&ucjSV__Ehibor2{+Zqu}L^cuGt>4UxAQ zuT92s8$nXC8o*QHk1<~7gL3Srq5odPsJ_;Sk{>!m=O}|~N*Qp0ppCATlTHDjVwL0S zn&MYs<@T3myg@zpfW9DzJbWGh?O9|Sl`K~;8>sMI6Mf1`(!b&9 zJSLI}bqu>Hpz$aJ{5f`^s<}1hpWov9+f0A%A%mceawX&I_-Z8q!<&i~`_q&l%iPSIGBkbzVmZ z1NMaE-00+cM9ls2Fbi(MB4}(fFhqqi_K*Y(WgR~yk`C2O^LXzb)%^U0n()6lXH{d? z^F6wBi8Lmyl2m<$Oqy7nhbO_LXt4+pxGInOD9o(n7Wl zZ<#jf;Gz-#jjON38e0`m5V|fV`kmC4UCJ07(?#8@K#-$Mj z#AU7JUaM9*RqHM%3w-43*~V=IM9}Vf*Dq2rNy@&W zR?ja*O^;RDM{)uYIzS)IYMUoVZ2|Whc!B*O%4+@%(^z8}^#ak`;-Nm47biGfBNq^b zZgV7Un#caJ%1@d1p*^9GEHctHu;1i9#G4O%`s-qGH1h;^s7}YSg5n}!HfU^?!^Pr zQ-~<r^t@S1QC9y&$WA;S^2>0*H0SNadDq!Lf-AKaV+8NNPKJs!H84d zQ6zNljw|2*lreprsH`KOAtt}c5Pf%1G{n^Vt!5tOEfL z;uen1fv&~2SSc(A04#RI++iujH3BIq61_Q5tS#wpn53p(O%Z_)5*lNy>%X25N(64pex2fk8z=B?^?)wJ(Bj%E1vGP>^i!TWze6U0&3)@Ny~N zMKXLugmbSR8^1f*9YM3cn@qm?sy*ZwT&i(&D|#bR*1t?ECzX{nZqCKkNgya#DA^%^ zVmdWrQFi7=ScJH`=}zIfBkwIhvEirehGFverzS_9OSZMeVqqnSsnY*;X=zQaJBlpb z2iTodboSmdkSNqet5az5ym7nm!q3?|>ecO5^<&nAeoH`!lA`dGP};twPJ8Ox`L*Rz zHTC`Kf4b>r?A6RwSC~!{{%poonK*8vveq`gqzd?BtVGW+Zm$?$>Oy^kut743u!+-U~_j!bxp7hE8 z`hun%hO`{-h5o?R_Ts23QPa|5bZLXd!5PS`(I*HGSQEQ*%Cdj4(XsyOrfjBv1(i*6 z^DY%-5$69c^Y*v2E-o&|*$I!pYG^6mpq16UN@Rul)Z23|A0N@fq8w(%y|(v+L;E;6 zs%9KNog-udyZZK2yXn{8+*NGjHCH{^>ack+m1KRmKqGv3YL(^MANpEam}1TJ&}j%Y z#ICGg*wZ|si5Bf&FC-3I7rxufsxwm}NsubJD;uwZYP@B(RNvTx$mD~TKHTmJeJ6?b znyMTYTBF;pd^NuUV!TV6F)^En=%c=vu6vLy-RhUF_4o9KBNF=~9|&~TO+_-`b}*A+ z-kwM8jU*UUozoJPgYR#xekPV{f)@@d&V+^!`ky=ipQEhz*23x$LXO2$Gw}Li;e-)O zKG~P7pT2x)zUAGVS+G#APB={heMi3Y%5LDjaLFgyB01_d&8XXBmC{V1fWRt52Q9un z>xpe1RQY~x{a4o`@y}IN2(UDSF7D6WTp6M2P!(k2=ELXVJcAjSJBCI38I<@*v5odBChMPc4l z;xAd1@>OIXQ@x7kUeei1iD6{qA!DduDhWfZdyk0Y^6_pOV+!N%jdT>*T;6m(@Bi`J zAyb6o7iw0%(&-BIE;`BXiuDeHnY{5X*WDhv?a4G$AOQM0oK{b5R_NH8bT7!Bu>@e# z%IgY>bWIjZ0>jmL@;ON4ytDC-Jc?C?ix!@QjlMpnY7QP1eyOe`#5WrqtT$_>HuLGd z=ic52-WS4Q$n!VTNTqan?hsd4`rJoKaBH-=BY za%=FYtAMB9MSqN1BI;{*7LOK_D3_m%SgV#;kKSx)N-^8rQhI4V9;FaI*hwyOq4%=$ z(4}$D{Do=gW1JQPDtwoRxcaowOGc~KjC+-{6zVZ`UGo>OlOEo_nYpzCfVmNTVi0$P zNZ_l${_KVSBJ5@9;U>=bfuD@+4`?WYR?Wc_L&a2AICq0Z9@DJ)cg^}$6D#X~LEb$j zHAf5GX|#($S6irC)E(EjsOR5g)3eA_B<}33*?sqoMMBU5b4%~UGZlzeco5Z?l-14W zgN%?qBy}Ew#I*3!fbF9AyF!0g4Aul?VDG*XZtZN*g>!0v-zZB3n z%|Q^1v+BS2)&4-z#?-n~ZMGh>nCOtrQzZUazhj~1s}0GJ^#D#pki+9l31{NhTYAs6 zr4L5vR7NsxKWDPh$Gth~dw;Ytl!ZH8J)5}x>gv#XNe=Tb@%hhh(M&p)zdS)^5+iA6 z{nTR0Vk zP9TK?l3irrWB9!4Pl-c`x3q*Ivw8*DY`Xc!y&3Rc=yxbdWD?I%klT^DbhGiUV5teQ z*uz&;@`v&%BL62#NTSxR4Szq_8-LO{8&;^UC_Bqm`;o)q!}vppfDERCprgoFJU<3c zPR+Xt&dmGKT);zRK{}q&-G_0mmjdAS-psA?I*e83-7C|*FcF9i@Frnc=)4MHIOF@9 z4OyNti0@FMNGQ;lLs3;hTIGtDY_4lYEQw}IddgDY@YhIIn5ipimHWF1gh6ZjGgtFC zIHqkN)|>+`Po9e)Sq8v$7dX4c2tvYijw%p+eGMVt-(PI`k&p@e+R<#DoPAdjs<7cQ z2@v*&1|AkHypJe-DlelU^#`^zQGi^}iI2S5S|hNiX{Rj>e#aX{fV*%)n6%smK}QUAkz(Y}VB4`v~c zL<+G>)4#gcSD3PC+g>*8U!zK$u{iR+*BH%Hp?qBF!sHzOTAsWY}wyPduuHM;eU}s)ljQmbWk>)vsLDF1o-E)r ze-&$kHzg&jJ|_aHN6(NhcE!8DVaVM!TQqOJtsMEMWuYDXUi9wwk%R(?%i1v09_#yp z3DCi`s;7*Pf0d0vot;UPCbBdq=rHYUqEkF|#p=~3w}K+&z^pH?Sl`}6plH{8O6LaV`Fs9OtXcAH$o-KkN)HZnZ2oKkfJTXp zkK-mn&JeS`JY5mbhYYH*PtNAuU&-@?MdEqIl(qFzz4GLQD+TKmIXpgWLz_A2pX2;L z^224#e#4C@dQnDa$Tt(VL`N;HJ`TE6S9)CO*N&;}kHcgZ=(jU`bGCldIsd%3MpjrU zqp>t{c~{(rp!(Vugl-o=e!j#PjJe(Lk_Y8ziIMVW=}mx5KEyvg>@W+q8i!U&wjEdJ@ed>rvV|7i;Pr@op@ z8|9>S6g(!1N@sxGs^G3i<5!d(0%F9yDm;YPQF8I_AjoY$4mWu!WEtN)+U4*S-o<+X z?~_}s@*=Qu z9!0ox*{4<_?}14Dh8t57u}V_ff$Rr0PCp3nw%GHD@Y`qsLq#P`walxj!Wpp{ZRaU} zSy1NVsn!|K3+c18ONxD#UO;yLn$WdsLe*)-TB^73&Xz9qbSy zLg<#+Te@GTR4AnUz=^DGGK50}7ixcfIEZ9eA|fh5#5z@Y=P(KrDq%Isk5iQ^>jYs#n<&cyQGvC;wT;{jWsyZ4b4fz#dqHIhr&DsdyWY%Mz zp*aNTo$p){V#$uU_^?jNsEh2Cc=w749fa;ts=?R3?zbr^TXHJ@9ZpBXVsD=-gq*5m zAJk7?1{3q29x~i}YBnSQc4p7Y8EpF}c(hj)@WV6^Us{f$&Zo-cOQLyJ)nck=_Xu|w zv`F>55X7shLHV8|vn7}LTi_z}ec@j;VXqaT*<{}7Lv{3uZVVnyD_uKw_jz`C`~nnT zR~6aSNJs><^)1vb$i;`;8QnGMu!tL=eomDxm~T!vD%c<{{+nPquBzfXtVoLN zXenAXe8BIX(R?^AUue&=Usy#1{l@@!)m{k2tOnOidEvkAdk7JG8`WLhGiW;+)G9lA zXOI*36XDm8=-1+thKde42*9VmI8??wQ_g^;7jxS(vPVYSVXz%ok?Gcd%0e3j%4DbWJ>R1wq{F@TZ*YW%a8F_MH~RuRJb-uNgFAmW z32_1XPXuT<65=5AR15|t%ZP6EEh*GN4o3B zBb6U`))o(FWu`kN>SNvx2ih>$i+fd3s7Z6WP6NXmK5OzsM+YgRHD@jI6V~#jgF#e5L`PLzp^%azE(B+Eq`vy zO+H6_`Uz6hsIC--i|<*X0NS3C)KM)^!vq)}MA4knUB`p_Wz+lq^wg{8E6+cg*dX4f z>1R?AIz3HfhjR40DF0RzJAleY%&t|ue2}fuBEs$J;NpJ{AqpGgZ5>b2J4!9L(TbXJ zHnmcBzs6b5@Y`fb{7K%xCa#L1ugsOoB;>A{)<4LtczWDS5fUizr4*wphuFXma%x)4 z*Ib)ME8POIM1Weu=9a?H=f7R_l=if#7%u|6OGpsRa6wA;9eNY2Z|h6daK9~${6~xT zQ_;EMdo@xO7b1e#*N>MzkfjF=Ev;yu+QZ~VJLL4ifxZI2B1glQ*L1X?N`jL=89MY# zMQqYn|Cx>Q+GO?X(Z1G9i{uCI{*oT3DgBr|HjvitP!WQjNLo6(+hYZrq&mf2EPlVQ z$eQ(HEq}Y};*t2cRGlQchB9W~Hx1rLZp$L!w?c+O-^~15Z}&Q9cMlkP^@;)ue{fR3 zc$@I}I8W?c;H?ZSzX{o>9<~QEzJoq!vs`1;B^vd^#^BY(gFCRWiPPg&(7lBSs_nRa zdmiCzdGw5{>0yErYhYZXIb%Evb(D332au!NErP708Vdn&rtH)0OTp$nzwGZg+Y`l9 zk0mb1C{Jn@Ol_lt{z^KlZ6}w*LNt{B>U}TDy-$7g>8s9p$Gz3AMv)tO^`;Bfq0}w{ z$FpjOSn+y(m8B)jdwpalW+m(1;oTdh-NNnV93Z5x&SmTcPSXU=X8ThH$$ zn;8{UbUI`Rr&RJ7tuxL*C3(?|f7X4rw4mhi`FZpL&HP4YUkxD~GumG68^0e=6zXa{ zGmQGk+T(>B8vHCHxQE0gcO86E%5Rq?k%~-<9{u7cxOtHl)3!%x7LE}pubT_24)wSB zIuaQ)*F;8ohR$P z^PjttId`O@C4@u58BJ}^*h;G_wLP<#au5eQPe4&KJUxp7_&`UAyClDVPm07p)8j() z?OeaLq5));q0YJM(}^dXpda9W1F$;|UO+{%BElZ4_+Ry`P@or->xmBKo`AbpfA`~N zo9AZv9_cxcsVTXi#@Wb+-A|YyB;sx4#*MG60SN51;)3|N=G~`&6uWVZj-A_|^;8M< z`6WYfd=n=FYf!WM)jV&5|2Cs%jsP^~;qg8gwIO|wYxx}|a<~mKAE6RiBMBLB13{0u6rxZ^GnS#u>H-ws)7EMlN z0&3jr*8l6m$&L@rG^r z;=7+?KDxrZDqfCp=(>JVh%yd2-sRN=_D>?ZYn_k1=%vrb0CBvV>q89 zpN#On4NVegrosv8oe0+j(^c%!7m(rO^p7*z18spJ1&0vOH!qFc{fFPAeg!B^e5#)< zPeqqMZP&R<{-X1vCqH^$!2je1cW*(ink|F#*OViH8@t78gKj(e0f@3Vw(B^oG5n5})9IV0Ei05f(#T<3umA4jpNUnZF}3K*_X3#h0{CWQgz&*J?q`OmG^%spa5ZyclnXY?uio?R z;H0BZpNhdJY^aQH9AB@0KOKIoTt(3H8U1+ND`wp>{_CF6Q}t2a4F&LLO;c5_yk{?j z=z2%0WES?J!hdDO)=qAa>m_oljwaM!HdXXfOrS=XZB7N>!;CKV(TV;ro9RCIcx|9Q z@oJ7yk_~($8w%%<9w^2)%zl+6^efEb3_KNrn5@&Zo#xnl^q18*wODeZr*p*i;O=oP z1oX8BcRk{Js-7R4ox!`r86Hu{S$dzWMDPLnSu^JL5{0^J8W zdge_r*rgB^yzpA8^Lm6jam#yoA1}Ycxq(@;beSl>7vgG2dpc6_69(bRKa9X-)iO@f z-7L8F|A_huuPFaF*msxiMmm>n5Tr|5I#s%*kp-4!0hN?)DFvikx{*$$yF=-aj=lT+ zoqO&*`zO3JpC|U28Bmu}jVn3AC`lkowd1=2E^cu4q0{b7S-=#h?^^reue*z!Ks8W0 ziG|Di%F6lpb9<$twX*Lnuy~kE9sX5Df0O6rr7VL=cZh_OHPq)|aK=fM&qq=B2Nk?E z>eTeBgMQO!7+!v`VRfKa53u(UX_a=a4w+*w(B{^Iz47eK17ecZK?X&Gmi&`L&s7`- zV5-Um(nJR`myu0X<6Q@@->>gi>+4f%j_4(en9jB8?;!7@3eLWHb{mYl7Gi61df~ih z{@hlOOUXiY7`hezb3>4S4>-XN8V^GolM44zE)>c)D8~mMm=0iKMBP^Dm$ItYS@*$f zpk24^_(f>J;GE1X-3Aug(|u3xmgFHnx$4&?MX{cGrl9{=)Mc?!<2}W3_-?xed}ii8 zG}YoGbfk9Zjiy(@AaQ}MQIuD#W7OFmuuykW)}R1MZ8BQRKB z{DG&ncp_^>KQ|dTd4O{R>Fix=y{Xd*D}N5_s(Wq+n#U-+skHoB)kUz*nGLmtCJKyM zeKkCU)@~}81EB#gqZcyDn~4cqkSQXUN~7XjPjP=*xF}+UJfj6KlhB5y{(I+tj$E9y-D-7DRO0ouH82Eu6vD55LezD8Wa(kx7>AjPc4iIT^Nx3xOez5 z5DWskE`5UME2o9YakVqhk~Zgs%`(u?Ft@9JCUjFZl*M^{u1L9iy=aYtSSGOV#?^0U zVeS!^PfyE!AoijILR64_@Y{yZ|KRHz!N}|~8t!K~1 zN}QvL$fPHY3#WR}ghCJfI{iNn(d^uAQx;e4Eer_Z#C*Y{mX^ek!dQqKQVFe4Yf#R~ ztskZo%}>hRaYS9jBsB3F^Y%*Q&B;9)lc0D^mQHOhyLf5e+^0q`k)xiGW%EuglIFN%I|Gdxu3xOuFe^N%bi%Y=Oh!HL(|U*gR;2Y$_ojHJ|gmyb7`>P+V@5B>;dZ-bicdP_TX8gSZn?Fo?o^t59AV{C=mq_+IPz|U$5(Y++RPL*bN;Pa6U#q(Ar6$3RAG)$>_sg`Ech{(m|9qu35M}jZW}leJ zKaiK`G8AO*SxSb=K3029kaN!(ndGV)Pp=wv_@m~RgCIxJa@Gd3A$jgNQ_O3JvJ;iZoUE0IejR!$FV#%2bHXwO%QR<9!%f$r9d&(>)!) zw-(1!o&UQ>#@h=0Q$vZxY~S^mg(o3*CN?T$X>=*1gjTjF z8y}>#E9nd$6q3FAtO-rqJT6~q7=Aza&=WX1OL2d7D4hD)HYa3e4vN71AD@W-^@|M# zmB&Epr_%TArE1e!nm88dl*KY(A*wK3IO%91T2-ayBq-@*`Ae_Iyze2_lj5+R^X=C+ zn5?@N^j1Y<9GUsMw`ayaZneWlwywU)BVVB5e#HKt9yuuB?XGNX2T&ZsODv=ycb6KZ zR;y=Vhrjds?5k}cMW3q>A^e`yAzNy+xk?-HMQ<*A^~+qKJl$;6QHyh~Zpg7KMwISa1J=$`Zj(|3@HZ&SwNd z9nlB6+a-WzKHI$X-yxNWMTGSl8g;R_|ooqM;@1Of5qvcIb@UNj-3~7e8 z+H|*&Am*>LXEhS#lu1qjNcSOhT}f+Gt9-E}&g}O`Ehs9=@2`eXO)x5nNDUsVabs*p zRl3gD_TSliC~yGX@azczFo+Y&#u9tUdhf@_N%3l-e0h6TUwc*8Zgz69%evS0HzI9m zGl{}TqtjBJ1>=V*Vj%0JHahUHV9lx_W9y<=GlkP`YQE=6ojJQvQWU82<*88Qt?p1$0kWb-047w_-mA) zPo3T7yAn6j&OB=qUZ)sMSLLTwkMVfp-$S{)0)*_w6rRZOT57|yn6ucHxq_2q;jso^ zV>3ja`#+aRT*zf!a(W?_J0><_H86v;^yaZMIy1cPJhZCXrZyaj)4l1#7-NlghEudl zJg)xpPs|DU{%TbK3OVJvY)G=zkQA91kt*0~W91qpC z3%dohz3sE%_7G)-HckpaHXWBN)U)B=qzoCdrB7y`>NbH8GGw?Xw!5%m#WMg`K4{!S zS^lJhD?@(`_k&#q4bCgI?%Q^sJL0)s>18MwS*zL7>(=ed2Hg1i@-{Wt{60o1*Y8Lu zVvznUp9`Ll&X0UOdveQU8ysC8>77(?&v$)`DGQ7-1v+RhjaEHy*{q-4n)gJRE;w&& z?N^GE+{oQe+R=-(ap^wAw|7e>N%qJh^(drdKQu<~lduk-|H-DSKwB*+s)@eVTx{d@ z89(-&tv})aYd=}iSEB0j`w;%@%;gzPEb=OcX-SD2Q+fC6BVYIOBZT5wPBP5C5aPIjD7{8Fm-HFmj>aSXF6Vh`#a*-|Odj1Hn* z3LAeSqWmuqMjDU}9a0*VJHhSHv->7)!)q02v4zTEbpb-P2&+*<%9;94YQqvvDu=uS z39jh8QG+ic^Tg#8{GYo^sFHHTs;UL)GFZ?@v%y|PnBWpMOksA)|~ucJwrlV#lGH48oksBUhQ-)r4JGFrtrKX z`@0aKEV)|^Y`HPd>%acZ@`pLvZj+X8(75#KKsoap2KoJ=gCYt0-YBDcc`VLK4P54{ z86xeQ@wSl#+L2-ImRP~v+z|Y!ldr{HS?L$mASRHNh}>&P)z46v?QXGCd94T16{B3> z+HN;HInWpO*#MBe^Sd3zidjOQ?9vD%T9hRo)i~YFTlqiy*hEB(8@J|$iPiqRO><0A=D6)88Sr^L9crhf_YsP?q#vgJKQJ0N8#HK~s78KiK zO>hzd_dRq%;bT%?ey$F4Sho%`>JJekk0Vi(slAjuT2k&1#!(1kCug@nhyGvfJuTEB zH`Uj(F&;8Q-=zFH@5*{3%hbA`D1oks9#|ZzEbAqL695*eic=09Tk0@nUUIRHsi7}& zImCE6B`UCY6VWn~zD`$;jwjVipW-o1DGO40Es!hPyG(GcNN5kH8({QGF17ynJD+4* zHUGso&xF`1v ze8WXvg!C=$|7bP(7I9o4uDhF8`|alCX)k5U%8);De}nqi(6ieJ0M!y$J%3y7S1A`L z(^0-Sgflu}$P9*(9^m??b*WsX3!=r3D z_Lu#4Q__#W#&0Kb`^N+e-GbJ`ZZTo(p70&qeEPO2t2C$^9Njv6oqAg9cq&e|XZz|t=-$IibPZr_&{X6vP+RZX)?)_fe zPYOLKAg^2ab|>vk5gewUp8d5vUsIaRHe6|s-Cu_RE=EAj@R~UXbT=hLzdW%%01;BI ztl<&E2NH(gH@$#<=D0r}r$PoGLiK&GyviYUmOsPozwR;I7YHWCU?c7X=6fiYwu*G5 z=aevJhexGtlRljr!2kzG5~7nhufeAbuP6J}7%#qvm}PZvZ!^9{SjsBi_0N0D*hq7+ zwcE8f(vQg75Hd_){e#zlU)Sy+dt+k_H*{$zE809{MT}{on@+1gPg66k1uv*hT`AY> zK_+GY5KsWPsYV|c`~Ua(w%Io z!Ae-k-UItFx?i%H?j4@l{UPLPDssAob;kP@q%!;cw>_*UQCT0QEd3G>RXR&|b4#3b z0Zexb4vhHcZLEdUc)g6tnK9Q+d%j-fIo{u=@ygM)W@lXPph z#HG-FjhcjprGu6SCy7J=0o4A*H@|5~YA0?2zdbdi{YAXO-!}~Kc3J@>IkI;zQ*mXF z2!r3E=N&9-20EP?&6@?RWXaT988W+WnQQz_9|NTGAb(H_zb zvzXbq3w5rC)l-^R zEy+n4e&Ja?lYtdjcFPy(hMGF}QR!(O?L(LA9FgjwnC~|VS?W5Tl#EV4^Ojkk4_=fQee^q1kU)uWcv z+QGg6Vx(Blczu#1{wS=|KXjk$=YkjoGpGFTp|-|p@`{)4SZHKmhSk;;Mu>}@xN21` z!!-U)LAQO}xuoOx8I6C7f#bk%`ACfY=w&bjtKa_izH-pUJ}SIbj=}qT{$024PWJ4F zE(s*qf21S9zQ%yU{|&+R$0nHOE5f|FE87_l48hXH%q$Cjjv}hx5YCCFzk_mqBBQZ( zffP84^ZYhK_kUL~AvWbW_iUeC?P!%Qq)t?;*4_AMi#AW;v8`ZM-CaW}Oi3i(BEhQn zMS1Y}=i`Tw4>ze-<$E33Q{-yODE^=6N3UJ7@4cILsHnVrDq_}=*DkYQU{F+mt9Dct zITMVCXIJ=#=e>`9Bo*>;GnN+VY&=aMrnxy_ZYb+Cd@!d3ER1MQq7Q}w5*7JoXI5Vy zI2(4`bj0sV7NR;i_xX0ei8yNCddLX@*n@~shzSNwgd~h@c+?C({f?H`JbQH&IxeXEn)+Yq5t&L3^2vVta5DRP z_wh=^(8%smXuG?rHg4W)m8{{X_^5g)&<`=c8LNz=11WW;&^VL8~Pir7DoT*qjpeOI8WH&&H+rrEg?rjQ&Zg ztYm$yr=jPK`HBoPRX|R-mkWb>!U}JDT{@51Umr%<(aL1Xu^*-gx<5kUm9H>q3SU-^ z_t6n{WrxVIp>%a@N?NSYrhuB3#A=~1zDJoM^(!*`jiO%&wh=k7aI#_vt+EjOCEB)^ zY-FsQ1zO2}_FNrvYqY3C%dcqX)p9;|Q*{M-XgH%!L?XR4Rh%(9BJPg7qxUFvBvLq! z@}CF8P**0`zgK0hZ;5*ZS9FD*E68Wn#lV!(1Vk{YYf}hiwg&2?C6*HvnF=A&jFv)m zLMw~N43tV_x>Iibo7(j1xyC3ytw(pQenN6-(YhluwT3b^r`x$Gg#Y-WIMN+;uuTz6 z9b8$9(}G{vK8_D{27;PtAM6KA!SzWC^61YT#*S{PbVDhP-@7hjwIL6lu`1)6KkYGS zE`z~Y&B}VrW)_;{tP5Ytk@7`EXs1)hN3S9}j~AR0>7So%CrO*$72io#GPfcV7294e zh&Q~B68Fa6tS1t}c)328bCM-21*tP!MAXgB;{w_t@3RAs`d6hm__2)BiRq5{LI}LO zRtT#2){qs2mf{fKL+b^7G=JNHsuBmJ?fPJrCPhjz-n zlx*_rAU{6$_95nY!E&SMcY6nPqBgJTC5~+PhIL4_g&EJEhyV00h+no>?gy3Gs#MVe zq7GbR%&N>ZB`qPZ%P5fbFhgVj*zcJ#Ugs__Suvd!%1Uy( zMr6PY(q#2P#k2WcK%O$enW;||K;z%di{9g1w%&%NKmYPK;yBKfiWMR|3RRj+nSgJma?iJ68D+b$E|f4VbtA2pPx=@cf}^#3VKIl zLlA|TFR6;7J>j};hkE0F3zerr5)EZY8ZIaJA#YKR{5o(D^oXQu2w|7kV2j3Q-y`rk zDNrehZMYi|yW5E$w72(KtNxXkEvjwt7Wc$uif*WIgWoq_W;vi&XgST__*DFi@&03A ze(KGFfKy?}K;uSRsV){d>t~R?-)|$bBi*O5fRV#POYM0*&gay8hEqF11AAYKAzs2K zgI>L@NfjuaMb-xlZcvujqnCv?eTGvgSBhe>LPrQQpo?rTw+rITc%NZEV90tpwuvXr z_`Wyd=#1pzZhP+YkX=|-|IA0ut0)Z$ti084PkW|s8!eC0;{^En@9%oQ9U!+a6C7M5 zIvE~GJB=tootf8F7bbg0#_B_h>pmfal^qSHl`~LgSFy{1?b_`>}1dxWgNq=5ih zmXx=Qtm%UDHGl#rDoshAT_O~rJFsYIpEOv+Ek5`DyC*mzNxk%ZrOt_3yL@!9i&g76 z9yR*QK@&@h4EvG{_o%{QGK#K?!dTZ>Kg^ogBl({jv}9D=S9Ru1baT7XP4vgn_sqW) z<&(wwd`&7wQeKvNH?dN+v)Y;|f~7V7!+o1_Xw%#6sy`A*!t>%`?XB;U3cJG>qxXhB zL}im5nuhYVerNBuPQub$JJMHGn<*gd*U>LZK)M^vzVt5i3>UG->H3y$n#oKk>&4l? zZgw3C9OL7bRv2O=!WO)|61Ue!#}s~%Jde+DV?ArM2~)aD30*N%^rW&l`OTL58)1zg zNoJ#c9gbf4d~Z$R@)l`y&^qr2;3t^$ctiLTdnFEVf+4MaGnNOkfaD&Thln!Kc?M?g zZE0*vry&|)b~d4w4E%Z|Q;Pxqe4o7F^nL6fb?YKAnBdSD%r5-dRjYT?8NSTt)wVhz z|JhX?pv#<=`HMO?n=1lNM1*`nSVi7 z(cDbK!q#@xesl?E2ys1?kQwkR72n}t{}K zN&A~D5QQb_-(Rj9dy=qPEZ~T?tKa_tdvvI0d&RUeMQrS~_lXbjO&}YR2K_?vr`BMQ zcmw{0L?s)@+J8>}D2pvz_kzBhuX4c4UW4FH;pUJ1$oo+Ms8SKN8y}GpmOxbN9r8@b zc!v51 zc_UH+8Tyw{MZ#!_hMDM-T3*`x8jk*VysgV44%<>W zzl(r2*>B66NaYbayktnBj(Zt~!cpQe^P9!O@QW&37L4W*Ls)P6H6)A&=ZV5TOE6KI zi!r(3Uq1JCBz6{yt=Vs|Ut$!$#O`4hTSU@sr*20nwPS66ua- z{AOgsWZ03ivqlG7RISod#9;qL#*y(#%w`wi{LLR<;aTq~BCDTyd_|d?A$=S3)Om16 z`87m_jQb4fZ;KOS6S&d2m6Xn}Q7QxGi=SoAlo=PhX1jPN|EE;gXfL>`YTrVOTQ=9b zEHyt!*1IB;u;?A+a|;?r0L{2Z(x&mAbflTHkA{bRc-on~chg3`#emF^NIP-9*cBLf zx}#XIAzr`vcq3g-?`&9`UQmG5fhO&$)>4>E?KD%L^S0lBuZ{Xv?1|Uw+u2u%&NTla z*Tc6rU91~(7B75PwNwe$vfbAtD%BD#%Y0-ep)L3vqvyQHfB(k7S#bug+6};e=x3)L zw2=Bwq_H+|{XHhnX{3pB&PWZbavBsXBmY}(m+DIR`b1JN7^z7_V*k)ucwX7x$mN16 z-_SzSR{gXsF*Yk}K=0#_(K%FDD}7Wsw=}x7$Z19>6z2V*;^!sAf_rXy@Y((>xq#DH za8pOqvEkHD18E%$fFX5z^gQE0tZ5I|v$A2l6I)$(KN+_D)Ki&AVys$r=Uh!vqs+F_ zAk1#!GGsyb_el#&6}#RvE3MCD_PK_RH@R^mBkb;(%@+g{kSDmas9vuuDDV9$qoeh~ z5GM6N+!c8P-mbcNa4v}X{bIXWcUPF>EX96%RJDPf(k{8Gp5v$ANXl(`Yo@@0y`Z4c zEY=%;J>3D;9n=h#pU9{D)i1$H5+^b+v{usn4Z1`T?oFnylmHVjes!;&tm2 zfoGNT^prB}LU!$T9DwZ1WMwuhch6$BHK;g0B%p=ahYTIyXmk4AOwE+Erg#&Oxum;#eYsYwXvn+HWLx+p5Ps9o~ge0h|)jm%Mrlh3#Jq`NpMSS2Ob z5SzpfYGrz5pT3_(xvU{@z@xlF2SYgw7ZsKhe!Cn_# z;a$ovs@=$r9_USs@Xn_=6~xFw;Rs|DoIuGyrHW9-L5z-2VnxN`zbTS;h|5RT{x)Uj zft3(B-M>Yjs6jAgf=+PCQ$&vk#56C}plGFCup)4;joQK}HH`AH6Z0<-ReC6jX?QMg z8qyvu&df(kL^>Xcv2qFOTH53gW7&+G=pJKf`gZ^kZoz z;a=AG2T}WG)ib0l=U(xbY0|Rr&F*4bu@576#YV*JVR!UAC8lv+D3@#Fk_;m@%xL zC9^MC;8`)}t6QAzT<|JOb9!C>8~yJ$jq>@dZ0|P6{!ZCzrbMr(iCJMXVJ-ijvD6E5 z?d);;I&|qSkxzfonCaj~ityT~?9T>!E;}(mbNyRlz`uGA-vo--!0~7ho%4gSJiV!7 zBbhPSiPF$XSjhZ3D|<6;yjwCYt;<8<)ReH;akh3VWhMWld0loc;->8q#07%a+kSrI zbXXkqG#&a~fBqZ&AV_Cpk_?^}06GL;%uwmR3c_|!QB>fKJUOB)T-?Lf(d7@#W9A5$ z{j0*gTW+OpsiT{UxI3r{F8BbL;VPk3vVHV*t093Bw1lP8^V?#AmKA%y+>9`pIK;h& z@#3wOJ+u=)R*H|qIHa|EawUFSqp(#&bJfO<*}dvaQGt_YYJI7#wFp&qgm)Hkqz~s<@O6cEBKvoBR`1`~0Kqa< z`hQazXIlM*)lf@l@`!P?5+zI3C{u2#h8QKVWGkc^@ZDNH6fGFY@Q;@Uw|R&DCULVm z#Gt&{{}|}0sG{1V>v6?`{=Ot_=CHlBFk<>kH6NO?fTcMzskTGN@@A%RkVkwDDqLV@ zlj&UiBCD&Ge4E5mj__0p_WZ!)FA{ABcVn-E<9EMwk|gg_xKV#|Obc+fdT)L$ zeQ;bV9kk~EF^!l5R(3~ElrNz0K&s=f6?*X(1?d6M74)&dwD!%-FI|C+*j~C`RA-*s zQiW3D@^IX8Vv($1=>G)GId1dNTf zp90eSe6W>4uSx%~DRyT@txsm0u6gMapA_@Z2mDiEgYAb1;E15fP5GJutNo0&98|5{$<@1Hqe5 zoBl|BQ54N_HDAly8ym_keayDOzBbW!xYtLrBDTw|g_y1x!>%?XGF4bzZn46CUmGl*pL!`XmAjnaez`O4uc{=RzzW&uCpc5K|ZC$0ShR7p77xrNjD_5U`h(qTX04! z%Ic=|q+DGY{{EXlREByyQPN69cgsdoN66!|Y_Z+e89gVrZyWfDvQ8Q04^M|4IyF|B zfpo9bD-uV|aQ#RROHMY))?ch)VD|chL@&7K-#J$o*sG;*Ize0?S1zAMGf;9z!>%_r zIV6sfJJagxMZEDE~$6*gZ@L z2i20Mt-iG_Q%K?5LXCjZb29cV_5tgoBnb) zE&zWTge6;P>;{W`ImRnc|Q=WY?O2Us+SyBtPm${l^9J z85O=>_t3^1IUfGC<-|Wcs6a_>h^0iwOg{M3&URR;qhOK2$6u+0Sh=3vD7~Alr=v{c zXl1d~U%*a36~=;kMd||UD(tvFZl$j;PG$nGqGvI6(JbiIZaFxoZ%q4PcTyeCJ<}T8 zNh?hCX`@^srh_7w_^?~QvjGYV`1p+&5;aIBzSRO}j6}=ErUl-uOTE(68Sie>y>Vj( zoR4h#PA*_|Buc2Kr4#u^}Q;*b!@FiTIx7!k zp-SM-@~s@UO8h8l%d+ds@u%%2UsVa?_?$ig7ic6qvZ{oFMNPmj>dbH4{OM4&;;;7LlB%|Cx% zRs-wK=9!JuCflPx0h~`=Lhf#=I@(9*Vi&yDTu7>&O7Qw~A>Xc%y|t3h>r@6jy98?~ z|3RmY^*`tscp1g$yz0#oA|FGnvG3Y^V?^9F zJor;^Woh4}RMRg0VLM_~%`e(ft2cDj(aOQ+@-cNg?yb@7by#=2duUg?{v-X+im?G^ zBH(TghLiHrMkC`+iaGRVPeW*eqo)`_#ZviPbIGLgZ56mqAF?~4t+|P{ZOMe}49p1x zynU-E6DarG^@(V?V0-jVuux%XP7l|h)}d*A#T#AE1IpF!BL*MZF0&fn8202|bYDDP z2P3XA$|>Z@C&mdHcF0lTgH8I?dZj&oHGp|(A0`C%AWzWp@ zh4d!&mL>koIm{7w&nO7zT(By{w*K5(yPDl4YeS%_Er_v%!cHD&V2Tc-{G9ehLAF~KtDld~{Hq6(zH+H51;e#y-^$obfGuAnhyRmC^*nCxh2 z4YG8`xvm4=?`BAt@Oe*6CHe8}b!Cx_P-T4h+B zC>eZ5C2fd(ET1@P#m#H8)xr(YZbL;GJ|>1$76Ah;rY3(zC1N1fV9=!mP*5DMzmbjg z@K{6?m@MnBwaB)>WGLVePg@nZJ z3QT}*UBM#k=jzDwzJmLof zzpCQ(L211x?SeO(jtm;C`5HvfSAc;KJG_gBO1YxdB&q5G4>**QNl7cQL*d)H^nyHZ zlMu5g7O;nLE6G;@c{*Bqs!$RAO(2Ks{qb7AP%Zg(>0sns(yd%ljD*lW7#e=9wtTQ^ z|2a34TXYITFI3B^Mt|qwJNCFvM?jYORb`Td|AQIJ(2wivAN(WY1(U-BWrKR}cYXKL zA1nUfXY-DcCdt|3VYk|8NNmyE*V~vSAqig8;&eQdn%TnXYiFq)KkOj9*ZKcns;d6x z>5Ec~mTH&(@@wiSJ!VaS?w3=mqYG+D=+1@pOnFem<*{Kz0xJB%0C*df?1W_L$kDy% zwQxN<$7t4q;Vvf&Y6-|bv}#`G;hCNww?!>HBhAw?$SU!?8yW(pjsSMe~br< zP8`~f^X4jQRw2L7`U%DQyxKn@AG&eST4Z~%R9XbKE)RSjPTPS7>e^xH`}O&Yz7#yw zPeyqmEAg;z>eIFD?jsa`*H&O90XSvVepeyiorzuW+rZcj| z2m6)x#WqwQ)Kx=M0%oYjp^kiRKydL8JA=TEvRJ$GuflG*5YmhwDfLOV?YQ@(+9yDlN;s8QzM~RHq*}0P$cw zjnfho{XH4teN}H{bH!em!YL}n*nLft9W&pWmYc4=;T;BjvXwHklT|)FS>|k( zH~vZ(+7OH^@m+=5pb8P(iW6Bgo~!mv|NQMm^1odWpp69eSAe`a(TMp6a`2bgEWdi} zr-ZwQqchuF)V$H@NiNm|gQIZ1&6%@ znaN6(4tFwh9~%lTif3UZKh7JwB~+yUMP3l5EGYorbVpt8CU0XrPS5byCG1#N#ypD} ztZv_&N?IwL7#*~`o`q9#_A`2SrTu)i-P~*s5so?94d5n&N^Q_hK)qupzfWT-#Oe!J z@(I*#PxQ}{pF2#bq;J@(c2Cr7>K-1;K$27@!*;mqu}ruSounfvoDCe(>(T*belEm7 zP`S$ABi^|N&}*iD_D(;z*||6JmeVW>qudx0BQW!3fkWj();*F!l0$Y?udAYwjIH7XBG&j_ytDU1ztJXLcja`VGfN8SiLO?GBi;x&fyA+4oT>s86X$dO zrH&t9>!vZmqGI$SBi#;7=HR-E=(5nCTcInCU?-tu=7-v^ocq2)a0ssz&enG}_ryTow+DU)_3|>t{9faAXIbWV?YtdxlNOCkv|VRmI?(6qvwFOn>wJ>_ zP5xPSWL?^&oCRoe_^-$M#E3xGjwp3&R5ac>tF}~2CyJou*?56fc97Uhi)F^!kZ|Gq zy={6X1O(%^Ai-}={Iy(xEb$82cJTVnNtQ}i|<;kdps(Yt?cn>XSh^6{X8jE}r`e2CRJ@ zcI)^eHC$KB_mJ|m?F@6o;mSap(93;`XN75yE8xRwB@nx) z7d}T6o}^|P69cOnz`J@-al%^$bXmf6Go75G4>c1?{~;(b%7|-r6Q76(xbO9c%50^v z6I3*1N@1*q%lypPJ`9c9JXycDAQ7t~L=l}TMuU?GGAQs2Ar4@aYUfB(f7Yh-#K(Xl zm2}7hBfEut_eS@S)L*_GmT?e_>>6@Sdf+F(`qcO&SSpQLz1N-LY!;EW#UggOTDkvs_GAn9HWLHyONr7>hpe-*1f5k)+Kh z?wY$?I7ztbL?{V_pJu>y_ag7fPk=DgCuBMqA?vp;-Gw!swN+eeSH7alq*02UFlL@) zwsr4|pEE!?=fr}V1e{6}b|tZ`dNAF+sDOROs;KWcBmpHay!E>r1aR~(Q3VfVyEML0 zFDbIzPT9hpt}EZJSsF!JXHWyqvU-Ql_qviEM}?$w0jxx>EFSc;8Y8<|PSKRCNU|(k z=_JNA!OAjXqW4l~X{R!(0TIi;!IVmeww(u{7YwJXzA}eMbVd2=ITUt~R|p9&NxEN& zhV`mgI1>h(m7M*KB(pVl{P4|8P$xNzZv~`Fu_1BmI?grGBgCN^N!tca&iboqy7)q9 z-M80xU$0C~@Ryu(PPolyxKjeyb3%49w$DV@%T}uxZH@L&Ij0V~xA@3X!lWvk9$0|d zIZ+dZmA2coJCPdjw&Rtf>M@E#<;-g6YhMCN;Nce;*nhDk1#*+Y{NQr6pL1eXI!$-E zq6+x%g7T)(WrvCN#pX)CS?j1pP76|hMNZ)yoMuiDKyP_Vr!Q=rqoJKVY~aSYrg0NU zV5B6i+`y>+gy=|osBGwkxhs)=u{KUQjeFmm_!M-#mFo`E)OA05@f||Uq7(R)w49lf*L6($53q}5l|4MQ`%w_Dk_a* zaIJ#|ga^0@=S*X2UlVP$7bb`)m$0c{5U*yI7I)MN?yg$ zUL&M^_O7Ey;5G?xA6+$L66FyBSpjFHdH}1oFnTGxXqharK<{!b z$p)TAj97-=gahR_E?}B;YK-bvGZ@Rw_=1=U+GL@hN{Ketvygpgvvqq^(#aX8<}rv3 z?Z$627)-hq7r6|A>oda+S4M*P&Q=bY18#_TB92o%rNfX7bz#cv+tW&+i7>Vy(Xi$2 zQfFOZ=O28U?1^{-d)`Zx@>wimH$E82hl=HzJx@gb><->`XgBJ$cm7y_&;w!a8TSn| zv63Zdn|Ct@g!_yVE}__G(#s4#T4tR0xI%gAq7zP%w7rI z-~V3_{iqVFDpty)BTC_Oy3f2|9sSb4C6u^*B9hZIH~Ry9-ON+IqwKU^#V)~wm8pI0 zvf`^mKh}+=S~TQAr8f;6pY)5>5OMqdlA3`CH=M8IX0ysX@Nx#jBG*xdMzu!MGILWM ze1mvGj-Mcaa3@IUHX9X=>}io^gVu)NE)rSB!+17C%0oe?*f?)P@nufAmip60=orgBR;fl=e~ z{w1q?cm0B%ubf^d2J}hOHyBx3Ppz0Cj^T3h4!cl>8MLazt0%vjF%Z()aw6x#EXnM^ zfX2s>uM+~8`q0(2<1i+xoF(;w<2SE z0$@rmQrXKOpUuf}Gv6i{A7Xyc zRn-snXRF_DF2SrdNHq04+iu%(Y6&!A)O>~C;fZm&Q2Ib#{pLWcv>?MqO$WvQXZB*N z+i}$1SKfZpUa6mt^4!YmC04vfUOVmc1c-8bZMRn ztTcW~Bz_o;Okz%pNJun}h0+)s4;IZxj$lNckDNOxw!#0D7^=44FBl+~Uxmu5r%BqM z4Hg}LsxUQpkAKASwM?x3(Kw7?1LdtGCf>R~TulT&LEi`ert1>m&rK?9GmpJ4@i}rC z!chI_Pd7>$FtIdB*jP3eLmH%j8~+ymiEHS`CxnLct1XwCo8$Sn33VUtR*78R;WGjX z=<%g%{C@h+W`mm6-I>sUYRe1=Yj5|ddJr3ET@w|sMJ>AH+(+cGED zJ89-YfYR3eMqa?<=^zh2b}tXUP2hK-Sjt|{}O>)rJmCxOr% zj652kNv_9oS54QsY2@InazBagk$#XoQN~(|>ArdHH}tP?y^*_Nu#MTPo$j57oOwqG z7Jz)j!4aOj5vn!@_aJ-^X@C)}v@|$7tO%Z@MyE_^0S`VV7*FDqI6khDMgJTbeCa#z zsam=DCCDd(88A5(DP=voXgWwY-laq&2}igXE3rCn?>U;X9O23i1k%ir7#BlguYG1S z`*RZguLLLhRyS9BjN;VbFc-9Hk-q_GpmiSE%gMK+f&}x*4_E2&{~(kCJoY4OM5DNQ z*>%kKn;)1|WV2vh-i;^o_sExPlD5mf?EK3K#BiOmjjwG9#{h24=3A710xvt67QVM5 z$O(6o+4Q|$6=O%0MUNK*&d@r3kb+LJ<_T_4dOdo>;(M-tq>MnlAH|k7g*NzP=^(M| zuGh_}odpFlPs9b-S0o)E=;pRcbu_s^Vlda(duNw!`wLp#S00IF6ZH=aM11LPY?WFZNy*wzI9rBsE9r^{~E?gg$@W8P6 z$q;q<151B(B5*ZB#5ESAdz;?*D`Q}*jnDv~H37x;%x3?X4(8$6< z>SgejY7onv)WXF7A?iE)ss7*hU*|YxSqC99&mk)zWseZYNk;ZwIfb$^k8#kj$FXG< z$}A%yd&NPN?8HeprDH@^#`(RxKi|jW_ZOV=c;5HxzOVbbulv3O!Tvo4ezJtES<5TW z_RICXTwWUfwj$idR~9_MTcdi=7nKt9byae^4L4u&`vv=9mCF?f@t)shWsTR`!(&Zj zIUz5$Uo5H9WN6$LM-&{1>cuyfr5|C!OoMIFC&?nnwKe*1lBNLDOnh+qO_dR0GwO!U zvK^2~>5v%-#=tKt2;|zxbU-cv!i#(ft|}+ysD1mVuxB4~O%4RNB25+OR*tcdRE1|% z@2pw8X#|OS^mivKo|I~bhFXYS05*TxWyAzVU?l!%40~u$bw-=WNAB6N6-TV>GN6E_ z@CaRs8pyLPa9EtAcmpiG0l`pi!&OgJRCM;f z#m@PFM~7(*y@f7GH7&pq&Iv)Cq`&(nBj5wL#cFp4c9xVx;7OmFv#9ZU96hQ)E2B2gAoLtuVIR+;cE=$}h}{PaY@xIZeSz2A z7P*7YA;rGg>v25Ep4-3`vpcWjxTEoA@j3W?j>fgOVK$aAhBidu7V{a>-AwzbfK!*Y`SZ&7)skesQlbf(_PkB1J zqFj<$vXqk!&GtyO+C*!&rYPaxiQ0DGIBGo;|M>!Yyv&UuimpvD^zrO5CxKQ*6HA)B z8T))Rl2tn@HgsP8afjl3@uI)B3iG)xzQg?=$?A@8=5oj`tW;c%k>@YIaooVt1ie1F zg$k!B{77uk+ui=ALbZEzj-H~RPp(7m8nz}-cZcZYiQaEi7#iU9V*#ApeaUi3Ab0o* zJpF~D)zF07j~taV;wgEYT(ubG+lGGjdQ2adI+i~85IqPHQTNO)^e?IZj`{omsj3^z zIIZs~(&)$hb?-r2NX?)5X9*hd!M%#TsGHEvcH*-ZZX?_yrR_>Yt@xkj2w`sK4B%Uz zP|v2lY7rxeRkY%7J}g~E0Jw5J7#BPi2>#|dDg$HDph?g8_kk3|nfe)?reL46zG$1W-}%8e+1aLPL5SXSWoIc#;Fo|eZ97iG z`+%yL9zf*gg>uU6~9t&{y3M@pjySl*(>*xr>RN9Vt1^Kd;Mn)ZwEVfNcG5NWG*Y^IqXCMm=C zANiA)87rnN{s|}!x}IaDDmhoAsTLD9FDJnMb0d8TvpG}M<6>Rr>6EpDxQ~cM6FVkZ zV(cFILAV+a1hubRkX$t%CN~X|4oBBR0U`%|3(lV*|&x= z5}(Yf=uK|itG)qJ7JR+Y0*kOLBt@;glFeYh6=qaG{eak_LF2>icqFbNU}o^$rk1uw z9@iZRHm+7XOuNppQRbe_VGss2#~w$Vp{7~10W-4&f>ySu#r5)=OG)RNqjx0BC#vp2 zK5gH2r}h>Va)bkTpavP<=Wyd5Q)_&EUqKeESrn0h_;J&B@{Qh&Mo-RNrt7sK-F`{6 z@fD_-?5*rhL74e4SJ``&7vk#3*bl}qK-5h3)zQiP_aGiFK^d+?gSIIhH$M)Z^~VKm zPgVQ*0T)Po*Gq}^+rvJY{2<{($uD?YY6(E2I9CQR&Tndy1&{vlwT@ax{HkisT+E2! z9DY~%@P@3b^ey7rKYe{3;WJVCGCsds4j|Y3bxis{^QE=wceqarWNF&6_;9Y{Oe7Z{ zcO zt@$uY8!4F7EV@jumSrkK_b)hNx2jMVmvPE4)0zYhKdm@1A_@%n*FlI~WRy@jzo40| zdxZAHEvd4ss(Jg2WO=D8biH%~mS0~ro(JUtG7Ow21@ckxYzEdFxxirP{g0cf={Hx_ zzX?+%BzB!R?lo&^*T7)r(vF>!9GjLh=M;S%vt#W4!USeL#Kj(U3_q0J8_q%8<=)rW zmub_Ape?!-j3FqY^#d8jipDb;_?t`20CZp)^PET4xJTS)$g@Wd#A%`gEC|{sw}bN&GXJro8APO z&y2GEpo416r5$}N=u%y=x6eLxd@oTQEzH$uKD=&Zc~e=<_Fyff}x*P7|p4ExZ1+-yYxdBM|2-x4mqXWOvoeZ4YEQ zw9~w(j4z{!^jK55T+>$=x<%Lv+Aj#cpc*n6OZm{eoYS@zc~TJEHGj>|P?+f#H_j>f z%Hd6_enRCXKc0YBAP!O;CP(GHP< zI8S=ZYv4_3$?{YtRx5b(o#3|PyKk{nl%0| zLG^Vc^A|Y<%s-y2z|1PS-vmEje(#M%%vNi4!@~xyte@$Kj()CUoKmP-!}4OAHohfI z$mlfke3D3%67yw^-@g=kZBcy$AbA;FA4BO(b9uf?i>r}Wbrm|&3g5Q;scc!F1ZK?g`CYDnhoWyL*&zgf1qAjg+;4JxLt_-+5sr8V7tSZgC4WIP5ck^jd<+g-r$utI=?beSg})sZ>n?bpp=yKmen}ou4Cs>> z?}36Qu;;pg0K91?&q1Y~8mfc&3~(D4RJKiSZm+#t2v@MNP<8Qm6WNlsX=anz5FSl; z=QR+Bp>`(AX78NiXSkkOv)Z<;6e3xjmd@A}rszjb-QRN@Y-M&;Q+pLNG0yZWmv00E zdD6B;mOFbc`D3}Z-bk0i|M}he$QeRJWPQaI^DPH@18AMeRw4ReNV?<5{>CWeR|Ec` z$4aUrEw-#P#W6cx>7)SoG!O@SG@piOy}j{p}!=6q<++aPzz$51^LSDgHUxj zfJSJzS2JZGN4x>L={IJ=?I>LzfE3MW{;X!9A@%CcZYHPiex6mb!E+NDobdWbRSpL!w2lgL!M30L`^{YRCYmzuPXARtt)wiylrCY$rGm?JqQjyFaw3gn z-29bII|AG8SHWlZpZM-AZeJ$3V{g>ym}OTC+MZW=X7QjRCl%++#e{FesDIe8qcOqMEcxGiWUAeojVC6TXM?P$Xr z_5RNzT|F{smW)mvvgNrlz&{zQZMOYw)#Fd((`-P!(` z?1&?|BNa$j%IlajmcpV`H36nu_1C63-_zQh00^APkd8&5k510#W72kBqAEse0fk~A zocvJsPHqv$RIV28E!hdkv%HGhA=Yb0A)2GAd-YQ7bXtv^-%M^je>}v>FV#0Jqquq> z?-F|HEc!l|PSlbYy4}L?Eaw?m_OtT`0&tcW>RZ~#vA>lcMp1?mo9E429peT(I^ey<`uXAcZvx!n?Knsn5j? zx0ax`Gc4+(v4v+()C(be44g&%4mK*r{T$&JhReTJjJ$MrgL+tqvR*d{Q+Y_2+5fKnuZJY6POks;(N900?rG+^gFu1zEOaeBqUP%1H}5$N_RpAT){w09B_j%wDI(p=0~j!m`f1?g z5mK~Rh#o{o{uSRvBu>_ilJMZ&GW|0C?Bzh|QCcFS#y5%JWNDGj!cmrsQVpE5Jk`|l zqwW$qj#Y7*p`Z?A&$K338HRHUy^RZA!C{jRQ=5%m6e@miVGosSLYw8xoc|X_y@?Op z-5IxLVBc$CuZ6)V%KEDI!;J;d3@I8JmuC_@gDz zQA5cZNo4I1Qy zLNL8Q40TTn{NKo8(0r;Ry0M-$x?(jC_a;mkJQ`PSRR@9O@_prDdwvg(?;Cd)4|4^D zZddA5d#ibFWQ-2}G`qcV!SHJftAwo_r_8$=jJ7mE$(p-^`E_-4_4tvApD)he2o7UelUB*r4NI{G@RQ?_PsY09VK^^Q)(Ddx&?>8rWh4XZf2_9`&1v6w8Q#dBH}r zMkf)s@%>QW9Txu5cG2q!OBM`w_|JvqNeIeXs`ShXjXGllV~14OjL#v)v{LHm3^J{D z1Ig{!p0-0OpwZ}U1Xkndj4ESTz%%Z*m4SDM(~_RViIO+jM_KwwM`--_{S;GWljYBG zn2deei`|U??>6_`^MXx_y24-#V@(&CcDIpcyZ*}G7UOxV-vsM8cHG90@_}{S+0N~{ z0)UwMNFV-_q)s_-p!_O4xu>TbUwRH+?aHeCVumB?KOta23F4}z$x`nddB>UipL7xiY_9(72Tw*+}@v-X0VX@6n;c`Gj2 zR}yCzUCJz}I?7kh#54ixeQJwg!CnP6*;cUv&6!-kTrGAlc?`(aaq7^eEM|lX4s7YK z2EV}9cOLGttRTBKYJ;nF9O6A|kXIeBdr~vGqU9+s9sKi?8ya`6VS=vw3Oj=eN2v-g zqAZx#SNY_3O8@k>*>p2tS6-ysOe?)9*7eBe6*b559eCSwb$+9|r13i8(1M!Bh}BmSw81;|zpw;21a@K45y&IuygjZF8CT`2u`4-ID8r$zBQ?q(9YS_Rx=uq2Q5&4nJEa^p%GG)=M}I{)2)oxmjpXy!e*MSLm=x}c)~OsjHoo< z*jDKizOk?tP2l@0TS^`wmN|E6)PVNRjInj{bCWH88w0rQ2~xoSOc3}6ze^rLHU>!p zflhqL>i!nL_RV`%RFhGvUEjLr-8OBjH8tKW_@7n_9#XO8$Q=vefSPxkm8Q$nU!S;N zffQv(I=7Dp%FqL`+;fqD5&lEKjuhN=-=%t~JVuA5T)^H(tyJWN_=28z_Xo$GeA8aV z2dS}}kOS3`9P$$o=Y5UUuH(5x+hIyS67j<>7~^&32Z2!isD>2=tZ3{$+#sqpP3gOq zD(4x*F~;}{g!xQq{BsM_hJ8|V<9|CVojGnd-LlUb_c)}od^^uBV#FHukaf|@W*0_( zoar^tvAr9u0Q+tupy%deo_$PAjUjFtj~OXdvd-y z@!b;ZV~)m^7nwHGvTus}$COy*T3(3m#Y^yP?WJrsZFFunZI=j*8$phIjI~Y-+EN!u$~Yo*{87H-vQD5>YdCMH zWd6HXuC4@;n=nV3us7bWfaoI5ZhTU18-EijRt`oZH9}DStA-@`j?o1%?xFxDg&c)v z9%}Kpg1u6GnoZKB)pLNeX`e!5sYs=T@ZYAm?ih@T4X?#SN}|{fxO0Kq(=1p7LR3BB ziVbP7?EaOk7WV6PVlNyR(vvj)l+sSNm1-+1GrvBQM%d>;ytGn>lvTZQ#nobADf0^r z)LyybvmJbE``D-t5X=~nac)!o0&S@^2X{H))Bk>pDdt8duc6pO@xxzn-ZgwN2@1Ok+f_}-1fPuP$|UW zS*Mrk zl}f%?Rda$Y zzH|~kELx~@T^5XLd(2mR(+F=p1!Nd48F7P3FG9N8y@fQS2bhcuiU@Z6%gb*}Ap1*c zhV5Pw$m=ga#?Cb{*kV3b;C#eKiRtvq#Ir0sE;h}=IpCHbZHb1@iWO054eVk-%g8sd zRCRv(Pq}i{D2xe<$ekkJ!T%$>q-)oRR?m03U-(W9C3C_J>)$~^5)uw5MC>=_T@&SO zz7mk^%YeZx+*!-BEXjE1o!%^Oj!zt@v{^>#B>vPpN=f&!1|SfM3B z9ASdHkpto$gu_}(jcO?R{z?ML}H2flN8jHLr~DU#K-YBSGHUCNZ$-|**9 z#^x(Xo8I0(xU-iO+qpJ#o~HC_7%WO(yFb#!cUqy&`nP{k6DMRy9E{4b9RU|JqZE1P zK$tVPPe=c`8V2mhc$hJ67q`1G;3deFUlZr&f9c)yD}cnA8HmRofIh`-!|M^=!M?xf zy6E}Z-)Lqiu>2_5RaYk8#+|QJ9~)^`d?&#y_Pb{UxISH)T2keWoXXxHBN)5dPq;dn zHqOdi(l1-=-+nOt(|W^{U1Tx5;+xg4p?MFcnl+$o?i;Qa_Y(2gjz$3wxNAxmVGw+F zIuDler#D*T-(_@@R#7J{!TK`&Bqx(yi+67ajKM;r7S^cz|%eGNBY3-6VO zj@rvhPVyBpwY*TJI*_Xv4~o}NGekuxzx|)zv`Syy?IY-*$EJle{OoIV_I=0^tnms| zh^iX9I_X}8tx;W3_nM(w2>ds1#h`h^ip$eINP<5S66T9&KoNV5 z9sf8JguaLRhuWouv3LY>5<4j_4Rj+^iK zG41lsiBeQD03w0H)Km)toRmb7u!5;*gMD#GXvS>Q4Rp7(#{kQyt_R|@W5_IvGYht@f z+w~h^3CLJAC00`O`8m5~Rv zH^=k;bz2&wg*e3&8?`U_QY({7DpDu9lOFMepC@GorECXhG7sBbY7tZn@}vnclv-Dq zn(iwNJl_54)$y)a==U~vt8iho+jH_1{)K~Q{L0daNo5&m_yRfp z-q^N3-k#LUC2FA>p{QNBpTUv;WcxWmz~pJDARV0v4e{G_A$w8Py);_IN5ROK@PNK> z%tG;OjgG1mJ;2=prcQdGIdDS@aAewJTHndtavN33D0uTy`?dtUa*lf0{@djPgiSC1 zV%4VOdFS!(cT@9jZxu2pCFwnw&5H&V7*B!D1JQd%RM}2aQbPuHJ5o%yxu)t3trly8+3ALmX5hLQ|zu&vf5{DQ=VBU#`0{hQ};GC4{%~d zAFWyq-4wmUjL;-Yg;;Y0+0X;?_K%vEE{U%lTT6feYGve^feu)tZyl{?&rX8Z@*zuGlZ^Y!f>Do@&SQ~!#1kx zcIaiy8Ay@t^P(S@F7BOMdBKiNQeTV=bwoJ+`4LLgFgiBYKOw#Rcq*?3V|!i%8Gytt z>FX2d@J#Yg>P0D~lGlk>>ZbPXp*UB|-b`t;`l*np9odvqfC*z~K|uH6Wg*V8;FEAc z{Wtluhf2FP=sTODI!)51EqIv6lW)-MpmBLo5ye ziI6t?BN1%eVEO$3pu--%s(UK1{O3^rXQTMTFSpcOm~2bwyWe>(f)je>ZOMDws_z-W zoq)}lg-VCZ?R1Y|>7~}k@g9iVBTNEUNwJUU!u&UyuGvL3z0(;rhXPnj-;bt@jp@S9 z62QT($wdXU;{U$GjJ_4G7h(5y`d%ZEg1&fLWD=6QFF%g)*<;JzJjab1>}h%qjm^$O z+ByjIdBKezloxD;V&8{vqKtPOmadGkl37YniLy4{duP-v6RE z50O5}FK{aDbi9h1k?(S@3{-M_LmLaF4aqL0U!B9&7z(hMEO&;;C8J#`Lzj$U=hR#B z?&o7ij$(?!#UhJIzfUzN%3EA3Kr%` zaBTdVtAk5)7M?Qg{zlvc`4;gFjljQ68_l6|Rwc_54neybP~#ZN$~Nm8q29@HXK->@ zhq{LS#QkKw`pkcmjd;qIoM4+)Otuua>X7}es^oPE`m+{EWoGv)IB=n%NY7n;F45DN zB}~#h$#VUSM9q6R)a1yBmGAl^teU^1+86p1)c$AB{*4C?9{SgZ>HTgiEezjD9?gT{ zf~^l5tdmjl`iO|VHmn(LE6*-)ME_CHZp{!SOPm8;kvT`{6}~hlUZE3%bnhie*yDj?N_!?NijSEBCxm4Mi0CWP8mTP)f=%Y4+1mRP6(2z0$1 zTre@(eyV=-Jm-k^<`V?5YQ@|pRc_R!Jeh(1K88BY(@Dxk)lx54j>3?gR3W71e35Gm zI~CIRYwXW~Ny>DOq=6A>m?#d$X z;D_Fd?8)Hh8o$!N%RF=dewI%WY@erOFJtvyL&g7un=CCh2V-_+ce#vi-E@KbnEtp? zns0L5bZlSeHL3kPnn0ea4wp?*6`D13b&mVBmBP6Rp8{$SVN z=#OE-k5Z!Ui^?Y+z1!ckM8NI@FQ$Gx#Iw9RLHnU8WHq0nf|<&%x3207yj|6e$*N#4 z9wqfgE8{^>tw@{-HWw(bA0I5vXzpuj>N4al${HC?Pc$72jH>&i%F&Df3(Se%M<%mD4kJu@;`pV5H;7+G!?S6J+wO zODdH;NpFAh$ycs|Xp-1WcJAVbe9z$v^g!s8v6j^BbTa9dX?NjqQHgq>NF%-h!#7Qf zAXcu-&t#Z^k9kov3s$gMk7gNeJ;9g}JJK;uw9r23BA zRDO-ChfZGN+M^ItHE^=_Tj!L7S@r}2kN);TowJnJJ#MqYb<^Ro zmaFgmcuDbu9&@tli&7`T2vJ$Hx2)W+&oB5!q|S#2dweYT)}5vEP>t3IjLAELQy&RA z(i!kzf9uEjqer{%P}N0yc#)yexIERFf8HRNYtv~{wcI^xw#PazAG8y3HhRb@bc{v$ zmH%fw_?KvIU>_N~KeKCm^Ms1#eV&cff#GG^9`2gUB9vc=G%2Aal0WUC8fWeMGLvs9 z-mt2>=S)}Wuuk`6lUvI(jF#4&BMPZ_<>V5W>dH=}&G3tlE+KNwQCY3 zVXmC6C#fp}UTS(PQ!Dh!^s%>_;rcZ=u>V^4=^-1Q^#^yes9-eF0Z=lo?YVF!^>N$T z%a#@MJ{{5E%*88HMnNU+8> z+)aei&kAJgdQo?(_O_Q3=%x?}9DBpweE*qAqe?#@|3`^gWJePqz-`lG4k@S`@0rqWQUC`$#=|gzm8TVyDQkW zaNM=k?W?Ye=qOtng-e9cJb|Y;gdZ$F#aN6DrcwUtDVrqu!yrYXg0DOF!kb5WbDO<# z(a%5M4vpP35>mdX2Zn@t-SWUNGV&wu2cjt^GFZJL&XF;4InLE<^rzyr(t^3jH4r5w zV(dy-cID^|+cyTf0`-hz`hd4HZ(BlsdSsi) z&>IukSCb65Lk3*2j+*|d_a+Gd;}!U3KO6mU1W6w|vo>T$D%j`g%NSLs9QEYsDW35x zF5T*~mBGB;GKTP{0hb9glBfc-|JiQyU!tQks-xULUa24dqFszkTbvT<4>I=_Tzo8g zPL4%)lCCz-$19d7es-8pmPuz*GQ*YyDrDA*GWI|n@isG|Z{D1(j@JGm{uE;bB7vr4 zILF4jgwHbZoOQvk8EvDT1!`F43%d#gm`L`%W@}Kz%cq5VGZiUKtKZG^&DR!YR1j-+ zkaRs9`wnr;{IkOGC3WZM+hn~eK@M_cyeI`?Uz}%i^Teg~?+GpjgP`cL0wEG{=r&6g zqwHqsEZ0D+HQ(FW3*h|BOtI;N@v3p}3~51s>!B{fnXVg4FKL0qV%fTdZl{a;q8%UY zVOr4}9ai8<55!6J5V=GC@ATuI61Ie7XftU)3*EV+H@FH z*Idnk^>ScN_O1j@>PM(Dp^-YJo{1c6b~n~rq$Q^C;FKB^m~GCQIRDDk>)w#SV&RbD z-dpIWu)YQ927c84S*h&ebMuUw4VYj3W=Ah_``F0wbSJBc=grjgPvrcNpctoQ4+MhB zN8ojgwjavPh)~83!l^X3a>g`uvEyLnnT<$)l0Z-IF5PR_fz9T?vE#Dt0%I61wE_kb z_Ix9pKAdzq>^!(@jh!_c%Bs0VfQ;ca=F6Q%j3g%jI$ro8YQ1GTyM+aq?wGrW zdw`!+IyDcF(lZG_cM(UzGO(6mMb}Y3U5KP}Ve`92z2~n^zaY5<6ha%{1MSb!Yn)Hg zd!$XK(v)ledKR^!yuO^<_IS9ugz0y*SI0O5ZuGhs09@!u**K^8W7QmNiv}@6G#W?K z3RTSrLIGOC4>{D^&)5=g4Wg^hMX)1!Cnen1mY3%F#tsK|pKwdq|Hdl+YU*EKR($p& z@4kU#$78@qxJS0Xly!phM&q}y*T*{-wyx(HpXkAXg7|GGEN_QR-7%4+*FFXImV$Xp z`=8n=jSiUiE+=-+&b?KnRcz$-<|SKZloXWUUgB(vODLFG zT`txb2AKoqpZ)cl;iXIJ6dD=85u^=h(^b*%h>t3ZcUxK;S5k;+{y!*F;sxliW>^6H zXKGN6JWePk^=6*jb7af!&KAVKIYCq3cUMP|L^aKP?C#JPtBc4Pvx(<}A&_O07=^Je zb1#obaiP_>CDo(1c6UV{1s9CvFYfL2g=m%Z?6?M%RcQZLN{#BdueW&{M7w_Lb!I^n%n2A4(Q62PGXOT!BumCmD zSsQJW|9g3R;WT_MNJrMtadm{ZsfAV@6?-nS9s} z-^arTtP|Puas(THZ<^X3kIHfkrMWl})Dbi_&^Gmizo^rsrh}*u1+PA9iN)qBLPL>` z#Q8f+3LPG^UM2a%uuiLt8+G41PLnDk@oUA2#Tan?b=vcv7Rl0(np~e@F0jg0|MT5N z0+)F{_eeDqYE3gd&H&an8-l=F(-HT>tLDi5ldpyzQbMiK*|$)&){@g-;7ol>N{qFqm2$_S?7U6#JqK)AtiXIdf)le{aWk)?(wTJGiTY7O~SRgog zVdqN8OSH%HiwZd3cX}&YwmoNz{zQyPjrogc*^Tgw?g~w0%H>|Y_UmRifiwZdwNU0h zemEU2W)6pqK0P+;E>slYj+5#Rkmbik=E<$Q?Lco&sGEI4T(A8&*MctTd7G94a2Z3I z?id+dH;biO+_%&_obn73yGJ}3;Ffw>a0^JOh&sfivNNVMX~&(cw`}BA1FwZVz^rru z2D}NkF;Vn5u+lZdjjWLN%DS52sn+2H`?v5rpjS^FZ)_l1?CP<>BP)uS>WYO1LhVdQ zc(O@*`U}?fv)GV(MvqbOmp7WYQhWa%e^Saac^u=>cZmB{+VL#hY#5{67mt)r$rWEW zwzP)>z8fGh-5u3y_YKA_?GC%}x%m%MzVn|v z-+!P_{-<<^Rs3POkx?|_r|T8kJ6uAo zKtAD`jZIf#o4`2%kxl(BJg+|-SVse!fzWG!`hee6lH%8Ayb`IDg0UxE@%fu~1V;Qu zvS_e8)KkK{u4}e8ct;{O8(IZO1dohH?C=Rb>eWgyhfAL`-)>gA2*$($eVWY+ZUS7F zeZl;>G%bA0d{-x>`!RjC|D(T@j6*$8@(es~_3(-0HFno;q9will($)U$MS+Z?Se<#dSx5oz*gSJun~8H zjM(0eh*6UH<}*!tpd-4z{fea=T_r~3u8q5YbwddDMBuoBpK~ysUBn&6)}O84ry|$p zaEeSo`yqg(u8%%Ve@|btk+qf83p8#!l8DlrK14?vN)|xw)(hJPSrbaBa%l#0%W}yeiUHb?GDK^Y@6n z&T^1=g{*sc$Jdp8HX^=x}0PCh3QulC-qKZcUx*D_aG=}LF`KbU<6f+3@{UQI$ zR|XB)5Bp4^jTKy0%BKAz4^Gn?i4A~-w{*uWHOAg+4Kp*X8&lg^VhCU^+%FGsMwwNb zN|hbNO_aqkqw0_lBIRZVok^WG3R4j~HK;*Se72W&kI4L;bH_YWSx|WQxLpg|oT@c8 zq`HJ_bA@ExO9V?4-O54yYU5byu;Wr{AunR@w4cCBaBQad5N;XFe5i(H^GE zZX&?tr!dRXg8QXJa}IaJmx;p6qU`vXWB=v6piWH=3)Ji6tIVYuS{XK}r4*PErd(;S z`l>Z>-Ox;zQBvuJ_=TF{01K>ZD{oTEueF9 zSS?7Mv4Gp?)CkWW@nGWoS8Sboth{3G2unO1%lgQmanPvS_NAaCGMeaxIzN0JNO9)c z^Qz_m)LhHsYS|_1CQftOs3=flxYO*xe&Yq6W0ziehbH(A>wKGow*mL-mBT5#F}Q>3 zN5X;CT_jq6+W{Xr;&*jQ@vHdpnmARUr}Q=Vn!+w=vGAYwm}}nIX-K~PbUY>0{lOfU zG^AN&5OM8T{K>n`gvYWCZDuBJIT>{6r#=jlbM!#4w_o0+r5hW*fkv<1Dec|An!AlQlffmU=_cV<+DXal+>PcLTE}e0zTYpH-c#Ln zR|9jVt$~!JM+P`F$XrgJZVG9d%!L12Uq8|mk15nR!NnfgZl7T2m0VT-b+Xme! z#e3Rxc^!Rha6M5zJU&Go>UqEUTIQZ5fI{4ck_cz2y@IR)lrJ0Gdm>Ddv?>W zkp9i)SjBYXg%?nP$HLy1RvxSoHzwIN?+cQQ63%5P>J)v)epxW+sL-x^E!LK4-JY3l z<>ny+*4F?3eQiaK)#1ZR9{id?Mq7m#a}MU%w*^b;e$jH@%~a~a+K>&H+^s-13FYur zu3%EirKk&589CDuVR@|HfthNum_Qo;N%NoWgd17n>@|#e$9I`W&))FZip2@xgvm4K z%LE06B&snN(5hDsuEC|C(TFck5+R*I-~ehMFDXAPCNU>$Gq05=x}MfCzS_8(|EfA< z|FgL4uOT;T$HZ}YeL~s84oM_q>rO0soNvMn9I&E113E2wF%R$LOf%pbjN%2z@+TVf zjw(@Cz$pO)kiOz{pW2hK^6_jqzq~?pLy2Z=!t8HwT$dQq@pbgNl$*&=W|{1h4ZH5k zV6^og(MJrG{k2S^Tx!T-Bfw=TXQ$`6@xL6q_SIS`v$f`sM(q&}5#1vjliT9?f_oDM z?YDke?1-V$mnb%fVtiui`=tcL@Be#H6fh9|?1%3!3af7>hAf(Q83V#| z?eiJVV8QY12;6zHvlpg<@lgTQ6M7G`veT==#0HQ-X>7~n;21#*Wl~ibDuhW0{V;c* za!HYdYk@vfj#R={M%6OQHTWvrzBG$vztMS7pO#Nd*Mh;_ln_^aGURYlmwnM9W2Z{X z(Llmdf|X|-0P;`(F~Rv2&|f26&s>nU=bZ9Mdot~i}g z?#TVP5_A zU!r&&(FLy>^{>|A?e0H&T#wwGhx7nM*%RR`ZccyC>##gOxgUn z)M@pvZnO$`*wSnYv0SA^KVFEbe^Jt=EmJ_ON6imeSbkx^wG3(cEy9n6 zlGXnGrzM^~`;s*xjeYx^c`>rFP)T34d}>#8DmO?c5EOPGkI(#AEr%uV>r^K?71SS$ zSPgC1xI2_LkUADZ(Nw)_4{}bs8gj!BPIr1YUq7|6F&a7 z_s?v;TMi##y+!%Fj`bjLkA|KihD`(%Xg?pQ82HJWoShlULV(f}?Vklm{r*licCuRP zGWbQ=D5dP#rt7W?=iM|sK{wuh^c1X@N4qw}Q?Gh#QRqJTp_J5RzoEAdEcPnzH^|0x zVm)1m@E?~TLD!(21r?4_71#cD#b?kXW^6jOD=q#GlwhjyrRT=Wa=X%Q;N^b!x&4)% zOi6$N8t~1M>0xmWLt)@@e@a}_UTt)B*Rjvjlb64S+KZt^8IH)u5*wd+Pn#^Z8fS+?k7niC#a z>VH%9E_o`#zhO`Ma@mGd4Crgw3l~NG*0X4+;q3a7Vsd{|G_}X>9e}sctI{I8Dx6Cl zO2ckC6}zCOm(TsGEVUg{+dbz9#)vi$`_#(BORQ>Y*c6{fKX@`44P!aN9eU1MRcLi~{;KR06)k)th}p+D2{mquFTd(~n4A$fs&9Ta=5R(h9&y`r2fB zu@o>;%#%i}{$dyj2|G1icJ8-Oc2Q|TUA&N0;@!Gend0u~A5zCuKIa;TN5^=}mFg#- z)%5bdl-D~Oo~(Dhe#7=dNI9NbHVr2diG*pxZpaPhNTnv|;}hFRSHN{hZKI>%yYzCsYG9*u0ZdI^BDJ{_EwO+$yZW^T$n{qk1SAFt|@i*oJu2T0<4ge@+ch zKqIJNR4bCDpmgdB;fHGu9jAjPhrK9(EVwDW9x|Cf8Vt21u{XQ^%DmduXR^(u7B)u0I`>P;rg53B2fu9WrIFDqx0O5$&o&!SSK)y#Nh?TxUmoKgFRU*SJUMAi-GUQlN|4FeB~v4XM;6NcagVg6b>4|aYUJuqZetX66u(h*nyudG!ftXtW5|?r&u~j2%_OJs=Sx*!M-5k%WEekTSaTnA(f^*ZUncFHT7J5qdjBT8l!!Q4x79@Q{=I9?&= z^5$ppKSh0aJjTNNmUG8Ml$M!f{afNsGew*uS1I7p#X~em|D9(=vx$NM5D#av^KC_{ z((jO+s@1c)IQN05!Xg1=?)#KDE zBd@^k8Gy{Ehc(|0<5-t6pt-3;k?zu<*zvsF4&Ed_wV(-t+wbl+OEy^6Q5s^jaes(;tiC7q76|F@G z@@*K0OTNtxC4=MKxepZy_N^Q_+yt!?7n`cp&$Y~^Kra*l`ov>ci&%pGFoQgv)HN`6qH^Nsb% zaq$qitnpz8)A!$BYNKX+K% zX--FF;}DmhO0!8?v%j+}%WoTpHy36*10j} z$_|57xPOL^4k-WyV}yZmaC4WGp-)E-E>s~0CsPf^W?Y0pceTyWUp|oYE9=DiuUgL? z8qlpN_OBEo#SihWirdKqXj3)!(yxAgna9a}^D_G#k4^O6iKC~?O3oVj!;20X+W6>_ z)CH_im<7(%hJMHLSf_4z7s`n?6J}?mhw^h^5Hr$wbm}QJ!eI=mwWiq%N-+Wmr0Gxq8w4TLybf2WQfBla>0BA$EO*=| zfVSGC(vQ8OMOczDf2gf8#C)?7O0ZmPcr|pIWIy#+05R+S)ik``%LYvEqBj_G8LZ$N zDpI%k@grMRui1rYtBf!0c=6!YVLPjqEcZdfBwS3`_KE=DU=5 z7;y=_wiDsBFbv5wPy<0%V2opfC9q_m5tt^5?sP7@P_yn zUQ6&wrL39vZxShOlUlC;e649$d$xuSXEQQ1XelfI0{qeGYxWzY=LUWe$A0#`7}4gS zy{^0qaj6v7UmQ~$xytp}mPki7SmD?>l|p8WP`xjgDo{peaY{H*1pu52gN=2hwy zR|q7jtcMoFj>w$>PZiIzRra2O$^xo-hwl4YzEySFuZT?=jJG*QjQoucKYqiXoz)8$ zX++-LsA9c#*L3u3E+nhz?p;#Fj%c4n(zDU2fQV*wr6+yh3ElL`cf)6LuT1NAT7zR4 z!-^bFiv64E*XBtH&~%zce@X{F#}G&!b2*m{^l~34`KTB&$PXyBhu){@cv^Xv<44`Ma}9XiL)h)*B6QvpQ#gSz_(wH$@Ak9znRA_wrjk?CJVVqmL6rI9kv{2oLPT-);g3AsKYU&w8i2KN zntV_Vx!s(o0!O!1~!r&ZTKv3;2}cy)fF= zu=`48Fh~r-?cQH^^IZ#y4!EilNPl5WcPsk8%KWm(tw4JyrWkhk>Fww3n9rJ%zXt?N z0VGh_*#-iGbxX9A{KnFU*$LZ+oz&YYHe zE%~b1bk~AP=q`&@+*YD?FzZn(lPJOkjp2W)RLs~cQ% zN*I!N_-W%FiTq+E(KsqseCQMRXLDPrI%w}Z0pQVbgXd9P5?zQQ^+)99j||n{<8p|K zEe@0N89?a=&kl>o7f`n)=keo{6b0Zz#Lt|^a+R3z$uwq%o)IHllqny;sb zrag&J`1UYT<`+~n{_eL6@#FMpLl?!`T1KFbPJb^&Pbp9a!X0)*3v4}h*jgwZ8Hl;S z)q`LuVsl`&L{;Ive9rO8er06my)x4Mw&R`PGV0lH*|=cZ#rp*jZe0qa82kx=7Hh5Iwu@xCu_TERXn}oYaEOLI%O|7yr|9-82=8$Jg7 z1N?y*)By_2JuV6IdhNvkQ4t1Ybzm0b?9z#u2`kA%cv+j9w4pHhr=5iQ-b4VpWM}KN z?Phc*G2CnBIXvjrZwqY3Q+WLdW>`b(*%&m&usYUd*L3-rn5;5%*y2}QGk@2pi;ZW! zK}u9ndv2|WNo>qCn@Ia&R%Km(SlPYD$>JvBEN3gA~ZDNwi`Ht~M)P3L<2c)pi9Bbwq~&vyvQw-Xez zI4_SJq6dBAXCX@aj3faffq zvXKI=MCWPSKU=qvSDzLG{kH}1V1Gf`-Rl#nJ!ckiLz5A}8)AW4CWs;7 zoZ>+ISks`&YSZ%y_8WLR;i=tmJ7J(ULgw^9A>GWkAy(vSo%Xpa*h1*-ab0lydZ^oh zdzv`;wzLU5h_hmw)e+DCb>CB_TP2B^F~7(uT3>w zxRWULfma+5kUNmQR(|X+q529R+)+7ls8c#;5!P+)h2M1 zPQ!0cWgj-05-IbuThI?kkml3P6TqJg+(Oyj_I}bFG7uuBCdr(9YtQA67>q4!)j+Wf z$DS4WNqk+K8TGv(mKZ;M{#~LZ07SHfDLSTWjo`(S;%j3SS&=4?UhZf8?3HF3lzfoJ zb?Wn#7{CPo2tS@uE-dcGccm$Gs&~j0x7j2rR^#ALt}Zmd<>G6OO7e664)vOx zEH)w5m^f|rf0ODvFkp^*4h>cE>f39mNz5k!^jsn(U7=2?2l!sH z)O>M3+#5qK8_Fa10Eu3@6?Q#k*Z`txm^A?Fg3G=k@JeqkvRdlHVSd}**B`&HD+?Vi zQEY%#V+h_HH`r+f%(_?J8RzZy!YPUL{j4RSy*jNLYAj@>+a|m@ry*xu71D*FBZz9p zz_?%$@)(tZ{=8V9@cZRanP=Z?LbbB`hp83^2+-zf2BlX>8~vWv(JDQYE}!dvAMr6R z9DSbLkpg77=fwE5*?c|T3kJ%^H2|w4$5e~e4@kR(>w67TyoF161Md0PvhdrjAt@{v zBHG|LP8;yX1f_3PjOb&*?i6EfXhevv#D!2fLsXkadC=QL8h}y$TzCLE z#jz$2dco~@xggadPTSSIqxW?Vrw7^bE|V5t3w$-pho9p7Azo>5Y&R0habYt6IS&sZ zddgi*wMrcJ=JE^3cUosdrMDRTM^T6G9qk75?groA*L-47{o-36$4sUMSk2xCxMN7H z#P8~(3h^3;c?#=LaOu+$KEDYgW>>#b-tL**X}Q3^;a$6&CulyiPBPD~_4vh-w>tTC zLz?f)nDLM8CwiU?#q@j=uQ5ZeuRg1v}>C=P(rk} zBDyR%hu)i{HRH@YF?FedmEF6~O$C6;MqC)8dldR7sTMyFpe@zZ-BQ}J`#uvOx(2+$ zs&&y){sw4ysIc(?MdNRG+Pk2PL!N8UK+4)s#*=yKj z$rSc-Bnw-L)d(; zUryx3`N~O}VpJG8X_O)`Gaxsopt;BO>-!h)P}y|j!+J8mog(9aQpNv{n&&#lujGsG z{+rZ*UFA&(x2}3>(;MGn=o^zO6c#?yO*cFaA~9&zq%50Dl*s3<9*=O!fnXLkEi_%Cp|Mit!J{`K}EY|`e+8H%Pq{ABdGsEzI9VdZr`wh?=+2Y`?cEozL6 zaP%Wdk}wbh_n-#5*M-0Zm?y|i^1HA}rOkGn6~0Z|ydaFyctjbTQ?DNzYE;UI#x}Xa zfB^+&@#YB%uhn0RW=GNiBCZ2+j_$4O?eMXqH7)cEw1QcV8jjanK@90M#Bz%>Il{xt45N8urCR zaT7jmSnC2yil$w!9@eIsH^_VCI1#!VXBRw6gecjYPIG$K4ty&wz_BrL-6ZG*bTba(hobF8$aKWwLWxAqqw(PHef=B4hWY+&W>Om z`I^>a!ZyMq@r~Dq5)a~tAK$&tbU;afp0NK|N($`3AzPUhAlaq2k59@$JDKv{uTe!$ z4QMNiFt0SlO!>;6f6LDrI^fy1ea(12m=yjz_%q&Kv`U-{3~}8~laA7hz3d#tu5+D> zEO7adHP_xQd9%6^sFZ98)udGMUUVVd#OxT)-0s{$&=ke)bv6rS9l zW*7B(ArdSEj=-_ybvj@e<#KdmB8yhnIyEjkaH{Kx|5}*%9d-`LZsRr5!6B!>+5^1c zoZMG|J=-6U)Qz_*PBA*z&-2Fs^5D3C8$ADV)YdxSH<3jcw9wh7n#lO2F!WF+W-B=) zPg`*@Z8+govYGBa>Ov`;20|PDzU+)|M1<%3jjman7HNaqJM8wF9tS2VxO6w*$9$aC>AMrV@Jg7z;aO&fEw@)+_Zu>_w&uO@H*LC3G(mQ162c`nIJ>_J!^;*Ls%9&Bv-sSg{-XXJB$9^| zl~%_rp;y^pwlJP_<_3Bt#A=~5q@VfiEP0%_H!XrRpeojEEAuf95*e>!`aq`U=GFCG zv|K(b1IkkCy_RjZd}_eG&K9w+h`@my?bw3K=RVy&UMsDtytF_W`UG=HIMW*HFk6Km zf7!XrXK1I*$c-^wm{~p#*?yH&l_*BT;Si(6RvW=GFQU=AmVUv1!F_RKr7x~22oC5% zy$uPsTPJZtQbs1bh~zT*7?-WU%Aq&k-|&SxQJta|0&`-*@A4y41D&@c{?LR~rKcSf z4PH|~uObpYosZ+o*kr|5BYXwAJCIMas|0r*hK&tSw^s^D|cWi~g_O%2|t8E%7UxKURV)l=RgDREXeAP0KLSe;5>(_8TSn6P* z-$Jyk0d<){ZWWslGnYtxt*H8f9oHx-J#p7p79BDj7CXG`ILq_c!u^ASQEKg2l3Hm+ zly(%TqHnFEMo>XVT6AmHFiNNdP#NA=6j>Od5C&L4NjyMhk1H%Ogb`ILyek1Pwor0~ zYe@v(5q5RX9y9myb>;!L>x%>4ZiIu{1E#_66}eF{VcgKY$;)#BD1iHZSNTai@Kc>{ zIOiU>4#-T|3R|$8^mcT=Vqo$yoGV;ITqpP7x*Wem`tXV-!=SaxV;}4>_7hyB zm`}z4g-fvWgpeMeD(V=WK;6m?88K+1`}1hPg_34O1m-~PT-o(X-IykG}o8(=*5 zTzeRf_mN1A#TU$dW1nH+{fDvyH?Z8$Rx99s?zKMl=|}w!fyZ#X59z2QKQifxiz5Id zc#VSS7H=C46^Jildi(K~20?Z|*M9NLf>(~SH*A1F(JE%r@c9Qb^q#d#i6jq=xT1DB z@*;XwmUY<6?>4R;hU0&68#kv|m+1I7m+!Lqf#@UmG1Jf#>(};Vv-dq){mVJ>R|0g- z$xNDewhas-TV9GOk*9YG?yI{ilOMu*fdy8 zF;Ji!rUykw>z|;quEQ_odx_D}l=>t1%w~6YTdU*Kk(fMCeM0fum)WuI$K&7sg7PAp zQrlxmdaW!W3eSPo{h9$i=KR)=Vs&pF6VJcbjAwu=FAzbX{9EeEinyxG@>;Qu;C@_1 z36oor?nFJ$#r#`)u0Y%5s~xMR#Je{Og^P|gLs#Wc8dfm-E=>Y3a@mnk-yge=X5?(4xjSCqQWURQt08{ zM8C3mh9_%ITI|zcp5S~-wlP7%#Ob^p$vT2$;S76%4FaEz#_Eb_#(-B%?$FfVW=;ww zyEL*VoLinGBw8n~6|`X}OyhH-S9_%=TZ)k(ZL5;Q_ukYlPQ)Be$mi@Jm4()DpPjj| z--*T>C*6h@U;_Q?Q)tyitDaG6t*hB`LE-;v`9T0Kry4NGUik>gWo-?$an?GSu{jHa z?Zop&-|s~&E(uv|D~7qa8Dbb@G%%+fknWObEm92K-TUf(x1p_uEoZ+g6N(#WF}Gg* zxqB~W?y>&M)w#frX6bafS`oP;`J0-ky7#pY5-tYs|E75om8TN^brMq9sWi@}U#9cy zeiXtZF13@>aodL|2POhainUR!UX-gbyw1k0XrJ9nzj$lJQ zSNH>a7L9zqy0R4)6Sl>;v0-6?S1R6$ISv%@Y!-pc_4~Gf1Bp^CHf7__kf^(mKvxm8 z04+#R89g0vfMp!PFh9~FJL7vWaNu!E1D#tZ@?vHtJh4v+3A%yLH~n)Nj68yb=b~hi zi!1fqB;;osQ)_o+iZOknrs;~@+x;w#i5lw%0Cua5G5UPMZbSO$tubW$C4y!{=Sav4 zYe`UMLWq!1WuNy5MDpEA`_xSgq&QQJVSwn!5NdLX#r~MGP1pnD+X@5u8uF{L(!MXY zMYCQUGAX^g{Xr21)W&tGr)e@iy)&Mu6;)H8A<(xSDNr?5JZxvE^vA{5=aGIq8D!C1SbkRYTh1rC?JUbWZ;8-r;t6#C7N72-U5^7GpX~V z$+-7=vV+P_#Mh1&C5i0k@Hv6y6_2upbKi_$)DTH%1m((V)@(2Jq+05Tt6cq!A(~;K zZ@FH0bY^!3GI%Nnc_PPDl+wTYXFl5~5xA-_RfrSv``jcte zNKi-Yt-ih8%Inn|btdh0QObN3nh#?Sh;3gg!!h2D4^vk+-uSE#!pu6=rmA4RF*jmB z|8SvhZFbQ4oSwGin2UXv$=quK8K-6t7rpdsZuNyd;dI()yHlS8+&WgMXW;i8aA+R^ zrNJ2oSiLwMqiaYGPqtwmF|;?7iW5pxfQ+ApsQz*BemLVCNq>>Z%V?E{55Ha@$kXzH z`-qv=;h>!xB^NYtf`1*vq^ri`1Uyn+BAmngSwqf|U&h((-3v=+Zzkq47IO|C+MOq}0@Hy3)JQRr*L=n+2Aj=^CrK5GxB zJPx(29#ai3-+P8}{3WF>C?dP|Ur}1OVZ%%LvVB{_=oMnmceMiQA_)rFfC|Q#j9pAB zHbr!Sb4h$EAL==x_~z>3OfE@5@K0Pe6X+hmURtldOy;wJ29>WY3=NjyK5Mo3rSAE9eNR$A@F@@3&<|3txwM}na#9YBWy7o{LC2IOiy6iI7RJL4iM5D&?sNY`wp zKiSf~`HHJWWF>d0iiAwN-@Jssa@#W}Z3(RyEgep8!J|9J9Jl!@a?v}>3?uLXlvRWq zk1=;ym8L&(?ATf2p?Uim)@QcPxGi<++I&u-|FY=;)Imq45w|%l+~JBP7W?cE-l1WT z)y~<{9JDnEAk;^-qT~_7z#3i${_TYC1Uii>3?ctXTsgh8V;>I9F`?;z0&+G8n$*V6~eE9@Q z991jLTPME`B0`}MZfsr5&dc2VoI06%nr?)xoITz5_|e9LKVOLc&7$V9+B+?~9Z18w zjqO~s_kkUsJ;%QS>KN3FeCjhF3zyLj5$_uZ*mB-oG1Ti?-ls~2n;=;@cy&W~Dj}rX ze_wp6trI-bc0{z>Xi4x%81PpN6+Tu}Dvt)V4OcN$JsRAc`?rgD8Zq2Yai6Z+Li7y%XS`0bjVU$qHMFJfT?&A(~ z(q*(bllDog2h3NaeQX;#EBYq*tUGRG<`Kqkkxzbrh{kx*|M5@24K3YT7+}oKk)^@+ z4G9tyHE|$>E~R^Rfw{WKli>#ZK_!C*W{EpIpN<1Me=AI8!hoHIorTZB0W8YDh_p?J zsNf9WeKGlu4Ha8rAAz7D4ilq)8$~;iGXUwkq zXK`can10ptK}8ek zcvA;yx<*-jw=Kf;uW${dh59tKmd8lX%54c8Ak3I8SzMXA_Xc6{V6YGeQ%v}au+^4t zVY&({J#$|eHeH%6VK-Vbf!3?FlK4#)%s2kq3zt=1>wP-?dW{`6QD31^KWgaKz#ANL z$PdAH_LNvWI8@#GtF5s- zUGJAp#*<9fG+pbnnUU&7VNMk8*gJ8D0`Y6(xr~3CDWlDn#70W9M<;^I>y9LtN5gQI z!=o>eOFo%wMlVN#a?4N1`h!gUf^RvD&}C0VZ-ZcfBAuL8JkdGdS^h}|;%k31zU zFus4q5HTNrBC`>qN3quo!)Ud|i)&H0j~&FeL;&1KwBm>r$Cghi`A~|F*%6g(Da^pg z+dODY?EE*jt$E*Zjc)A*2Dw!nP|0W=V?thT%3E_F-bfGOrt@@(IoJH+bsC!EA(qSy zeP{+&mlE<%snjWtOs3*$_ibI>yFxtEu$^>|jd(GTTq?2E_w)&U^q# z6jBdcB!-*$Ne1LnvwcMcuvO3eV?v8V&;*ZRDIfS)zQ2?59zhc{Gg&A4{DwIWocW#UkzS;ClNoggDiiZ)O7Uzwh`e{z|`)9Q&@m7KL{rOiwg7v}bsqMy_&%L3xckv(hXAM|B;`-lK`963&9B(kRAI5XKe&g{H281Cl|Wc z-%{vUq#`R|n55Dj9;FuhUx}P9vkki(bCK@*-)ZwfbP$9)PjhO#Kk3?x;|1%2YTUER z=ih^wihr(C+i9~4s;)LgR%iu((AnwMI>BH%FyPtOVJdb41DQerE!TpVf=!_>YdK_| zZ6DULX2yu#l!wgy9T!p`y`%S3cv8_E&Nr0s>xV5Pke<j$_u^0k7}5y1-yII-x-Ci5npx zYYqKy!*B5#XjYkjAZBZ*3MKQ?T8aj3!_8Qk-GEZ3$W+17@^9d_x3OWEx{fLsPHVN5 zga)++Hss#c@(p3IZ%UzaFYnnC3-CTQT(Fcn!Xj3$UJg!TGzB4S^-}Tm*rsAM;xfXri=<~=RBg? z5xJsj)Qt%>>6S3*0txhBnHiG7wnAEF3J<$Ld6L-QgWvZ{(qFaNhF z_-9C41y6qXQE}KM$r`@Y=?{qXXK$AYsT|Q#-7kT$5Aep2Lcd_>UoF14F_iTIv>z)3 zk-!jU>^Uyn)AIgMCGLA;-DYbh0l>GS;5BGyX08?dG;mB>WIj_5z98O*AH&Q`6BH{( zBA5DQN!l~RpNQ`zQ}fwboMR5o`q!!Qh4mH#k((3OHlEAciDw4aM$K;!0J&DPaX&)G z=#G+~{RIDYQ4yskz)(7eGBN6fCM<`W@V8^d@Rkzhy^G%bIUH6ZV)Ee~r68o@he!G) zhGR~2o?M0hQ&5HH?1s&vtUA=H2GlHaOS6Q`9Y>?Bq0vp#YT(>It{+_6gM8OUr*S5W zO)>0aJ?2=4#W%wYV_H=l{wCn^H(_;Iu3;-HurVyXi65-cobC0nscod6Yj*WUSkcTe z=ehU97GOI)hP#oW`ecSTFrK$Oe6O1TTI-DM?5)vOn^McD>f;L7kov{z5Q3*9yWyV+ zw)Z~6m;hcHTJ(60Op#*WI^C!~8>;yah$LPKU*%R`oXL}nKc+8%Dr7WKglfmH!oy|= z*>04CjatNnjpmHM%J@z731oW?ZT~YKog}EJvMy|qb%&}Rdtm$|#0)gzF)cU0t{f?a zi3pZXqkwFs;vf>+j-eQv=!_Ln)k88#z&{y5Vs8lSa06a*1TS*{FGsb?Z{dju>0HwF z)L}*j(exwd61|I2CUUa_M$_t~Q`lpXoAtR#6p5Q0!8WdL-V8Ddr|Uz1^jh%NyC4|= z^H_|inQpXdvxoKFI9LyZ;ERg7*9k%sX{OazPlbWCZv!&KT1hT;U#gQXrdb8CmoR8n z?}zck($V*@CanA`0@XEvGqKP_oH>geeWi>%gu4zpn9UFM)>(TuLWU4}4+?44kODHw z`S0H?yKX$#$ZVY#*;YPXs+B33c--X&y8^RqWAf_8g%7_|M}YX2dMGiyVOnyxhlv^vQdEeDB}27F6wRx-VODyX|S0qyywVDSP9uNd>w_Siqf*5=?>x*3mC133wAX{;di~<@A>HHc~)&(@bbkSS7+h~d=(yp zNSSnKhGiV#DDrm6$&jUZQ|`B?GdEeNNbf%kuHac)6B1~T7qCN-Nb5mn%ooR93Xn5e zX%xum5NUcq%U!+k%&yM(=u3TJyC5R~&I;BC6qthB8l+AEywbqxy3Lz+ADv)G7ie|zQmnuTk!qZD;abm&8_klOe z+{mOWWe1r(vY)eh?V1Di#tpzT-CCwKG}Dzbhxh`m_;U6uigv%|HaHLa9ze`@SyfXe zgwi*}Gf*~(?f$odENL|N&6WC`5~)jxp>lZ8P&vQ>+9vi`SAvaV8Ed@SN7 zi#xhVDd!3TMOU~EloUAqZ7pHqRWCeZVzZ98OU0KK_Jeef%&01pRDo25^Z{J&D7CkS>k3|6pmTVBRZ6Nn1uxoBVCfzxW%wN1SxLto_+PGJ*`&+BQyQsEg$ zj24gPT3d}}#z!<;u3dJVfHrYUGSIpjhu+Y-u2>F?f){=M@^FCTN?dvAyAjQeTC$@% zAJbwZnVw9^G`U*_3IF{;zn^2^z<@mR!m2Yi=UrZ_tv$60bZG;vj3t4?(68xA>}xom ze`9`0ra`wGAx*VHmqw)RKI1Hk+5g-*)^pvP2k?p2>98}L2X2Zz&>9d&#eeQ3lBOQN zvm>+ORBDK}B-)V^4@p#W#TXZD_HeQwzH-9F=MU^jw9O&Mh z$IUELRIVzlQQHzV$8QG24Tt^z+3qkz#t>c^b0r{`(4m3}BPGZ-ISYFq@}N2b>4|y1 zBn#IaHvfc~hnD%gBh;mcim)hUPR!^KblvJJF!ZP&?`bMBw84x>W;IoROr<$i`oepT z&ZS(J^+Cn#(9Hf4>8|)4Hgo^!POb5CWWvnc5Ra&-^xL{% z&l;iPt^1Vm98lrrAY~ly)0`AG$&5x1X$@{0Kz`p~25;_O$mJQv){xO0yq&#R0k8Y0 zg{JAPNWIq;mScN%%*0=QReH0}N=p(F4T<8b^bLcm7=ID0g%*e7T^$-h`r}(*&P54@ za&3WIe089$S2j#5J$;h=*oNmZNOKS5{YMM-pzU$LqEe$>8yu)gk= z^AQi;X$-ryZJXE8*i#osdk0$^_|_MUbT{ZAtvapFff56vN=)1E_|_>^$@yuyBG^)u zJ3^;FreK2ww(VMde1%%~z|HQ1CZm4TQMzD4%eB01A#eo!1CV}MIoJ4AzH~McxjKf} z3cT&c73tqjR5@$GD=Fs2W6p^hOSwAfm9CQj+k!KWf5jv81&gFL&!wF=*)}+3{HX-Z z8?kO+7ijCFBfmaczhc$&i9j|5CRcjAF_MRb>rR#)@`Sh3Yh&G>_GwjIz7oq-=7k_i zmQ`bbrH5PNJ?>0
QaqZsbegP%ba6K6kbhoy`UUzX3Gj!|5x`z^bxCS&*jKG)TR za8TPjz@&9l>mqIGULx7J{b7rxuxyo}@Y7DE6zw|4ax!#1^Y0rh|LldB?NKba%}8US zRb6%B=yFL9=R=A}*G8-G{7S_(PBZ=HsJwxG12}+EUwZW8K>!53+b58FQOwwCm{o7O z3cyM@|XiK@~kF+r>?VW8mM zkxJtn2Q#9g9Wnm0k^WCRN_Aykp+;k(hQ>_xSLT7<-x<%p9*%&*=nVe|jE0-MD{3)v z7#=>Rr#cG>wcHc^KICPGGc}dWiTpO!ltCX#24*e)Yf*PX#I?7Ad``X5z**G|pg(|l zPcdz;hR~<#dS1}bh_H~B;gYm4+W{6pxQ@qd6v{-l%zNZQL){EqA2p2)uwC1u3Auxt z5^;sraav5|PYM;E<)U=qCT`krulypDZ>)^Kj6C*rRtXe5YS8F2htd=_pE?&~WUocW z7SL@@i(DX7#ExxVZ+V_lX{KuRq2MEvA3ThyFoh*o6x$3 z+PmGPUG2rytuz(c%w!6?Y0yEUYUk|2uivE3QXY`-BS#6`Lu*3b@D7w}Pt?PPcuH%{L^}$MG$|45&!{^1@cYDWh+;| zu{A6zdM3d~Uq?`Qq{*|frRghW6tcp#rONuWZ?!w<+H$PNR)4aOa@K5DGUa1B0nmTP zN~Eq4Iq&>L*hr*d-{@4sZy#@6yH$XfJCAhpGaKu{)(R3-3j9X6tCE1x$#Qru)A zXSu}VdYbmT^B()XeBY?N*gNxs94LP5q(R8a5X~N(GVvt5I7=%;Vo_a!9B7U+vY6?E zRtAI8y($QEIt*>4A)h*j@xlh9h1UV;+VxX)$;nnXqblwHXj#{6l&d^9eEJ=~`!RFJKA`9pl!AHn_wOa`%epPuL!@z8iw~*OcPz>cU z*b>?e8{G}$DOsE&-oD%0O3h-^jBBHq3^dUA5f{OD8Mg6a>b}~{Puh@zy#4_C{npUg4FQXn9h ziG?25?xa-6kO!vnBJd3Cnf2IFbnq(?xFf}LK5Kq5_*UcoiTg}XhcYSWFcDgx@pLs;* z%b%USEE?_N$An(~HlR#g7S?Af>&XX&zjJ$N%Dz06tn7z8=74J)KdLkLs6%_?I{LS8 z0D~PDFG2=ddoV5#!95B5oh;| zm7RqYRI@B3GQJfGhywrjnfw%YhS$s1?N^4Y8HARio;o7~yL~%-XzX8eC$!#IBllf& zBxEmIIzXyI&baWqToQ#j*?en4!aqd^_n}4ud|XM8LALe51We7#%3OOLYC3>^YfXj#{hJM#v#D#E&L5DXTia*} z&k2@b4%GJf#U{oC%8e4B#WJUem{k=swTCn=2HJlOeXjt@a&|wBkbx&gub-b8jLwxho zCPZonb;Y?xk28#PU=u2+Y^i^?YiSu+Q&VRA+aEcSv@$(&J{ANGZyMNXboiXU_jXp^ zr|)sLzVCuVgXJ_nGb&ca;zSVHd@e|iUYU<2_`2sbdy|+di81LxjN|CTGy>YU&ntvx z!s@37(DBX0)mu+*vO0;v&9ogrPr5|OX+dO9GS0iq27;ULXBGIf6atG>C>U^YM1b*h zzF79A$mO3q45*`schzI?dh5dOYA?O^m98HN>JHVk%(ICkeg3>}m^v7#N<_-X zgg#k{gTFxDi_iV32UA9s+d`AaP_nJT{t&4WdV>Qij;NVFdF?S(H4n2NvUY*g-tTKa ze0r@I38Qi~8UAfng|c3Dt8PK?8(<#cn&L2C5MZ50cUW{a{^~dke~drR#h>Qd#4L-( zNb``c+MH-vV$WOF+m*kud#4hrpf#K5gAu9Lnr1~jZ!N(eZ5n6*fjR0jatgg}zUAUpX| z&zBpvn}q`YFUs@LEqDATw+i|EpTO?2t{r9ZnOdXv&uVLqCJxUY#IaE28_ z6TkUwq=5j6acbp2zxS~om|IzEwlS)WzY|;F!F$yB9qN`}nj2YUm%=-X_0r}Q;{aTs;sZLx2xFT_L`;}syQJv6Wfj~Pq5!dVIcTr-F5P%5)A0hbRqiAvS#J|fz8k8bD zU!38e2L05t){t#j?>5-s`Etj=+nI1vl%=SkCWA$C=-GTI57-Ghg;BeQF)FCT!#U-` z?_2|hO|pDx?xoik{XrCbILYCLa1*@>l<-|h6D6WD_UdNLSsg``WA)ie{AP!;2YgOe z!>(49pA9{ouCIBsW2@u<2QK6!{aKOa`=-g5r98JiQ~T3cs$j;{YS^+MX_}%a(63gY z*b+;KIIZ5!jO?{um}6zsD2WD8PK^u#oIPhiDhay2zJMWP)YDKyvoVbG-O2LtalsxE9G9;#xwfw<-8e+RYN~-+VUaQi4*kreXFVNjqMB+6FaDG z!&v~Dc3WUWDAf2anm6KBQL3qTd$^gxPnVtJco+X$mB@p+hjVWF{$FDCg*#1;+2qLs zsj3}n2+?60>r{Kb;(5Ah=FQl1rJ;nx_d@PAFLTLZdW*i+7CY9>8x1Sm z)lMa{U*%5z=B6ixnYENjw7L@3`e%$XBtjwauAXD^r$o?FPBBd&3tCNlfD z1irmfOtx!o!Yp#><~Z`iC$qo1AM-vx6TI)B>T=NQMvQBH`}kEahs)J2+&No1<3?vv zp!$%fW5-}xS3B)-P^v-X6x&hkc;qDEF2$d3aaZeGW^K8$8qY%qn8v9rewJAcC=Sq5 z9)Fbk}=Xrf(`(4f%Lf z-T9f#q~<~W)mGIZEsKTmmQwcce{2~a5NjQ=L6z^Aa32pUrForbgNP3hO?gR8L2r?x zCH}6+ArS>LE-ixQD()O#rs|@`0H*EwLNNvTx&Q8D@R2yPk90380$&(PUfOZ*<&y-=%MPwg18-s1R}&&@0nZ(OkI>O-EQ=&+xg@P} z9~n9@xkfbg`68}0{nne(-HHt9w`e~z{(rc7>#!)_sB83@p;Nj`Qd+u0M7kSE0T~69 z&LIYn?v_SCKES))I5R@^xs^z!xTUw*hVfkf)-i= zBF|iD@!pANvCt*WC+CqbNd@ztzj)(Uj$MMrOVM7hwZQYvi4C%h3k;WGSy(kr9TW?s40={xX5QHDFxA-w{C#C=YQU^{uGp71La%OuM8bX^W0J`C+)?X-ANG;_m% ztCgIdnM?Kl<%7A-*$-0XY@J0gFjk)d1)%d!Aim~1XWA%QMakjuhLqe|YJHP(sT-~E)b`KA zz=NN$8@FgBZZj8!;m#l`henv(Io->a+mRUf2RiN?nFgt}lF&pWZeNaNj?-quA0|l} z$o_Iy5(+U8oS#^R8uVThTz!)spV;x;mSyW!%KIdZB|6&vP+utLm`=xlNDQb{kZY=q zc1Jwwusn5^9~+WgZ3p(>Kw1c$1ZS0wUY<^xy#VqF0V1lvRQ~&s3Vv_dzbLIUewGzV z`wc14FX(Wi{pB&}ucVEZ;j@dKSBcCP<6L_c@pQ*j+Ye;E*B!)gr@>;#p@;j0cgdUY z;M9x#t>TXpODojE-aR>3?LfHgR$~8p2xI1(;@j04>SSOI2kIyJVZ1MZ|db z5nSP~Q5hs7ig1@8HX&p#zud|5pC###zqUQs7-d{PG1wv9>~eqR$r#@|Vee)1jwu#R$0!<2VEL42BQq)_bhe+4X3cN*!8 z%3W}6oOA@WUJ2_;Lki;NRLj9JCbYlVBpn+d8h(>|6qhThEsE!D`z|qI`;}Kpxp&;5 zgVf2ArUIR8)vF*I=c}J-7zx;~hb7qrJ2h+V0a#0&S+D0;jH3~NTdR>a)8v{**1hDs zBw2F!NIP|k{1Q0~KE#J4cwAibpZ8^sb>foACndF>_a3X>zgm*oR?9^msh_!}zty3ZQ40QNmJ??- zayT_o0el7UAi(Z>Rs}z+=tWSDf1ux^>>hx?#$4TCEL-3@q*Y zN#?JMpK&01W{92FY{9D#8Q%#xTh`0_h_Z)M!`djbQ25qmNA=~rWb;V%Dw?f+;^8lO zz}X?|UKo%cr5vC8wRo)4<)r8&m{Oq(uivT|LtLPgg5A6t72iIxF`kbG-ClgBYILN& zul0BRXq6d;NDiw6bBFPZmHGbjSXFKE0y^Kt!r@WeKMGk*b7;)&z?HkjZq>`&9FHLFwG@#{VYK9+s|NC9h zCAF!Dvfz)n)U=pn9K%4)l;PN$mo_`~0@rC_3c)+v?4{pd?3eXhqoaB&Han4xk*1N& zf)d&T7MuR2&SMhr@bfT7+Yj!D4+JyZ)jI)T8iN~hc>Vp^D1UeL7B<+&?BC`T2so5O zyz$hQKA_5uO%Dt3W9V3`&5GbrYIkEmsSJrxM?#8AZJcOabKB$b zzZHT~%zojxw;kn(cfZ9PC#OO)8KR|2Ur*mRG;A1jxUnv#VK18|OC}q;kkzoX(MH9_`M2b@=F7I23qKA!)?N?14zY;#ie4%2xp+T~tn3`Cx36Be z6THraf#z48=jivAad6<4w#UW|-lsf~J2#P@q@*c^S8^|egD)BU{j}FUy92`&AMh8@a$a8l! za0|e3<-f_`nW)ODKLGp8ve<tS1=M`*OFF6W=Ez1^^uwYzrBe<$P&fLQuG{AbSX#aee0DJm zzULc6jS?GPWXfbnli&ql-N(#Z|EB73H~n}(5w zM1{IBZLeJO$*ZdhtMWL%>0`*GZJ;xSp-p@C&`kuSokLj**sNVFL^C{EzB5tx%IM-2 z{CQt$gFkym_Vt3oR@HM^QEDKN_37$T?Q+y9&NR{tYTC;=@Z#~8`+$*sXm@?O#g&%I zHHC=9Ag0inbaiqvv0~c9$9TJ^RK4I;RUa(hI50Y|6=KPK)p4npHCgOiTM^snrf`Qh z$0zj3`94V&sb3OLMpZDu)_l7^(CALX=l**$;cgk=-6(pYlK(wJd34Y%HFr}RZb6y< z%hU>Jkyll8@|@@4r%(tNQH7HertD$bJ5*RawgaJ3oE6y^4L^bH`;sH12F-W2?AB6! zxyel^!(g_Ml< zP|tqMDfrsupUhQpc=tSFKa;(cmv+z+0&hopvN?``0rOp+?l+1*^U0qtDnH~u4O`KA zKq5R9bxI)L3baSeLUJk2vJ3bp9nYm(6N=#O+r_0J8@mO@Bfh0O19Ka@1LHc!gmJGv zkXIMvAq@c7R9@>wYAvflA@ z%wthH0b6e@Iz=cnMg!%u6so&Pw^cb9GNh_5P7sE?ZgwJV*35d`(%!c7vmCujp`#V#wWtec?3@f_J&mvhe z;?1es%ncH|Q0--9(*MfloP zEJL%f^R<`h%|<;9;4&d^suRgj&tnVZSz0Hb^|@BBLpuybA^hUpx6!g##@AMOfJ;*S zeR{Af>(df~CUE!$y)!gfjJL8zOdlDfu=*{YPJ8)#Pf5S~OmCQN7L-@j;=XhB zviZ%4Mc>m$ALc>+wqr_b)?!c6K6MhzNAz4~%_Svu#3Qu)oLvtIal>?eHODYNI`{rD zCb!q#K2#=6V%L;d>9xmP06CK7-%jLt&Zk#@9*!$$!8!x|L%9vvojgGD^VSdy>(nPd zARTXA7*fPj@=v{j@^;`iJ@Z^8)N+pB(Q$U{G_aR`E$h{eyJ(VKl{;3;17{pJJn~N0 zss6GM=_?^yu_Z8YfhLy#8!q}p=Z-RMt=vIT+Ql6JOqW63<7@?71#Rm!8d8+`$@hK# z)g9jk$NPTi31&Sd7)!bm?t$l6|$emVAwd#TOLB3CS2&nf1rA#k(Gfp;E-M>) zM@$4ePVW4Mnk~_1`9}jq%~#&t=lAx1%OR%8&6}oNMhWF-*C+%8*WZ`)t{a48p-yXe0G5Wv~c*2itM&>oi6ULo}q_&=j%R`QD~HLpVU_!jhu|b_(nyx z(f{T`0$s?V^t=8&O2U*BYjIzXTFE?6x8uaWuRhY{pMlUGH7AB&9|;uFt7gRbP$&gr9u+t$)Z%eGp5sYYr`k@`V6C zG7zbzyt|$Hxv6XH#V1jIAW)w5S5pACW4FmF$NtZ7Mh){h>Q7X^Ez&pf;p@2Go*SO9 z-i=fyioR3M%uHgQ*NmNM%rP$V2ZGKG$l1!7gWKv@*}u*3YYV%Q z)6c#MWHjeLZrQxvJhrkgYqMat7G(P8@?hE|jCyZnI;&n@4mS_@6EdNZJKKUX8>9Fa z&75qjAV6`02?1*1C~jo3=g*biPtBBb{M|2Zy3+H_J=OC^t_DgNoaheF{=VW1zRsb_ zTPfew&I@h)79w}air|!$I>?h|hhQN-btV!ZgsL|#r7345W_xyQ9G8w7OpL$VgLD50 z{f$Ot9y+u!ZcRB@KsF$580RE({D-B2hVNja4HC|F5lXL&3Xm{M`w8f%62>-!oUWlJP>AIym@F%xUfyD$E2b{)l znL2hp^u0AS@y=g`kJ=9%y(^un1|(6-?ZvG}tCud_%Lx_cY5fVveOe(1?7OT@szA*Y?M!U1PC2Sert1V%!dNl6 zt8mVL-x0qON7-#L^#NQrNM_TGGV@}&DaP*fNi(Wi= zDSuG5!m`L>BfQ=}iSlm^jz8ALc|95Nh&ynrlhQ}w=@s*ZKZW!y!7l}Z<1hvI z7n=y78nintASuajFIlR0kaOi%j_b#7f32n;Q89wWUrw;pW`DKoT4OxUC=eeybl@X% zknL#At9+7VV*FaL100Z&aIrWlUpZ=WS(CGmn8M^i5jJtW!Hc+US2_b&Hx z(0h$#6|eRqN~#1kzejJcF{(+ZxiJW%%r!LCeS<1bk^()J+y1d+Q`co?@|Pw#WG#_b z=X4VFZ?%WrYxw5>db8R@;ndtLX5)hAv=Qi6kso%yVtFw+Vg6h>z(d80D^$F=y`p~n zET3W1Lw1SGySF!Q*An8h!jg-J8=fZpIDH~_e*#_fOsk$c3;wKrlq8G?YMI+e|cZOMAS!@8|&}2Q#2hC&8}M1bg?T2^U@4D7d);kjE-SoSjrP0KBfDJ za1~K`R#r~pf_DtbRhE=yD^VQLO#baTcg$0)d_y*|C)YR|4^Yxsvs_#e-!;ybr0A?> zp67tPPZq@Dw6AtlaV74egqmye>x(^5FfoqtQiNQg3icNfgbJRaoKHJmeTdHAOJ?aO zVkM`;mQRj%r#^`#*}S$t44QCVFG}>VbG9LE(5exV-f)VfkAG2^a3xs$D<)vQ-qg{b z(tT8-@4<~8MLTqd%usl1z2Q5D2Nc`vfPhU#Y++_2ZmjRls&T6axaR0OxN;_e1PJtQ zB@8u$=uR!kV?SpR&_RY-D|G026aj|eCKsZoi#``%4@AEjqC=3e(PPuTy+K@~SnVVi zCH;~0BOa?~8GP-PU=#P$R!aAiq8R$V+J z`vWo0z2d@F@WP!xF~tUVo_!r>-M3lOSeU$~8PJOH9AK+G39S#$m=ES%!3KcXE&N{P ztbu+^UrOsBR((f)A-4g4f6o+Dxj3gdeqgV6wWc8Rb91b-3YDxXOYW~fVminmo7GGS zX31d#|IrX;C$xBohIFDo#oxY}&d#TMnG%QSabucWJq^M+=4iWo|MpRk0vn0@)kP=_ zQ@^jW)@JI7Y}2ZQSOiN}DTy+CP5U^~yqdFKyjCIJ+2%cMWqQ9Zb<`~Vy zDo>gG)RksXyX3JNAqWjVPlu?VsJQfYB;8v-F%JC{;*mD5-;od3W&XK$;={+g_=H<~uQT`Ec*=)~2@6%j=F^J6UmyjQ*A5rR(b z;Uc0H;wV{LIdDtKbWW#EpVn2uD^N|LVzko)VT=Dh9%Tc#wIghRpG>tp0X{%Y&*~c0 zoq8mmsksG%`tf@fasD5aO|n2_g)>m!p$SOr7^059Htz~OUWb}eUdtqF_3XsTS`M70 zZbdY=A-U&O{GW-~+rfX)3fb?+R!gqiE~|$HY{!W7>X`3UpBL6_lhgV)R_rb-F=Bme z@V9fmpyqwwV~)M~iQosHxD?L$Kvye^bA)CJe)U-#pKt3Q)m)I7y-c4hIRIPQtZ;3e zOUIWV!1NWf4ieVq_EHx7$cwYmo|!-iSi`({b_wX1*mnpv%t*qub&$rp#Tjyx+p8?OL=|BIK%Be^l1I@b?7H>^@dEc&zY84X6Y~Jd55DtymgnK8nvWg+&f3_GFr7Su;W?dYke@OMkzAAzKN_eE z)-$gFE-!Swx{O-#W&(CVBQLQyIbV#UD0&@=OU7gkIEMDRS7A08RNL-8%(2QQK-bRa zR|*fd+Lw;pEF{f@sEaUwcS4VfoUmB1CkW^w1&D(8`9vnoa{9MdU#=SKT=eCsAdNB9 zW|3*XhzhXV2PKK7A|+h{YDzA}a3LL*^33bXhxoF1UF7GzGG$Cr4?Te!jMgt}$kRM< z!-BtG!xscq8N=c#Hy%c9y0W0L!f(xCEWK)1e+3 zww?SG)-ky!Ir zY(Y%|MK+&fF|20C(u-Zw7-L#r&4vdhdwN%Upf#~>N z-l1%)QHEP`b)NLWy!Fxt&gg*nb5%H;R=#eB@+}XGHIlIP5BKNSBfs9lQ^b!rw>K~V zs?_KHqwr=ne9B8C7>E^2wFw}?sPx`4Xjm)IPV+}Vv}+0q-;~o{@v3Tn@q&H}wtnG zQz;&B7y!dMQLO=~CAwE1ieNn-sp2#pUp3!0Z4S3Q5EZ7lrMC;CU(Yj9H-9oaAoO$2 zlL9C>Vs)Lz1|fZB$Rln32RBRwZN*C8*I8R5zo!@vJVQKn#Fu-Cp{C04N+$_;l`~ZV z5{UDu)kI^I59&jl`Q3AZqU<2o{{Hl}V&DFQO}y<6H|>w(sL+;26l&-MjFn+Es2I3} zih*+b)$Y|f^R?FrK`iziECstW3t!jdlb!D45A2W7=e*=$p%-j zzqw?MFc8_+*%uSY;fZ(INLj~0thZ+ZdNc$CwO)vZw!X2D8A+~bCg#d!*`kBI?YcJr zfKX5L&;7LWP3xDqH9@J3d*d_{3?)XO%xMEJvGY?@Yva=z%J8v|B=_+Ok$YyRcq>L{ zAcGw1IhU%HJ9*7d7D|(8af(zBJ-{?Lml5pi$%RJIQumom%96eyQ5R%9jXYGJxfU*iMbFB{lw>iv7lDBj1wEp1dtP*WafBzbAuS+uMa`h91{iT64ozRwQKOC_3z2I zqph>ggD(p{-OTh`M0?IymtO~^etQoBX08zd?B951qUA+RPxD0rg~y#ME&Qdc zl6?Diil~PFCy)(2(4qRE6FZY2a3?T4>{8sid6%i~M0ArNGl?S`aot=|z3dM@uQ*rc z`D*y0BZBWC>K^~<_4{yTc^)Vp(x~-o`r(TiiJ*u{HX^l(SI-;mQ4ZGzZ{`0I zrvDEk34Ad6cHACb#fv|F5NfI~uLj7ITD027AiwHLg*ainLvgLu(W*s~RYFaf>59Fm`V7V? z2Y87i6YwEq-8Z>IA1Q2%p%?mNU3uoG`|(V|f0@3_rz_O4stq{+B!fO26%s*1FbbN5 z-rk(&@4;i%`lutA-*MXxJ;mAWMWoz@fI%)zLfzLI=6Fy_T5Z8OPM;`VKhBj_Z3O9v z!ZsKggU*#71`o-#Ud^thpprhW3{EJY6}pfZd_KGKvIxv}N)64D(OS0P>z& z-5>Si(a5Ym83C!Ta@?rs+7>NUSNlifVs-EE!S?pA%~dNqg50~#BMgdO8e3HP(Mj*= zn7Ublf*scvUWct<`%$I+%?{{aEZRxmV6b&YmGmC>03aj&(#PVLExBXUCz?e`M;G+5}n37%h~zlo%zULGNn>N@|^?rYX8fj`Bwo>#~o(}KfMR&KC%X%Ok8 zx;zmb$x$^I6&EqUWq!N1|B;MTu_OgiQe3M4aQ)OuSL9U#BVn$`p9i9jqIVLGdRSNm zlZxfCTv* zSEBkdcail*4PLrJ^)%>tg}p4QE|ew-%p5D#sMbMT#hsk(1JUno2i#gIQ=k|$>nN2j zhirveA_~Wguot55iflH1USJ$cXVkD=CrdNds<|RmNaUS>Iq1=QexjbYV4xt`NoSTW zRAx|;$@Hht3Jn>KPiMGnM88yiOv2FZ)6>^%UCl5&JRmVNzSSAhq0>>T{GZFX3k}dA z=U_7%TIf}_%dZU-hjRPvQxS`03UPj&(6qb^QK(rAQq|?fn)`G#CFcB&_VwDvKwuY0;l=c_*KDBk3<5< zbmTfx7pU$@$cg$7IP##Z{{apvLG}YBc8CemB_&LlHx)DX=P^Ykf4>g0Beccpv>A*R zAh9Aq@_VVT40M>1_cv4F-?wdzI-wwwuDkq_M{PX0qlZ+(-e%gM%ivLgqT9}QlA6}x zXqdMd#$c*D@%2B)^t{iKDKHJ_jd0W-kGvz+?3vVGF01nV@| z39<{vOwG!@b09>7S4y=bDS?8i|Io8Am;HS4wnLkcDqas+esGsvhU|XI$y!M{gZ54s z!=H<)iBOn?yb8`MkB!A*F)RRjC@F>*vVJ5lFVgCels)zugJ|umaNu*D89hDuIeyo< z8n2P!Z{JYoz0lgBjMtl~+8$f@_o_JP$=|WGSv~TpE&fAlbZ|Xcv2s-|i+gVwm~tF6 zk#leZX)<_s6|3MRTJY;XiB%B31rHJ}GR9nR%kNNoXM7t{!3#uqFLPj^I|+3$uE4tn zKAuV(`Owoyz-ju@UB0y9sK#9cw@<8({bWs zTTO;)1^GV#m?$_T+%ca2s3L;a1^Q@RH_J4vEWa5#V3i1wu^~9?;H+`cwxJ!tY%(rj zMo1m^S)#FqG@?sQ+5T<8ME(C@XkvKVy2FuSTWtlLcmSyl0{q=&kJX5JIF5uYi#~e# zyWM;K6AE@}fdo5=mn)IkDGB(vKJ-)k|Dn+s{piXt@E#%Y?rW*jX|=9T(} z&~$S}LI&<*Tdnbgd+s4|N|Os+?I|rb({=%duh-5f^M0m`>u9?aC~uj)dLJVNU&H|B z>_I1(M4Lx-;p_uE_XN;ACP2=;CS&tsRMGJ;w=Ku8t=o7~(MBtAMijzIP`>gtk(co* zW`;~x{ap|Ukfe*pwYb445_>au=bKOSobJn`u^8TI)272{$!)_r%r>WK(geRHJ1@713~-R)=tY_2g`zz8*&rID)HU(yM%%jDUUS zaJffs+6i5)^Nf&s;q}s~H_PaigL*y0PbBAi6?8^naTJR8b&jI+p>odtwV+VZH)JhT zytG0YF}e&|S}sGM;91I)E=Pf8wUam}$rE{SlX7w58L1gZPv?zAY+MJZppCyJ@RMrH zkVDP`FqMR(3iC`wHOCJ)!hH+=%uzr5?7Nf4dahhWf$=+R^wmd%$MLTd()M%l`WlPE zgS0??1d2KyHH0#6(9MLiw9$aBZ=)?~8c+v@le;-7AXZv;0&4l(3^+c>MpVlhf^f+Sy{!rOt zE)+=q2|<`wbGBm#t=2o9UX3iQ`VVsKSpJEVz_qJygx7!|pvWwB%=3CIV4ej+hKF=z zDC(qnnk(NydD(;!jd86jkYbacLz6EP1bO`6{6c!cVCKsxrh%q1pUC$s8Q1E~N0xfX zlcH^W_}({@>a)ta+tC!r5g2GN1v)_eY?|d%;hm^E?Bn^nI|AO*yL0+upcN6Cj(!?e z<+1v8+2P9MglL%DxUZ9RV{bG;)(YCTw=ddOvyzw-%A@-tqG;0SwzxL!`63D@6wjZX z)@9tvO+^mw21s5a#n{50PjXSU9pO2?yv_O3=_XEuuWh36j*4>7JRS6P6oKJ3guoFq zc!sc-hn1~xKL;>C$s16TY2$nAq838WrQU*+y>qnG;tXpxJIBdFs3uHV(y(LYwb-U@zPVV$QLbG zp4KF`>)sQ;S((m<`Su<1Yp*iJ1fH%z4nd-e5($R^s4*&e4h(F%f@+e%b8QYY!)~uk zAvY7|6Pb$&=zInkr_qf))Pmf8J`h5meM{R|eyd>r0v;TNb7eR5SaF)ASb0ys85?{N`N~^{TzWbER zBnqLQv?hNMnDcLQptjNQl#wZuhA34i{#UQ}za0$V*|Q2$9j8HU#>G3 zh$>UYm_L6m{XhqtxBEp0mwZ@44(-0Bw%fvn3m>`i{^3k_I(o-s@jCb@6I9K^!!&T^ zbG^WqiXNiwOaxvlqiLwcRumy>$6`f~-D>vilX!I$i1>0loFfLpaO>Pv+QVEtiHE}j z#=z^RU)5ruOQE>`T=z0j!xgZnhMaf+)^g^Q5eC$4G&u4&;i``n{xx9tOjzd8cm9`K}bX-YQ1?O?MrBn(y(tR(+`+g92abgRAu|&o%LE~nVBSuV=xtg@OCq(+lgQ32bvU8H3=|iQ3ix>m*7`B%vb6cdBd^}O z_FsR~9MtA`pW#~OYIuI&QayxdMdO#ew4-$^fW!OshH%R0{NGV>^_Isj&+DsJjVQZ_`dF7ceVQ@^$LO7p=RQmzA-bMvy8GcOnrsXlHP<`_Wv)eX+u1V%fYgxe9=-OS zxy8Kip~^Ok`xGX$@u018Y+88}5hK_h1752(FQ0 zo31D0{sHm0_xL1?To+aJc0?<854 zu>T`BY0*s44e3?5dXtJ9%f8)Yy>0h!U|rDsXzd(3y=y6@9GItYc#mw+pFRKI8I!-* zEYm6x2GKlGuv#nD5ypzLUyU(&#R@uLa?`PcYb5lqK5t!4LUa6M#u{-?xO$cIH zUpX)Psf|@L{l;JBre~q}ZDeJwZQhm;)^A08zU=SRg=-!Vt+JErS~WyV90r!O_Sdoxp8s{W`C z0F|do0Bq{E{DIG21KCbvU|Q&xmxQ?N`G9ge#p!HsN3 zgh^@+&3Q~NQFa2_&2aZAPmOB-7S8Z193n4Eq7m)K?R9|IulS{gWZaPy>=q{o zV#hc^IV&G>FAHn}_Ee`OWYS@Z1r|3>T`ZUqD5OoO=w%c}X8jfSG3#R^X&Ewozn?G` z+A!v~{nR1QR}6aixUz3=-|2gLcBD8+&I1_MKtKI2&QALpob9TWRiBMnJJMnI+`t!x zWQmMV{LwLW+;U*q)>#ifOn==}E4C(Y(NvDRlAi~Z)l`3h+aKGk814><>DDr4GfLeX zo?M|5AjnppBvQYUPX2M|Tua_LLD}`wOKCoaWA=7m@KMMl9h&9Kz$(Vft55!{HU9JF z1@qTKEU!>$>=F5BVL~2<;Ar7zW2dj9b2>8MM2V5`_%kN+(C8#{+S%Q(Aad5zFV%>E zWz7HJ1?jEQJ`xfD8l#uU4Er0{Ts~9k&q3zc?+K&Rua_35{?vA?yZD2`bzEQh7T-VnR-J)8Ahs%D0v>+h*jkP32 zi+XE60}<@3@{pXS&ZjeU=9;b+tP?}b1Un{HXFfM1Sl6>0Vj!LRmM^w>J6ApD=Y%g| za)^I}h;ED!YT5WTD2>Ih-A!*qRS2!=F2wI5F`sm!Tzd* zLdtzfu)x5nO;?u$ry`7Z|47KA@Bcz<_|eyUF^xUl0>kQ2=hDJgCduZX9O-sX6^+a10VxL`e@LJ4~gDLXN=%S z-#I35lo+d$5CzP#B!}Z2?G>v}lfPA7qow;-&3g*Dv459I>t<|Uo|Eokw}(~&i9?xV z?yk=Pgoz%jnu&Eb_H->V`udy|_(R-`rM|#Q1nwP;e^WT_sDE=bmiZG^a3!1Pb@?iP zv=RySBJBa0XfnR0xHO}Hf-%0|G3CO!{1GGbDcj zf^Wl*8%d506XAaC(M5?|*h`gQF{rWsCHd?)UOae_dF59}RJiGRc+eVwn5_5FiXxF6 zxJHfw17Ku^DIn4)U5_@S2E zC-3_J)2heq>tmSuQQ_3e=eC$ZnSZ8jYlmG4;~OdNk(H09LySS*-{HeaixT4%#z!f4^l{tc zB&*{UHDQP2ZKqJqc-0?x-zu1MS;~dJ*Wp`qW_ViL<4}c+tAW{ncH5=i&zlu+(JMsE4lWSVvH!&R$YEhJ?l;Iyql-2+ehEE##beyxXI zP{^2Y_p1|PS|e?x#IevrnB*zdVbS=eg+*!R(S4vEu9NcNY8%uv?SD%T(v+Up(B6DS zaj!;>q#GqG9}48{&&IB{SS0rLX5O;!lR2_=V}DwI&l$|SnV3|rCf_f5IC@wV|Mo2P zlV^GrGo`U-dm788NV}69JL^xn0l@ucZH{KVTWl-jco{;bklI`k&6xy}Z|koaasA-o z@Kf|sn81AP{AB6Sb6ZnG<;?rp@^{-QP-1N&3y~e>r$zAVbMHm>4|4@<%hD6XJL)*;8gb+oa!=mmM7}y+HR?O=vn2 zJMd!L~9H_)@^4Wb0oI3_NUXl2;-BnFq)Hs>7FlvRRzGq=1ge!+Tr&3M*=zT!r#4|(Px|r|g zOUvj<6JK;8U6fFawzB(#*hxxi5RAg#2p^ZbB-li!OR!gy678z^AWbWPP5A{Aqba~@ zd{WX#95b{00AJvsijup=Kg`6&jas@zVGxH7nte5<|EPM&i}ejy|RXiuxPXY@m|H>QvX4e zI(&JiJbmIkhOyEJC(AGUtS7>M3Vp0nc!ddGt?<3ANL=~%7%%Ta4D1<<6NX5GKgC+B z1Q6zr?u;6v_|4ZTLulQ+9Z!Dk7qgkgrCIK!aUDBnsiCUbAbSdhelNr8fNhw6myMeU z>q$q|c)p@n_{@X7rbu9=_FV(;$JCMQgd4$J1$mG-;hHjA0Xsr-Do&#S!2d=FX6dG9 zEc&=e0Je5MKO8QaG*PS)LE{@>L_Dcxk>mB{JjA$*xMcGER=GAQ0CS$po+Cyu`yzN% z>3cvOShS(ek-Y19_FRwMxByHSVblNY`CoG@y7er>LiJ+~bZT0hN0rrxOY zBjPUg5nWM&@pHpo4@Xclk(?#ETVL+7t?q?m98#?hLL)2wI z>E|m$go8nZgyX8)L?D$yaoWAUCa}~OJyoSd{C5-IalY|5h$utgYJZmg*7jz;v{n`D z_dMq21OkUv(A%t!#3?yF25p+l%Io1nH?h5O`badA9!zc~R_vA{_cYeh(fJp(1ZnC5oHHG~+R;g%Z4IfqLKWZ$Zud-okmGl)*pGDBea{r;RuKbA= z|5%+IZ`M`)ScQ^SdzZeMhsNJ~w98Vj_=Y zpe;fP337zUbnZ*A(SR2MTOey@fkWWP7?JM2L|guO%j&4+uYcgD zhbJ{TcDLE?y~f8vHrnL+Ds{~bX|D{WrLcP*=y4Oky%K<)^&&nxb#P+ms%=mJx*Qif zv5KmuqxlM{FH0Wr>?-{f8Nw8eY*4V$VV)BP1~4PF{pOkB{rX(;-!kQ4eskQ=D%~;f z${&7Xe}}4WW#R-N0YftKVX*^K*$zjRW@k$^#$#evg(yqbt+~~rd(Js^K}+LKNBri- z6aYX#ofW3!)+@u?q%TmeolWdo(@h0aWR9lrlwIr2cOrOK={wDjIU>E5%uiZH0HPB= zn5(}P=Gc2xlCp#RoM&FxW?$<){+IOTBjQnnSM=}QT0izTE_!x4z0%BzcNsZdT!gsv zx9uJ&Icg(&snQbr?_4~+V)1dW_*M_GKItfh-6I70db8N1SS~-t(2T$3NNq?x?4Jr- zW^)&e7_SOZ)s|g%ky8!Zd8NW8XCB&#d-$J}IKnyXm}}*Lc^HUbEVp}XRvq2O)8F!P z0*A%=T!K4XUK`h-!oF~cK7jrgbstPBkNmP*o?E>NAp8k^dvF)y-g4{_& z)~NWo+y=J`V+_H+bG*tNtipAJUc;iH!LrZCFT(<`QpRvg0@T*~V*coEqd~7eH7wy1 zpeI*FR&ZLDeQWbGWU^mH`b}V18Re!MQ&@B*MNP(hsH0s#t8hn!RSdrUfG)_j(+YRz z850Ug^epPHgB19DE@W*3pNXrKXfFJO0fBe_nqM-(Og|Wp;p|r!@eQ>nu@KcYAEeLj z$@qt>`$|xKNUxW1TzyyjiX{JK{y(QA<54kt0$#F=cY%>k zZzae9cla)NrE4{wlE6y;*4d>ipQ}Y;vb9u54)0L$g{AI0NN^_I5x(o+Nkb^hLlDoS z?+}z{dZUL(M@j8f&WmvZ@^%R^Q{xK8Wa&eF1UEwEm4~Gn2=DbYk__v(B(zT~Q91_yI#NlQHm7Z9W?<;x}0Kl?L!?FLrz<`)yxZ7H1cx`2-+PZ6gvj%AWfua zVJp-Ak^i9mEMSS>%R+MbxUG;7yJrUKwqH@*6JUo==9t4z)a848{pei#Zja&U=%XAs zap5mLT1&lQ#Zl7riH&FKjmvwfF5B>`?ehE#8tlm0CKG*|b(5E5r}+0@v6GW$1O@0& z6SU)(ZDXR&5_qV_BG6(GV9HBbe`B@|h_)BX>YJl_pgo3>KM(OZOl$}0!ZSAY2 zmd82ZJGh(jsFt3v=jX)?XDS!rc=*t$Y>l?z(c5$(F>vJDW_mjF;0t<1X|tkr!%KR% z=p3`_vkLe90Mr~Y649uRXc(~BE-|DynlJLz!t^3z8UW~>xOf;g{Y>2F1Uw(yA17A_ z=D)q(;*mW%00GX$TtT5zBb((L`B-Bw-v|j_2_EsdMa$3?xb^rZlD#U<@3nbTW4dv^ zW<9myD-(JZ>ZMdy~|(CU=R^^T%QTUUl5L3dV` z+$=16v;V`>S;sZ`esO<{4v{V?32CLJLur)mmKp*|j&O7{N*a_BP&!6POO6m2g2a&S znzVF0_xJZaujjx0x!pVGy3T#h`Mf_!6u-oEgc!wFP!=Wph~7|sVVTz_)mxVW+4e6R z<_g+-ZONdca>Qtdm7Dk=?1ZcAUX=>TNl9)Bx`@vvPJwlkVkALAyLVi4zfKCXMB~;K zF%rkiqRtl%4NKa3LQ9-2VY&D>*w_Rx!^q&vE)BiGdxzJx(R@qBodfTipXLTwO;f9r zpW%ylmp!F+$~~fPzH3WS%U^sm_5qT>9Xk?)Zu6CJ0l;YXugqlm%@A#t=dA8shJ;7# z6g(YQStt;)1FxZ{%>9(@ekqmYd=ogCl$IF9V>azl2E@l=eFn7 zz9Gur6_B1I&%~2kHgcFA;P^>XZdiqkJqO(Y<6#jH=)!=H_D22(;a$L_QGW@CBT_!n zvz_;o5?P1|Mgn^Y4@`%|5Ff%ucqHb&s;bjfF_y}hW3r1m0U@iEQ%3gHMyGgoYlZh4g zvfDQEWOHo04f&#z_S9dCJS%;ts(#Vnekf`tIY1DCE}Lbb&Xz|;kSkO+BfGn))s__d z42#(IVjHC%X-;H&X*Q~ESoGnMg@V>UnM$K^JmKqee)|{~5SDDiZHFHC0vh6hP8uYr zJ*#Cx>tTq+BcL@s@-rGFrB@tduPft3WJ{7jpb-tjyR-LPU)+m_x@By)C5wgjT@{!a zPx!Y!M-Sm82jLM%DXz-Pv5Qj$+w~rQz&qHgH7YbH0CB4N=h1;eu_9hEnO*>zJ7BH* zsVtVW#ssB=6GFj!NXuTGBQtl9LY?>YY?m&NiM7{&!xA_pY3}`LP_C7@twvaxci_yvc5O;(Ksw^4<_-;WR@{l*tnahl*~9J*9G zuyJNpW?270mdK8%=bgVV9w~QdOE_U(;oZ{9xG^yZl_d&X03&C|jXcY4yN7L%61*a` zNVgz^=C?Li^j$GARb$)&@vrB_RAJ%2OUkki=tjt6we}yvo{37q3>CS99^(d_2O}bJ z;MoU!uch$r5PK6tTSzTC7j+WD(=Oaz$V7dH*@jKrqRm?P1=!I&S`{@4oc>(D&jC&29@O2c2o@SKVG4p=`-2|U9 zKlU+<54|r96U!fM8DbO;-nH10%1_B+$bL!Y#p+d-_4*_W@vSsDq`PG&*3R?6)uR@C z1TGQB#+~FH)e>zeXk>48eQqv`+Z@!%95Pmd{a<|71MzJL#5csu0hw)^-J5rJb28R> zT@L6!OfWHCowzs!D98{2!KCS|W+qia$RHQi-yEogrkmo;ewY`IkegT?85g0>nA8)R zfoLCb**y9^ONCn+X~%xQ0yFT5c&pApE8JfUq2}qPjV+w!mB}*Fnmp;X*(Lt!Ow9*@WR?Ox!M@x6Z|hf8**Qct90A;zmg zYVNb8?*E5x?_)Alca*kLeif@qc8YCi7|~Smfddp;&OFpb>M4F4G+oJ%fq0EEcEuAS z5+W8)r&AKLikFWGA9O)+k6#LbLN>gU)crgal-Co8?p?*1Wk~xvZ|8v4Z`jMyMhm?2-Erae%vt6Qqo}gUByqJH!&*=nlQ% zcIR5we_Wm}+E&+Vn4%@~jJlP8?dwt|Ziykj|1)ZlZo$NxISvJb8?N!8jvd#J%uHKh zr`zar$gTAvmpiB%qI_Liuw7d7i=`6_S$;kZ!3N%c6D=f^Cs+OPk_NZG+uUPR=@JSI`DoY^)sNkA$RzXM?#+9 z2R&P794}TmG1$u?J2zGvW;Per?*>ECcxPjj0~gg-kAg}Wu+w7J{FgNaI~PM!Iag7Y z$mfE(8oX}A%QM)Av*3%r$(HuA=M(Q#{o4Vy$(qx|)#oi1^RA$Oc=#B0+`wsm))4-v zWNi)dh#47Na5MI=Y@q~-Hnn5b0@P2d)?z0>_(LN{n1a?PtKr_N*@Z88z3WpkL@b`% zK@@Q>cx7JZJqr0j% z{vA{@YHX$K@%Zu~7MBHEH&!1+p>MI2ThbM2A=9ai1?JA{Vx}yTCS!G(jB{PD9D^t4 zw7-r^&Jlb4iYQCv~@DZ%^sLE!4?4VH&-( z&BCe56Yp-L%1|&)s?j)YCMvchUX3=UQxDZCJdMa9)9vyL&3KWb6Rp+pC{-;x+4sMq z)ck`IyXrHL%}|bo>er-RB@h=}RCj$v)Mn3}ia@PavXT;?S=O}S)a77Yy7$OPw~iB= zb+Y+)TBG+YFK?qs?NzZ@ujEd{Iq;eT!jg~x;aHqo;nHFlLem@Q?Pdb($;sM!QkPTt zKhhG1)YI2SH=mEF+T?K;)aSDigRpM-yWLps~7&;R0Q202kP)a<%b7igtu z@m?Jo4c|3az?!>QXF1pwiYG92# zNf$()6{m*>46mal2cGaq_HP+`XF<*qhe_TZ*cdA_`8BMm4$1xvmNPEE{d>R5e5Qvu zG0h`5f=Q%gEy0J)?izhxOTwZkSVEK~hW)>#oFDd40Fs^xa|#w!+If zt5G%jS>An>O@*X6oE$xDEH64!{Yf>2l-L&$zFRhssoa;grU_VutLy%Pox0&7dLFAH z2+~9i-_a4J9gw{Gh9%))FY}oOf32L2nZEJ7m47?X`)j7T9Dhu-3JW59FU&O_dA2ZQKrT>)XG~O z7@P}SUKt+ij~lm5XdyeSPoKbYpMszIbWlHJWD*{)>4@5Gn+JkH_dl<+| z4_H@?irAJ`glO+falirK7y&RAPVM1M`f!YpD!OLzvij3Y|IW?%?0%Ug%YMyA z=4&dDLSrW&Eh&7@1-<2SQmx61_%3Q}HpyqG8cf4@@H*%C15KR%s=cU!F^-=vb>xz$ z%03^37wes5GtLW-9m46t5bjFVXTMejiJ;q)Z9=hW z9DJ&F4-{dB343zXZP^Q7&4WtAtoanIQtvf`kx|$0MWq#CQzg*s)fh)$gIZSkI;K2x zzoUzGq-t^4R3%jY2e8y&ER?-<7jdIhF&yFI`AZGyw7h*b2<~cKA<25|PX026j>VoG zV2#cO9#z8jUA&_!PA73n^+W#HPV5k7oaqSlvLSB?2n40sJ~i@ZJPaVy&Zx^nS>!$9)GLsM)NOOHeMJS(^r7zD z58AB1AO>x{Qj^htf?QzwJ%2bB999FLO7BzUH)@G!Ewb6uTbV0G}$#_dC* z@his>LImW=n{!cuWs-u_`oQ6)gf1-zgW;A}+D+_P9VgE&!#{Lofjl|K&8Q7wl(7G za%oRGNoAtXYKmnaSo-)GDfG>?P2h2!b@1&!A7E~6C{$C}tV8t=!Pn8mzM$AwrJ{WN zG|wwm0hA|j`Ll%MMGXk7X4_4VLGRg`yDMBX_eYLo?1@;kCLEBTgaz2Fx*RVUt1Sf* zfeH*C0_vPt_&s8dFoHYEr{XZFXb`KS7(X!rUSX=5LW-w)e{x4O#ksp&@oMzZSbB5pYwO%RBpgAc!IlZd2>Xvb`x*8k}S*W^TuHE z9<7!_Q$)TCxh0rRP$0L(`W3{+?TP@|=N@agC>JUo>Sq@-jCoTa#LU)_ia&yB5&W`tI`N`;#avXgm+=h$}V%2 zU1+oqwa)E5!tC%LaztV?*&#mdP2><9qUy=I=%nS#O%k+O-%6uj9)7+cg;uxqrNQ}f z{-F;|6#;9%krI%8iYSFw0qq^a1Krf|00*|2*@B#wBn$C(=p;((fz~Z0QB^s@$8*^J zad*|={#RNg-^5W6)2j`&$x^#4q?>0^gSr-(#cKZ^KU?ae0T@(cn;#IA7i+fpy^sN; zJAm<2q5z_Lq8g@?2$pK{6y{x4wt?6)KtJ>7KpHX^#dbr~F86^A$+{kq?>T-C?%Csp zv7Ub@8l--}`wHu4{Alw7_2EsVEiD!r?#$@4WPH(*K9u@vL7X zR-2@bD=Nb5C--x2!R9-bgL8ZCC|&hTbT@CrhEr=E``~XkCW!786&5;`qgkk$=+*x{ zah^&LwLt71pht&%`8Ul7KF^}wrexvk0kj`S8$dEE(>2XjmP4j+zi#kGMY*QY;Ng(>Znh)iKY zZ6{Xh5!5)T$8B}XlVUfuSg^7zLjp#1WWY=wjY9Nwp;Kn4{w32%7bEEI*KOOKuXvh) zx3kBz5prVH3qZEcKw~P}!VBT>K15MAq-5WndT7HOKKljGhnIZNi_VH|>^<3%Ay1I> z(Y0JLkJO}gNT2U#u3#;_9orIl9*}?5`Tw&4|HFwDGU$kT^=@EELT_^4@`QYQyP?_? z@k#RWCe;6-kb(o@v95gO0~9&tsa&|`t1=g+Iaxjeg-dKyz>X$lBvgaBX;V*qorb#7 zgx1e;uYORSfS{{=^(7XnFT*R)ELv5{7!o^7fb1#5!Z{~8RDmCLKWtc>KZhWk?^BS&Iwd&ohn z=u)+66(L$5xFnzhy`B!8IUbhVk+G{eu|QLD}n-_r4&|+5a*kp&^hiqWmH$Rkf$y6Hj(9sYH_ygzP^Cs zIvqOVWD%|Pg>i*n= zC+qS|OD;ej_xW!oRyzXl&gKt7*#ysYkj98mMjq$$YX3X=BB(=0#l->AxjIRbd@yTE zJ>c_fvqq;VsnrZIbJxDG8;73%Q;Vk7X(pVmj$18EA5NfKT*j!E{?>4OFo zEy}pV2ba<{sdxMbZq$`o##V6;@CP~FmFG1(V(;i}8whmDK|v`$U9fJ0LX4}%5r z_RtzTRwAmlmyfGie{?-NHE~IjS^SIzLQ`Je=21Qp%1c!I&s}6BI;X`ul6Q+Hv>J2% z(@_-wZWstv#GmdYfeZP5eo((x+nXo$(=Cs>i1AVxbpk_GVDy~IcJGz1kB9tZ>pLyj zXMT!7g0$?Rj6)4=O$sbqjU@{5U>iZbpv`Dt?H1ddYpM7As3RcwW+lMLZgksmfE$0c zk;N4h)aJQw`l{<+%3fE=a!X8i{8q15ysM&BS50eG$DY(9l;26jsBc*WfAw)YeD%d9 zPaM0-9rtFB7H~g8b}EgJhdBrjV@E3IDZfX0g|EuAJlNHg@p7%jgJ_7=rhZ`B!avyX zqC+#d?&wPn0ZK)bsE%gOmbTA(>VbZjGushC64N6VY$MJH9#?lA=~1iVpm+2^Gh1Y@ znU+}IW-^j>DfXUQ`l~@ib%j}7*27!m^=?Z1h5#$+)hu2z-E)FSA(Gd4FS>cgzo_bk ziP7ms8x{!U8sX>o(NZmCO4?7!+2pIu{ccR4N+XD2y<$Hg0a}mei~%sxF0Szfi9i8+ zkEneg_>S6rFwWP?)xRabb$1yZ#>zBhG_d?Y3ZVqBZpm*E;wQ9iLrmO)(*J^GL@KKN z!A0?=;r8JnfuwB7lov|f>~-|p7S7Q7)b+2+iW8P&B#;axDwO*}sBO)y?<0=s2hdM` zM|x>Ni^)6+xmn)THk}^4j+@2G$Pu%huVS6%`&hwQ1$mc0r5p}5n^iZ{uJwNjFv#sE zkNpHQorys3$DTST5OK^MdjPTWiS($FPes`lDym=UO3gfW6Qlb;-r$JZrvwnE{TSkw z?O^}hqT`u#J9ch?xW8qVSTYE32(JBM2azT$OCFoq(Pww4q`mJj8HEaSSvAmFcG4>H z-A+jO<-#X9YO|9BLa3Jpa>`tCAy3FaTfY`<|D}&&+v>&Ch4Qi?RC!sertwbLdG$Gy z%?)aA@;XnXR(X)`0<`$v_xh+x|AoEP9^c)etHRa!2@0aRsR3Q^?4yeLOe$VAi`O0GAf*o#`I2&3%1EAu&G5ho#^py z^iXeaiYo`1vsH38Hb}A}EM%`0Br8m=b!UQIt;lO>6u5#S!(ny{s{feJ9`E%NU&uU! zn!}8JFs&$t2eHQawU@FU>05>$R(q@j5ye>FS4HKP1nn6dT_Y*B#$rD`NCk1o zO7Z-qI?hswZhgTZl(-JI){Xwj3~yqB=0wGwS#I1!=OXW&bYUJg>qI;6Q7l4tRX-}9 zEmi;B_Bn^6Uv7h>t#H~HjRWTr7egbT(}yz&17V5+OEbL4&b>xoorpMaGxYWNvg{Z0 z7vymf()}+R?hDy^$&1{(%;NTRh8R`b5P0ULy#(SOU9o!wtG{Zi(@9*RN-L~ivitXt z0V{u0D)sT%ByN*`GXYYQHV}wB``Y^$IqoBCc`X6QVUmaul0>z3kMDoI_kD2A$7$-w z{C=R23WS?O=Xxw-V4(A+pb(j906LN4`xmVJIQ_&ta*|&H4tNPX4y`-g-_MxVIqyud zlfhPZv0u2SYDc@=YDs-ykzV-fQRH>B%)rCEMv>URc z9{;wgsWdBh`lhTWBg=rdC7PtUC!nl!J@=~&F$NR~1H>MWi9OZ6#U>`s*oMzOI}R0- zPfr4V!i;y!Ks8-nMd^wpzIq7qmK~4#0B;x%R*}i00K4lovSm7TzUVZgA zl%g)XB!yDG*InW3?>8dGA&Z;R&WR=cuzt;+3U;B)S7j=mrSEN}PHC-q~0+Zu{3$;9+Jbcr(K7OlESpv^IhUJ};7PUnee==}^4-@Gk`SehzR56n5no{zd0%8>dls?2A#92ndA=si=qJz_4-pM7 zHqlPl{XJ_Z&jfP+7Wye?GXDl`$oNS6_a1JXzH7L4!V+6#Att%Z=2TL_V~D zM1BamHVOmW2C#CpKE|sC-7C$TasI{s&=hYfgqlV2V}mkHfh_u~Fx zybA4g52wqm)Ociwp0)-}DT z&x3I`iT)$&%h%Ex0V?pT3V3ujRfsEnsD)ceY3aRbI1X)LpYi$9qfJADU5-g*UoIm! zpd_XK#kB1E9H4Bzez8}dkF!14%AkDo(qFN-e#mGX@-_H;tqO^3q!+Vi~(Pd98sJY~_YwM{bF;R|RD@d`ef-wXf3 z+?99A3i+=;ca(dzw?^}w$$yWs4Wn&DS`>;cva^4Oj6%HIg%<06*DojAs;iG>8)?U`VMo z{Jez%$DImPpn|hF4DmZ!YV-XHMM(~~xJQi-ojl&RvqtK`iggt+iHqdc>Q;{Yrnc}1 z)Ls0;$37id5crm&YqQ7qwyGzTp{~?6O!Anz3aHdK)oX6Yx}> z)84}{?(Z%^mPtZnHop(2Ar00e_~XK2H1kcidrjUM@0=4&H(9v6ENQFamxT3(#F$?PGZaT>1#nYd}O%Px8y z$PUS$SsW(ABWpsXlJ9R`+eDazlS8==WAR)>z*;8O5--8nwbi#F00UGgbOfsOuBg7)wpD`WHhU$i-RD%L)PSAFJA|8wDO~PqwHqUsQmYoqn3sfU*lfbt;cP$g0InSv_b+na-v?%tj8JCHkF_Y~jn1UKiR*#940VjO$2Y zgJrP4e-o^f`8^GFU*!ItILd2o*&|f#wUbRi<|<^D5-gz~6F<{^)iuCzhqB$|L(^JJ z|1#ZAE9&8LqQ3XXLoG(<+5{aU4bE4tVwTD-b+)%mlbliQbDJV#<>`&Z6ukEfoWD>Z zvHlOdm>m%Q%opZhu>Ga-r9)px4^toDz}IWmrHA>AX5x^_+Rp1 z)S+?$kInZdn}e#!^=NFq&$4XeOL7*@xp#o5zD8NxpsxPXexemeQV zn|ok7Vamn{%pyD@3a(!}nP3=Z{wfHhA?bDN29Qu@;&UM|g@K|-MGJQceEG#p^mqQlwEz=$B zoIY@a6zZpetGOc@KFrDT-NqRY`Q;-|%IdxX-Gm=6=g+m%Uh4Zwz?dE(m)HYaI7&aH zNOFW3kkH(^R-|(HJgZ=>>~X-g`t=VM4clW*#00-hBqsQkf?7SWp#46e_`WEv2EiRt z^Q|X}sIRXL#rs5E+KR;#ne(S%;u%z-89qtgj=Fi{*0g=xVUnvpOr}Z3vS`V@>KdQ& zZp6}uMaXv<@tNzR{}~lc?JYck4QN)KUr3)PR&4~r=WbSsG5eG}+*2Cx!pxpj%dW#ySlHan#h&*7_(-3;`U%a|!)gS=+Pn;$Sv4_%*rm^O|>|#Q;b?xjo#1 zHG=;5F--cPe)T%WLMS1`P*<*h##GmeH6kzSGl4kT*;)6MK=zy2DdyU4-6vrq#y@Nq zO~b8NbbedLeVw@FkRS7xAA1N>d>^&P=7=&QD9~l^IAjf+BX|D`DbI519M$gb?Myrb-{o`BJ^uv-a?k(Q3DAK(LlZ@&HWgh- z`WS{|`VBiq@`ZHW9+6Cml7P%%F83r}CoejW1OYCNITrJ^*kvI;k}`HKzJ}CdO7J+j z>j*81#06}|zFA5O2X7ig>h9RV4a>!F!{&f@A5BV zlI}lT>Vtu*t)dS{atK4u?JDvoN&=<2d;%K@YAZ_nL?(SzZabBeWa>LtkltXnup<61 zX$a8aVw)6m$F+5?AXuQ*+V)&t+%%r`k7^;M9GhkBz}@f}td7mcepklOHg8GjzEbF{ zDjE37G|B5!{SGhqah-vvQB#SxgVKDOG@Vwg09H7H3Y%q{hpjN!CneFC~Gp z!SUQ}-l){MjP^xcvpZ(`u=tI7+z&Pn2CG#ZM~ZATr`uf91H7kSQCmA62SMV#eDW2O z_7AFcZe3ILOk14qveH{*x+iY)z^6H_F$dmnpGmL&pP1`PVr`nfNc6v zeBH_0ZbXF)0jCATAcKi0seW9{S3CrzFmf0XUik;nQGicHENuh6(cXDbOujBq$|R}6 z@4^||KUJ=xVY%~#{wbYPp%N6r;!+6F|5N`yomr(+z-|uUgl^{_`cL*I`=+pG9m= zRbd|OQ>G9K%6SY-Zf#H&vv3avpQ=F&DHy=x4QIEDVl@|r$9Q#`A%7}R-kA+2&BXHL zODe33uR7DZ4=&r_lcZLVJGQQ7q;Li_LQ(`}>8go(U(6xyqHP1$sC)2;;Sqk8xftm( z-#e_irY`gL{4Hz=Ma#_;PyHjfeK58BUx$MhJ{9)2EMJdB4`hA9AccVkmQLh{v>Uqt z2LI&&`}V_+Dd8X-1B&Lsv6(EJ0BMi{V$~7|6o7xsF&K9upwTYz+Hdbw5^IQws0zw9x=0e;`qPlze$)^N<%unUVT_Dw~_LMdM~}c z260C%Ej7Fu6ZJ>7kp9Th2AZn@pcU{ni=a z9YV58u-fXv*PA-Jrc^2yoK%?gTdqaLit*0(V_JI>_6uBP>ebvZ5#nFDyt+&WA*}Ps z@Bb!YT7iJi1%Femn3sOhp{ZlZAmPhiLl1dCWKA6b$Z^<}A(O#B>ae|Mn>}y~N^5p< zt1y~aJ{P$;PnyC-blQ_Ess2b70YKSZ-eyX_g53#nVF|sjqftKjSfFL)YyrT?3Dm`4 zColgcE{OW;4|{Q5jX>(=_O&BY8W6r(Kj0}+$GV3jKTP0R9Kz%Nq$YtWTciJo9_Oac zn)gcGE4u|*!&=}T`$+=5bdwtrT1*i1@-)|g0Q04prcFDoLr0N`Wy{$HDERjW9Bf3l zW}!`@JO>SdfH2uIx|vSHpwDxe#m5w6`6D_452ko*iAi=ns4W5B#B*7BhU5&4{V7jH;b|b}bOjL(tI^Y&fmbT~KP`M* zSjwL^qPFjHcs;0&gdy{#2oQ+n)xmnJG+m?7%G8c^J1f>FikIJ3?>~==aI^~*&g+vn zeI1Kk>wR1bmM0}774+&?9CiG)Y-7XZeCT#Bjlw(V1;y#*^v1n3DFET)#1xJ-a#J_2 zksZo(thu@bJIOSLid=CW)W%2qeI*pgU1n#>fD!djL!lU)QKyQeLY2$}m=U&ibh+>M zt6$80G39Ce1934fkjJsIRa~H39~^FTl8~TZ~ZJs$b)7S4rQdzsgVcU;ku> z^*xm=IqKoH)Ka9#QWni?=(^R-NXBam-0$csRWzxJ>6T@^YH7 zjQCi!T;=N)n1ZqGzaqT*i!gNkeM|H!skjbp*HmgNKG0M~{4d?V?@)Ais5?UsDZMx4 zB9@i5?3{nsSq^Wo=u|U=P_XeSW2rp$loi#$HSW}nIu(z_jRB>h?`R3ZKiV8Zo2()6 zWvtkYMn~pIu)At}zm=Dxu(r|~?VI6>f7Pp`(;csq^62zMPf5R{jyvgJ&XE7I zoA#f#ZSszKzQOEQeW7sS#=hAmb>C;7tnQU}bh6ss)(+WpGm?}Odow?KnNR((=C~~f zF%bwjBd2LnwN>e-(jmz6cU+BC1FVN^*Q6`_se>6F{jD@MX9aEYrY1}GT1bY)+U1UW zUE~2;tWePzLx>X;WPkchd8cT$R4TK;)iJ$Tw1m82k1aO-|A4iG!(ajhC4} z3wP_;>@V4T-4xvMe-jhRV!S8oQLS0^%kTNPN4RSb3Y>zmQ*o4; z#<^PGn@s(+8sC&!8)Dj?uSkV1DsBTXj2XLA`81h7g)wrUsD{>)aXyX^nFIyu;^*y2 zGq4^s-e0iiU}5yJsAlsg&^`iMP#fF1X5#?-{m(A4dfT>(O5ObQU(3nRyLeA_9vghH z?`$@+io8ZS?UW~Gji+S+PXv==z_iM1)4$#r5)4pNDe_lhSTy*<1WLA15XozuS5k&u z^iTxx&DwG#FOS$UGx{F=##Jp11y?`c#l%t7m^0L?Vu7kw8&a%yU$KnxWXhqK^q z<{>xa-p!D+@b`WMci= zeZbyJ8GgTYP>IzNo8~vO%dGr zUYu*-nVvt)M+-rT!_jdkd04EDqeq6Xh>C8Wx`B`SnORCqZFRl=H9Zh%j4|HfRj)V&C#pi_w63yv|Z2%sb zn#7ce%S#hc@5ei1GXA}@%&T_$%F$S=FX3d$UhOb&o7&(EbxpkMCLkhHctg5{Lk6DV zxb^q~2H5!Mipah{-k**{Ca>5_E6lxj(|a3?o8HbgZ+%sqob9iVlajni>`Xp=qLw#~ z8k+Ux`cC9ixBQ+s>vv(7T=~Wop(SM@slMJ)Bc-G^uhf~fk7%GRa z;Z2ecz!+!%_qW*3P2p-_1b>vC=ELlxg<~UmkAk4_(MQIcV;)u~E9AcW7hP z)bZUHbJ$mQTEZV~8|9ZM#;m^de(<)zj*-vX9s$PSW22E0I+24j8<8X5ni z2ZcP}mk4YUDe_Q}6k*un3CapDZk9V{pwUvg@2Z3EkJbRN8@05p$fR7Y_=c0O#5t%TI0S}aNwJw>a!}djidJpAnzNu z)swJ2seeK?H55Zwf1zw+EM}$@LJ2uee=WW4-pn$MKQ^EUvS`avV~T14|~+os*qrq#Y=xq0S|dtMWyZci<_43t}6G!b}JS5vMNW`Dte-g5+QVM zB%IwMvA3>T^O81I$?+851R7 zuBFyu`UZX;J60Yia;;0TFy2BM^uewhB~8f08Y)}my}j)jM>WpgieVQJ*)zJ+)joPn zHIW_cosqlj8p>DAgtf&z=#_Ng#!+4Kr-U8}b$c%8(uF-CZ31G;{j;caoI2SC^xuN+ z)e?KUMG$s>;TO@eL(l^r0@cGLv{*w@dGVy-xk6za+f`nV%agLl^71Ed#$>FU5==d= zv|@AN&~NFCPnhF{@Bqpa=l){-IOp8=JP0wJFLk5S2Da+*i#WpJwNd_^1Kj8eJJIu{ zdN!jB49yz;3`^WSn#z-hF7ihoHH z*UkLdT3w-TlaEks44Wq}h63q}CfigQt&*_|RY|w5FyWuIyJ)9Bnc99boM-k^s%UIW z0^J0`zm8>mu~7ni&*6u#l7%6i*NdDhS8Z-z{W2&k`qENv3hP&`vg3G#s$R$>f@|zL z-ZZ0eByt3<_;LuW29^UQ!~*cV;(Il0Yo_SSd(jCEr7U)kc?<}|_+0x*BUu1Kkmt5U znsQoT@{d)Y^D~<_v&~|tL|*g|xSju(GnS>W+4qXJUydxi0o;D>yS0^eaUo`1LYpFd z`vkd~g)a=;b1Zpcdq(*KtJ`q*Z-k0N!QbHW3;X?30JdSw#X zpa)HE28>p)B}D!_$CPEo{i_#>8!b%Q?AikMVzPM5g;R5A*=G}DM$vmy=L4|n<1tuQ z#+0v-q#OQYmHWgN=?_5zvK$3(9r$LAvq`#B5vz+9H&&mY1zs(p;~?o9p?>l~zosaV z)V*So+?etkD87OrYm|ngzd@11!J;|jR93R5ULGhK>Q?)hTk|nwQZGA19*Na+6@AT) z^-8T`A$bzqs}>8-_kT0Gl2waEC-9SR)2wDKP{K3-Z!ZO$q$6E9^~zI0omxJAL7`1w zPz1#K`>``;o0&nI?~V6`6iVO;Ubgs#_Fj zC#-RG#_PlD$4bt(EBNrVnEuH!-C5WP=Mx|0wH#bJ-k{FcQCxf9Ghe-+b}B2_u>4$9 zx`H^E{nubZSQnz+6;BScQHplq?}}Nx;*MM{eZO4QGHL>9SMycQtk4%_<|%AyO$Yu2 z%WjW;g~!&P!821M5JOsxxJ^%D!3}-}UyiRTR95CeC*xb6bJ;I`+NJ^l(Xmm|NO3g2 zB(L!r_RXclmfJl9Ns@pK%c?c|(|Lqx5mu>ay*CFjTyj{1iv-n1S3kXj6v$6&KEa}h zlTl4M3wU@IDFYg_8MDdnH4*&0#4oVaao$T-^cw*9gSyhUsKg`w^_wEw6X?(_0?P|l z=9*vf#S{|3o$8~ODaxq{-ln3iG~ZV=92;g;iUGgRK>JIR&^14CSs_2n3tt!fwPbMMEhA!dg0TzSZ(eXO|Fc8iC5A=fObiukZ|oy{bmIa65xaUj^YUt4fFVe;O& zIu$q*vsK2t%E13nt{5$n3}ctymNOX+j(;G40t=Q;PYnM+ptk=v5qRV@V}5!sus)}@ zu)tN8otu~;MnUOmknDZ>d+WxAN@N)C`Ohey5bY7VnjRNyOVVukOaw77E)k_Tq0H;y?0KPUb6e z3V2Bl?3?ue&5sA)5SsG=;IN;+s@R^3({x_)n>Z`2G!` z%n6i{ebgS~Mgvwp;zmB#Jnq|8f2>P>yyLvBfsfZo`YylEs2K{N$h}^djj=x9^-Dc< z{85BM@Z&x4>5Q~7n0DoS#Y^ZOhG&yMd*c}TV_RVnCQq_>P89;!4g7%EyhJ}oQ*OHU zb!nre>d*d|-AG_ax?paVY>7TVFcib@eT9lfpIFwF6t(hdo%cT+AA5TlVSZc#SYP{% z$x1rmi|*q-mS~>e*x&`Nntnu<${GL+uXRKX9(7Z|c!xNP4z@p0u$FHA4{AQEWPFB| z@PAhy7bgolWQA+Y|K2t~oTdi`!$31fF$1 zhxfX?j_Thm?_0>-`vMt(?9qh2ucd17~7 zzB)hNlYyTO_I(E}9Uxud+nz|2I@b!GUE`Za7Sw0H`QiYnK2$ub)a!4$ALCaD6XF4V#S9of3k)_*w!^FID`XV!`vD<~hV((7d#cUzit9;@WP%DvgKbUf-8-89+H`<--fvVWQ|HEH!wY%kBd)n&@L_=d z!A?d0e=cq3C>6|Y-ZeQp-NBX@AUsoUPBv;5)xdA@GX!@)~BZyVXFaVKW?<(-hLLzltqT;3j8Rlu{X^U9H)5-@18 zGH#3rTE#4^n^R-G+-0T?P z8SBNuVsw_EmoNrn+k9-HnUM$nW)ul1M!mZ#jlh%L@7&{yKH~Ab?;X9(O;tO&Z!bUlI=Q_?!H%T&4qNMMy~L)V#9wp9y($!Z zIkta>{mf7a#C`EGaig@lu86SaRp*8J)kbA0z+SUg&LY2xgJ6N^djYS`fTtjZp{A11 zG1%LecIR~i@3pC0%wwlr{|{4d85Y&!MU5W1Te`bZK)R(7LApBzr8|a3q(MMBC8WEB zA*8#J25DiWQ|g}o?|tum?$`M+&&+f7*|GN8Ynj;$?|yWo@$Ix!aOpDc?`}uvC&%YT z6!2Xq)@N8As7PwywhbjQhcelmq?{S0e7afG=kLVuG|wdpyC(q)QCXifNevV|-~wzw%j#v5&Jn9D%v9$qw0G!W8x~3Z2ddM(=aA@+xQ}^Jit97X)0ZFC zAUf+q+;l;?L`s$YD@>DQzKV1+C&W^n0c``A8p+Lfd;2+Gk?6Gilb-GaWz?qR=qky1(3-Saf!DXLv>~#5Hzw3j~h-4Y7%BFIWEX zOb|D7P~|#2w%PmPcHqmY!9_<=iK6Zak^j@MaM3<%nqWCsx}~c;pLAKEyerI|aPyzxzne}3f*WQC zos%u;bS-HU--^4{?8u-<=8*%^@j@yLBGQK#28{+6vJ#r_6xk0O_**SSmZNPZ|7{t+ zt&D0UOGEZ^F}JctrsxSxK@hvC#fcq*wI}=u>?hjChR!jA58Wz8qjd0O#I2}2(FQx5 zJbh7yzTx#78$O3B&tpPb#$A&ec#F=ca3ISTo+Pg%1t2|xteA2VZW$A|8NQYqqVZ*& zU;q{EtU6e@;MT~lCHmYJv^-qeO0)aifV9|@q4O|kZh?$3A`;1ddm;X4fO(L}yomfl zL%NSDiOfo3lgcFk5qVSFebYU?_M4Kp3yyIMCN4*-PW(+402s0L86?envo8Iwt?kCY zPu_2Yn4{&YH2#$dz0Ut)B*lyG*YJ95=`5}5VfV(D`47>bZGH+UGsD4oLce{;tg!py zzJbQqeEm`@G6$DE#=ncj@xQ{kfD~PW??KriZ1L|;p)PYo+P2Hf5N31*mX2!7bp;X& zadbF4x;1sNL{!>;y$S4opJQY?(O%OpZGxkr*!5Zaml`p&`QcG%+U%}QG55rz*;=~} zW)pS{fY4w!_xm@+c;ZOHb7CESOe7qFm{M`c9W&Jhds3$ZrFfe-sZ9)HOqwzJO%oXLdxnxUF{G{1n`{}Ta8mUg>)wn^sOxt{BA znBKrpC_DuKmK6KPILd!6-!MskWWk7(kUu=k-l&&?mYy0(FA&r@R4|Gh-AZE8tYKB# z)zM_9l-kvu*#2U`S3n(t5vgLo$4JEuR^h1=34K?J$RYOj;{bk2zXAU%Zr#aM13{ys z2ae5gnt?c-(ZwK^EvQoJL)mZDS6T>wUoz&>UQg?Vs8jSw5lB>iQMT6d`Ct=fPgRl@ zCu4c3@=6+;r}@V#hM&$P6kXGozou)gBA;lX&NcSqPR1rk@x)kDC~Uu)gm3!qNWO|` z*-9rSCsW|lm8h-v1xv+lJ1-V%CXOG^OTnA&fKMLR>lJ!JSzHT~l_Cv-{Aj0APuo&Y zemJ=Fd*@uIsA#1di zZvoL^6oJw;U$JZ|knk~Ru6EzQTN3i`wpAV451mL|5IDcfmO1!0B_UZU!M{ z8-7h}sHBOSUqUU=crfh;edA*Obt@{Wi2v%9)L$2g3cXtXh`Vy1&0 zkIW(_^QtDg$(c#~j{2K?I->QIg?q$xGyja@{=%<;+R$q6P zFDeP7y?uW^BLULLj_rC3VTUF1^Uh>On(x|_7A;2(r|0y{f0`1FP)jdJ%%Cam78-`{ zf;ByZF3W)v^m3VhT%}e&t#+)pz>TB#yZZ(U{I!0yk=&DxqWTckH)ufj;@l#eKlpcg zng)3+_r^(O4=g960a{*{01F8kzYlQx(j`FZ$1Z#@`$;cG_x*mVdCI%qf?~41jl(g~ zX)cvm{?8uPr#A*dx$@(`rFlt_5)Zr|N+ zJU^W?p`sBeMA5}xb^kCAaCsgT^Au3i7 z?ZjGpU$)dfLI6O4yvHx>YE&XF7i4<5Og#PhkR?fQL*`2t%cyc?$rC4`P>jLNLj=~*%r8K}j#|9tR@*sZ+3+pFq$6q{LMsQHNH6u&w6j3Ew3LEj? zv71i}H4Gpgm;gTt0f2dh&+eY*PiRXljCxAKtZljEj%v}exvj8!`EbHnnXx7McNJva zNWZ!$4cb)BA50^mP{Jy<7NrA!*blS{8e^1TP7+w{RD^l3W$~ZCXA%%t)4|9|qkk zOM{EJN0AtUeRJgQGWGak445_BzUcBK}=xv03%vP&nzwtDFprw6X*gu^NSB#ZsWyHX* zI1cu;JkO^QQpS6nU_yJQ^ea(k^}LppO_APNlT_{rv<=xyZY#swgC-eD5dfQy)+6h% zbDRNW{#aeU&VOlQ-`xEDyPqLdq8?lkwN ztIlOChA^w~$56!s+pnIN){hsew^6sUx0i4n^3-ZkGp}ucLowaNA?uCS^Eob&kld(o~FRKsojb#@jC^R$!AI zdWOGo|Lnqm-qZyN!Mk!>qI3I&-(dWxM!pjadb-F9%8@~n7@zSBkgsnerm`-SBudJCXQSa|oji^W*r7OZD>jH?yGXKiuNmx&ycUH4 z686^pJF5q|ot(o2AuJ?v3^{q>iHw@8!qwtP*Jy(X=ChUN!5>Y|;}r*tm`LMiT;t#8 z!!lGpoOD}Q(-eA%KY=$`gCeYh#qmP5+hdSpbf=Ql)jsekuSqSHcEbH?qwr$2dKcfueC z;bBjx>&`QPhWp@;o^|l3LH`A7vM4m~TeQWo>dYiIP?}T$Asy?&0j8pTIhH3#W(&@` zRjnF_eh|~V?4y9x?P}j zgEE*_dmc~0A@J?Ts{TNN$DeHKykT?m1o(SO4SF@l3^5ER?{w)VNY--#5r3Ii`$X&r z7!j7t-yhbY2kGI@IS3m&yy*c%+Yu4wvIp;;ipY#ahG6;iDN2D>5$~JS z3?ob?ODB`)L{|&~DFA%xaS}R|@1HAj3K-E!H3?trU)x#4R~J_#QxQ~^%zZ*hB+1z@ zEh7r`s_v(cNsi+mvv_+NUieyTx-~_q+xBK)Ou3jqbfRj@XddTE4y{{tm}tVrALZdw zDYY4KIrp66p-%?U;>O|pP4(Nvc9i(&By9hIE&BV|t>u-2%31 z5&*PWb&_6a`yxpS9o-@Wn{Do1v8l|YekMuIhjOFgU#j*&QwtFQs2t=F$|U;7D>NeU zTPU%C7Yw^hFzn!%&0#)hQg}wPiE#mg7r)(;^=g%)vr)na8Ya`ykEW&K;O06qk?A!B z!?^;Wz87_diJzN&-4{vC+iVozi+hB|=s!n3UvFh|Tir-qW0H)eX0rWjujLI5bXp6R zA!rX|LONZSpbpJ{^c_Rhop}3F)yHVKGOzWWY}UJ5uEvMgb>=Zq4WNgGOhjVhq;QVC zrBqd~@_oYNZ^;bSEjN8Ym|xu6ZiehHTaCWi*3ycVeCbC*ex%NF2%MMfCYN`_WN3~K zMZ#W3mu?|Kg!uXe`1<6W^hw?BrkQ1SnvLDmLJYCra8C)}|9 z0G9+R9x|rms9x0GzBdWdz%fAyybrB?Yt`Z$N#UMCL-h#qg z;HFL!K0gh%B>JAX0kUTgu?#n}QDp`IkkNS+ufIPI1gDxhaPCFj^oo?pZH-w~V0|2A z$)+y6n&^iK+J8sW0h-p@Tm>qXH4dqdi!urW;~ATvikS`^V6e$uyYa$n>NZ%g3SmK zjJ5Qao_>fA_qCSX4qz8D=-;TyfoXlzGHn$-VH2#_!=|j-fnYoqatgg@Uuie>L%9b`(GQ8 z(oC`s>x^AWotiFmlA$MY0ezx-Yl+F00f|;taXsr(BNeTrrzk^N&5L1@i0bfu)zv>K@&Z+HJ|6X|LGYZKRXzF!bw zO9%JRGmT=%x0MtlRI6xD9-~vlo^csjefUsq_BW@iMDeCH*biBRZLN)L)nujmqydBLciaUI6vJFs6E9sM{7-cx3 zG?N(WlK;-0X@Pn2wuW1R&oJDUQww2gy;wR=2Pl(2!v5WAlteXW|>-E8hPOdLu{uE8`22s>@dD$P&GfONRu zAup|e*kz9@5N!Nub%tTm@3p2KQIYA|AN;1W;(6{#hSC z$|KO&yC|5TSS_8l+Jf)BsMIk|NuoE~CkqT-f>&!s+Gas?EB~ zVIxxIhY5SCRVuDU;y3wk&e=#3pmS#h>Q)G=3<6%o1>*(!%H{*6RuUfHdEhBzWw-Os%GgH|J@}GQSE}-$X8Fvf0M_hZQW9D$zIP$1|os#k8mf z3c4aa{QX01JMvTFi<;#61tpOS4y@)+`_OwC>gad3^q!`G9?*?U_S78QHIxA z?0TDw0El3;f>(kkRngO`%NB}Dij2WCC20FN?kk6MYnj^(Skut-GuyQj$dEEP0`dPZ z4SQ6}Kkjk}{Sf-rbwfL2bbHk&czX@RoG$4L={R}3Fy{L~Vma+rL`jk56XA5Xl(|nb zVVIN8w=e!^488H!>HZg$6b=EJ47DE_O^$R1+nT!$7bXI4soPkvwiw6I08nh-L75gl z?z*DW+5g>zOW{4sq$5uwu5odm0*oWKaCq z7E32Ojxxr+LE+460<5~Lek|xZ`6n>B*5nGi=R{I%(&QUo2Q+kO>*!fao)Qf8p2e4b z;M419SFts3wD?!an+X{vOsPDBog-%axf-T29y3XPDFW8(B1X-H1SG(4Sk_cLxo6*3 z7@Ka`pXwb|M5_Dfo4tKTWeIGOjjr(M{w2`Pry-o)mhpq}ypagViXYdx@+bd&4T9?2 zb6Uj~Z1pi=kMHEaE+GKi9!wDu2phWiKP{{^9uK|4;@`smt^Lr0qavJ_;KDPMaXlZf z54mF_>v#Ca9JwOpM5U_Z0+ty)LuWrz_xE&@<6{Ts#Hlgj(y8&?U{V4T{X}1VrBHzP z%m~+BCIfQYS@5lrOJ!kqVb5IV?CD#q%9|m~K403Hb5stv#AI@6fX&~*lbN>ojTDVP znznU8RwbhM(Q-H@rwuPe6vPOu5y<|`P12zgT#n^&0cV+sAazT@^PNLwNT(`~KQ1zN z^TS8maA&rsxW(m$u;Y*Gt%66PcZ_F!qCJDVkABV6pjPg`8!!j*Wo!2bS=LmAQU@Ya z(}%i7ZltqflH7jSXb$T9Hg$yxbW_Fz>mdQGi$Co^lI3gs*&uhErt-zvFy$^MI{fnj-<7;E6d3>Vpv^vuLceeEAWGA5w^1o<9 zQb27W>o?5{E7Wb@VPj}1mon=hzu1YfG{0QJI1#U^2tTT7jJHWTanZw#8_s0lvyy(CUHCzP0%Whno}#*_A7xiASo=?;O6>Xf zDSjc2T;Ah)R=D7*-}!QMk!}4PuYzrdHBPDt9lnba+dkp&=I*w&ul+S<=FZZMdF8YI z%m&b*x{9`nQ{nP-mAFKl#h|mbU(U-61ixjj6L6zb_n8NMzJDx#H&7(~pfhK}GTom(fJSE3$_FL!k(SJID!HSljHKxZvQpM8 zBtlE8q%}Xzzx5^Sa%XwBNI(J*5)i+mMleO`NOBFa2KTjN^nx9h#ND_A2Raw*Un&rD zT&pUY);oEJb7YkArjFT>`b5byg#0D1IeyX0NY}STkkr_j8fgOF^s``F0a9)LqdWcjZlM1=^tT zI~25OkZsk5cP;y*6Qg!dwr_pZL&S~Gv|Od%red7lCMI7zRMS-a8}*UP*^nZer4ln zoFXdcXny@fnUi<&&JT9{FL%{G{-T;E{#qZCqd8%SN5(0?-|s!|x^;8(s$&2%nG#9x zke%{6lF>qRNK^l!%o9o0knv{C7wB(MakT4j7->Y#poV3t1P3FnYrd=K#n`GY5ZHusHhV5mj(b-JK_KxD}GnLQ%MM!`K}>t#H(kL1NWQ8i;a+_auDL?|;dJYB}+q zE8*gd#%Ds0!n&6w1u3hCX~uXsVB*3E;59^ooJ3+j#XuM7VYB9 z6mp;2-uPoH1^? z!@hdW$f(2ndU5FLIzCYx>xf}5z>&fdr{1>hxjdtH_2s?9KHbSgkENI!lKq6q{{0|o z!$$4&>ko%U@`6i)l;6OL7J0CV;QOA2j4@>T%T{0fe5}*Y&6cTm#LMgqIr=_S6o;9Y zrHuvaDqbD|nhNIz5W?8gmUa76t9BdS zgxQ2e)=IPf3yzF9u|R3}oI%}pyv zypWF;WC$7CDw=hu=~OQECTvnme@}vs!A*j$Y>PQv#bmX6@DVWh%IJX&u9R}8{C`AW zbI=UTcQk}>7qnu4NQ{%->k)U?BkT6P07Abekd<`zfbiN9&T_ii%67kP4tM zv8Ykor$WkNc~F7^(UxO8Lwl@q+|?_6aYx`UbUx?Z!#y`>*eP-n=YEyNXty8US{%u3 zE`Lj4{48s$#~36OeK7lta$cLowL6Yo&1mPi=E3SYNFZYxktoqW;5VeiJ#q%?v~Or z!cm}v87~-u8CTdh=rPEGd?wXrGkPMYWPln7c0Q34bN^q|W%FmJ^CD(nB@BBWSm^;v ztHpyN;}85@eN-e5<0+OmBJG?aB7qYpcTN|@p@?7EnY?3ax*r!)DBKHoK3owrUpC)V zqPPjRg)L2Nk`Sp_)5olJC~kG}qlHJL+pmm-DF|v~wbzaM-o4on^ZR#2UTX9qTygnH zMWK|VTyK+ngWPwi;=)LR4>7R_7*2HTO(I$;c#ArZ68pYRenSm+!)d$RBK9uIQy7j%7o#5$#cTfdJI8V(e?XKzf1UI{5}-Rn zBu(7eXne}`@>+J?h%s(KaZoo`{+Vrr#7Q7@fXaHJCtjsHSNV~4Ny6`D+_bO7a^32J z0w+UVh0Grt7@`y;>Ge^5(EiFqmVB=uuu&*9rQJf34GmCVYf(rEkCunkX|NV5& zuB7nJEe*Ai3_3TywCZOIbj5be8wz0(h3~(8V?rq9I~86y%iFX3RaM9>)0t>;`GSmC z`wdc%0w#w18G1sC`I#Ri7X9t6QniY%z#;_x?)}J&zNZq zQY-DX0Z4dtWWr{Z+V+MVI-e}Htjea~+x>xmd=T+>?JGVGi^dRZkl)s({|C?Pv*-@o zoO2S18)IB+J{F*oV4t-b{5$Wr0v0_sJM*{bbgj6}YgfaDLt(Jq6Y@%%Y~pP3g_YRX z-oo0ev`TMob}CUmtfSm49d#!ls3IfC|0_{naP5~i_f|W4h*o7sEm4P;a28pX?@#h^ zEjF&Ug`N--`7BmocK1lF+s~bO93Ou_19$&clWAD&p`JIfj2O3!8?zM9KPnJp!2C#L za4yfD_}QIq8A&36ltRG345-Ab<$Sk0t<_FMfdZu?xSR zd4y4_kbVDOjgbTpTNc#&nEN8=ds6cD{sT=A4sG6~%S+=hd)Id%Kro>tZkVTRF1_F< z2V%18YLva93G?lQTx}b8q~Ki;M5bn5=}Y?$o0ZD+_jihFF~UPm4(()TRe5a4U_%ywDo-|1p-}*0ODNUj+v_r96GzwdsPiO$PP`|4a#&L(bmA&zF z-O|!=!sTs0n;u&ySTSOl(|#rOIM~$s-3@VW*OD6dJ~<;j4#CQSz9QC+zyy^;Ak^cgDfP zVdFi0b-!<^6he_gkA~lL_w-0um{)9Be($wzIG!hdy6$#MxYpr9ycCp0Mktja!ebCr zKt?>xy~D+ZV@Grw0f0sX8$;z{*vw?p596g-GIOd|*ROA!PlsvvZ+EEARJkSG!_=>Z z2WDhBjweCV+X6lgt29DbacPDd(}5U=yX6#nbkocdvCZ)Bewr^o)T<<^!WI)-1#ZPU z({6&12E?M&tgI>R{tAdWP8?(h5&^*sFA)Sv?GagWNwad387srY88&0PSbctX&?|vf zUi?$n`uP8PX`fCjX8R?yzT|$^wVUE`rNJ-SDJr%X4h^nl&GU*MT@a z_USuhDA{AEKx8wG812>mHOX@Wd`amn=)dcG1mM^N%A4qT(bp2!jsR?cT+qCiBq`U+ zAt=D@1NN_)35piPY)(`KcN#%)xjjkum&P$Sp_8gqZ#zOx#=LdjkrC{unG$!!wWis- za8CqQJ6dd1Yd=OT;`6=X?eOLnR$tZnT3*@s-HKm9{?DU!V&uacTT+eWj*%b*c7b1{ zlG$sE>aR)v3s|pSz3}-5yuJ6C5!SHCwNrz@mL}(Ofm|T_y$h`k7E8nk4T7C@we;s$ z^*tWRo1Af80%ST$RVUqk8gu^ImJZn^(>hYvgHt~~0LU!?)!`>YO)0DKp2CzbGqfyK zGA(OI>zb2yIlhv+7~=pwVRF|QBBWW#i!b-eS_W8~p*#PeXp)#mA4VbNn1{=#AE|C- zGsYSIF$)Ur{g@)IWRd7b4Ah!`y-@K|ed%?x_pISqKl?Vl?}xTq=byt2FDGb-y0-=r zcVG1`epy7MPh(HNpfeczsR7B54I!OmsPENB$`Xs^{P-2~8}E$G!{0f#!U)QcAvS7- zY z+4oT=exzcYoveHI9Tb&V#JV=&d9sqbGniwWamfw26vYHwCI@6WNS7y^Q)BMN98%YYti@T!wSw(me>T$gVS3Z?l-)B!X^J90@--L)PBF7GjB=b z06E63#Ktd*J_;5Q&_#WyKgakrzhg;99?{~FOcru*x1upjt56Ei|Di2?Xz zXK)c& z@TJMM&#d^dSYfG$qF{xmxQTrdYAo8;`H=9a@>_SI6UJqySBIk2K?{TY(#JZ`U5X~p z6%zTW&H;n#GLj)8BQ|ivTBNcdei%c2*lCq+US8o z9qV|*-LW*3S#;$06w?2EqZRz|hWCdsU0APvfn-mOqvcPo^W3`R`nlo3FM2;CAfb1! zZ$9F)x#7etSM{tn#N;6Hde?3*o~}**v3X~%h@Qk?ukd!CwqC`#w;Ytgg`gNtm({ojP7G0o08n!7~fk*uU9dEDcAZ__3p$n zJd?ac8~XUtxISa~P1I|)dULr~fEu0}s5?O|h_Nq2ZI?S6I+e0Kn^zrnSlLZtVFd~k z`S2*N-8CHKp5NKXj6+A1hfjJJnk2;RKaT-A zaNPC<`FPmPgKFbduC|DQmtsapD;*H$2}IxkRBdwxQp@SSLRUGSo}X^IHi#p^i{Afm zqBi>)z1QWX4lR}uEjSN$5BdQmO8~&nS}@s&1a4XE2{#-KMl;D$?sKdA>VF*c6GYpO$oiqnem z2kQ9S1b@E?1KYOfY}WB@hpr>`fzM0&q(*U5vHXtSC(!G736sAM5*zA@;!uV#mNJc9 zg-T=NNr8d}$~sGGZlP*~M-*Yih z8DUpua`JrFP2w6QmkTg5)r~>PC-zAcuAzEd`1ac9+g@_@goetD>Q$~-3HR$2i_$>* ze#~yMu9*0HM*zU}q*A+Q#Ls3Pb3N>d|j` z4KJR61f6;_Rp5M@XA{L*YZ@GwMi_l`dtSv0%F-~Kxjy6172`KhS}V~tgWJNt9k}>2 zI7&1tl_c78$_8K1B3+Xxan@%qHu?_aFl zLaF))Mf?m-;xN66rFn9ZZLHv*XkX@ZLXFtjoz7;u*GFvyd;a|-TAnMAWgm^z+~K_7 z;PX2j5pR~i93H;w>UwJ{T3nJUd@ekYILVxoQsve2Dr@XWa||T0&Wot4HUPMh+#sUp zP_IgFyJh zkYm>&ufH`d^VnV&#=0H7==)#XEhljbk0!(dJNjg=St`%T#~3fjW5U#5dEjpo^w#S6 zn2}hXwUmbfZLckh^v9Ud!aC^OJs^zW6`QzFx#w&-B|1e9jlo;jPAiLM6gmf9Tk&a+ zQfeBDQM#@za(Dp0Ss@S_En@N6_U*(+(OgGeV)Sk?8}9UqzUs48!AV}fHm_&YcFWwE zJ!ZVUfr{uf`rvf+K8cy8AG3W!stKwJbiAg}8dv9)lY}YH$%S6TnVJ6C2OAP?1gjg_TTSK+jm! z$PkA+V1))t8oQ&Y+MK@m^#{Wqlo(>9bK4S@8J`^n{^1}&q>B|ymU=JIa9do&Z2xxb zZ3`Aoz@7F_tH>1FfpJqCSkPS57umBn@QzXtDP^$SC&h+PXrElo!+26`(f6ZowCM@A zzX--}y&krsYWI13Sg)h3%!n5FBrQDqVvYDzf0Hzk{cv=vt1*DYT;5>I;WvJpq%WTE z&q|Hsu>8*Og6;QA`kNUTNf<75#3U`;3vtMBWWw4;?eNx$rtWG~pcYhNb?6MyKvsSw zl`TJ}QM8CSwe>GHQ+I}1rLedI=l|qNX^+~%cS&VCT)IGY*hTk09j(}=y};aWb_?#O zN_OPk{c0Efrh7l@l@0d+K=!_6mLAM^m-U}>IT!HS#w=EWuyMfbN{zCv}H>l zIQj;5#8G~2{~+G(TZ0(b-kamqk}eYckHr1V)AgR0Hg<$&2PT*mi+6)>P*HaUm+B@+ z{HuepIAm;y^E_ayylc~zI#Vpj#)}5mSyz0?N`CRCe%c9VLNe>Jz$8jK&H3w)t)x*s z&3sqK%OFuu=m2f0n40euILT<>WlzJEyA|nxvCD={x8Sbp zWt64m_n8v?kh=Dmk>3733q4{gjzPYo6$O&u!_6s-SjsMMw*F0q!n^fjapi3d;*2F^ z2NG;4`D70K5fqEPsJLo zIZ%|od={eT9D#fNG~oi;S3zZkZ`VWzoGJ z(LDKjB=!Bl1OgI-FkJhJ62)j79l#HM!Jdo#WEd>ICqWv|==iV%mzfd4q_~9FqnrL^ zU%+gZ4x%p*N9YB;yNF`&yuv+gG#NzObXakR-(KTFi~qsmU9L-NR*pEaFRO!ck-`Rtpu)ALV}={K`YW~CmrHq-z9+CI z2*ZU;hxa*At}FLa7>Rx8wFREU9kn!Yp0)^F=R4Fa5??byF!u38*mJ)Lq*Fdk7)BCm zfXqn5j!44d!f}4YzT1w^OpNTAf z(On|2kFBzZsOTnD&oi(pa6;-bR&qYCg#^H#65=x^xQ2Jdh&o9=^X;CdYGsKnG*1;* zh55f;;>b)u+Kh5)t%w<`!ygna$e*MbesQYf8iGibmDRPM--0F7fG8*Ggqi7}yN=-@ zHC7R^{y!qiO`)w17o8!O+p*g_kX|naOnO-c9pot^(G7{}JVzp6Gxc zb2+Dpb$d%4-ito=B=nCKk0E0Vh!p^XnE#M z-&J1vrievjbR57+8y6UR>?wJM*3u-rGn?IOvQ%dw=tEQ6nO2`KEHeG=5SwZN7V zz=f%(EBV0^!GA-Z$Y^@isV+FDiKO|*p9+agG5-@weuqC+CpO%z6?c~XvhMbu6Q+K* zluoz!M;yBE#Mg4G?}qaU!v-@G+lz*M+Z3AK!OOCxKGva)!CxjK^*Ie+tHciE?wHi6 z4N=z*`h|5_ed*Pti3ZWB>VU@1zWqqjIwB*+dX>wqWEI_p^LCS(uE)RN?;FjY9$BP9 z0jS1_{ImYr(rzmLiSTK5n;0F)%;OZalxGJPaNj``-Q#ZM>eyBDN(Y;{^7-p|GTQ^? zwH8@{;MGUkn0?^}Z^nsN($F%;n}W9$o1v3#aVoZSg?F}v-$f{qrC+_k(s{9)PP>5N zK39-ENtx|yb)l#W;3ltzjC%8yIOu0zl#Y&5Jb1)|>)X@E;QD&2%tvz1837jp0}L{x z-hl_ja*wC1Fl-!_;ejyM}%h z0m4|*eihIu>;43$_ppD@ehOI;(R?6Py_+Q(_-}Sr2LT`VP1rrZ{ZgP2>#}Lp8VVr1 z@%;Xm*sRgbwFk3Ex%dLn>pF4Seni0$#M5EJQjh6kw3>$n!+c+iys?ha*}5q(bCKHb^Owy}uUi7q5QBc2Ii%s${yy>gNuwLHd9X#zH!L)c zII=;dWlpv=qg*7#`~!cBxsffl1GfsSrFftYgJ_#xE9ab1<(c2q22~l$_3NLZey?E7 zyP$LwI!Y5qd8Zx9ZZ$y`#36Kcg?XZwIWUH@Voh#iaTZaIYLGr2bhn$vga3PYd7~J_ zOI*|nzNRVBZDXT|p^Mo2%^eH|+vdac`5&tTd%CDmYeYy6Ow(P=JT@SejyzNoy!1Ca8 zNyrTF=FRB~b89re`SjSvpuvqoJDKa5_$R&y{pXLW9fexPMO*f#3+aT|`z`0@B$}E0 zi#EGtYYvMlMcrU@RN#?Yht!_6!1vBMaH9RU^IdHCvxDqX7C-U0rJnDcdbFB0*0_O& z<@%Dwe^KEXqG@Jw6g+4e^1!^G?1v_9y)P{#NcS@z}*)N--mL~guQh%4Z$nFixE6i zIYn-BpkOItOyK3lPUC*{x~-OMmQM)4$mN;=Cu{yN{w&W-fxi_3a|OL8bxRJ@n^L>{ za)!5LdAgG(yBsL1a+J9-AsHgUC%^B9NC$AQ#Bx20?4KO1Ja8uuW4n;%JX5L0)>P3p zKDK{2E(}aJu<~u@z*{L;Z{cZ3yTUh97hF_&-1zsjh^~-)v80JSiv;lXYaBo2MJ;>9 zAXdLgYd*;IyR@I*8%!LZ*rbeIP2ADu;?o)$S*y^T7zM4~-dmeLxtG}NYA2t5jnA5V z%bMV7o{upoOVg0S8q z^f*lXR%CKuv7-VY4QDIMF7=nx)mGEATvyI3x*+!~l?&9(NBPACJx&OE z*nS4hQ$+LB?PoA5qfS)YK987DG$n@06Fk zJnZEQ87;>Cpb3cysfW6X=;!|zvU_^$!=1tOSe`)$tj{{31P_8SmKR+`>{$)r<=~3t z{xP9EowTW{!kFYpE#Ljv9CDn?fvJb11>3sLun&1a@|F#A2d@s#ur=xJibfjuX_14HPuPdzR2j4^E+hNNpUJr%QIHHd4`(Ykr?O z^vJP>Q{F{)=c|}K@{oLl<3SMb4_8p{P~5B;A=nf8)^+~ALWJa;*j#0ChhpX-?sty>{`Z_HI`9;jCO*SGn}xrhz4Y>@zK?vlzp zZ4nLPh5@3l>1XSO*II9zyWen}Ayjlc9U- zH5_D#c}*1p^(~Ebx8G+KT6(U#0*b)J6SQr{%Y!rFx~XydGZlhDGa*614o` zW_>9=U;IT;qyLAfuY8Cq?zTQdcY`!YrvgI?Lntj0N_P(m2-2NOcgX+(5=wVUOXmn8 zAqa?chcNWK$LG2CzJCB8n6rPo*4k@{-xqgT^7YD2qkhCOM(b|`VsWE&~npbLd5aQ;La?N~WiT6m=F9j%Fqj&R$;)HzVW_WJ)dL06S zx1~n7V^YI2u&jL-YycFs9m5ZeezY3G zKax4o?63NvuBPsK3oJT+?fPPA4!<{W9zPHB?+jGt*{-iGYa%-nJWXMx)u*#;v^XnV z)%&t2{$~1av*_zsTDO1rWLb@eXY(!wu*ZMb%g5&Ms^KCrc+mIh<6Eb;|K&^NaX0gs zUBNU24eqsd7V~)GP&@CjP+&i8Tz2 z?lx7Oj~$%17M#7udX;A=CtlFAfi@q?bwdM>_}HH^Y44zOP?c_Z$)3PMrL(=@;;)u- zS|C4CGY?z%!^`ZZoWJT*`RRYhc|J#VGpl`3zN&TsZM;c>NQhHXWcagBua7trkYJdv)6H<@<1^}zyW$li&MJUEWT?30qCbpZ{CZ4@aOCi$l~Qn zD~8@@?JIf1gkg?zDE#XE;O}KJjT5>YS%Rk(5Jt?I-XFj2;21!GJB4RuUXwda(OIvg z-Q@e#YtY*>v{4}Z-3j#^b=$uG-|HKVL9>Whr}wt}5K~qM`_TtrWTf3^UD;J5SELKW z+hT=$MY%B{9%1du7>V}we4R{DmVM+bqh;$bx04$jV-$W^o|G@?8+`RWFQ{Xg;Ns(H z=`xvTe$~t;nFWjIIQI)JO&UH)-QV=ji9R@qx(%k}JX*38Eu5^zYq8`d-_Qk~gfajV+up5vnK=g!BfBS3)b+2+OkID%wnp^ZRIN;2a&=E)N z+Z+bDsM$7m+v*9D`)dYe%b>DRM;T*1+m zQ_0&AS}#3%j&9Dh81`Nz?GJFlVHlfX^#Dm_#3dgH-YmNdn5ZqQKjC()alUU~*cYPA z`f6?B8>csCKP>}s_!EW9%eB_|MJL9kcqNGq@|oB0#H1?Kt+RhLY|A(pXD{-RaT8YvqDc}ShIv>=e@BcfaXRoN zS7CLP2grXP;_rskS6$6@9(6o@kgscp9y#CGdS>YS$EPKdi{o)I(%9WvGXi(%c z>#n2RH|B+A1(KLyFWNZx0aOB|iRaQ9G-T!7Cb!QYci$uTAPWc~mQ<3DC275dr@Z9T zxoRyAI-gldLz??AQvZf$GW@6a$g}5iKDP}N(xip*p=L2e6cu?K!x}%+G<#?ZvA2IT zK|SnBG3*qn=6qE@*+{2#`BMz$8Ya&y4-l zVz_t$Yi&||A0n31NQ1?K$DAnZge|jV{X+6PI$Brt|n%OuK1>|c(J*ZLdE z7(4z=-Mq$ck{%t-dSRbiI_P@70UvL;(ILnm>1{fh39BnVWMyVYPXwq87`Pp>yJ1>j zg7Eljl~ElR&=zdU`ze3hyUhKMm+WE@$fK=#KIVR zpEl>;=E9q4MJ11uY}zlhM&eBb{4%V3yu|7`ceYf8_Zvx z%p~SC@jZI~Mn;{^QraXdb5eL(|H!;(Er^gW4Zg`_o3#q_JE`Lu*Ugu-Mnf<7xlVE| zYo*v}VGQw|;8cC^YgMj^K6`(@L+{4mr2otZ{(lULs#&1wiyHhhNuF=8K6d2NnFv&~ zh@z8C`HDk|-Z{i$1m+7K3rZrYXXoFAV0?+0l@g!mN=F za)u5zcE%v%##!Tcb7j#^5Flv7Vc6OA+?IuPmfiTnQQ=9yR)RqBWQ<<@!`l2YX(H}>Zdy&;sSpO)}fwlp%|l4*W^?e8TwvNBTYJ1)OR^{ zqE*KKkbj7AVE}l9ia5T%sd;lZU*yDP1i^c3jS@U8+Ufko*J71sR2>k}ilj$*Se-dM z22qcwlJ%1E0oaS8x(gRnux)Xa0~bby5do7HRR|w(6`~|sq~DG-#KR!$6dwpNL+`y5 z>*a}at#K#bBlL;dV4XJYMJviK)!kS#nrK&37q{-yfwQ8$9}h9PiIdb zyXZg5mZ7~olc@s4NOnsI+qIM1bKkDMueP2|IjuA~-^(N~Y(I8ja2bYFE_q@RBaNfX zKyjfp@g~4jNAs&pixWfyM=3<7YH$ooh^_j&b$RT&+p&x|L5JF(>$JDLTsup?y+M8p z7am=Sb+86o7A%Vij_j$_x~*EDb@;VM{2j-Fb8>D#bo|Vmq@xkS+3ckeeNefKz(9on4H;H+8m&2sfE9440R72D)bC!*JGtP5IA%ljd$@>wwIV zK*6xJC!YaKWIc2cYay9BgyUuPbw(>A1f$0OlA-P0e4peC1Xs~iZV&^I|IT>8Cs|`^ zazr65;)G##)d)Ku;c#ud@`;-=#~TxH%f9J90e4(ja z7^BbO*hAF0;xWq#)-vH$^4ik|F-c3ACpdC*yh#7p4=44ZD?TVzWyJDaG2vX%<49$D z%uEa)17RaHjb91KeLCc#{_OrgfTFMJ<;9jwC0ICV!_plrfw~HJ-_}3bpow zT2{s-G2t9nCY^gsy4NOCSgqGJ{j^dh_q`y%Myjihe!-6GU{&f1kP3;p#`TuSl+sev z2KEWJBpQD2*{eB?6G0gXw27LKzLS^Mkn@_5aEk{GN#!@)feZ}&$r#$m)|^l} zs{yRdUjWedd;e-Mf#SmQJt9vl9N0^7;>2?P$q9aXtU9;2}i*ro_3Y&167Ib`TvyoPVXCErv}w?(1Bhn zYkyt;7_B+{{(l+C5sm$tCIP)UD;Z+Z{s|w7FW4ttt|D@J<{in}GGk**MBkKCHiBmbZpkgJh+YIHf!n!bcHFf~UYUP;yJF~G5L9Nn{uIu1^M$~%# z)lqtC?GsqE;2h!`;TTv>_F6Mka=M+|ci7Ox!BY!~BlJq_^Y(QgOmlGG;_l?V#b)^C zM)2j~&`+5c0ZY;@N2751Y2b*r8Vk^c<4aA_Tzl#9BzDdU?D1}3IrA)~9PL0D5~?Qi z?Az=RoXhdZ-S*L}DA?F8?}k-*80-xt-2KyC^~ubQgj}<|3539$DR|#ZTMs@Rr?yN( z(EeU%*uzZcds6}1Bp@;HDao7*kg(6z93jsxnI}XPNOGvP z_VhO1AI|cQUTI)CUb28B1HW`#L&V_QTA2t5`1ew%_w?&KCBU2qS$v&^);C z+513brDemTQ#qx=hk!hK>G+S@IYhutOG&WJ{Me$SwQ?10=IHuAT*--1wYiu4t|DA2 z{6L!kuxO#-(4W(T0o>fuR~SzD5lKfXpXe>0x!`s zTL1X`+10i`@*zQqjCa4&i{TI%PyOZzhMSXlWo))q>n@f2#V8^`X<^TwcUREVY5_L^0iWkdR5@mxb+5#JwJ$)!E$$yJmO$p=CqKUTc=j6h|dH^f!_ zKmWw7`%ALS6&i#vCU^}#9eumM?lkJJPQ|$gJQsH}pV0i+=NI!cja+c#QuAZgBNH}j z(YhPgN`!|TIOvfAB^WJq1Q_6VHdu7fNi%=50fKDkMQN{RyyWtV7vxjpjWOtU?kc+Q zS?<{X+gb+Q^VUchcs(0~4XlVabp3pr1MNlyO5?4>h-Ge{G|RhnKvQ;~(+0fw?Et>- zGr2OH*vbdK)gR7U;TqkW6Kj>e((Zx{F$W?e>ui?gTS3al?p#7&Pu_L9G!XoToj2`R zc$$y5AMoL1<5g_eqMBxG?P~%HNc28Ph+mJ1^U+1~pZjxSk`+h!@6F|=lPjJT4fu`u z4%jc}uA%fcziT!8x-eTS8I!8dojV=l*G21Xc0Qv@enXRoKr(nAlSF?uOhx=i&m=Tq z%$^Y`+|?l)EQbJCno#WziE%Wv9NMr>XXHYPO+Ai*eCtRC z%Wn)DHGX3d4sIB4V29}-i+GRe9Y<&68OoL|WwWt_dNQ*@@XK`Fhn~_Hvtyd)2!@Gg zxi9qx$S0U6f{Y%je@;H_D7dXK3}WjYd$AG^&S$tl(pSMNJe}E7%?~|-8>Qw#e%{`H zz*mawpyYU-9}D4zoANs}UTEzBd@!$TS-t;TD(*HYsu%&P4yDcrcbxh#fXy(Yu?O80 zkjFo>LXOB$$FanD*-pebUy$&hJy9Zzn5M%KRPgzg)tuYjH}80V>VVLduJ{*{DI;(0 ztHAdKnDm4Kg4^!LIouB9nIqNDVFQqn&r}Wp4Xf_myDQE8bd1et+_=WX| zFFa!iv}ZI4Aw2K8{GxR(-AeB0Qgw{CRb~j(K-Yu{;?&0|W%Xjq+;UCgJL|kY5EERN zHf6Vj%E+OZPkD1%EA(cZf`TS?hH|*@*fH_S?5i&3=NdklULOXb+72ZX6CRXAwP#TS z+LOldY+fqTg+EH7ei_m~A{eWDLR{2EsNsxXy2U;YjU)`+qpJf@RHF%N$P&g7QYJG&Z#_h+@%N1y36PO>m1=%|GZ@_g0KD32{*4i~kb=$cWo@}Ff~`h7Pn2P(RQsmE(g*7 zM?%W+ckya#G6alUqxs-T3INBaT#Qdja-h%<#<>y8%|owR42Wqhj?JK#KtmiT;#FG4 z^ZRca=~7BU$=WSQA7KXMt9uGQzkpGuS~4Pp{~R=i{F*CdGJ7eYGLXP7_2QJMP&^zx zA)9|JSbOrl$%i_5gYhTNSzj~HRC(}*tGl4R@_e+Gyf3VDV`%z@s)jF&-svymO`OmJ zo*#%)PnC0N#~kaISRTXOun`Qhd8lu*_oz*3UIhWNBZAQOk##5MM!YQcyIb1@rcm14 z@sT?Hsd|ymwM#x0SEmQz8E=8zBq*#NS5asruJHg|>bXpkESEd}m{LA;&eVvG`bNa*c5-Kn|A}cPr|}aGPcF2`YfZW=ssv(q|cG zov}~ANt{Y~gjmEqzkt1`J@qfE|+ z#qneISoPl@YD6-6|BUGnf8h0WV#Mv_I{-Yg83`4-KtpMg)NF;SIdpl>UwP9lN}QS# z2MsPCnF9E(KK4_zY^@bfEI#s_O5JOlDe9RbpLos>C7qy~u);(L4r7vcCtE2e{TQpm z1^bF+GY)SJk>{?h+2aa;rhU=e&fP3h3aax30M4!kBfsh&C9s(>VN2gtQM-c6z`8}5 zQV9@x_z!Y5TMzI&)U6HIl?o@q0H~A;sL;on-6eqGjUJ?C5qC2-C9r}VlBAb%S0*;M zv*GN?NU#()U73u%QW_BzbEa~IRu>0x)B|iF?rIR~y){uobMByG0dX3i{$YYWLPQ%Q zyHaty4+0L8hDp3r{!aHUEwE*qw51&d?Xl8kQ}%yZ*I(?Z_pwE2GghPztF0kKEs>s`L^BvN! z-A5|37V0-%O}l_67%DNF>qqdrRu|%M9u^9&$~7OQOL*<<<0UZqU@>0^oCn*5gE^!P z`PqQ-L!p5%|m z^_YMvF=^(4RCZ6BXI}H2qfNBaa6}|SUdu@l%;d+nW zkV!O@=Sq?$-nhphc6*DcY#JLVnGFt7nI!FBM%IRS73kdrXRX`+PqSBL2dODpC~S!P z_xV>&*O+YwDEb1iqeMmUe;UeL$DMMhTmIf9ljfbAHQ_d>>?!-BePmzmZaz~shH;d( z?Dh5xl<@|Us5+uw%@>2v516q1Epv$M_k0%Y%W2a1kBn>f&v%539A4@Cfqk=gmAw!8H3B^=yp9jopUN zwNm>2nr3k~>atLFfBP{8<YlENk>x^gY4U{<;kRIkMh}J^0JMJ@E+E*v~lP z*$Y>_-bT0OiohGJ8^0yw!>*eX@9Plnk+yZFuHrY25I~a%Cqb5Fxff^9P?0ypd=(jz zt2BtQ-pNCABt;{J8kO$3a79Zt2`J{^ye#l-lDwens-HtHpGuY9w$fzd6pp~G^YPpe z_lQo3)aE)cooU4k$|uUr^K;rdo+U68b>IVJlj&>+exjiT`_`)n&mMZKN-`{^S0~U& zN8MMdGxXOgA)^SUZ}6WjZM;#S=Nxt{6s!D^nUql_J5qX#edl4RonVT$vUA^3_wgxs z=P!g$nAPeRr2Hu*CT+>XbKF`B{G)7aR>$a4+yVdgI-c%UP1f5;xtrex!4K4RT)j{Z zh5Q-pyy{Z$>XySi5aYYGYeKZA{{J(iWwq^!8ur$SMDT#%IHU@hcUrPYE}McZ zK~B1oHfuYDL9&{V1kKT}IoVZ*gUGX<4j6c0E?m3vY$MaL`O7yELizKBgEZGiBUd2y zw0X_)!}N^6U>OT2af2IFyi=F{N%EU763+fK%}DYmzL{sm2<<{DtAlXV1Ycn*0JsxP zkk7S6zL{*%FwZoMmsS#_b-#mcla*W`+iMxS_`;**Qzdk zf5y;L2=_uiT)1tn+Z8wE|2X5=Vn6{eyRC@z^McM`kM1>a_eV`IcYU$HiN;TI4ha;8cws!k}5! zNmopqO?sexEkw8)@Sh}bIXFAQXQw`@U)O{IS&2M;P{Tsr=d7VrFy<4}had{$Xb=gv z>}PYR&C8s_2_NEcunG0WaVJ_NMTymER+)x}Nwj($JpN-+V_|9~vP?VetbYD9U-yPzUMmJnS#ZsT`q z3=UEF1un4iJ`mO%7fXtgnZmFBiydz^GJT{exd}mzO~lbD$fk?BdonkloLxM+V09_i zlhI@B*VpgYaSHgc5(2yNFux4QAZfH|+xa?yN-M59g0<#8H-KMSh1e8UhPgn0SF<6&qhrA@?F2>$>6(*zP?l61TR8bd=?d^ecdv( zRfq1;51)e|3ImYDdtx18nNM_-+8A#pYZHi>^t*z6h*OO!79D`n2R<}z9qinp)xcGS z4E-6+S)1SCkS%j4JWYMle05Zom;zLz1UEr<$AM`X$yL-VUZhyvZ7VJ{Q0l1cUHrNY zgrT$u-c(>F9Tq>0LhP*wzFWr-2d(t&F>xy{#l#rAXfTKXAOk7b%L8X7(@U>H`K*dr z%9#a+2l)}ftMeAZBYzEp!Q4dx$fr|gA((S9@Q%m7zmazT6FAf~le%w-w$I0IqRL~; zpZGiurGBBhr>Orp6&BU6Ky#C18=LQ#Q zK$T?Ixi}3(+3Ht~i=_Ke^U$FZ@Akh)L4NpipjL_=Cq*b zGSu`H>Ytu}@O({UsrW_BKuLLE@h|XUy5}J=M2Fi5u;f=}c5q@EDrx12puwb5i5Oxe z4{%vf1CKY2%6bkj7|3d0nIHM)GK7zc&9l-9lip5l33CKwtvN$Pqk>SYBb4_u5Kp!2YRAz79?b-nI#0*zMpZVnAw z4PoD-w+Da!0}h@f9!d>j(PTkO_GFHY%Ape%u&|a|xjtRt+z~LB*r*s`)vDC?%8B4_ z3}Bp4b8JiTtIQ7na1`?T__EMOr}ovz${6=={?zmp0dPEt-G3|{J4qm&nE3Ku7CgA0 z=gBDs9M`|gKora?z5t-L_I8P;x&D2Na*@g*HQqe##TU^_!?@)Ia>0xH;v{ElQ1%va zFSb-~4z#2@QCp1asn^%G4dc2P5<5ph(US+!u;u3^W%NHvoL40N=7Qqvx65c8-3yv; z;~!c}1zzA6dZhDrZ}v3dyTy(3itpz9IMY}n&HkPuRIx)rht<7QQdCu=R8wWyRjamU zZtL<%GhY-#JU8!m^_!*_Dl2r5-z$z**TlLHs$8zdCiaWssL#C%1WazqI z`w@{=UNp1-5 zSklKKygx+L67{vqIz=2uL()*J6if^j)bETxd`!0uHTE#Lmln(iSdzZ;R`OMSJpURu z{2RB&=l{?WGJ*pRrIZCQSIVvZdX{V${3ycc`ks-5Y~H7{m+3!Kru}cm;ZSrIiqZ%Z zrx8jV6aL`mKLdu4wxx^|!ccc{=aYmKtwOK}y6=JL{PsDEGo7*5?vKZb^%mGYctHaZ0Xkg?mPB1%0V5!-cLhI_>vd*;!q7=OI@P z<+aCieol8<<|=cHh(tT7=k?6l(JtSn$_H`F)_9RlGqpBtDbSt%kucx_CQEw`Q#X5~ zS&P%cg^D+0Lxl+r4)in?#!+8~=Lt(L8%b2i?TKF_zjn1|m{x7Li&5#}P?>9EUQ-CSAq>Sn+ix)!p(#4L9U5$-bI4P^jPJ zY}z=VIKhc$Y!p4d*dw$c54ftaFb!>mkH11|tA%dZjpP0sKmiZRDN3zSM2v1pSwQ_v zz|DTnSP?(S+(xG!N$_NRi0EsJvQ|LkC+jr&ji?AcK0>8(pBAW`ROha<)Xi>n5(NnnGkpQ*J%>zXt{Ai=PVj zGYumG%|ih;pEgeJg7}`h(;TXI(Pwd=4f#dz_)f(~{&g~|Z;5e|)xHg2OsMQHfU1^; zx^dk;*46|Bu#syD4_k3`*YfqNr!dJrMVPndc95K+{;i~2J!`QIE-vd@ZEPl?2j=#8qb`5Umog*mI>`Su|?-|wAzxbE7%|rxS zooDW^hy5*l{@~GT42NU}^W$igYK$V{;fX6KKA2Juz7Vj1p~ho<6+=56&SsZPfVCxz zVS1*;bOAmJ#a}zLSa+bHof7}H6+iHg@qIXIt(9E_!YVi;Uvb_m8OnZ@v4yu=FP`h7_kiW8h?{5sD##62h2vBfQ~}PI^uw zX>n4luvGQ`;v1KP?jLOWgus6ytl6%xXQH`#VL*3|yWuUiVZEj;8HezqHCcrr*1pJK z#Jr`pFg~*xw|7MkUYrZ(h{;qO;*#6z-y+Gcmc(Lrs7bV;cgSSVmd2IlYT;?y1_L*rh8$)o!v?)5+~tPbm;7Gn&j z%_!->%`3X6#AsMA3VR2#4CBj zmIv-Q$bi^XjtVm~8XMtmjwEahR|~9ctQmta$QD(1^gxu=mT1}X0AR`5D?=%oN_Oc5 z0Fe@?2?b5K0#1IWVYfnlln#@t7AA%vIs{VGuy*Sz8NO$Is345YjE

^-Mt&CGB^Kw}Q0u147vUh=bY3eiL_?L2q#8x81`hWTOpNd0?#JJm&XD1)72M9_YeY=1K z55lxP8nzBEU0(BeeSBk{=|SW1DR|Ok8I(S&kwBgD0wE{>ug3b=mbAS`u8QC{6ad|= zbw(%|@YwM?se-(#v$eX^oc}WK>M5a@Q;7OA>dcL)_FQ-rtJtPcssgJlt{EM6`+DaQ z*PBx3veKuVy8{cwoXDhvf+mjC$4`H%ev+cyshTO5WAkq0e8E^;Wl1zZLTO-AeJRs= zFP9~oOHzcSGjds2^6-*q6MIuq7TCmF(#=i)GCK?C{K3KR#VMb%q0+uaQ z-6(uapef3`g%;?vRIqy_^s?vx)C3rMj3Ok1ctk6}A|!D9$E0~)m!7q#gwf=<#Qjcw zT>0zA^Hz6Q9KSLNW(@RMQZ%G-)Q=zEQO8#Q%YAWYB1XSZB7EYFuqTQ?e>8(a?++Ot z?cgat`kiB&AO5i!3BTkn=9I@KHKlwAj$VAU6{Zbh>?{~cx^A^?X zd>CeJJL;~OWb@SfUP#?(LZFlzT2fciu^z{f>J+~~4NncJ-ZOqsd9!41_yQDt6Reaz zCFAq}{iVo=wRw{iN9^{5AjKY|bM_MpmHE1Xb^$$%Np3hzH*x{(V5&Jic6!zYawobt zg3H_F)RnYbVg(=_2`@Z9<6H9kDNlq*J8%7OCyWa@x+QOSuFZmmokAEM52ne7Os?MO zp@IJOPZ%e3PzYd*|0H!oy}7}9b?)7Ok>e5v<{j4A@ejn1ZigSTwHqs+~oZ^!0XJ){9afJj~%7U{YIN_mPs^uw}Jo=_F^fL z30?J&5=(fv`zO~htDYb>7Kv^O17jJx<8Ert$BBUB2gX?~=^wd-dl?Os5_0WF)m=MT z0J3dqweC4J=%Wjwyujy$T4fL9E&UHIc3jU|a|_B}=&udk{wb>6IN(!E;6h0DX1)lZ zjk%c%+BT}?_i_Q#RPVnuwF@SF`iCIRgQj7Qe_f<0PM*Rdy!&~XUHF9gzutJ38DEv1 znn@vWhG&9E%A1oL>_l0)IR4scP$`Z$gf_PR(N&ASN$gH9!coHsE!CR*{$CCz`XY!d zWG{3~@YvLrlq13B4jTj5;Fjw)>X!1jSIz)WRSKTqpZK__%W8Hf05X!vgH^&v^#@8A zM!Zubowhp%9yAQ)4EX~sgQs;fdm^Kja`SP8M= z=k-D5gIn0$AmZ+r^0{nYPVx`)78}7M5*`h`UzbJ%j^)x_*7>nid+dMFGVZo`40d$j z04!FOL{;>u`-{ILZhH_3vKBRusHWTBGCLi}nbeITwF$zI16Ux-9NslZ-MvNYF9ota zfdsDm!Yn7I(^M6{_ydF7`kj0vL4(~E{R62slgL-M^~1eaPtroalwWi?p_V14bz;+C z#VLR3U%vhTp*p1p=_(>obW-@XM40OOdk-JD0~s zVXRd)NioTsyX1K#IH~jHN_OsYSi$2*_ z_i}hYx|%4UOchH2seiZN$!DO|z45ITXh4SiEqZ z#_Ea)4bl{1*p1n=el2x9ZL@h}DX8zU|K{28rHds5{AI&ctR8TmG6; z`m6c;`g>_*)kIIf>&w%JKS#V_;~_B#eHhZQJNyS6*QCI^g#-Tl0_>Z)HzS^xZ8<|t zLw)Vq8kF*6Jqh00V&GZ{Oj+oQd3PcUY8Isx2V2Z2c>qxAYndZl^=kk9}VAt(dpM?KD&uO^R%a z8yqoPPZa+MwU>=-jjc!=tA0~M1g#Z?#zpPdhJI-GjkP9r8AxeJ;a7fk{py9VQv+m)vcXKamWIZP?b=}H-~#MF1M1CVq79o&a|o= zKL%`STKJ)WN1UO+jt%Q-C|M5`$9;A~ApCIwVzVdzkEjee(NAox`*ZnA-kM3{Tu3yC zHjGAD&7Ojm@@!U?=QX&d7FuK zoWDHvi0gg|xd}CeKd0m}DG+t|Ezp^Jd`OaE?Cd36eY$|2jxNVT;za7z4}`~}JE)V! zKEd_n7T>2pgCJT8gE#gDk^3XmeG59SxecxYuJ?n_H1>=zbm_%Xk53WZ(r8S%PvG`N57LYDPB4JFphDT zX5dj}Yc>|{sH{AQ7bMX-j$YN$cIB|(+?Vs0%{}HqR|CM`FP*;2?VAYtkTP1krzfZ2 zNn+ODN?*8{p-^s5GaZB>Sq`xs_qH)+_xGX0XnM6R%eoA?qxe(5CZ4N5ex+IOPIBRL z)@T6=ye%KT&owcsn?p}ThRnL&_56OpJD~ohMl86-h9^|@F3&q*pDj=6!8$`G>9V+W zYT@6@Id+0;L47H=jSJ?Bh&iSCw$f#p;SmI)Dk>5Bom$p8O>wn-z+y+3J&4Wb9Sc_~ z`-k5bpPM8$K63f4?bGS?Qj&}E*B*OHm>;gi1N#Zc; zjAmXAQN2h63O>XqY$KQ-Oi436b9kZ3m3#%NXrHHan}gR45)p4#9t(y1icGg(+&Y5l zBqr>-1yU@~9Qi88Wj5S7*qnv2tYciDCFd5Q;9ghAJptNN-}o&8&MxPEuie24Lq5*? zpJ+0S%bt;u#0A0U_2f zqyk0+t8V-T(a?}zd~#KlxCCRbDqNRWHLKa1O7r0y!_@>$Gfw7WhHO z54F?jyEtJ{6od4)nsFHkKyJ^SaAnr|DeJO!=`@|ej z`D*DnS;ZDG-Yxzi(3iXBO@8wg4zcfuj=5PlvC12HBC@v<*z3Q>469F&am;wIH_o!V zP#g=**qP1U1a@AZ6e!@N*@>}0ZKK*(5Xbb<4IrpCf)h)A5eH0YDQc% z??53^py$U;Bmd(~FOu!+X_X?NW_*zTOpTS3R=zCo0AiCknSAc-aYPq^+l4DNiy~__ zOnkC96>zGXQ+A@?|;y6mcPlY z;AS;e-shv}R0^l!Z7pQ)0)Rsho>%giyqa<}DBRJ1xeZy;kwHER@9_85O$(o!md31# z>FM_V`Q@;p_%1Dv6-u-`zG(F)-6~4WeIrwm?>rTwX4sA5nU~9N7O< zLd!wps6mA{1gbU$FTDAb_-a$RQTfR1_(ez4F9s22vq~KlG1Kbu5fSYAW*DmL&c3iX z#IqjHS&|_@Mbe-9EZGD{mwtCU zslxQ9!+~y1gEx>M{^)L6ElT$k3CXtWI7Lx7%%27{vW>2j9rK;e>W|a|^^`C8F#VgW z(oY$o!IT&6$;H_I;mH%XO{{S%rM%H^fAY@jIuxI5WE1hF3ph0(=0`p$b6c zOPeKZ4A5q`IDHv%9|+L#PT_sBJ@6SJ@(C2zDRSA{%TAto^{}AvvBc>rZwjbhRro?z z2#A!ys-{Me(_ zLT%*8yMtu=Ke;+2v@!}Jjr`iiZS`s5CF5kSoYC9Y)W{!nV<;wE_g?01UnG9B^yH54 z*4WRiSFif5R(D?>y&E<|Sh=pi*nLQKxZd4j%EwrSZKmX=vw^zzE<8S9S0DxeJrQ%P z8$7$h?q&h_VJcO5;V-I2jb;cnj9i{Splq;YpHl%oNVY-gVgFi>gW zT`xnI9zX&_DX=j_E|zn;lZkOGxEv)0I5ekSY;%1My&a|lpG00r$UGU94Zb2WGFQ_+ z*vhOe*JDtWkvFZ`^tHDas|W!GdPeB*aagNH<##)X!VpT|E^-}JxD;%ZH%viW*-v2z zitN5=j@~m$gxmr9?qprwo-BK`#vrVAup~fB;Nr9u@958$oiTzV=;fFZM6yToNBwGf z$CvX(=y8THDr4Y^@-I5sZk)Cl6?A^VlC!*aug>L3XYq*6Uk*xp{xtm8Q63ktuKfv_ z=+i3LvPlI}1n}QR&u;?$$6ahCfW>QNsYn;xC+7g*zkVNk`uP}F^&@wPi4YO0>}J%Q zcNp{tn|XqDZMVIGclC!E>-KkW4T~+dp?rvACQH<%NJ#sSHQJs(GrJc8aI!bsPS4uZ zF)Xvwx6(?X>NeSJ?8{-*9MI3gNSb=;pbQHA08Kjn*t;rkx=Km87SaRmI-|g!Lm;_q z6R;f1vjvFUQy@Yeuy9h9goGT1OUB|LAN0l2+H5Y)7g+)zKR=Vn{saFvX()^ll`5^Q zLRMM7l|8wxS^L&o@t9C06>fc+K*9)@jjXL33VJKjK+R*VYV^?Qfw;9THkq?SfxNuD z22E0n_IuUJkFQFe^}%gbhCYyTt=im^8J>^XS0M1EEGt-T@tP*c1{d#blo0AOTfw_< z^Mcedl6QtTv-$JSy4&BB{MJxb#ydb@zD7P`U^)hUolj)3ar(lSP;=isqUrthA{%x= zK13%bh{*drhdeK;6ba4lV#V#P-%Qh~>Y0;b9N$C=GjYuJKUF zPEdKoP9bCQslDmRO@hKUQO__Kz<3^0%_;GaWsPyiy`(!^#TpfKyf@L~573V%2G|uN zK51e*ReNg}5hTYhJ`p3bdB;Rt=`e1)cm^g88+-n@NdoE4n87cRr$a>|{=)KdDqJ!F zqBk}Q$95dNcjMtfyV)IZOEE_ zD(qt>>^KYpTFAvQDxeDEVqW{VRAO)s^Pe&8@+&?Ne{l|a%C^0IV1)87a({0Z?>%+h zJcu6ZE~xa(%8s$ezNaV5d^%gu+a;}4hq`?|4Cu}}PNLaId+2+-n~kEra*MCNmcPb$ zGGi$@`x?tVZiq4k)74@Eu%&I|N1d`!7ClpXV=aski%_ehqBTysfiK1dP!*Mm%H6 z^{tblK(B=FN}yE%`E^r0rDnREu(-4Z0z4jjMeaj9vwOyYSZiauOFf$xHyX>(=ZyI; z;@NT(1QHt3LJ`)vn0S~E(v#G%Lm`z`Y`wN5hg`2%>ePxOqs@-=MA9GdlPZ5;dnsF& zJL?4U$5|M=%fjOxODSt}_!SK&;=hs|Y**>C2e%4A)jU(bofMUEi5?-@g}TF+Ce%ZT zE6Vk2MZHxGDwsgTg9!+s-HN9wU^f)Rk@Kc0;P3M~%!Lblf>-tluJnJ;$KE6AA>Ng) zE9!gwz)3FQzLydrP4Kn`Two%=4%XE!@He|H%zlQ>Uxw`mv1YNQ{b3^3CqZ;Hiehpr zcUcRk7;GJq4nLrfZTEGa=ydMXL=kDenoAz*QNb*o5Hsn=x|yla`JBPNxlVmNE8!6p z+zVB_cCtFDfG6>)d}@VZGDw;{S7ZtCd3|`QYK9j7qOvE~)SpSfKp8p{R*51p!ouDy zzjX|UykS?W(F&L}vKE~YGE?TDul?OOPqzX`(Jena#+EY}=CR%<4`B&LZi;jx>t$(l zCAk-E#NXslTQqv?`@dV}(>i}EU^MpbSb#>Q=~18DvE0%1>Z4yN)Ri1B(h)_a|Bs}r z42$ypn$OZD(p^fYbfybnZIQAK`k3 zsBy3Uur-fOf)ZQgMQWrSWnsEgj&*q4WjuI`+dH*N$^?8bNB939nCVGPhmdO`bZ-8G z*=W0r;dtp<6+1;knKi9h%3)cy9=jd&YIi4TbiV>a|5|Lx!(cq_Nhjj}u(`_^LT`%% z$Z_)TnX$iE`p`@8RD1yrS$0N3YA5HrXo&Q_HX^d^3c%Pskk(2I|%S}NY_b6q2?kKl#_~HO3 z8k#i25pBM^GVq84NTa$sfs%`VSUFi`U{D-4j?(o_ZSbRiM`AkHTD&2TlQhaW&_Dw| zpQ8L|D<(Y*KehO|nUXb%T)y@es!!n=qJO?G46epd>-Z}BNSWV-!rk72S;JQVGJE?U zAZfwpO$mWJzTH-e=}OvD=`1c9oRXX`q1jH{>_boVz%} z(>^4i0Atz$0Ub7X)5d}E_@BPBr*q_~7l9f>$$exa&dxcn2Sw`nw-B&n1XuT@d3k;#z}be-Zg< zjnF5JFMsNLzeawo_cfb;y9h2%zmVL_AR<4&tE zSg;;&)Ej-ekgW09Su7APm5N)>M0do61i91k%D{Ffle&BgzaXZ-S=pj^4>GJV^s#z~ zsR!3>LbEYSXYcML3Bbh5)Gtc=fS6uB%f1)0dTDX$_7n>}#a-$v2Lbf*ze^PrKHIkK z;X6UwluWb?DT>=dC(eCHAlSi5pO(j5`)^`-L9r-#!sCU`v%jn)myCrikdR()nP>(D zpUt1S92cjUW;p236`du7c1!36v**Eu-+1*BO4kUAlZkw&+ZIAJUxHG<01(b^nR3#7 zmL!J;UmO&;&riVeHVWCKV&AP?(_1`@Qory7nE^qkAV60+DBsqm*!&k^5V(W=^F&Zs zmAw&&99A11KvaozJ)PtP3hQWT4tyAiiQssFe!w(qIhG{LebYmCf}-3^*V*c zH|Y#fKvxmt+3NxU^~6C*{PO%zofud8;E%S7yxcY&DUDgE|Lw&OoKnNrKFcJ!%|m<4 zs>m$%I(uUf{ITA8V(Cp;ks;;cf z_3_!i!d9^gT@uKT2&U*0Yvy3ptrcWuEl0GWn3UW$Ecp`%01xQjwF)X&oUjKu_MH*` zB|QCO)WpZ(<*q|jK0c{2envm~jZ@TjZIeSX_ilPh;7l%GE|W6ZFw_od z%2eQB2`ZBR`m7*&?vnC|PnG4)*_Io~{?qNZ+zvShdRBikddLt5I%LB18byHa8mozD zH{(&P$@bp?K1OH$h@0JyGAYSKPxqg`?L#C3xmGto)?d+x)sc|iv8QeQ(O)hJNABaG zf3!PBHC=+;ZnjWw#jL6`=q*kyQGxz_W}Aj2xc_lj0|i**2zH?YadZ0YVFV3{5V*VD zzpMjqi-FB}c&yK)Fr;ElwS~=&UrWeR_^v4) z7RF-iXml{otQHthZD@AyBZ(+=YfA@Zduze;%sb~dPQRsdQBtADJ0Od8ejSksqRevE zXqMc6Oz^lJMV+`rD51ZKlOYu9x}y3=5}s;qGT6w2;xUHf6ij@STm;*YPOvVVLX+tR!3mfC-Y7m2_i#OL^~2UN69)XSz`s65jIF)|XcFtvSXV8{!Cueq z9a^tT{w~z$ogts$3+sQ-IVvxUC?#3@Z}=ze@IpH%uB)7AG*`K(>tnI;ZXQBE?f2M0 zzR!w~Il4ITxVmy^ee?4b&u@&YLcgpMYu~=!h5HPsg+z#@!;WL^VLM);gUSk zfzz4BA{lZh+#Mujq~CE`;2!KfNZ!>cbRy9b2(FvK4QzRkD)|TR@BI#REc%B_#b~I- zD+%I0{*eHrBug(XP;Sp9b^;Ci`k>oCOmDnVQ{(EahXGAcrecb(hN_? z-CT@U>KTJYjtQr@^Dxa!O_T|l4oPReIqoah*imMa_GcHGO#zXOy=kjbdQ9m|>or}s)cL_K1AagK0VD*n!{?;rtc!m3+C4Sn7`1!8Y5O$)57 z)MvtykQd^wlDfXxesbBzdrx7l*be^L+O<{)Ma3|8S7H2e(I?t-uIErDbSw6p-x+^{ z+8Fd=8S5W_eCGcskyjwf)S9CdF`@8%&v@*H0|=VW?#I~jb9yE9iNZ!C(mpRWBa{=fQ}O`gi`%c16kHtK-r0SX#MqR zr$ObY@}DA3SL7y_Bg5*ClZBb?3PuFLBLDbCRXA}pc;{P`>LOq6izBg^Nqm%=9iV^9 zj)G88EZru;N?k78%8}+GzJu5O1g;}kS9%yfq9G|oTfSrBoVd~YjEI+40dS;XH{^ho zY4Jdr76_~E=v#eHmF@9QMEvk%8g!2#nt*zru9~W^4fP)zi#d&=q6ar5%qryvep@pP zVkkdd)CZae!UzR1-NlyY*jR!D`eCya2QR3TDk4*2K2~Z_(IrZgc(`6Rx$JV1z#-J< zh&707CBxBXp#-9D-{#-5)2)i`{IA4RwM`by-Q1(7P4%0ah`UU&?LHKrxh#md_Zp<; zJ(vphq=j9z4OQD$pDJaRB`_mve2+r-G2D@NT;u|OI?0u_QyvoTE5)(t?IYiVuKr(U zprK)mXbRr8seU3Apb=r8{EX5V?O*YDK$WRbEY?l~o5%^Ghir@cd5KA}4Ax3nMO^5S z{-T~_sxLSQsE#)B+U2{2|5?up`-XEQ*3ED;|EZK=D$4}6L+`dA5{5g@>`$n0*rfcW zuVW^me8=`1E}ID6{Cb*skWl_qR-6naKi1i{yB|%5LqeXUSo<<|h2Q{z0q`1#^Yld| z`-;F?txb~2;``uL<28uCg_yW*8Jz!9I4tRMkG$1G?!y!-rAHMGqSn_cWGBoLly2fXtlzHBx9 zS!fm{F%d}wl~`DEe1Y1=<;nTNC8OK(gR3Ljc<)(8)Pil?Ja-_LiG-}h321p#N5Zef znIi}kFqkcV@WG=44+K*<81Xjzw{D^3^wjw8Fwog1YTrK=paOJdHvH0o6-Kv?cp83d z|L?*OjoLSUPa})s)zwz+4}VMLSK*4yiW z>-A%&B>3ZebI)(^OTwK1%2g&98@Qn_pq}=)oqGSvIQ8W8^77MYOV7aP*Wrvw(r&Zk zerPv?Ejt{N*mC++7X{qa0N6}xqtXLb%gMRwLvX}PRKJM+Bq8f+g(8gQw$oZP*|`k3 zc1qMpDHUo(lI?4ofiJ}amIsaNDR6`l519ElLpvYRcqq zgsc4P^&_s0YDyzfT@sp_GF!SE75jBT-jch-(NH#QgfjrWZ3WjPO~qc^#~)D#xQ+Us zUOm~O*H{#lZU1{`<9?p&A|P)a_-n3Qqq?<^WaQa`)@Hwt1S*|67r?l^#eDzfAa>=L z^ko)vvNy-x;*(SeAU*=BF()sNQ1j(Yn6um0`JIW*R_rxvt^?khRNG<(367RH zsFZ0wHctOWTlEDwCV+Ze1#@=cJP=S*N-s&Wc`}bJ<&G#BNWib%90{Xi^kgZz=O5<9 zbx{3-+qmJ$onAcH`$Gf6Rh-d2;#C8t2qh#{nfV;B7}H5Etl~k6sg}*8wBpufl0gjT zCT`nKh)VJW2im9rtdPP|IBh*d7}587Z;XK10|;0)Orgs`IX%mn`^EHCI+>_L z=1F(__4uol(}Mh)D$nP!Z8on4=r6VHdt2%)v41B9GbE2937^r{NR_#ca0NrCa3)}O z5*?u(F!x(!Mqn<|A3@v@{9kgon? zz;MwX$PMH1OgIW_2+s_9RCMgEspUpCx>#c8)~umxh!f$Tm@rO;fyZm|IDD^lpMeC33uvPnu$K| zN%9}geDB)wa$7a>GkStOW!%)Q@0I98kr4`zUss~}R$5Heuu_5pgh0g2!UNG=pajel zh1|jSW!8$ehkFvLKXNwNm_nmJf^>yV@I4eS$Dv{;KNTMa{6mP2KW5g^Se+Ld07$bX7Ov823XT@RhIa%0&-*TaSsoiw}Kdj zY2~N*)Cp4%W-z_^!L`S7O2BB!g5zp?LET%MkDLqAM`~*nRK^mhiZi2UiMxnfjIl-@ z!rodf0&BVC1#UNL=O5!p#Ykjh=k`{$LgqnUK`5dRlt{3m+7nPKx{1Z zzdBWi_^{JTvFCh>x$?%Ilr{;s70yxl49;DtQozZksz*lpcrLU=pX9^>yQ}@VXc9fW zqhrVsL#%30{5OC;bJrg2P!D$TpAN7i$10%z^fT@5;~mGshN1rMnym%BShxL?Q@a^z zZ`9H6rfVT9E9ux!#g>bqBrp&Q^Lz>D)Z(zTDba|X3ix4;zr4`!&@k5W#X&z&`%+#0<;m@vg zpWmVB5ki1+U3NBbxWDFoYEv_Yx1e?t(kvnuLnLSdG>oABq(IQ8aKWed{4PaANwp)L z^(&BcJ)xfW%9sluP+=v<>*X%aO(8#ZVRW9lb&XaVT%$IGnjhs{^&(W7 z#H5HfgvSC3kD|Y5=rqZd2^zn_m#94tfqvj)4-70&)^saKsQ5R+$=6c9>8`&*_4oiPz>C|5+ei~> zkuHJ3hq95_sAnXyPyu3tO}RbKez>0bUhcyFtF3Fk|8WF9u-5W-xSC_A-ae>(F7)7w zVKo{?V^H&}GUR(nTGFB~S~dd-if3ciyE`aIke~H#Ohe{hBBVyg=66vP9Qz9b)~T_# z)us#QiAOyxR6)9N48>=%A1GBmev)vkSHoYFLzw!kEt~&iS=75$^HyL`HJAoVMcHgB z=S`$_`Oe>|&Ab3`#;J31j9b>H)5I%WDs8TP->z-ww6ALux}w%$qIIu--V(PV{QZeL zfcWNRiqcIi(LwpS9uUhL32n}Bq-1TwSd?yvJ0Purrye~iG{otEfY)mL(gNqd6s(>c zS9?_oL}rF8+&3yAr(C&JednSPsvyOZ{qnF8{0nK(Hiu|aed4&I51n6u_k(^VDM1!0 zv*t_(YV$n8HEPRMYm^W_o z`{ijOP5DNPfxe>p$)tf1D^$53PEZ9mc<|~SzCZ%j&XIue7Vo3@dk^x|Ep~TW?rKln z{za?g%v>JgJZHK`b&s6f(<7nX;0LfNl-r&(Sw3J5B-7+*ZpoUJgTVtob6f%quVd|AGCL_ zxzo6vGzARF>rpJ0gy*(_Z!x%e*b7yK@`vn800g}r<4@7%%S*7xSk=CPm)>lYaWD6k zn&9_#BNeh8*)LCX3AHf4dAxj^<8x3I*5FdOeDVPV*6tj#&E;*{`F%bhb%pTA;mSIl z$VNMLh&oR0fF14T4R>~v%hJR=Hu(fB{l4tW(!ZuYw9+jcxg#!o^3S^F zM6`fVGyO$lrH_G4d+?o8uj?NkgSn)pnX0<^bzxERGxC*NjyDg#{Dthdm~eORBK_^x zL=Gp8(y)RI?ETCGF~0Oxf3!*?${u>bU0OFVzJvCkw~cQ#Q~sxD#{*i4E%s; z{)<70az+h)7@|wp+iwwhr1tBk1v4Pvx3rQ>SGWl6& z+BP6)9U1U2xqe+#d+1=dR(+6vDhd-T)I(QM>W_Ih<7z$OlL3A`2d&8$jiFb(&{5La ze-=G68HnB%Rf@uZ*c;H8Ho14|=wd_kI}O(vX6vPR zpqZ78K-RLrC*F|keMG8xE^N2b`jp^w=~(UUFE1IRy&bCUr==$jx}ejB4WFy$jPS&p z9*Fj}%$ew~d7?StpBxoKy5=CSmuUynqTm3fz2Syyr_d&7==Mj1)d5Mh3xS);KK|h> z60<|jw?38(*Xx(cg4bB>Ob?+?@&OU`(p2e{M2L==jv|ZyI^R9{d7AujaRbcX{ltS> zOJKa1GUh6TlNcj0nc%%|3FQ)hx2Wnq_WYNJw@)8~rg9?|Ra(>}9kS; z;!<8c;!?0s^SR=^6aE7yQy;dGZXMAAR1g>j_1KwXV%`vAv`O8tJr0zu6#56Q%lct; zb@1<5e@CW(Bf*#ws&Ge4nSapUl04@;L9x*k8P&|>>?f9Kq_}#E8Ki7bB5A-IX~R}x^oJSG69={!@{j!LmS_M^+{F%TUX#P-tU(khCa=g_o0 zwhq=`4wD;vM*oGZ$sq#7Ty{Ls+M`_Q>fQSBQMYK966uB~e~h=C`kcS?YX`J;zNVDx z?j78y-!4T%?Y`^PHZX*T=ILh;09%DbgbyGBkd=r2tS0PTvbiMJIcl6>DW4(VIO_k( zQ!pU2!DWecV~XD^&j^ToaZJO=v>jW$+@YWQ6Vs!!VbNa`+c{T%kv_)$52@D>k|PrE zd+|?+r*qer-V>UN4UGhj6LCyH+LFbcq^Uj2;c67x+2rk1aNhG=n8-?r(oT3?jRd-` z?zxE#(DSoVOci7WDOaiz)FS-R%*~?-krdfRqNmx5zG^62gnFh=llqlDbibwn13YG9 zQpuxL1de$x-F9#aY5@;why1D0qP(0CViXWw3|WIsjBY>cvmDSCe_W+&t-*ip=%-TzxA-%&W1KS|p3Y1#5KCQII;~bl<+x|_ z(LnOzU`XbXwGDs_B3^KL`OwP2iB5x#`C?e|nF1J}?;JS+Vw3OP>G%kObKW)Xxte0V zA(M@$h5dDRDQ7wZ<;p%Avv#;hH8rQ$JVn4N{J@HTn}6d;0N7VW5LjIo*H914^>&gFnk zHOtr3*QtJuuY-NkNGd>A+L}U0)4EPKh!Z^r2x5z>n|(5gnRSrXSKO~uI9!mEeLe1T zVx6=fGKX89FC7kL$yb%aI zhiE4fCRn-H=nPMICYQ60`J&Nnf4rQ6zBL*+y5S5H%pt2F+GfcR{x$)hZ7CQc^H3+d zGBJ5?4qnHXH7_XZD0U?7YfKt_i>}%D4HLF<&A{`rAxpS0B&b%S{vu22;)Z%K=k$&Z z>tFJKY70M7^K5^|x$>}sUbr%&1fI4 zi0vK2bzix1?kqaeqaj*M zPst~)BTr7Kz*&Bir5>QA2SLZ$-H(4j56^1Cd1?;=j(kR6fz!NWjHx6&KGRp{EOwU$_`ab7IEPH0eIH&u>eYvZMO z6f2-Vd1d?K0c4J|9#+Lfs@ygH+Bc0>K>5V?pbU!WYz`r5+7LD)y+gWU!w5ROp#Fml zqk}}>0E0o@xC^f>*8VC-J>7a+a*ZQDx>_1-QO;xWl)H)M3z?i3NI`!a=!_m?44!?F zXlzijAE1*Yyoq(`H7uK$^9&v-yu<$IDqxz=W98v9){^-&&ZfI!(29bWC4k`<@?_D= zAkyNU^R*i7r?0|fz3Oj~ixggL>XpBF>cZ|zmuUX~eRc&_ssN8%^eYx$E?ZJw?T zC9{3nJdW*Ek;l?)ieJlv$_RHd60SNe@`F(J_r%u_E&PS^? z69u#(_fG^zA}2ee_uIC->iT%hMEr^KnTuEKk2e!v2X7etX<{IHk1%w82YVp~&*A)D zvKXe1Www1wh>f(gWS{Rb?cZ0Ji)(CI{_1}cMra@^!M`8hof;V3G*06rZ5gm^<6etM>rSds?ROb)n_hroyFIaog1ys#W z(GT0D1Q_k(BDCKM=XW~&gS&nd05{t+c--=%|KUxNnE5KfP0jCf&b#XeW^LtMUYN)#0kn=vY}9_y;q;$>JVD#z9d{;p9z zx=RWUkx-wm-z7UP{C#`so17iUuc$b2mfd^8lB@W@Dg*fCHxjLMde{6MO(P)PGIKY} zrRJl|oX+XB$Sw6FeTcp}729bYLaWB2upZYIYMbeLx}?dSDqeu>!co6!Mpka5F&}gm zNU%ZI39L-#QsE76S8(X2gAFS`&E{^c;#3&L_!Zffu^W(`+vwPWas(`^a|Jg2Qsj=8 z2+S9`>Jfc1DpWJe79B{gL=^n0imrqU%$+htlP5usc5LP}Ye1jEVOJ+8i7luW?ln)p z?o?ET9E1kR3rv~Hd35IWxp**&J{`Kv*ihcW%12$V>z9L~o$&pmsK>OxOyo;`z0o#_ zBmd{duBCzTtOE6Kv3+q+=%(7XJ)9~hVM>kyAkI%P~ z?fUoPSPvZWjNlxIWzu`LSczHIw0Nt1;Eyc4quBEg>Y%EJg1)C%U9fBV4TI}#TPd}< z1^|p&Gl>pJ2$&`Jt9{u>A?QGP@6p6FARxmOo$qOUL-0^GtsFPmj5vISKmhqX~TJbvKd7&}Cp|-Mhmqb|J zX)X}Mv(`&;<>zz>x2M? zrz%#T3WM-9Yzgx~a=hM#InlgQfsEL*%dPVSDLVwN7X55~dew!3(%- zWB>rgR+4H7O-~O4Ka@B>J%6rHJtR~r4W{WJ0pvM><^aF^j4n$UjXlsEk|naF{F|{Y z<<0*3^YOZuR;jMsUo{8EH&1Yys_p1MI|e^bhI`4mmue&0wNNHf%7XF%z+5vC|6>!; z&cb-VG1PQ z>hj7*(afu@5eDv_N)c=F(vGUG5nl95IGO0Ud_p7(`SAgCp?^8J7X>7zhh{V?h&)Le z)G7QaYUsCdBJZM`VQVe!#zCRU9=#>~8IRRGyLM-%e(@rkIK)J*j87eT8%yW1b)E5j zSRRAU5l39yo1ny?Tla)GNhIJ_Fb84A0KkZA)APDa&O1~jbUWI1HP4KE1n1-K2-jXh zvSBJBG}Q>}v7x2e?!rEq>rivxnsA{wsjJ)B3l5H`%;Qp&=0&i8(OVTfAZoROw%~;SLuyqrHVhIoRBTYq>4*u&BEG>e#Y}$?f}6 z+Qj;MpPG35-97U77kbqL&JE%N>DQvlHjZsK$rDYC`ometP1DY1?xt*|veol{%EA~g zNL}3ae}Ga3zE6^PTmKhI+Q*a%Q-Etw2+fXN|-AbTV;PM_vByWk)&y8>=!q%PDSPD z%1GeiJ-LM2LVg*^dMm|CYM~XTtnc~lTBjg8a;3E39FMl}$o1pmY++hqNQSD9l3D&( z=l`~nAy8ho;d6S+$r|eYD;N?L=i0lcYryK*phU~oB?4Yu832GZnyK#k_-dJ|GHq>g=LuM!&K9R zjnOH(>nrSADN99nO{<{o)GUBm=ZnWNrP+??IiUjPBdvNRK;`TmJysoGkd_H^HwKQL z*c5bzjvU3-?x0P&PT;=R2&s7r`bVpCrFq;|@P2qhxkhIi%y{u4p8Gm+<1ZH^mXqm| zZ7Iq6kfk zck=g?i4C*=)*_T%=I3<_eyF+TI2u04T!r@^DT-Mm+EH2OX*?jo#Kr9Oo_D1v%nJiv zhSnrX@&VDg1xIW+Hu?wVPIu&z3C9{y*z>OiCB?RvbSeXnUOlK$8#!4n@bg)PiTH+N zi_)Q|uUa`QNshkfCFP|)gB%IDmyBLy8?(YPI~#_Nx_mci6q>ZKRsRR*QOH=rccKjp z58ha99ajv#?GuB^OVpS#wMUOu7qIIgPyWf7uoI|Vc$)TH($2SLlZ=J}d zZVs%4-PPxIGdc*&R#-4yr=df(w2$o41rB!o@Ha}E8w zxFrfhLL=Af5yYx{F9F9Wm_V*$&gEe74coTaab4-gxCG&;PNW8TEB%Wq9d(eqm1B=1<0<1Nq zT|~&K;ueaL2gs^&aEPQk36tv2pf(bvsxft`SYy%TIx54(kvFM0UbPc0ar)p3xN%rr}g7Z&$5oOvgfJqu;y< zz7^+DOlzF%hbk<=7QORB>;K%1&M}RWSx_N3G45VT75LPAaA|6a;S z>rQs`Mx%Fvc2->9H^iBqU&~7R2Ka{D#lV2&8t)exmI#Mt_%DVX1$T6kv0d`djjxq1|_!)wcFhNPYS4piC5tW zn&JmYGp{n^1$lp`9THWi@I8sUvMh?Awpu;o^OO9DP&hTZ%u`9e7Bn?y3vfe68vCF? z>;3Vpsi+U7#FP!Aa3qfFFOO61>+$Y%&cDj!I})`#yDDOw`z3$u(%$W5XXpPyhmv(e ztlL?2V;N|hdDw0xm6I>kR%&N|U8{yQRAlAdWo7m5%^N=v$C)Z0ul|)wGjEMK_GbDI z?l2PK{V(6?vu}*H1%o@m0>}O_CI^W208N4M5K>~^v_%PeO~M)aU};|%{a?Koo$CHtC%+?5X3dS>}0oK zE!s@MZ_1KVyhemhgnkftAAJflgldV2DB?n~vtPf)P7g~9n}9~cO=_(jHa|Lbg+DM% zR^Oklf>!ob&Gzv)oRS4Vs?O=Nm!G>guMu=Scuj( z3>ia_8@K~&@uQz(on{6ySAPrYJ=IhrD7FXflvP=AK?z&6F|q!P6<1HAm0x9x%0-J-SVKHU}Ft+-^oGpF95{1gC|1;7nz0Db4DWwC!Fl``|j%rkON$$_LF8U=2? zO7)AGjs$cS+&#obq_H#?eX^MUBR*U|N+uS$PSdwoYu!pty`|vGFbPvj#5AVS6?LI8 zJ!qUQ*m_7O*-57*d6U9rL!uB0Ht!;vciu76o<*#h?%}KO&K68I^Xhbxm!sVS zW42Bl89{TLu_RO@8w_f;Y?bp=aR*8#R}jOWL=Hfik!|aTEMBt|_rV|9L$rvxgZ;Te zBZ|4=^6DaO11Q3}7pEjo*Q-S@TDrO*Ebt_6Sjw?eUc zUvhDzat}`hD?e0j>$2V+Tkj>-m?Spus%4E?$rmK#Tcy$o^Com40cEJ7YqkI3Gx>xF zlG|BnzcAh?L`dg_dr|$tsC;!8vXg)N266Yh`|o~q94(>hj*G(g911gFQa%<;l}x`& z{)S`3Y;s6pwyCc9A5M_~hE5j7-^tHEWuHsatp^G{eqZYK3o4I^&b&;!LR;7J;uA}% zO*0~t9%Y0HMd{mA2*12_IZ!@Llc%4r%-diDkOHnR5bDJ; z?a#S3&|62{$Bc5$E>1uAd(1c2^Z`f<=1)e8X=#dd(kD$&H!MUMH9;7>A6TZOX4K#Z zo&Mx~Cx{e__DOS>(osQkD?~tfEnc8mZz21l6JjJbp1eVT(rG;VheafIG!ChFR|21Q zK6fpN_Fp2?;aJ=jJ;z@K=qAg=&giOy(@T2@>cr0^zrFss*zy7z`hp+#u+^p^1L=UR zl@Iur0(@o(a53bZ?KZf8fJKkV`;Y~0XS0a?b%15)EJ*7z) zztygpM%eWeQ_44c^@zZ4<{~vE{ogP`2-UxaKw#WNOT)cjeQJzKzG!%qu!)Yxf!*DL zhr@T<83F+nQ50`sPNqiT#Cf{qX4=GzQPRWLtP<`if&U622mu0hRB;foeAuhwOhD=T zzZ;b7N?9DC{;2w1u^%P{AxLDEbMah1wGC9Cz_n5>CQS%I(?jpq&HGk&Yi3GI6 zVCKMriSdAfVdsCufPn0Wm+=^x)3rynepEX7Q();rx?#_1)9Z~N?9s6wY%<|hQCsoK z7U7bxw%UjnlBe(R#PIOo+FOvC>=Y2-%RgWR+ zXqUvf*%Rw?-b9lgl+V_Q!0Uw*n0L8mH}mCg*xhD+=9}jZChi%1GEe$;%xGAyIyD9& zclU_Bb@8L|KD?2(c~FeIp*D9NT ztyk{Sxw>OxDrMhk24|X|l1iTQP@JzqvZ|J4+66&i`>q1&q*v2a)^~4ejpb0Ja|!na zB}tEMj=KVviwTDS&~-1xdZ7E)F>!Tfp}D&pZ^4L_i$mua8&S^nw+ z1*5}uey65i2gQ#~LN^jb`)1q_SAhOerv22pp`moKq&Z+b(e>&2%0@Ow&AmUTlKP1B zyuBT@y(==zF{YrF@y0?&w~E-!rAE}d{_RN$5~S{FNjVVg?poJ8XFL))$C-sKY?a&u z7%ZCWM)OGN)ffyOA*p%jl z)VAhC!s(E+eswUt5lGr(lU6gb%yU;)LM_J1;L!{j@4sOhBJyqaj%2gRR(_B3Se%}^i_Kp8 zziy4iP(eo4zff@QEBM8WXCL%4CEDLmO7H#~goq;RHFP9XVp<^j4`=Dh-x}K&WV->9 z$1gk1ubOd>m-jlUEVkLXiKMg=8~S8RsA=AdKPH;g4PA{*Jo07c!!(w(b~V#A=JDM> zLSJ5U1&8l0iJIknNDD;A>xa!ud`Nj1{Ji+RY*a^cp(@U)Uw$~0d4=|Qc=g|gz=Gx2 zi( z|AzuHUR;c|SEvx=)-2m>-Fe&qp_|&(YGY*CiC4VIz~(8(V`ytVGBJ_ST#vEQBYbXW zdY)^<3>|O;S$pQ)LOhFWl+y$Qzbxl#UBe8r!6V7I#wu94Pu-jpt|Zn?q$jMQR#dh2 zP#=;7T~lw}FHh|M$rM9g^yJ5h0_;!j(?$g{@**l6t_pZ+aXXo}5GTIS5nVa%ob}P(I;vq7OC#?!;<1C}KX}3Q6RA%<(%>rR#2HNdvoCxxGsdPMm~)by^b45H@(}YvfH@f3+2xw6?+|nx;lqSTbz*(7ogZ zXGi0<-XMAhY_baizO(14o?%tG-PSQjvhjR7f%)S0C%!x0Nk8cmWQ?!by+UuY?=Gz! zjdjU$Ff*MIU9cKJ{C*q-O{t<6yB)&<{K?{J}_%ygt5xPub?VbbNIHkINPy{wy(D=QNJ2YzxT)N zCk}-GblC|3x%N6d;UEXtncYU@897N^-cY&4xa#IvZ%~aUOJcO61SKc0Ec;lqB;Tj{ z?=kT&+P<^MCXx1J;^ufvg0-(0uk5CmtV4b{qr_DFfkxUs8^4qk!oCnb2x-dk{XdSb zGN7rp4UcXFL8L~9bPNzSQt1X!7$ME1M5LubM38QfQo2TibV$cWNhu*Ux?v!(Z}0bK zzs|p1=Q-DXKUeHV0YW4yr#jyemrLpZ$~u`p4O8l2bE3eH9~#l3XfzO@hqS(mp3nK6 ziSomhD}En?;Z8hH$|`+#)hbR9%$QC+W~`ru}}OURxJYk^FaC2N4?@)$Ox@v^FoAJVHSJu`FIVb~K|D z`yf^N{U4Q9R0Q`W2b%ZB)h)>dKR?@m8@a*j@PEiH)I0L|nd1!T5ddTpJMyPzZO7N* zD-52t`OxS{N_Q0C!QsgbS~*?wVhHBO=fKBf{>fTtU z>tS2Je_zc8dDy!2Kt6?ydEJ7Wbsiajs;2uk3m+`Pif*y9Rr4d6@=8Lf@WA*bz3V=q zmdTMnCLRTq$-+;gmnvH(83zS7KLXY4&Jiq{*ZT}zy|T5betZXN;`HkW+rX2n5Acdf%FmTe@0#abGgM^BA$0A^)pP)trx)|`d2h}t=3Nm9V; zC2qs^=iI=f$~R^U;mMl)1K|vx zR$jDR&@e*&$c&+ux3x#Me06uIpe(4RuGJoFdWOOSQkBtUoar@hzqD$_jz7yS3cL5L z24p3wg|z9zzJpm?mDeg+b3%LW2Utx5_A=qC>Xs_szuJ}IliJH#OW3Ar)bMA=lM?mZ zGsN7Ms@&*Rhop=(;pGvDh3lAz6m%zQVb#O`OOYAe!&tlh(Y`|Ti)o^vfYav3i{{j~12NpN4%zRqr(I3@Q)f*+mBJcUbQ5^~3R8&(r+Z;!~v!KO0#YQD6c ztFfpbFa(?c>YdEb9T#uy6!c{97~~v~Mz$R(psUr+```!tHsAb7pLa^^ms_^cL#enN z)eYS#UUL!V#Ugs`I7HtIKBSv=IhG;pQln-Z%>1rE#QG(Ecw-q(o~st&N6NwFhX9vC z_~8ahJz?4hpCJ{T9?FpTGv%;~g8JGkH>JFv55jTlbh;IFqPZW{X$%*3w|u2L83q87 zamFHBVf)+{n=`@It|P?VZQYaolijsNdzJ?%$^IK51uoYYs4O3$wvZ$|%?~wj3s&gW z;80y$?c@B#%WZ4yk+!3rzFCb`-t*gd968U<8xHzRVd@_@0Vu(Xmy`8{avuMtO zu>I84C`&v*rIKj1P!l$##02$5gV9#G#S(S!2G`E0W@jF(3ez&C+Q;p|eljzKnZgPi zhu5nB>%4T|xdo_QEnPhN&%~s^KPuf$WthpwV!(f*AB=1k)E!H(qTNI+L z8!o2@KU9C4BG(O*dAy(&K(rF!+H1@^*e3OqKS#8ynTL0*8&gy9~}SSR&i)=FXctIzd~9pt<+P3Tnc*fH$9{+oXB%E+fw ziwP;xee1vg9KhV5ZNVDMK7w&riJRWn6@;w5lC*$3*`+s0K<>B~*llP6$!=}8=rC-d zpGc&bDHPJZOzy~Md7DI`nbXBZ+S18k?q>ubG|5H=TzEcYXvpOW*1%vpkzbmTNq;hM(?8z>{&w<|%itdK;NP)^l{#Q2lI z{z%5kIMOrd`GSZ397h5XasTuTt&v$PotcNdzx-MQzS-nA!@7~Cx9KC{D1C7& zeB9Lq)f_Q%jBd)wG!CH5e;^3g;G;DMy%9!56)?+oMzWaN*n4{#WTFQQRB-m2y*tWR z>_Y(q)JBL!MfNF}T`p`*2LF(u@zs?)8ID@$?icpv$(Ye{H#d)ywbWV#(Ys3ua!Otw z2ZHoavUyMsN)u)KykBhZxnw3B3*&Q($cSNC+nh$XjH+Kjn>0Z{Kuu#s4l4kv;Tn9- z0`uT1|F9Ez_5;3DK=2LP<8Xj9y}S4iU2G(*KCY*}BHdRS5v6+dk82K3CMmR=|H=3$ zs1X2WxX!0C&r-fiI6rzaHO#_U@(M+;c$UaadwtY3u_SL(F_v+helV4~m*}A; z<$W8D&qyj|4(7dPmWp3lM_+(U_O65$Y9>Qv?5Is3F;Ul)YkMWxIU~ZwGb_ue(SeqD zT!6e-lyr=XX_7X*G>^5TBGWL;Jxya4C>MZ^G0i}A?>+hGFw`SSbP+=cHVDw;|N-uw{m=@Eculxm|+%wfJLo|?%ofn zc6lwrlOE>@6(=&(w6IoCNX@TH$oYVZDumZPEwRpb^`TF4+ z`MDf?vsUUjlG0vC&f)&v~^{%a?smJ%qkI$JbF&@jhR1fdl#fN9W zL(h}kDo+50R(&M1V6aT7+XpCF){yLjZSiHe9WaA=8~11$Uis!TRTzXGL|=NXUJ1fd?;a= z&gyOd(COb8{vL1qiFo}@a`0oBPc#9lxbfdnf;}%ENZjYym|^dq70>*^Q~+e^widHk z!~5NyC)`6owh*icl3rGvm)^(G(!_(7531(fI85H%q4`LQicK^`Da=WziUd*GMWi|V zOkrj8!>~=)KQY6RCp$`PpMHjO`m6rM%q462w+ZH~R6IoGY3p-?ddah2x}sk3-zXNX{1X$eDxl!%eJW1I!S?O%j41ZH@z=@A!R) zBAvN-0RR9C#=nYUey@M7KGwJl>zL`t^No{~Xz*D)?AZIr6;hioMuFlGBdgWY;bEFy zC8N5rtBXJCDfxj1z}nN|s&f)^YhXboynp^XFzz)(M_;sp=EM!;U)tVaXM)f6Kf0)| zkl#0X8GV*deisPr3J*hy_g5C{+0MG03)3zSWo|_f_)s?b+hCy)JL`e9mpOyjLK+Frkx7FiGbD-^x{d@U3lz<=>d6%XUEK zL^=Oq=HoIm%U?8b=6{m0khI~JYjzWiYl_~Kg_s;el1(__{ z-r0O3ZXy&)QmKa|f#`?Y&NF7|w%qCWg@0L75sWAiSy_|3`4(=IK=d!67c9{O0_WH3 za|<7vHn4L4NNUYna~Hbz&|5+$60hI?8mVLhl(~yrr?G_yJViF+08XvaE(sz(_JW=1 z3ZLLSbT*)%&;8z?bwBW-oC+HvLIIN?^y8c7U$c}E{YvO5TP)xChT(|^<6n|Rm1 zF_kMkT9NreJ~MxJ0}5_~PR@`_nT9_(*v53|dYv{;VUE6i;(W7$CqA^{C(aN&i% zp_fmeM-EVD>rZ=oGx^%&L!|T8lLeq^M3Yb@zMB4AL6{D&%;a6n>|Z)S_rrp5GJr3c z!Ajrjn)FsQ4RUhfFfjl@bLYH8} ziv>9*@b!aDXhwAHu|B?S|CJN0SrUR1yYTW8#?6?)c2=X2gx_NPvCI8@!4xv>wNJyf*DA3L9^zic`sZCb%H z^-9Xk+;ImD(wmKa6NS_WBPr2Y3(e3aA+Pqq<+W;_A0^_GL#oMs=iD`l7wgU*2f4TO zbEmDDBCsRBypZB{OoQ9Zzbkat$cPuMRrepUNWV)LhW2>g@b6`bTOSs{DNXmQph`6< z>x6d!vr`TQ_P+KdWlIQp_Uv59{{bW5n+W z7N%7z-@%LF!=zLY3-5=fN$e^-V8++r?S>mat*x=dn7H z^?$yOWyLM}4Goyi7vh?b4uIFl`F1;6EqxVm7vEm~bSoF2|o5|c@=s|Kg zDfJFchTpqozkcx0BtLl$(P*@w=5b&IrSAN_XE!N{6Sd5HR8{Y`k+@@nq5ZM-n6T4k z>vdC%+t4(ZZQtxk$X$m8=uPYvbw7S%@xm3X;7x{Xw5W?s^f3KW#l?T8lA44ZeR3o5 zVAe!}sVL2clL;msbE0sMCpS6`TSglZ^8+hAuf0Px2uDOGnf9-tL2MX50>uhdNZ`av&cV_O8G@Oy$6pa!QhFx-mr z1M@7e{yuZj_$K=5t|%~Y&g&kmvZ?*Oi0<)=71^>SldfzLXQ&mT7{fZrSWK??>)}^1 ziW*dU^y~t=JZTt&6y8`T)%99=_f1tj=ex4{4%;(!F;TNGW`WH`ccKM?%k@EXL%NlG zxiiW`nEMsXO&%Z(q;ve?lbdxDcB7ala{gt}xD8IWo1QV8BrHiowNTXU_1dbN3}C`o zGf0hRc$>a8?E$5$-#=&9T|K&b8co2!dP2^XafRUJcEysNYt-&biQXe$SCmDF>gdXH{<7sE=n>ScB_?z@Y!tS z*Jm-)er<#WSp)tJ>;)ZaG0TCk?G)6R47d22w{-SMw4>-nI|S=WPkGcGS?aE7zoOj@ zKxQqD(4iK>>AI#dyBohA+9j-p!}IgBzo+U*Y*Ggl>s`?=2pTd8sQq%-tj5TWGE9kNV(jp2g>yCiUuS9%ppCv7G6B>8 zh14e9jYelVITmZ=hjuDgho%$N-O5a?YL^rtQkS7<_l>dFqcb;AJY9`-hj~AmxifL? zUFGd>oH#Y(AOY=?6h&m@W_!X$Z9@)OYV;r}-Z%ov&L>&*(Z;e==y1^Zr-9Yg)pwT0 zJ7QHrW47}n<^oOFlWK{ z$S(cvO>ph=a~G++w+gGEnoo*G@z{cuKsq?0kePXsx4sF0`4yPuENUMrPHmh23YjTv zwInXj?S7KJV{R{0HzCshhT}p2;J&lJ|Bx!vmx?|g3)pbKz-Ql1%&-wZLamn?l+asV zhaPVa`B(l<*=g=yP9{shf!KeywsD&KC_Tf=gebe{QA1Oo)J2?+-~s{yh@HCU;QOrb z@s6XjJGlO85WU{!-QOu_YFfI1o+r9;yDGVQw;w5x`5gHq zkAy-A(g1+Q{5*eIkHf2{uk%po&J5n8bYJPb&1891*BTSt?_{!%|G0ChA{}}cK9mR9 zZQV52kYYzWXF?P$3J3OB3@4UppTtA3GLv+R?Qvh5gvcAp=TbGic@Hd}_df*i^#nV2 z<7OUdx6KU8$Jwc*$T++kr_7kwU@ZxRaK1_?3eZYsegDwsD$&et>BvcO;|7Y^2riss zU9y$(%vkx+is(bn-5Na|QsMWAK>B(w+q*vJgH8A4EfWE1nA(}7xujVG;>JH2H28YW z)WWRww^jV9VT=XCJZY*d1&~$|$RL_Uy1qAHNifo&<8*Zne?6bk}F;ulrfkb{~6$ zBpDU>+9=r&;!02Z-irgQhKB;#bB4ctJTxUt1YgPR|JHt0NcSzyJQ9B_F>JLO4^XcC zg(Nib@cLEXeH#2W>wIZZtSiDbU$eJzvW&@~He7%1AO4}iY$9(1<(!-Ui91WuQ)CGh z;@q$Qfdyp?Y{g!$1Ir7lm?vVQ-Ibxu5euL4Z-=GOkE8GRfr@m5qjWbyhp=>Oid|>k zsnRo18mg<~m&NM(-PM!`LhTNQGM@iPQx1A@zW=x+FHn#=!AOMk6I17zZadh zPvcU&)U^+E?DosGqvXyFVg#v9b^rEla4@Nvea;jMz05 z`?v1V>80V=+ACgJBs%Q{ea%6Vzo!`Srz%M7vN{fk`{SQV(wEr(FMzJ+VV`Z#Cy&vU zQ8`5CC0Mq3jtKv6pu6yguA0>==DbTW{4-C*^F;i$&qWW~-Zen2Qv7#rBMYwYv6NAl z`DfsZ_*2Jz>sYZ)>>s9RuPrSt?};%mTc_ew~3qCYXS1Sa0_Bj3qq- z&MVqIMs{9&D1eV0g3HwUa2f`w%la{eO=H-G3tCDLrD&3c^yg04R+>Lsh;5MIy@(9Y z2nF%>YU>yS@+0RKr2m?|CCwrVNW8jVxVQiSI9}y>cdv-$syohbd&0;$ynYPUo6Ip> zY5H~a8Hu+do_ZEw<1o5!wMc#xztPoiepN5h)iyQocRv|m^7Y(8i(0wIp-R_$idk50 zlzd`#eAdyMc#`lE3Cm4TljO;8qN_Z>2G2V@BmFl-vh@{rX`HrUa zg{_16*0@gc8;6O(1yw_>5AtMp-!EZGcia0clSK||p+_ii7;cceXx@AqFMv20>uZo@ zGlSlh^`AEw=k1?kX~^I#f^OBW2osF!dkS2s_M@!esqOm}q6(Rp{u;$SHaRr^xocqg z`xR_bFE92^|4SMz9**{3pE}lK{}V9++#%-ZSlXJ)lr7w?m=-NjS2{{n&d_o-dhm)^ z9J@I0=)c}e%sMM3BW!LiOpJe2akrm$RB`aC8I>cFgEZ$2o!ro;m#ydYIAw4%O@YPL znIOBSGshlKyx#JqF(ytV37#NsUMBq|kyj;Be%h4;NUA400fy_3 z5cF#MJvv^+#(M}5sZyt!$6iKyDcy%<|)a!`uF#Gyuf|g0B?bl!hbM7WVrr4 zW&E;FBi|LCGmbiEjiM`D^W85)rM$$s5&t1ZQ`ib3`qB1%6SOi}c{l5JsP^xT=pfAJ zkD1zLpv{>ya>%dJRdEJ^{XC;`1#j!qj~$_AFAd9=W=x&vO^Q*`oWAnhM&SIcDElzNtJ-BcbvFZ<7xa z*PZOYsaJPCwH4}(KvLn?u$RA;U5Vu!kb9@NC=|yD;=lL5tJ0{E2%y_a-yk$$Hf!^4 z+vv^Oo8M_^1c^X4%HiC^`q_#!d}9JfjJCsF_U<7q+@c(!{d`d8A1R(x;~cSZDx{PE zN;+m}HW6QW>!ETc5#qas0u6v|H#+ueCj5S+mgvgkVhFFbz|@S95Umy0`^5T(w-LC7 z7cz>y35|)~D!_LwIgr9NHPXBD{9s-o3{wPS7zTJs?2C`jz%pye zDvR`Q1ys1Qb(244+k2CQ9>ZAA4xJR0Ym!c7t~BsJ!geBhqwywmt)BhU!>?SJ>{&(L zc7))|S08-@Dumm^HhhlXnzTv2v0ctk*h&l>40`xZ+Sc;=%b1j&$ZZ8@j8H!d_5}c2 zAx-RR{^Kn?e<~0s#4*3Q*~Cv08v44xO*|b~zWjj@u%?Gt8UZAbtPA3oUHC<~EMSEv zEP(EoRErYODwo2QBk&Gm;FsrfJ8nHi%XX3)qWbmi@eO|bqGz~>!`n~29GA%Nc`k;1 zrkbxz8w%;w1(n!8UVZlcX>KhZS(m<0vXPwIZED-x(%+jhJ<|Jz(xz)>?=^0Dr!8yE zg7iw5L23;I_qEHB&Wvu$}AHmtW2=`j%JAJ7iElt$-y89A}+Ajc06$}`Lu{d+3spI<)o(C04N zm3t+_cli+c^A8EWRWPZ!42M`BnmA(>3;1z8ulHtB83_U~iep`x{BED4;UjO!(8)G4 zX|ra%k1D>9Mx791-0tnUXYOA6DnA6j`5*kT39Y%x2=3oL^pNK^S;Yviu6y4(K2MbVX~t8j(rED z+KYCMQ_bK%#(02z@?W22@4CBeYUJY#)m9Rv?Q&k0#lAJlX@Q-UonjC5s#hl?-*H_(BzS{aZAJb7;E-M>HI6jwM z*wq~A#XjGxy*+tvXOMt>LqxFG$CF<*Me~jSmQNZ5FU|g>+cc2vCPxXXO%-K@)YK36 zJ;!wq@{)}TyTVd$z7h`oOpj?3N9Ai0X}O+lJ~LJGcRotnSQ()ur16HMP4}duH7_&x zdJ5CE{+YS^Wr*r6<*c97-4oV`4o=jdpC&Gl$@pM->cQy~T0M>3`PVnGiN@P{RoU zm3P*04iWwU96B__>i2RU?=}7KV)`j%AL0I<(_E4cM~~drv$WIOI!dmPn&jkC9bl%v z*aKf9mgE087U&VVDjXT+h2F(y;_D1J{b6Gt5A2AXR|P)ZS28fLiJMsPFu%5|h1L$OndZxx>d@c zKWIOjlymPge4DeQo{KGcCS|#9WFM#YX9yX6!Q4Vk07isUJ_Aa#6N(T5Ku-5@Nz1sx zh_P*O0p)3lGqJbDdmrqe_+F@h>wt0NKJy%77TzTE!M)%uQFiS9?POHx%J3pw$xY4m zYUl2DgChxO8pT|d@~TV&@xeyk-NbzgdIR%Z@L|KI1Gl3EJL8^I);!%!I`KF5WZFj6 z%F_`1F>rR*8!MnZpMGM4%z6M;&Y+ofzycS^aT2Ao8kpVV={X3(+3-@RDW0LG`)GsZ zA?l)na(NL!E;+OX7W&BUx4%3jb_{hC=NJ00mMu+pF5s$2ES8YU|M;@UYn3HgVPrZ$ z2sk%!6P@}yTI9mutvyf&ah=ERVT<=N1@*O-{9!xKg4capH-PA3k)Vy&P#T}?pK6C< zUIBCSFb|6j>$;s!<}5I7=d1v;0Xa?OS;XIc37%Pwrtw}rndh0$?b|_vo*x^}%ER;y zqy|fdeABur#Pjv|=I=)6y$tFt zq{=-#Bi;~I&8Z{5teaEk0c@?A0RaCV^et5u*g93<1%JkRthq^kXGF6nFT>tO#yF7kCxLv>9X|bzzIVVE9u#63pM?co@OxTE}sWnbl8SEWB?e8k53> zzZ!Ojv)`12;GZcp_B@Av9s5mXHp=mmdl+4#{%b85m*)rN+~}>5tM;p{=*EuQq^5e%rh`Og65$?9c z&KWr1G07ub(wsEY+iEg&%wc1d#G$_<@|Q(h`0EbF^EkwJ@4vMS8FPhvG6iOd7n}9Z zLo0hmxK3PT9|0x;k(P{ln(`uEr!h|^V3dBwRzU8yBKq~!A;k;hH_xYc%KVG>9`5L$ zso{lD+UNBD+vS2!9^T;J1H(88GF_v?||DN2`|s`)}hV z%={i?7dTZ}Kxc4G6nixOdBNpcdGB*LAbDN52?;n#yCJcMQP;LCFV^>_XKG;Hw|h6( z5>X3T4}!0!KG{&GXlsaUQsLo+Vr!fxFKr6;oIc5wyQ(8a*i# zMbzo@s^RML1Z(%%!)Y7O{6Gf?xd`@r$5Y+tCX3heVW2p3{u`jjbOiD6o>f6@K^$q% zFnU^Ioq9FlOYHGqh~C=@Uc>b322IeVg~qhWJw=CYU~}12MAm}HPOj+G zN}I1mZjz3y73xInk8>RD&map`ZAD~g4S7{@$aStqu<<3HM~KDqzNxI;ZbHtje(HIv z>OV(V%5<1{vL`@adGC3%J6zzeW?Y-h_4_lyy(Az6|8A~*vjhi_&3Yw>%Lkzf=@z&r zdtuI7?fSC;xb7Ut<{|RhvirlVL6mW=*qCJaKcUM{0ot?rRljl*A>CaY(DDswbRR<; zW(dkZuie71g59xVN%iCy5Uqm%u2d}F(9xA8Zpf{S`mck{Mb2K3sIyxlT1phgAe)uy zA-Kp&2>^<+K;;JFmiXiE)?ROxSjO?@TaWte6+<-4<- z%a0@aFSkoHcQ#O}pghm(RM4RE%)%mFMdgb_9d;%(9>g#?K6i%sO_jZb!Jnh@egRVr z`43xko84T~U)^;=wlT1%&vuC>rjT38u2G*F@7=|`P{Xv@H3r5DHG7s>t~ChNPoPEN z*wZM5*-7>K)OyM`Ut>kRX2v<2>Lhh0$NtkJAMeb1L?%(&%AVDIhT7IyI=GDghI*Ji zOdgRoLSL9yP)x5l%n6vNXuJQFy`E|49d=h9CraXRS**m05>Z;tY$ymmK}`byq>Gh! zmlaKfE7@zjuss6+K%)%wiJaG22{2H4`;^HDi8XPy2&bAv?hv+VKmlj{@Zp z?ChTE%zd{ehy%veldl21JMk@#kQ0)#nw3FPXe-m%^Vm zMx+$cej-lbpwzL?)ajh?ZUe@dQR=dWd@KWfnfdIxljXV~AI*<1ep^G<@56H1r-ih=3n86<~`qv<`L!d$I7OCpmm?$sRu zz(pX;*dewgZW6M1y1zjwgfMGpROEl6p0AIHqQy9K0Zwz19fyv1(@^#%+ycriATOLiccZ3|t@ivl ztaz-=lx*7yo5^M)UpopVN>d4yT`-Kv+c$2!Lf)*A)3mpyRTgovPZPHu!-2IpX+O2H}z7&+8`%|fo^m!jL zyVkW*F$PH9T{C%ci&2T42Yta2q}jd<3;*9s8(L;ZQNVjc7b*q+ExuCwh7GY~}Ijs8Z9whbK%)DxD7fb-@7Tr6DA~*)&c&%{w zr3alP`pdPQZKQp^fXgC% zuS|WTlGCiHT~E&Z@-tbPFQzj{-=2(~M1RBCx$~~_mCx}>{_~0Z746}qbXHu)Y&azH z$QJ=IPPWN^6T@bnfu@2z5lNWy<+yRJlWDYZd50i<9h=7DTHaa1mSK|IxXg)U-q{ga}#d`mxR$sPzX|M`AowDZDl;dHe(Hl@>-gdi-a2 zb?EqPZ@B@EgF!D>re9@}?BZe3%Wvg6YQ#nbsKFkzevZpxFoL5@8BRT&WJQbTJ6{eH zcIAu*m3TE8-tDs2P0Z5n`6;$V@Yjd5+GgtiJz8GF$Sj%WYqcWU;I=1lHgcHL47CXn z8tIYUul|k*usR&qop>VgZ$T2c1}gu+m)+#_ZEjIfV}siM+9e|8WOC@WfqYk)3mhZPX`lKq9B*UA5JsT_Xxj`L8w7S1EFWLc&4 z{vrqU`Z#wx54AGJks4tOa#Wvlc-k#ePyXj`_%KiJwX%DZkKRMtJaxVYWX1DNh?j^2 zLE>MO4Ok0$$n{w5Q?Hs4kUL;knCY|0PYF4ww<}v;f>$Z%(G6y2i4eo!I1_KI##8)^;_wKgj$C&p8gc z^S};1F&~aE5L_(!sViY|P9pEmFP(Ux6k;fDX^|mD;h8%JS=i#aqz1}|IsOhTdQTkE zNO;u|Vb69D5O%-hW`?s2x`A$*sHxo9ktesj?B)z1L>J1Bk^>U*^j7Was<$Kswo7mc z=aHO{TRzt3Y^#fhx6nBIh7}p6**bLx1P6|axJ(cjVfOQtfs!~j5~A+KJ=Q5d9efD@ z7&>q?)P}5irW00>NB$OPPqdPFjE6q1S^n-= z@=9CRGx^-`eI@|(vyxz1Gtp14LB9l67_sX~YsxaIn|sX{y-IKXX&5Br*k?Q=Qffi@OVV_Eevq7BTt??eOKuaVfM3^A5O z)o{;SUhXXRDqN0Pk@?%fl)N9&_lZzAs^`4e=ene68<)JyZ^Q5tzbF%SyxS4&*JD>w z0(?RKgr>&OWwU2jS6a-y7sf z=f9s_VFg$?fH>NU6Ss7`pJ4DNFBZE2%`0qv*o6K4#2;qQaRLihpn$X@S*+}7xg6}+P>_In@1(KH}r1{X71i;cClSH%{xv-%17MoH$b&hYD) zt|tlQ7uXq^b2`3{(0;R}RwpfDB@11zvu>qB#Xh3JpU|i`PEaB+g$v#1QGYS~X|`v{ za@Qj@N|*RvTZ_%&P7ZlH5)`4(<8$`<3M5mA-zl9^yb9W=bkZd;L!PaH^f^)Ft6*F+ z*&~5z_F#wQ@|8-#^!xXZlgYc|P>yylW4~mwtdZjpM+R=1dH5;v-7aJ5tg! zu&ew(I8!)w>UhRbO;*~w&_+q)63L7(=Z~a(YicT%K8i-#x zDK*?s3ym*t`cX7VL+b0hLx4<8#j9p*I5T|QeEpKr>oQ-{4-GV*xg1%hSc@MPaK11N zMsPdcIj1Y^g=8e4l_!ub{W3f4C3NS+zv2V+{tXqZ3o=fn93N{l4*%sIm+a~CH?i#L zBOCr)ftY&?sA2wE1>}k>d;+Zfl(<*Aq^~CsvVsHfjn*;81DKr2nepW#`F+cucN!1+ zl&wQ8Khmqd5?Uv_cRs9gUhVPzyZ#P%Srk5>dKL!zD__n6w|K=6t{y7%#H=L+vN34q z!;TR3K3XGpk?|h5PMC~1OR!mc!N?M1Z=@eQuqJt_B_)r(d1$-Nf?Z&UqQLFJ?;mWi zU4{i67YUWZm=lvq=r%HGdZf&aC&ZkkGtC$_yd5f2Kr(n;;T`*Eo2Tt89(#ca7034v zKkFBH`l?E1DJ(3!O;$O)ZU{{ka#5YM{3=)8rx+I1YMvj%vE>E_TOvN=*$wtJp9V07 za?1`jlM5((RjX*{2WQC&4q_v)AdcJHbbN z&VknW4LX+soyoEUdx?J{3~4hhdtVFaSmQmoXFsZ)RlJf%P<67J#k&n>cHf=K*W7po zo}$Oh>=7Iay^Tp_J_G^_eu&doa(&dqY%N&9k11OIdG>LIQK+F+z0ku%)@QCH7Y)? zdbw74&3Rp*kmc=eWtK~0!l(zaqOM}25!_fTVfYi^hfrHj&Gh2w$W|{{bc?eV^D%6z zI@TaG%`RZ@b^56nG~c4m$IOJ#2~ivtdby>%z$;rp;B`1C>!6vN^@Tl+$|c;*VVcI{ zRi@c+r~{{xeNiQH+RJg;U>ofKVy=xv_jrjL_pN3IQ07L#Xe7?@Ep8Z-t$$Z+4CV&J zUrO4WtL|r5(CACOmW|>Mp4pH{8rhx~x7)PJ3Ba>Gt0cQmS4BZQ{ouL|Tx6#}->BQK zt8AAM`RrSgJzT>)27%3Eu>W|=Xx90Mg_NHx|DFG-vIO1;l=iGNdi?W2wyY-Z01dwW z7fcZAg>mCvtim|!xKk;teqx-<#o6_$GEXx~0g`@p&p(j{vV`()k+;RJgpOBtKR(r$ zWx@>Ho^|?`WV7Ppw6+t<7hyF%R?AwhPJd;t{1Hx}1nr}C>N9$D4b9z(Uh+2HUs!C2 za7$CH7NS7A*{jyorayo>_;VVI1x0WXshhOkv)nMAh9NHNW>2=+xQ52oH)A+C&QOVc z2@UwH^WviGFy!~MWPdE}jg@8bAtfC>xc z40$2Uk&MzNZBh2pLVv+45_ZxF@KMPV+THHO&S4eY+)7Hg-GkiKU$F- ztdkFIKoBWB7lQ+v3Zw@wX%0T#UXI_oHtf!0rTxLILm0X9n&=%tw02I~c4pL~+kfO7 zrYeUAT`_#dLpE-zEDe7q-Lnqnxmdv5Hi#Pggsa$>I(wAN46$)%Eh*5ieP zz;iSxAz(yK4A46?&`ZVemH<#m1QUtnR($b~Lob2gS^^N_Q)eGDZ&JhFr)k#ZCIGOy zl!s(ifkKN4;AI9q9@Vj0A9kUG^-CCN!c(+rOnsUci0Ko5LNgr{M5v*7o4RxX1SOv( zp1OO<*S$!;_I;*wmab~7oFQoxv*zc55q#VhVW0ooc|k0=&?CinB@FETPQ3XrRrcUL zN23)a>G>aj-NKIoQsb!AOkb(o6Tb7K#uwP;x5geTq4o8l&bU*3i>Ih$1{JX4@Mj_0 zl=Z&Zy3WoXhgXNj`hz8wf}8EBnBlBL8>*8~sV~XDy5O|p;du53GZ55ea)qLNX7!tgPI51%#m+p* z6*;{v7IFa}#3JYIP%$1#GN14}iCM&|xu`g-|yf{_4j>dz(#P^oLC) zSe+yI-@XLuX5%H)wqHpCKU=6RPSx95`2~@Qy%tDRmtFr=it1QOGo!Y^UJfF@iwExd z@&ob-lRy%O*nv=3EVF-BxP|Mx^cQecTn_!|iyY_|K_~H8QbFc`LxmtBvv>>mxdiA# zuH-*QHPy)W!|fXw5YM^w@IS?I7+Mnip0=y)2Kv)sg1`yJY4f5tLavNPq@QhAFN_HB zXrB-D2;J>J#=$AC#A_J>>hFS<(rh{rO=fCDx=mn_>;bM3fM|F#Om4Psl^h;{NHfju zM9VbW)`E+2nC`69gl=uW)Kqg+9_+)AOfJ?pb@ll?tf~dxc<*>T8!og^o)_rO)X=s4 z?*cf1yd{ci46uVmo1Le)0I*DZE9*9H=@KQExAKvP!av|xg+(DOyc~OWuh_wE{tsNQ zp@Aeez<$(Xsek7gIiMRjWvJF#+%*4#TFy&o;5Wk`gVVDPyqrnvZ#sO{q~M{#W5cKW z3%~;CYU!A^clai=&)G?z?vfj;)%E$acWkQyft!i&x42-i2~A~eoZKgnZC^&B_lsFtrE0z3PYYDD3o|jl44BCH zZ{G8Ql47`kzGY{Rz%o(z02fz&I}tKjapchJ^Ufg(B$rN;xatuOjumf`WZWIwwElP= z3>8Fx6BCWu;f-S~t%iR)Of;8YkHqz>`t7MB4xErua-aEFIZ8DOfdxF4lJn5h{9}c@ zBA?rcc|_dpLp6OIIDe0DV~?-CwWmA{+aME1ir_thd1;coXALoVyq4e`cB1=0?6IUs zaTq|_+%F-KrO@<4yY}laQ!{3;k5`^Cimyz$tw*ko&yOA7U66`Wy7dA{Y%W?U`;_T1 zXK#R64Av}443>cZBpfioIqWtu`JDJKtRDf2W&>-1HY?wb%4G=~V+qyvulrtK4nV$B zE3#n*J%s_y6E&JZ_?zOwqADD7)SQ$3H!*(84E=Rd39IS(gf}taV*K}Kcv*-;%wlSw z)M66e1S&aofXkFZx7;DWA(wN*2A&GxqL8(0v#PL7rT02ERx7auWf(O6rOgxi+spVk z^Xq5xi;lufx;MRnFqr-K?Gq8f`!j~S2-yO}Yna{~7OeFW^AMp9a&I)9_<`b|q?oSn z^9u)~aJGJ^mDpgff|L#wqkkHzml9T!nnOT%`0{&k+#*H3E?+)RrpqjCC^*qt~*VZF{j{HJ+S?g1XRGXddUypxnVXtfRG>2u~ z1NCXxiBwtm9JhkKOqxeP@?U+xnTZ8Kwp&hU?0QZ-?v)13{b*Zf(x%a@G*+)3{&t`r zNZjqv0XQWFN!B{e}(x{*>sL>dI7yK$pS8l<~>@ALb4=~vA$@Ayz%zXcL`pBxbH2i=#INS?KvG7?9%av|wX8@1jX0Y=()1yVMWziWfqL<# z?CY>h?=%Uz8^t#F--GyainryOOkw{2yBt@QT5YwzG%$Mq=W0>p5FJ?Y;hd|1=mqXJ$;1cvIEGYKKd|mR*GWI=niDkTxDw&^J;VTP zrOZC=s%kBIt#L>$<|_CZP98LsSH>txXxoJFJ?qMB82R)8%Np@=F*Pi-U5x9vm3>Fq zDcZDY_NofcQ}A-?H7z9fp#U?4rg@;rjk0-`qdyWOzhK`ox#90iq@}T4#Slx5IZ@>u zyD`wj%;=wX|2n;3Rf=h8ogTpXV-&zui{pS#A`&BnTKJe*r z*gdIt^!0N1j>(HUr7S-uCsqiG$fu$O#yYpds9Q8>XwzpAHXn}p&Qk9L zWwvX+eEM02wf%J)?TXpdgY1*tWYPATu}MD4X&2>11BWJv=M97+OY-1YM{e%vO=hTw zJ9bXcnz8u{7Myeq_z+*`%}@~2@4hyJ4OSE}#7Gm`kM7#9%YTdw(bPFNE2`v!qtvb+ zjIV0JQG$1?-e3~gK-C1Oetb77{Ic9Ch-wj#GW>YD67yN5@ zC8kyK?5a>AU`_0dZa2EZAFE#a;?n-ZU*)EtX17#N%o9@R&k5g+fS-7(CI8ZyoaLuX zh3mm%Ul453%!Q0<1FjJ#hAoTMYM^q0TYmO=V1i+FoxxoJKs!%fX@ogsCCoF(DoG7HMl^OB z_JP(~u_$xtyM5}b@WyTn`@BH4_iX5CUs#)vt;Gz(+{Dn z2AkSq!AiZsr-#?vESZg^v_H}_=qv6^>Kprl)$za4GvOkMWZf{OI{8?M09yDFn|d?; zWQ#0FAX-DLKwXzpTVubVT`9DN)n=YWiddiINrg@#xuI)UJdPzq2N_#$IW@|5JfBAz z^czC{77pkuZkCf^v3l1uce6sS@_z@@p(eh7qG67`6mw-y6Q&r!$Bpt=35-g#ABeH2 z!8|T$&NiV4;Kxma*2ud*wZMTJ{dI0kxo^Y;I((`%Z}>u{*~;fF>8M78nls>BGVrFa zP*{3Yd=VPZ&v^Ii(=Yg^Sb7W}=HPU&&z#>G6U$&EVXjM%;l9PD8aHqivlx4j#r>d6 z=45VoFn;u>$p)^Wt-m`WlHyJ;@bn{*ke+`r)HnQaQ>81cu~wk_;gn$3r%ajXa{txl zT^BQOypNU`6riob5rP_k=@5H{zt>EFPXPd2%gHOm>LPe9SZF6* zAs;ucZZ9uz=xTLlJ)NXyh2QnkJZC|i6mVrbUW-yRM0{n*`K^xgQK%WLTScG0D(ho$ zx&e4keCS(kyuGo#)8DSeob6!%F(^QQ{ejVVwZ`or@3Q~Q@6*M3IoNXfuZgs3aFHOi z-}t|ZgN`zxs#Tc9j#wvcX^;^uDr(FiBykE7E2-;}Dao5gbd2cK7LvU1KM zy};c_2@D380iXZAFX8p$OG*&rml8tLcgg$(?PIij2 zY%#9DK2odtv$S18vQ~2jH146j$PeON@%?$uGx-~QMa`X&@~hv$j)v}Pc|TS8umRbq z<+qF{ch@5Uln^v1;oq`*+R)aaxj+6Ue>Db;>o9o&H#GhZ=QCa0PaGbAV|W&jMWp(O z%B{*l4{1$#c(qhJjc=>-OBM0`$9%^Cq=_cSeBbbec3$G%{9P>>@(<0+H+>%bOt_Z0 z*C~iMLiv1OMD%GJssks3BKds{VJKW4v|EC0GFwO*S8Aw6pDb%}rZhBLi8hlJ@qfb<3a?GgeZTG(`sV3PBI za#IA9gi&O42p(>tH$c#^|LpgdM`T=A(V|e@wv7+1ymZ8Ls<5Vc5_EDfUb@sXS-tZV zo7J5h-wIz7f|z*9^d>2P?>!a9F9>+ViA0>SNDF-wMTDB$@w%t2EQhl+De#KPsaym{L0H(L5_gr1n z{pFXJ=+xAVO-98`o18b6O^?ZV;DJQ^-gsP46rc`R?(H^24N*3qNlmQeR>^Jj=3E#y z`f0hIo!tE_r-Bgt;$cRPxVF;K|iw`X@uDT zd+D-fT&%~iz1q?YHV-_v7)PsPd3?nq;4#jZ;F#JA1zy8@WK@RY;SV?k}$#TKWf(q;*eyx;}9AJvCE8Z4&|(KFn_1bnGt z1J(f{Kn-d1>&Vq1)QrU_C@z_pSq-P_te-TYVTf#!s#vSpn_rW;Yr%>Oo03Au|C%RG zM61=^oC^8fH=A#jfQ}rRE2=C7ywlvuukhZ{NocjVjAH+<)cI10i;|;^u+hn2)y3|y z0KTyy)+eILPZs4pyp%i1BWhFj#V$vM?HSg`Ere?|_5_g4+ zJiujSldY0BHgXS5m5OnGQvg*B;O#U-v%=SB--9H1Typ-kQnoFgS+l6r)&e)Djm^G4 zD-IH5J$O{2KJP;I5VE~RwDZR5xP8C}oc>%bi}+eR92LJyV?+V=jq^;C;W?FyI&$D& zrZEg&v0Quq8kgq~b%Uo8%k!yV z#@GTGl_KXz|01P4(P!?1380m8He(GYH|5VyEHVPYgjKPD!%`;uYgd_)C@2I)EHkh- zDH~mVXBKIgdFzWpSBB+2aG68ggh<_C=+Oa>Xq8J6$Bh>tXGeu-Vh$qo4{do@&d5_; zjB6nLf_LSCN43!f6PWb3sjw^jqa*L??~HmOc<+D8e$6sedRCFzIaPcVE}G!K8nEJI zO_;F0gz}`R|07F>hG+S_3G`w&>DDqfR0|r#K_46f=;Dg(Xcu)DoR)sRmxOU?}%UtmG@LOol@y7=!b1*{e9sKJSirYYI ziE?OnE(tBcqeU!D$iG6{nal(<+R*TNIldD=m zZp}(gf|EP-ncq;VVrf;=>m2|FpIO2s%gd>S3GpZuTWUMxr_EZw-n1{a!;mn2l55Yt}AGj$2CrJ#iz)x7?NN_p6`ow z)yLLnurFt5?Muun`YAv#po-_=e-sCm$vvm=LBvVkuW}ta@{faiLQD{b{7+!?8a_-K|Nc)WT20 zt+hJtOPq5x9MqM6Y`r$cX9G{ub8hB%m;V<534T#W9M=gHtX{G|ziaaLRB}FUNs{_j zu{(&MGMJ6++=1XA(m5c(%G58oG9N|NV|47}xOd8MiCIcTgGUe^(cNJ2Sq`6NP$rN@ zUnjpsC}aQ087sr(_-XuhPP5fGs(bzv>CGL^gH5*Qbfez>?ikVUAD@#$+tbT2vQkp^ zrII}@cBLR=TNQ}9jL$RBH{xMYK=OSf}~|o1RX7uLDMS$HXpd` zC2jc{YiaOQU%&%vM5cf!KLuaVf&Fh(FbQ4m4J?Jlj=|Tm9gLBhtQZAx&8yY^#pM^& zRg>G`iuMbMyy?t}ab}iwZadDInrHWG14^H5ZN3qu?^PzB=5Fioox z%80okX#4DIqEwP%lDqh~lA>+qI!GvQGs>M?c`ur??p<5hO-B%qYVjK%+jd)cFAYoM zm}C}j6W^WA+1#A$MNGv>o-Th)DI()VnQ3yGM+Go@VQO?y40#gwesYu zE$N~k;nl9G3S*NZ6d~W05CJ&}ioOm0W$Z^>ry@J1(1{>OELQ-Da|G<&)32iVuTiQ7IwF~>6SU~BiLbTg`IaIJS`@H{&h`5V0cr4SYb2%bnZb*?(3o|Fix-7c&jnq+qO<$G4TYTV#}Mu&Bob(eaQzQ zmcg`QdDH+hPBlwj`73gKsb^0%9`c=1n5Ds&$ZW}7qXh>CyBF|)0;>ATS_6nY7Yshch)yh3tO(9;Nl`}XyX*_6UsKxTDa%8EY8{{Go} z^o+_a`PjNRikHuCph4X78RA&?vK|RH0Gsh4u#4$-3OdD20-#C@iUh^E770mGQtg(~%BlLaz#n0Y1r z;U<-z!8FBLnEAh(a_DF$o&ajFk+;s~>!^Wp8M%u^TcJbDs{#$7<=9Hi_c_@ljh8Gj zN%wrqojxZyJrk)<-+EduoPk7Pq>25w<>%&9wi*YMWd-|uk<%U2exa?G_kJ&os%x%! zdhZ~QBk*@KcpkBY5jk3-dlJ6krMnc9Pa;XLAE`;{A0=QY@Ke$Nxc^1Oewt-zonay& zt5mK^#KhQnF`Py5rcedr;RV+;2X;qSb66u6e?qTMTkvHdRkHMtJmc}`ZV5XAbg`A} zzbAH8w*52In4nVXe1(*tXOc+ufkZM`99u=hsp#V%T9il;8OGiS^rWISEiO4K=!u(_ zn}DwA*p;!)l6f%(7S*25Aj-Z(n8e1B?Bhv1=jjyzPFSJ0!Dr0o&U-!SFVxTL94A@X z{3+}XM%tRUCkAC%>+C5yxgD!^b=6{TIB${VHJBw@9L|r}Jle?|5T84qIsFR$=u|N4 zmKlRXuT)AMBkP?H39JyWV=8i{5Z4yJs&DCp0Lwc8CM$W){ZOXja{q#ad6qVW5>0|+ z^j_Rf-`2M&iLb^RsZQ@m;#8>mV&W^Q>I1sf3TS+U0{D;%@5gkdm(j%cc{Ofwh4xXO zOoF-^LO>wt0yK7Q#{~v7wLe4Dt2=1sS~VlgWOKGEgId1cRU|ZE0##`w_q#p*YMWDM zOr2pDpm?rkKE>9p5>@h7_ux2$I9yzS@z_Cp+_KoJQa4&X?1t+tghRBkCa%vPH)|<| zb5#0L@6V;9sq$-l!-EDFx{y!4l{i6%r#u_$rBcH*o6%3`et-FM0M9mcTu7#UD>KU> zWwS|{$(R`nIc4w&9u%pu{Mf!K-GP$ZLfTG3dO}7|n{wjwh6IGh(E10sq-Nv$V&h))uZOMA$o* zPVmx3_(wloD#F5lGsftzW*S#ZyJ=AQv_(X~wZiK54>(JBRtQGQjqU}%*S_hmJaXj3 z$%_9?P(>{(x2pkIQccO?qr|Aa5ht2|W?J%C;y|OR>zSSw6rMOT4|uDsk0?Pny#o8{ z3HBXuJcXvNEwi;!FRpSSJmv;ho@(xoH8&xv zWB0o&Jf400s+wskvYk852A-NAcIBw*0QvgsW7nED@w_LK2GjB2ZSKGS1OYJfD|W#^ zBt0#aIH^R$bWfX5FoSGsNbkXqeRwH&Cc_CVU$>eb+4R8EfXD*>xlNqhS6|!2dAB69 zpjE>-Edyc?afQd0bagg?WXpW2y+xlEdH4ma7q^2dk_vy< z-VO=d*!Wsz`%y#pJkaho6foCh=7Tb9l~ zO9Y;YXrn3jz@0H%Uk2%!+y(Jy6tb3QiGA*Slc3I-Yt9~nX->kta=5{Y* zzK+@;ga7!0gh*9nj*U#^wyHw)ceRfZVVxx6%5W#_@JT|0rwWmFm9qFNl`{E7b6k9L z$_es!>+p)7>2Qmx*{E{6!%Ukc;wvSX!dPN!&%^r4+jU<)$pQ>il|;vn=Z-|MNFTTw z-H1;M3zm1)8J6y0)I*;IyQ_`ta8)uM_n@cS#eBUwpR>I;GCrvA(GVQ4(|Cdd70^Lj ze_in3VhWNex8_(;17kG7BTlHQ?~VT^7!3N~sanMdB;Z*aPgcJw_l8v@BGkfbG$R7g zDP%$Rk=Ifs24L0;pFSuTx^j2U)wW5WjjCgRlXt!VFz}A9`ixB;e^$)kpe(DkX?-rr z-t5TRWw29Tw5HI1`J(oykx_ZZFFO0>VKp(XRm1YgzlUBSp1B9z_tj8_T{)}TmJtod z6x1_*?abOkBcU?s(@#R*$BIP!da!=D)nAZTI0S}EQHy@^h>$&lCpjZ%`b=;ni-@jy z_KHQlP8ZEWg~Fk=X*Jifj$d1{`X1nztvZR<{C$fsNZHw_Mx(xjd8626AIHo{47nfc zKacZ%C7FXy^%o_Oa|85W&WcMmbsWl7xsav3F2C~UeT=wyA@SO|t(Y#=c?U}M?-J;a4 z`kxB7e;pD2ACy-?W)#l5qjx#+J?P|Oxfl-C$pkzyl9khB2WNAnMCJ#_^d>9N4v|HR zrLL+`wy9b_R9B38gXs5bEc-5S$+PoM;o+8y=5J;G2^eiHGO?Fk$`cV>2EOiAdhQ=n z@#Zt`{I}xq`eH~hJ58V{5^_`)Bnp#I;>$VYT8qMop{BsI529D=Df|3yN3mRC3 z6saKVR@#cUouW|xs}ZA2PR@Z^ifZJD$e!0xQyCMi48e-s5kR#iUN5H#Ea|AZ>dZ3k zgbe%sfuv|}T0q}$%m}=Y;(E;E{B{qTkot945=)M%>+N|HmB2rg&wNekM7a{i2Tf zcy?>Qv`4#E!ARhLR_kw=U@6E1i)r5{d5M8N9vtMHTh=wfR*Vfm6ZlwVkMv1?wqOOk zXoHA0iTx-j?VUNAO-XA*4t>mv%saLPHYNa|fZ0fQiUYgw?AuIFo75GL{5%RS939xW z(%U^#^9sV?o}~fgimO|<7D@XJP0X`60LgpMs^vPKDjt>5V~v5L5Q(nZZOH(dOF7#5 z*r?Ck@&sA<=a`uJu8D&->~a0}b8;#5vYDelW51}XdB%M9d68(Qf8Vf2Z#tOCUP^z~ zXM#HN>aey3Gyr71gdBlXH=|>yu@;M$d`Uae=;K0_~JB z?jJBo=CQmtZH50R@hG|GU+;J!DhB$%&f>xz4Fq0qUQZL823$+#VLo~{7Q%yaFOtW( zI5X?&6VE{X9#7eEh@d8qt|%*kX!0N2{zly+#n%qW_om7RpYmZHJ|6fnC^J2gL<;Uq z$2F=FUpCs-wf~?cY;9$VFL?2}S>p&NNj&@fk4#LV+xQ-V(EYKtq7tp&w#-*5GARrh zpcA-aa1`5O4c`$){U-!2?pvzYp}{Gk%$sANAYNzoIMPN_?=GX*))c$1Zd z$$L-EcAE~S+_Jw-gCBNTkwKG8VY#QPRi%fR$X2WRo|Gv)(?(=I&-5ax0$XWJp_>8d z(`Jz-t^mhdY71!o+OcB$P~BpECHjN-Et?SFngLGNB;1Ch7#oc7F2t6+4NnGNhGK8_ zr5k(^&n$DmDu^&^;HoOT$~?Q5Rq{|%W+h?WRF9fuCgMsuiFx&Z<@gl~F0gsDGoMP< zpBcRL3YF3QnCD*7A30>*H2=d?`)}P{`w+f4hQa^){?kU#t6m3XvZ7DE?u%Ziimv7TgwoIP}`PcA}XQIp2V^_{$rrZp8-nS`KdFtbH577#JF4 zV1``i4lL~@S0D0HRZveY#mntaEmcuOQqNd$WSL1B<{Fu8vjS!mN6uo-^p@2bXM6MvAtg76_pb$Hw0b#$5WW;ef=_we0nKTj^6U zo_R0XV^sPNQ+tu^My>~}Rr^Tr;qZ%L{!-UNHKn85l7teta=1pcpJbv_A4}DhcsQRR zP350LK-CiF4nJyA(m#&_-zO808rAO)JxA7yd4Etlg4FViabjyfmIgBbU;A(gC~$B> zsFHrM9`RmgwNwhXw@`_m&VSS?Gx-s~9}0zGuYKVWsEBuC8hibGmi5Kwgl=-k@eor6 zhE~neE4j%cJsUcJY0;55ynX%njbV=LObB0a{~CNvi9x||A~4}s*S5PrW2T+h*JHi9 z8pW|ybbb0;X(tS@F2!|fM4#4C0s^lHWO<{0*9*9uFncWmjaIT5p+(l}jx~mp5|ev# z(wdNuaU=d>fd)_V{gu_XyR;^d2)a_~TQ#3OHn-($mTY~;!q2XDgvLPqwYt^FnVrBK z2J~WNwUwPF@1cy~WyEOP+OG}!12joa8y~b`JXw|N zul?>TYlt9TM%=Ci0wW!mrva#PwvoqebG6Wq-)>=LjG(`p4THQwFxmiD5uy8nPRn3^ zkILEayG{-(1n9^+k!iw>^taW}3unI?kN| z*ptztQ)=a9=@Ur=AvXImBZgQ&+P=b@wj?TP50qsR$$Jtsm|QGxqjk8yo&8x73aqu^ zNpO8JXFp~&YhyZwI`Ej-AoVg!yj%adNqElaf1 z-w8soETg3G&g2i1^JP7hmZEOMU(*hm>rmOg$I!8Tz!ESRQaU_A;nIHk@cv9KdmU9W z1^o|mpr{2q_E*qYS0j~rC7`~7Me@j!@6kv6)&$~f;`EZi9xDGh(;uTKd{8{19x}?s zeVfR5N&1!NGPzzq@8)ljJ_8!iA_2Y~Uc5G{6_hl-_CU}MWtU`#O>}aeiy@Iv!hWZqG z4b`hP=ADqA9-T*L(yA^Rr)|1pW>Kd$3}*?X(zp-)cDz<^z7qs8xKjl-SxtM}fsu6w z0MPvR2moTv$7H;;D|tr@a}*sGr@irP)UV6-dN;IRJ3(Gz`6uWp9j$r>zb!=z4M2N9 z(%g=kPzBdmBw-s~mXaM)w2MQ1A=w@xMx^GJY;>;A+<2grh&1Vk601o60_^y1HETU0 z^fo+%%hMJ#gSd{=ngO1W3;ymSGn{xvG~7}`WRocDOpLkY+c-j0ce-}s!gu% zOH@C}Njc}-N-wib)lt?4BL&st(8ENY z84b&YF}6ntiqeWToss0F1i)633uabx+Z8SBShd7!o-PAS%`EX5`%h;h;{2vGz4s}% zc0p6DBJd`Jm7sy4!uRr`TN`Mq_X_2sWZscs7*4-*UTh6e0t6U2KU$*JWnmw zerFm`|MSx813F9tz_DG+2wObLO^(XLCL~18wJloPTqQSoy?(j{N~*9mYdZ|?3w#ew zy26Et{$Te0R!XetprU#98t(}SzSuwLEzeZ0I-Yivner$4na!0Sk=5Xk*+|ZYR6~A@ ziHPc4=-c8e9l43fv!aijr(uxFl$%;cQLHZB)`TdUu&ldm#p>uTik-A=<7qk!{BC*t z7`;MM8QD%=TSxqye`vt$`w@t2rLdSK3c#b^W_52-B0K-3r{&>y(@<>P;O8cG)j!*v z>|*(to@ET}Tp#YU`pW2gXwWve&XLetwz!aqi(&(6^CHWn^DFkx)#Brg*73xV{p{OI zB6q&_YzY0kedCk(wl(VV^C3}e2b;_ot3@LZlqCA!Y+v?|x zh%U2l%FE?4TX$wyS;IZSHIkl3{k1hS0YOf!0sKpKXyM+XOzxqrU*rJWJ=F!cydu=3 zDCS3WCM1N)=UY}F01yHz`%jG)mL|9TcOJ*|_Z8u?Nm{jBpRs^r8E`!POFb0kZPz@^ zc>m$v->Fw*M$&NsWGi9-38}wSpT-9pmN?o??84MkFnw}T2bzmc}<6!g~%pZj}#We!D&CHyOzG!nO*)*4$bY$}4 zInz2c%U&{Qm{iU$k_SKqZDuNrJ5c=#KvBYAu#9?uTgpA7APe&4X>hrgFZr^r0$6!)G&V6o07&lhe-u?A` zvQf9+s&*{`w@)N0D24I!aA(Au?(g>BylT=gF`q2Its4EW=K5B9-kkY)1T?nOfqz9KBkn_kN^6dOL*8M0eM$y|w>ikn z^k-9Tzlm-&ZI5<*HNkcd&FEWRyfj^_cfSHNqFN~$)h%F)Y8zQ z=7c7nL~iC_`V{j+9HRR}=au-2s=0*BU&*7dXDLTSPI(W`OC=V3HYCg{*&oLuw(Vg! zmv0L)cB4VUAO(}EJi3pS|^He>=R-Un)T z`zU}E04pVczqrrC<8h;KWp63)VFWt70l=f<3W8=Dlya{{1i;3Mp!6b#--|)1^=Pob zSfbwO_1|0URGi{^6cyJG7Q=;}2KgKUSARhkPXoXtI4o7XoG{hg=0brwP?dRJ?V|x- z^s9Og*cz=HVgjfnq3S_5##fC=Ig-{t=nn2AtLU;(Mz!pP1ME6>dQNJ?2IeV})VI2G zHj8RXSts27ps{AT6-`1c?DmH#;Dw$i39kGBGhQ4D*q-TR7~d=6~bgIpPj`B0Ftm zB0!>T-_1)M56e&35qSTg30a{MXq^nA>MxriE_D7TnJWKa@`G-2a1vp30^C>c8iwLVq=_qQYDex%V}@g% zndi*cJ+z#5l(w1*tY}X!;dZAz#6NF;*?YhvXi2T)_*K!QL}U|6k52)mNW7q}Z?+sVh+ehbQXCPnJF;_7LjJi^!ZOrty9idcB6l z=KlZTs9O1pBoo~xq|mm6F(>Ho$N!3QMbfy=g;p?^badxOm(X8eJIj8})K#Kh zy`Poo?>d;L!8L3(lXfsw*PtD?H$0y(@-{P@S(Hs~`6Vnu=JM?F+1}mI>`R=W>A(t- ztI@;Vdwc8Msa#p><>l%y){}@Bj`wy6biFJU@Ra)cTiH4N1YXbGNP1OKo02AREnYFE zO%1aYbXhl=E9&~;B{$p7hL%^(Gb4UBao?ZQfUE{*T*zQBaGqTvqM7TAo`JADWt+MF zWH_cerSxa|OS{bUHC0XimPPlb4Wku58V&RGr?`=8dwPoCGx1C6J3S_yO|nKpiMzqd z7Hoj&nWLS_<%(Tr(G5pQ*y>0~m6#a6y8eOkgR3R#5_%I3ChdmO4{e5L6m)o@3lOnB zIwLw;)l3A6&A9jLkiPEmpMIp9W>?87g8xD{6fYl709LBJaN@mJOw@j>fQDQs*2;bk z10d((iPN$|UH;|Qcrp_zm`ORw$0nuJYPx^W({^gXTxB+)bp@wo6XKGt*Be*8X((c( zl&7~-Oh7*!UF0yP626x&mc|^#c71iuFrGI`3^0MW@#S!oH zcTEB-Ith0TKS)*dd%eE*k0ZM7tJ->~TD7QqT8Dz;BxpH8dx`m+iHHe>VEe6HjGkZs ziC=sxEY!iP`<*KveUXb2WDeUZ`5ft2zZf>R`Tr0o=qjKb!_uJw9@hW8aD@WY_B@o~ z!`dv^H%NN;6FLlRaw^@(fUnTA2^8S+P~RMU-wQRV6w@GJi(YrT=;Hujnrq}i=RL}2j3gBBp`xWO|dorcGuVjs#{ilS~0S3qa7#c84!eCkXVCljfmzJcS2N`(GWXyaCM;S3UdY?mJ^y<1ttx7$ z$Wy^)h0NeS*y9s+RMBAFl`8RrgT%onbWk1FdSN0Aj#8A5*YJ)Y00=HLU$a2v*Z4V$ zJ7N0$9MP`d}`Q>h)%T>7> z2`?}hX%F#kA@QQUs&ED_eT!KT-9DXKb%dqQ?%}I+7ee=Xe=!fE@ba_9>(|VyZ~&;A z_vr}hKes~puZ#|s4jgWL>-+95A?4YOQg3Qdjz_W?)qkZ=ji%4R_T0=XT-F%xVwjxj zE9dqi{JLexv0;nR$xrpEAfY^1dAqF~(lnh#`);%N@>I&&}S*sce1rOq^PG;)E_Ec0FUx6bS2j4U>xvu<_t&pv!= z>*)sXf%a>!J=r#ix?{ zX_aF_s8=>Vsk|vCavXb;#`Ngy{QHd=;<@cbYLN1_n6XiX++tdwu;sz3XPuA9M4GKZ zs~%eZuMb3C$P%mHgUFu^(){AMIQQ=MB-$PJnRVJV7`o#tJ}jo_u9tE99HbPNM13IB zRH3xASxQ-H@RWEe9osK69$UZ%x>>7oCaa!*yrh0q^yg!%8W0j>Gs`YU!W>Zta+^@A z7zMVt;Y9LHV^)v&Gza)nG<74?7!A+oz#mdFx}ogf|B4GNAwqy@E5B512NNQEMO}^I z8nk+qT z;QOcU(%#k-Z01-HJ%#%R*>dQ9O3+YBkv=BT8*$xrXLnr->vIw`-9zKu6 zjvGxMJ*CP8s1Ia;@8nrKSOVzbbo=8x1&r5#-!L ziYvb)&dvFubNVHrm5&?!#@Au~sTPX7^|NipHmx=q=Bm#^{68Se| zVo!p+XQ}B}e?t+NqQa=%Pa{8Sou>a0qLrvwc!M$QrCmdHLih98jB!GX0hb-K6}0ZC zIov^ljn%B4-Ql^LFIx}OfJiRR&t<&pzsgwa?>QPgOW?S?Ek<7H}Wr60~*ul;^|Fm;w><#73; zkI&{IG4&A3n6>I{l7jYu`E8gC00WyPbg)Qa9%K4hF_;i$h)R*RW^;EBvtR`8%#sJ# zNPw5JRK>Co?K87q2$%6K^{kO5R^C=c!9gRtF|1{A8bI6hVWgr?ndG+&mPe;W2>>e> zZISUa*tHJU^>Zx6JXpwfAr8kfd1^KDhv5!o8>XD%@mi3GjEmX@ob%XVi=PpgeZkej#=L)i@EA~km;+p1zTmp>8 zd?9a3jxVEFtI}_Zo@KM8Y0K{XgVRFWzkCT?SseDnLo7=&>0~Fada6e5Dpc(^cDdPg zHv1WFw%IAvTTfP=+6Yr%&u#WEP(sZzpYXmvaM!tyy|T9eWXc9Tq+|x$?^fu+Hs88P zCo~ewtP5ah$ib-OLLS`Q+J1%{en2rT z@nnC9J5$g9mx>;%y1>mqMSZO`_x54G;EgbF`<>MIAJ<6Qt3lM}xKSssCwGPTAU zgLrU+C{-wbs+Y3X$Va@dn_SpDI`&li>SQhk-Ys2at2AYe{gPLHeL*m`4x?J8{&A*J zSGLKDRK?0R1o^Tn_xBD_Nd5QWJtukH|_Qb2$F~= zUSrPeNaSTh`@?V}^+ju~V+_ELVk3%)#Snp-l@_Y?iYvBwOogSC#=%L7jyvW98Fy4* z3`IyP-E*e_bTqwNq*|%Bw=PN1g<8nwBI}`X>JKq(s^!d$*@72v4irFrJsIUK{hP7B z;sOGRnXTI)`qLLO%nP!s=zP@s`{L7{4lN$ASt#i8Ahk~SrMt*Y5#p=#%5TQ!788K+ zredQ{Z^+`j%N|N}w(FJ1TgCg=(tX(Z*vWaZU-Gd89<-J_uZ8}mF6W0Wogm?{h7|23 zW1fgHywON!UjmMv3627=7%*&sEcrG@QI?~KKas6{wed;MZY*Xq11Ou5p>D4ajh*9u z8DcPbA1PX$;mK(nAROE|GYR4}N6HU+8|qv8=;6OX%maX2tG}QrQB9spf3_&CLD(Np z#n-t>k@N2H(#rL*jgDR;IRFqav)k{Ko_yY~dSmt%h-rHPrHA1+QUX?!q%AcMG20zi z63ZU=g*(gR(O;9d=GR{PM~nCMO+DJimZ{WTD|oYQHM3{dj&`wn82pUQketygKUjo2 zMAZ?Q=J+u$eU>_1V%?YwE4cJEXg?F@UPi$+x_6_9q@$)dhkp>vYeDlE5n_)OaPMX4 zqf~X}z0&}yd+iEP^XTS2XYB2S?i8>}3*KcL)k68GV15#{CMQ!Ce?bf9^#1VMyjm`K zGxeZXMQvKr9{+qF8Jh0mtlZ$Db2iRTlY3*bS$ieyTf5_rmn0K5LH*WHta2X5>DQ|Sp6gmu%kNlCt(c%4!-*gzbP zdIrK5^?wj%28t@L9L~D#yha=fRu}C4c~jz5;8;D()m_bbxXPf7H@nxYzW*PV{Y?^5dc1Pcr!y-Q6$4F=P%K;@!CkvdL#pW&RkB} z1PdJ-Vw`Z?xfZo;N%+L#$H5Uln~$DQAb!855VB{mDN&Z#d;fWzuj2SR2KoLf7FK77 z1@H`ZnfJvkK1>-6qy-GVpj7AkCuiT7_zDHkZK(OC=S>4gcfmQ2O+t#bKa-GA?jT2t zJgQM`Bv8uSHoRk9on(#GqgO?C$PfAme7TkhXP+VYW|ElujA#yp1TR4e|H!vk?q5!L zYn+>-TvP1r_9WMCng&DO})K4e5*dTumF0_@zwI=HcYb-SK}sy=7F? z{r3g>onhz(k(QB`kdhV{1PK8VkZu?lVL<6_lu#P!loIKXZfT^Yy9A_$ZkRjI@BZ(7 zG4EJw7PHRh#NPYtvo-8+P+ox&FSO;8omFv=Mx1mlBS++Kh^j6|a5N94aa4Sy@6lyj z*ofyR_}lx#_M8~=0)A+CXOfZ=#n35>@Rbbst%0k0OtR2eK%A^7_h+%(xJ;qYb4t0J z@b*u&@g0Jh;bxpL<@C#g28UlB1p`x^LJkLPoq^dhLG~=8OxqqXG9Qw8o@G9Jh2hDA z#T{?jz6UGq>5le+51;!ZdN;kb^Q3Eg$2!Vsft8XjqpgLgAcLCkmgj$)C`&Pv%m2oX z6?YI&`(`)!Ph)J$e3C&P>wfvkliih6?|t#nUfmD<|2-bq+*e#~$bO(5eL&uy#23pO z&!%5W$56tsX5x=$S2lYi3O^w? zOWe0=m3q`1x|sA*0E2Mz^mk$QXw8Wy!WYM*`XDn*GM=J>vXR9tk%R8b3!XOWYWVumx) z>wo3%=PybITr2tV#Jt`)Z=%(82Lly+wy<#%C)RZIi43}0?Vd1%g+?`iqUlq0jPnCS z#W}T80yiRWxkFXrd!)?YCHMy$Npn$r$jTsFa_k^P_@Cjd7i34^yAdBP3FcG|>9?!$ zY5vuxXMVmiE&!_c*0F33{A8JTnfr%oHC>+$A;h}@p%eI5$hGAf5OPpEaAeh*&;i=X z&$+#*e19*Leyb}UvHG?4dh*5phuSS;lu9*sl``V)R!V~(KioYIxBZ(Ht`gv`zS`jq z0D3asvfkF#K3I}ZpPUMv=iMf!vxkSN$y4+bDATf3?5mO;-~%)Q_i*g3LS~QX4ejZW z_9?|HY2hd0RYEg=T&3VvX2j!(-3KD#@%C92qMiji=tx69~u&iP~e$p=%&-MN=jP>tc zGJb)oq-qPn6!PWptEf4@jjIGi!HoqUJ#?Ou?xK}NlHE*#RzVeya1DH_>tEI*?IR4d zr{FK>!XF+Q4_V^z8wkY>g}Ne!4>Ct@A*J`Ke!~NwTt^ypNDc(U?#!{=8cQDT#T8eV zDsP##7$~K&7)C7V_Xs6bxE{7-ob|ME)Wngf?)a1Sk7HqKBD-BFo@H?Mo}LE-P0%P! zP4<@9Z5_ikYdkq8iAu4G_(#Vcae0kl$WzC`6rNd-MBe+H3jCc+*ll!X@a}qb}mF^}22bmEII^&D~G}0lI}MdRXsjvx4uui_vwpA$_pn zx@_rKM;8IFWGNDlL3rC_{W-7w4>c@n}eGQ*p|J6TtVimfy zq^Hb`MjxZyu|V)+Q-y~==23XydO{$eI-tc|+Xsi$s^iDxcDi~0N6VPe?9>OpSi379 z&-l+!Z6|2NDBV1n6`v8^V*)qeh|PXa;b#g2p55cWH@`UY{HKL2zH&O?@MgbvfEjA` zpn+A)WLbT_pcYou5{Bx|>>3{Q%hnO=cYyJid9z~;LDq<;lsu6&JsKs_DN2~lJH(jN zQ*7K_bzMe}$;>E>0BX2w$Cz#}6n)lqKaEM#Js5WU-vSDzDU|p%b>fUR3g@d75U>sc z7(cc6*5pstjyG4S6U3?F;B${lR)n<i_mD3wkklvw6B3E2eT$~L;uPg6E zW(3iUE4x3won@U_mbDX(+Otqtz~FiYerx$iqc%yIv{;hTwk1Y%9^q$~_0?{1rnpvE zfb`0i+g`Dj=B4_AKwZc^G|`5^W0!&7jxpq5h7ox4#GXIhQBOb*CNw3-i#1tekS0gI zPSj0OL;j@};Z(qm5b)p^Nv2^>xrkweb^x{bebRP{%%OTNo7&l6T(+p3Qeux*OnftT z`)J--f8qi8iexDVs)NCJ&uvQ#sltrd)>_zQ_!IdPDc@*nR3Wf{%%cdlTz4BEtx-DP zaA`&NoaNbNF*!(koQ}_g3K>>z8jGLIj!?4i@Yvt;*YeakSYtne21m(J0S=8DZTRx{ z-tqzL8QTor*#5~$s`rN7PP%06i~z7lNzv11UHVxRsvoR1nnBLboNaw914eI4-%%2y_TOWaM<>>tVk!aD#pne&FDJf!j(6 ztWf&4B;hpT(%-&<{s$}pzyT4kT~vUW^iSsy_Ouz_`uD@fAAp@t4zr4gWIm|X2idMj zcrV7u?bmq?2VwrA9>%%1k1Q~Y2_F*LeHCVY!GNF{`+S{cEE$N@Z)r2XrsSFtLs?qG5Gd)? zqF=wVmXx7$t9lNN|Br_`|8!lb;`<{C?UPEK!@1qDE&3A^_Zol5aIx(2^lx5-^Xa;Y zD+uQ2)cLfQS*1Cmnkp^XwW_55mJpM*t$*l$cFfC5<%by$pqt9ge}o=?NtlYLPKLD6 z0{mc(jFdmh?TCl9zMcGv)3X6JO4Mn>^BnpcVeeJFYrsJLeQJ7mZmo4A3n46%cCNv2 z6Y4w_3IGaUMQp^}2Nqzi`_u1N@UhUUT?%TAggAYWgo(Ge`(Yn(2N-juP|<5!F&nhW zmL$@fwrbjYg|8CTG`QijtmB&okGW5NX+R@1Vxr|3#g!}>IdHK+axLr+s*cAp#RuNL zzi$EqkRq4;r-?YB!=g10_``1pjQB4PzR#IPY@UDwlQK7 zq%1Hoq%|!xCLNs4H;a4&3s40CAMJ#K*Ta|O&<+pn#yl}x*R#JU$UCK~Z@4of;SuT_ z5}_nMRn26F2HtH%_ezqsR9@_+C>(G-764^b;Kr`vYOOAIaHwdQA!W}Z&R*rLmOc%f zyGg-5ozbcukm?Wy%L|(i(+)RVaNA<(#F%FG`>>-)A^9iQ~0y>#Kl~O<=eoA&lYW z&%{&sIi3F;;KKyK4E9TkhPtHjd!II#27)69D7KTC&mn-zpq%C*B2WR>VwNR*vYQb} z2==S^Bj`9#J@rb+CRM0!pfwNngxlGZ+TM=asnp1_ta{72CU~~9Bv*M;^)~J~8~^KH zBdm@j(pfN2D^iojkg_fNL}2-wuP}*j{ARN#V7w98uz9sp&6e-W*ldYSP@#KCKfT^) zBm)`@0`#g&YrmP<%I?j+;$6n_&kLf~bI;U{0cRE4)7BErKc7L<-$D7?g%1GGb1Z1( zX+l-in1_z8fWBob?S&QeBo{0Y+V8Q+PphK2b+kuSl6L@2+!yw+F8|$n`^T_)J~661qGT`eM#EZF8YGxrzd8gG6|1h*#DGC)b!R%y@r}uuR}(Q+t6JTR->H*c^<^m*I~%J& zqF1F;s8h&kEB&m#d@*9YK%$1Wa1(PcUN28wH-nW1`lN{VHVv^*EwN-PabU$~K#M1~ z?eI!9uC(-XY48w#af~p{4-HNzlp8MIHN2fM#9-)HWC7v{s+LQzq}w@%lqE!iU&TC$ zGqbyu1Oqca`(EZ))DaQ6O&!p0uP2bkS(XoC2cw)K)dK+QW8L05NNEbf1^-5= z@dL@0T_xCSl-2*7V#0jNrYv_iCD|9pF9E3Gtd1?X3;W5#rduG~wf0e4yPNM3J6Z*AAr{Y(%l%<{u9z5Nl%wHc14mTlD{7+V$9 zo^|!qypcqbc-GjK^@!EN&FgapC%s~Qt5+e~aGYycxiz!O z;u)sQfdKx@c3J_hnH1t#Wv2G6#;M@FCoY>9ORpXs&&^Unhwj)~5(Jl9@xHR8skQuF z)_#2BE%lbs+~bG%t_^@9`1`>CIGb1}@JHXpc1y}K$!&#@$}r8Cbnego9kjvYzj7v} z*P#LSI!4E+mIomT*6jDQc!Qsm{Ue!dnE1nlSyS5dwe3Q~j!tS`=s2`%e&QK|AcYVV(=&>_~i~$G;vmEJ#rM zgrfX*pn8i9ykFsds8IA;D^jV&3?#QaJ2QhN;lYXQ~@b+k|DG zSh`c>4?}LU2_0KUsjILGFZdtEB5{jv+^oKG z7CtO6PA96MG`7^G@pD!S2)G;_mc=zW{EoCdd=0D3eYt-yU&+CG1Nf1iXOz~e+7cKH zbs6>QplWUT41P%HK5UBX`IxIeH1%N~`~?PGbAonqCyT_@uM1#=F_4vPDr z?68Q=lxK=2wriQovx)8U;v*)@q3&>!Sjmltw8J~zW!!1sp#eWH>MH0W+9$3njk$$0 zgSX7>V?db+zDboqWns29aEwSu#Hq`*`Vd4$=rnkTjpf3_KQ}=8XV*nD@`~=lioeb{ zFu9vt#VpX)Ut6a0SB=IC8Nykv8pj}x9TT&Kq0H)h)1Z87*fIHbu+MK%0PRN%R7_Mk ziyW+xnqmW^8l$E^3Mt=g>d@AYk4`<7POl|tWa77^&Na%pXX-wTx$_D^eleSO)ImrG zbf6T%9m-sBp=w~Q>T6RoE}$|Mx4*Us7mLYc*G3T_0?}$K`pP97Ka|hDOHjL22`x4M z7}#fF0dWvpgM`s^E2V39Psx%6j#LTQe&V73MK7o zpRuNh+*;F@?9N%&@&+URT2?795CbtI+)((gSQvPNrDHiSxKjN#vBRCe{i4KgVSf8A zG26A2U=t8Y8EjRU&UPP_-4-O_1Ji4+mhQ8O-GDyz`8YjXRiX@Aliq&u$8n(;=MLIQ(=g`rpk*VQ6uMh@*lF|v~h~zy{dj4cyXHu$=IMp%X zKw^BgU@^)*Eba%IT(0;;(4cf}+Vy4tquHzx0oQoKnQ#(O#0&ro3DSo{krTgI7e)~ktv1tYINGHMr zu_(gQ_d@WbaL+ZT0wVu#)A*TWCsvfm>VDZ+NxUQ#YbFOFC+!}k5h%QIj~S3 zQSoGdc=h~K!=M|z@Q;xuPsaO$vlL6zm)pzdd&F6k;z}YBO|#A!nR|2jE}n60qZsMM zrT?RwEf8Rb11$XT?W6j_uQ-98@j_{$KS=c=O)5@OR70{DDHpj(N{B@)B7VG7#I2LQ zWWm;xc)(!5V500QoYVfiQD!IMhnZ3w+~0~4&GV=yP}!e|i^wmX{^Me%282*SyK>R& z;EIR-r%qoL`$Dj{{$oRp^5=%^v3b+WmW!&-zxBnkVX?4}#eFUdAP`88T4AzIl1rBB}WoB z^J3Mnl~S-fg(dro87WFY<=*g0_RS!y9}nX2_fZW7`HTQ#&%G7)T+$}M3+OD6kPvVE z%Fp!Jt?8rqiE4I}&IIDjKNDeuPRPtImyp`~9o1T?Idl~qyQCrNoQX>L`2*VVIy0Ii zbHce42M5zoV{DiUmr)#;U;jVkLk#1?hT)53wh1U?6NJZv?qB=z{`W`A<>PWHXW>Kk z&?c-`ZhK?_pA?UNVzMxc!WRR`Gs3?2|LjXlZT^c`YcrQ0XYGYmgF1#Ou$abLFwLnm zkLq?=ux~Oie#v`8)M47p2(6(U$J@^?@!kJq;`^{8N`1Sf>^}W-Zl$>S7qov36(&iN z<_*6-O+)6ScQid-p1mgZG>cwQVaHfd#$OcM57qM9XqyeiF~!;CJf|K(2n5&_3jT|Y zuo{|0s@QW}MettPjV1A#a6_SA+TelSSY>U~(Kq)JV%(HSc z&^2K7U`wQn=BxL8qsq;4Ha3AaoXc^O_-P5^fA8TcvNxAlG-p;*?>FIg3aj1q zI|;W{3AOlv+9H=7E7r1DKQ)6HP$5l4H7a@Xl>Im3JT1qXAnlK+s%Xv@u)dEI=--&I zVJEg!=WkHh+z(s-dSWhTy%z4h!eFkXbN$nWgkSpQ16Qd)Q(=iROi9H^wsbAML`+=M z%RXnTO|%7ueY!1-L}AHIaZdd z41|8PSS(pAorXy4yYQ^iXc%mF+$$;$%~I4u05;1?ySy+mLsxBKe=;BKgRlrnXKcHy z#Q%Ls2Rna(lYz&%VpPSK7>C9mrwIhgmN4Gl80AS|P?=0g0ywLLByXTnZf`h_Avdc8 z1N4^55%;ZK91eFO zJ!Ama@ZF3MY4ns!(C1elcVM-ZN6@M+cp z>#CWL0wa;?Vd`eP&u#X?gZtkG49;3?C(Mj+E_J19GeJxZ2lRbs;lsZZok^A=J(*+Q zbHgls@xlDzFy^;9!SL}H)`~ z6#4S-L)%tOn2mg_AZaCjMX^Rx5$mur8B>d%&UR>;&!z(>%nUiS>mLsDWq3+ignsi7pn?7WI( z$taV%Ot)rvpY?TLjb+!h3I4J(<=G?)E|i;KFn;{IV2EILPisnJsD%LNd(XafL~jNnmP~2;2lPnF)T<(W zBr)tg?sLlZz2a9V#{LeU^Y*5X1|b*kR$|z_7#J1E0(W`;X`E()YQCmu$PASd0Z15t zkJo9|@o!70_7#luB+V+q*v@7k0!-x_l{yee_$3b z92lf;wep=C+ZO7m7H9eP$)dKSFSz@+k9AXVi4$rLaCsJ`A6T2D(YmSHfVos~PM9H3 zijiCW4>!Y9@;Z#|DZ85DgJ@%_W2%lsQo!u)4*ut-;%S`k(W|s?EC7ISvdwv=Mluyp za!)n#%YBvLSj&;x2`y=o6kZ27mFT^FMH=}6xR*dTj{1kJOYaY-$K3wvrYut;Lhk!4^8@34$xG=4rV0qk)(}RC9|XiH&}6lo^iKnHG2aFvgpoCI7yYiA z;tkY-!;k(p4z6+^*~W+WF*yyL8Ch-;iZd6angzi9`ydLq*AnB8tcJMt0?=+N2p{cr z^4ob-3;plb+{oA?Lp!{9>n@A1kqCq9qo^9ZzW~=3=0&!v^1R!c$MM=aXdfG9V)%F2 zOWn+L<}W3Q2tP{YPzB6;A9-m%4A4LG$93s@Si$kI@!k|I7{F!gi4VL9_9Fn}+>3sA z^s$Z|Es(6CN7H()D{>^G5@qgCxlNNoYq+dAl-ioZQKT$11??c~CgxW zQIR8^D2kW7^nopn4V#=tE*PU+oUFd$n$IZ4-hV|ug;YQxQ6gR&5a%HbvLOGJ9@;_P zGXA(AHb!E2NjQZJbxKObTSEVueD^bXXguqgk>)})&h?XfoB>ZzmU-6opC0 zYXT|T6|~FS2*5FPV_h;KgwD|M%q=GIMWIA$msaQBw82YCFkPJw&1ttBZ(hC}(a!i% zi~;K69qfZYRKo%j!1#sDnjcm);#h3#ll<~t4BjW{ce~b_XZjx4WARtJD@whK&1ivn zpuoU6z>HZMg1N_}*T>r*|6irSu*cD*#L4 zOs%1<%4wZb5JKPM0qt}Ft@jVl8T?44b*_mo)!3svr8oP`Egm!_UgC<~`>VvA#a(syrmDa6OXs zyobQ_IOENNHTDoV@N(!^0;ke!JS)B@zWM3ctSSx}H)iYBJW=kJ!+Kb7MpFSMFXm~gc|1-#0y(pjZPKn^BE@{mB*DgCQ3_@fU(LxmJVp8Eb zJxV=C?KH{Qhyy&pr_-kDyGW&eFSU2k*1_fHzSrC~O2>-O$k(ft0M%VN`y@Pxs zHkdXbtQ{blR)>HkSsqia_E}83k0d_YW`WU{&QjvEcc=58G9H_l~*n+C}hbR0pw60(sUsr}AV-o>ei^l#u*8Eiw;z{TGA& z5kg>FIV>J1sW`N*+uL#ifZsfGpS7N&RyMSZsv<4{tP6hobZo8+ABd#b)^YUxJm6^m zyBNX`=SQf2KcNC(VDj^rRm`jAUHSaF``iR;N$Nl8DFXAs&|tzgjqIcG+Fkl%%qNVk zr=o0lnCUd;yjwHv8_8`$kB9{3`;cS0+^fpN-_h+$6h4FQ`_-8&#or_eZh64%x)ktH zZS?w==u9eq?sz9-Z4Yb;R_?cRd&a+>6TUl5@<IfS2oD z6`O_Ro&KFoPRfUiAK|-DwOXigxda63Yq~shXVFLbkWLvHn|3?{Zbu@%^Sk$Zfo5h! zKLStp)^_=tXM(V5WZH>9heqZRxSdQE_A#vL!JeX35k^n6C zY5{9|g+`GFN%=2l2#(W0 zM>Q1?poz%h7lZ|=zMu`YCy4&eSwFi|Hk>yuTV{Vs{TeN|@5| znh>@JDnf^|NBt3zRkqIZpKPEy#_zv`{I@*U^qg|KAL`;>@K%4;Fa$G&TwnB*y)3J2 znGSFt`7iDLC-CwBV71XZK~ly7fQVlHL`lEA&n_kf*2m?$ zqc-|L$v>t#ud4a>73jF>ld|xW7Wq3qtvdV0KkBou?qmk=BWb>&2EcFT3sPlh0)Op# zXZV;zh%Oge>{DZAx6_EIF;0OVP+0t1Zi}l&zlP0I27GKT^&tZU`WUSpe&6~}2d1gl zma$vE3WdT;9GfRXZ6nQ3G5ZZyra|uon}(GWn0~C6B$nP4G%TgI_hw)esh`Vpf2ltV zAg8-0nM5a$yqrsks^YmJ$}HV;^c%Ah`*+@G+2Y`!G1$i)lGMM$_8<|i~*vQ(g z$P}XiW66J}+-RSzD_@I^DpB}VC^O1)g=(!OScH+GwwfyMTj$?7e30%$WFUeFjCuhI z?-X$aA5`&q)`J^kzB67}N)0sN)eSrF;(L<+NfYE_WwbcyCA|clZ$!k&E&IXl%7w(7 z*y~vZDCIyE9}U?`+bvp@9gWG6dPPrB+ckQm8!Jj)S5jo9!i8eHo%$nB;pF1dT%Px@ zwP8D`C>&XW+(%1r#A>V(21=$YpGEebB5P9tYJ#^2>4 z4snK5Juu4m;x%*|G7x!-xk0#E{$Mn(Tx$1;3FVo}xwB%5X-A`wSqHaW_N5e(b6jzM z*c$K!0kfHI6R#~0`4=de4P*)nw0Kq*fPlZCX%e_lH+&w#lADf%m)Ea=0;KY=Q1!fcoE41>SZFq7aYPI>&lAk>zL0^5sbUuJFKRCSdRb(QGG!FPRNfy0!SI(gW@Mc4W1w);72|q3% zFBtpuE%GE_#r3Ov`8Wb9WJ5FSOc#uJ#xaOHoU3d+Ca>fA;h(540^>|!;;nsD`Y+V| zZ%gM~6c@a)XeP=QMbt=5iRLt!!exul2olP`_rG8q29S!(HS9K zE-{kJ;)@{+MQPzj{bYLXKV4-)J25+dWTG|c{t`1z8{oL0Xkuorm$rSu^K%@?_NJ`= z)c*fv(-~*~#F)MI9O3pJBLsId1hmjlx0{L7r+poT12j9!6bBr7me3_w6`It|JwSr} zkz3o7@+vxC^z)#`GWRNKb1chy!r&Bq+k=pZgh_YuF1zEb9m@-dH^H@i0C{s0kE=>R zTGY!599>1;rkSLM58Qez`!l-d?}9!RN5G9fLe>p%c<-f(lGO)p%l2n9k%7E{DB2is zdj<4PI6Db5)s_hNlg2f;OJ^w_*2owfLdxkcivD1<$qDv%gn0t zF3v;l>+#0FpPVA6du+n7R|xjFjL*;!yzGR^jW4Dtm$sr6Ea~(OG5)kDxhkAhzM|S3 zge@Oe{2~v}m*`+!Zi^Hl5y~%-x!vq$#0tS}hHPJQHuU#?gp9uZEIc+v<0eM+T!nea zG(UNif30bf|CdcC1}hv9m+j!(7PDn{b?otA`if5M`m>1Hjy3US>2&(Y!?rEIcJMhc zr-*F&wD0SEdG6di7l&a?tMJjty_vmj747WHDNhjwBls5>Q~2byv@)fg8SB_cg#7#6 z8SNM{r{C3wswJW)4cPoAuM$3-voW0E#+eAjqX(&+`m1y53V&Le;Lp9OY+qv27`WfX z>3ECDuVX@eq&Epz8tgc-m_Mo7a659Pzb8T%z7X8)(-o4wXDjUzQA;EbXKNP#ie@T~aArm;wBM`dQzIY|Xe4 zE^KUoLZ%RZ1OhTkI`Y#kG{eRV8|gSP%VO$yK3)1ZY;}VZXn%B3%p(^R+Vrh0XunQD z_d`mq?y!|bkZ_NsgU*~E?eNb#ykQGTE5qvd6&%Xo?=t=FNAZW>596C!+M5seQr>0O zEa+K~Pqdo+sZ!zh$DL}w_pX6{i;s=+rHZjHokHpT8ZR5NC`;wfs$uk=#8rjK+2;qK zi|=>aafL)md3k~R-M0H(QYf*@U5W88SfyF&0U{Ay|v8}>?t{x>hJw^BX?G?9(w@7i<3 zIj%DlT8a(8ZT#wLMFU;+K7Vhyh>Zhx6?2Aqgq=Nn{*$9716mfHXPIfERJxIQ7 zbxNwLyC(Pvo*q1rDaV|Iu-2M+)qMc~_k}9fz>+%gurgP;%`Ap^{vTL*1frbCz{e_< zslaZ^%6ncOuD%qyyUK#qej^VC(I?;7dRr0-P0xsTF`w7c?;1Q6NZ%s{W_JCI7v71~ zUUD05Z6`SEC-#;wtV>)=UsCLG!;`Y@u3K;ErN$#wQ;!uv-abMDAnqbt7vmg_pG$f8Wb_FqkKrP@Nvmyx9E(%Wb=lIQh6yS zWtag$3kh-Wihse^<9P)8G;TVzJNq*K3~J+uK5)srqUyY*2Y^{*IajO3xUfE;l!r># zzJ)Lj6kfMkii@q?IXu&-5@Yng+cibAMfbU|-^c30(}ZQ2DDpZxHfU$Jnz3--PK$b- zTLyeYJr=(30ZbW=`lH;H4-;oL5y=-sKbop-gFZS zlS#3H6O5>7*`T;H*;qO7<0@o}Q{4ty8*^PLwQzSmQzn7r52En}G!M%;a)wx;f0gK5 zyS9*a<|txywkh#!=iOCD;z`D#k{!)KKskiMDs3SH1p$PzN9{h1K#+CT@svrG7 zh_jU(SaHUCsMda>$sItB2N;s4(KV4aft!G)q>s0;ja2k$^$=92xbiQ~**aC2xy*sF zD+`;XbFy;In1`t6vsbnIjxs4M$`*aPCxwvI=xPWJD~_-MjT^7Q&aBV&VOv$e}G&7d`tF2)jMtlVu*)7 z^o*wuPxT^cg5C0lQUoG|13*h(+D?YO(~bK^6L*x?MtZ)E?rt*-i@PA%(UFxdTOLWz zss+ys{}=N#ffLfgjWn2bz{}$wPn^_oz+ibz2 zWJNSLOxQA)WA=$9n>e8nlk8*HNsS*Lnxu^xrCu^4$OzIUi2~iZEfj|(qJW@IDLq^W zer{wW@>2;_SNR(XM^XlXF?nU@uqrb-9Xi{O3z>hokc^TUi@KX@m>6g27S*CdKIqlf z1M)KURYHG7yCfTih>%2Dp@!}fO3oK1uXuFM`@pAyrB)Q2OF=yKe zcppjQ1NFZpG3F5e3)}O>;u?ayT5teu1!1ErG{9EW0%d-IS%;9G*-!tCnwj&_HH_bl zeHLd$NUy!}9@+kzeJG0k67Qpr|3JSM5i z-{TAEXZg~{dQU3CSY-@4-X^`ZqD3)WR^JXu_xSX7$xN@M&c1jWk2db;D*RO>$QGps znV$Q}a{gT^pb?o|6V{PBlUZYI9M2lv+xz>+?us=HTP)&#v%m$o<7Q8!pA9lJtYgHacz5iB?T@s zei`y;2YvSgUA$N^Rr}jW!^-gLYwP0mEj8ChhSoH@(=-1@>DS@RErgTfbc%%EILzY% z*K9|xSzIZ?zkNGhLP1 zYNs;Cp;B&jQC4i!BO$R>`EkWL6U2Stnu=|ar|sL;!&XSn?V-X{xiV9=3%jR2spi)W=(7ca|<_v}|C)j`9UDeh5 zH_}qU$NOkS+%DWL&*-p3Sos;9H*0hweBR`OWi_g;K~(E^qPSNQevgP}SLA7mTFmP5 z#6!<*l5<%IfTPu@|M_q*jsOVr|kWy;6x+ zF6Ve77iHy5iTX%2?owgZGQ~Q^;>wRcIEGvphmg~+GVwa3ti($sGddgMYCQocISFlX# zh&@}mNrrhWLUU=l>e$=rDw;9iC@TBjzb>eC^NVypxFVrr}dA}n?_Bf|1U8mV%i_%huC#4%%LR$ zC@2@;ul$RE0(Qc;HhZE=pfsH2u5;58+;i+&@Ohej9^Rvrjl$Brs{9B3TeHc0@k~Zo zrjfe>p*q)y8`!OCd^2LNql9&`0EHt!B6HE7DD=E{o3>i>!glbrA-7$)HdLsOP%tcg zMUId}_H_&?26DKhuZmDTVr<2@sv({1;arw)Z>gE6nz}a53)Ss<$hp3l<(Fq@Kia%6 zacgvJk)j}BBd>f$a_^-AdS<^}gGxkaItlR*LIgk;+2p;Yp-DkYbgy z|E}n%c#7y@QnQ8<8WFbXLht!NpsATlttml<1( z%No&-`TA(Kgj)(yzC69k!e+`+O2ur}>|cj%HSIvH#VnWDZGK;!?_-1mynUEgDD?x4 z*t0=?Rw!==V<>}YyFck$1aI=|9r$|Ih5&#l`>BW2D{%hf6lJAi0+|fFIpiUU*buuQ zBCtq5t4y6kPswLT{$#lZk5Z%=Un`Oud%;ZMCFSFlH;l}Z;bE-G`7v#TWLNA!0De!= zr2|+N-!dO=EDN(I z@At=d}m+<$Tml>Tw7mbcZ!(@_YLJo8=B{H6htz=4h@zry-l ztraXkO6e8tY{uutaGG>%SEGMkTnyyoOS#oS`|#=oU+K7oM`}#=XDXM{zVsvJi3&2) zEoIQRb%g4LK?x*2oi(gMucD1yaBeW7Wbc8)Ij9r1_lw>lCWqdQz5iP>HN*7an)8Wc z^_Ua$W;!^lkfCxAh{m+Tual!=sssis#n-I*s=6d^v?p#cVh~Yw8P$m7+rE$fI@dx; z>CxNn-1Qer!82Wg)YjTf?bu(a8)ZxSM`kZlt7F7^X;r^21iJzM3qW1b)+1v}%S*Qs zauh6G778R(50Ey}3%|OZ8=17!7J}FA214oS!(aU}VDu54%d7cSDk#vwn(=K|cOc|J2!U9ZnmlWN zk-OKKFAI>(C7n^HDj0=nz>vxYDD`SU?@mxNEWry0+f%_+uqt8@W93BkXOK-fYWQ#f z!PW;G&vfm0tT;6mxc%xMaZ<=vb)V=9_SvY{k5_tY9M3@yCX1{X1xN_f*II}|b}FJ> z(q$8)UO4b61Qkc_B_$PhLAIdO8K6(;<`H`!FFI_dSthCpivY;?N5R3+sW7X~nkE+H zNaE*HOk|mO+aNWODpZ5oA%XQZQK|SAr*slgH4QtMxVmSmtYS#{2xlTpP3u3V6|4?X zI9D|r`oFkZsRjbLYwc+xxd9+9L}-2!({Yv2aopLQ%1D?(0aV9qyK7aPL_EX7my424 z);7?Vs$cTqRwHDwg8T@poa#K$5NMZn{WVlO?S_8^53&wRWTFQb$$0^%hgKk7|->(kE9ZHxJo-D+@q6w*?Z)S` z*PF5L4@HM1W#2aoA$So_y{#UQmH_cQmzUT(=q3PoF2ngtUm{CJ0K(q4WRu?9^SF^< zAKu@x){0qZuMiE}|8|ci`l75$^!sGwv$-MG%g%%?MH@B&e z+@_Y#H_(3fiR(4{tMHc_`TRSm&jYhg^}bkCwxvYpD}zJtT%Ty)hev$P3~9#!MLlpR zBwLK}&OS!${`wZXP7-L~t;aSXPGuVEWR1!H&-DT6Sov2)(l@5V4=i?K2;ei-G7Y}s z4eZlp{r)%JuA#Zwz;nryp=!Y`R&$~r^9m?xoSqq(+p&DbfEu{Cl-{+gh(zW(i*weS zj-wIF$1Q~xQ*X{)d#Fh$1?xmVtt5n;M&M-Gnlf+o?pzNCFpGI?n5bHRA&NOhT`m2+>x5veEE&b3<7CI9lp46WAijQZU3DTv zv05QwipF<3BibEOh3Pc@*X;jSQ{T@E(%azrIF(yazQGd!gUITCMVnYKs6c^M*=weB z%9*^BjpQpPKLvE>2JZAPzrT$?FMgrOz7p|AAr>r2*G5uKIYeFlwh|9wfxg;S6_(L_smJz8&cYu-u_LxpBLqaTL`U(uKAOBB>o7j_J3xl z)sYIt@^Sy_z}Wqg=pE&U)NXQ!S@arSwcnWvVk9%*LU<&0(0lRT(7WKhax-djJli`; zgSp~Fu9yM-tK`eesUYl>w|LEzDza!DC@|eUqNq!#6ic;|R`hCj_Jb@IP@diyTkg~S zYCTq}^F#<=7oB;-dWJ5f(ZRu(=g+9KPgasT zF^v?VtCbEZ%|5*ZV07P2(k=W@<=W8^&!*NK|C7hO8nCQ_lStGmap3UgZJswBHfOzt@VU#n z=_~`9j=Q)2+vt>*?vj!aq(hKK1nH1w#OM-6hjd9d2q;K{fb@_?TDk=UgwdUQcHj5& zf4i64+4-Kh&gXiN=}zw;+7sHWP>XVlIx*P|WgB=nWce#5CuXQSDPW+3wzup1=N3~L z`^DF|oz;{v!Geo1*~T~C5UFXd4>p>zqZCbrZS0NjF#!Lb@hl)1KYP|3ZU_M1uCWDq zFgz0ulkfhFzq^=P8WMkMOBfxzj%4skRP(MTUf57r;UHcGWQZ*@$;RXi9bY?&2H zWFRgeu8RXLYv@UJMAc8Px|5=?u_o)ILpi7#)Z$(n>M(|dc(Z|*`}9`v-_w6IHZC`` zI8AScAp!i{{wL*iq%A(B8EZ>xbh_DkurpN4-gtoF7)LkS9*2p;vK8@Rb4r{?fQ5SE zeGE$+v{*Rp3X&^joP{k8KQ;nRDfS*%jEmxNaTNU)t__;nQsPsEb3%ZL9I{CwB^F=} z1j^L3juWnBi>L$!ar`!ExLW7fq0PXHYUnuQQ7;;_%@8YSvz-)OS|Ut{B6BEcvl>F? zzs$tWAKOh!Ae-G6lANOXB$x?Ms8z9EXed1`n}4#P5H$N!mHYeA;OfmArt!_7#8}#Q z)#+tOn+OaSz|bMTRkxn_ae6*j*6^1eSJ4ygL++;M6sXbkK30;0?8%7lJhl)CQRk3ko`T zo%7s04-jsR9~T28YDIF$dC7dGCqkn@Pl5j4JFfJSygCi? z{WP}I7gE*4PTU0oCN9)bDDnYzg+xqXUAelPXqW+BFwcqUCHi~!b*yx2SO@a|N7^-m zZRQzJ_wBRpB@#lVq< zl~?u7?;FzAjXI!h1R$opw{pP9lY0KCf)O;g@3`pZd_3E--V@^}vo3Xj75G%UcC^ZD z?)1}}TXu8!691?#sBMz1Tq0d)>svoqBXh_-HV)y|v@E9Bq(kQV0{Hia?125{q+F9u z^&`9Ar)x1->JKm8O3AiYcfYG78IUmb*ELnC?!v{7nwUOkO$GmHGAU;-DG|i6Y>Bp@ zU3gbZq9ZDQDIX&wSBQYO#La0K!96Ytu~tVuasUtb`M*Ddu^awwvI%|xrc!S=m=nRM zp>hcxqog+oIOgLdo1KwX;UpxB#Y&A*PC=?@HoRQysE*Y|Q`_9@uq650E)sos&w(+_ zu{EXu(~V*|28*NRh4L6`jNKN(43?zc#z)8ewjgs-ft$~sQH2%s^gp_dRLQYjqJQK* za(FJE%QbP0dOxkaj$x0^HEQ@~U`vEhu156`E$8o0P`x(ILpw+DAbr5}5w^RLRi#fz z9M|~gCyu%|@9`;&%g27WXp@|nNHWwPQr92xr?Md+!xB1t1}^ncYu*3ic4da| z=oXveF#U$%kGhu*p@68&h0<{a4q`)p({Wwj>Ycl7H3-bA!Q(h^^E4G*5bk93F7i+qK7%&*63ITtvZ`7uH*{8RgIrh*J0D~p# z>At4L88)pw`tp2S*Z|erpajbqIwHV~a*6yVxrzl(a)M@ryDoc?dEL1Za9q?F0jTw% z7e8hK&CYrjqVS2aSZ(1{J(4y&Zx@4NX_H~WYF(d;C1&lctow7m#cNw>u2IV%JoWnr zQdb*4nej1H?fYL3PqR#ua%1pfa-ev_2wbRKOa_`7c;jJ3s z$5sOPX+!JEC<$wwV{@XCQ?*QAJwG}y;b1$> zy*d2ZjT+PzqLkFs*#2Ay$MA2$zN=_%{QNiEI*DLyi`d1vT8n_y*OPjt=?`eH?c*o9 z7B*wW{@w^imC&|0tcZN+e%G82FrktYCmjKEaK1l33Js0Z%B*hTZ<^i}{kJ z|4N<94SiZI&f~Cx0anzYmpIMVm(}VhDDk77&rHk;y5h=E(R#pw47#mCjHM3|HvK=< ziUf3l*Ed$rUhcQE69si&l_~6SA|#v*9=Yw>W%MLTA(-!m*T& zNHR5>(!9m+sj~ZZLsyqP3qML1{izkb=VO?GVjqb5y{}7a3B&-^UhZl76Ocd#hdAH! z5&=?SV~k&ZwG+)LJZIw$7Cz|q(X0HVrkpNDyNY{+18VMarOGV9}nDB!E)o~ zAWB`N&w9AfqR9~^YP-7fX%RZD#*8M+$t$gN8fR^+FP4MzBAnob2%cwS2{gl;l1iOA zb><|6UYQ7P>eqFWD{6t7*hGb82lK~-UTi%O`H9f(=VFmXF2C1ZR{I`0!c91m9nwx5 z+;U3I+8Whjvd1gKBdPlfx+;X{2RsJlT6X^S~N*wVY> zWRX-Dkx)<7MUA|jVe}jLp2qk<#fZ$fmG7c4KiGciOGIR~T-;AwQfq3oRDs$CE3YDP z0dsWl;ExiXQj?(m)676z@h1yET~f*6v&`42l2%T{@O*Yfc|@#e&H;#1x&_ZXknD&` zptJBw`ZYm^7ZTW$#YFk>{!;Am_;?m|q@`en;v=CjD2qRU>09Yve3F})oWa-TRJk(9s<^(}J(?ZNVA@S=M~ zqTg+MjQ$YcRN(3~3@&=NN98G+4K4-0ZVlvRU;H;dB~`K8a9fFJ_Ms~yPKhQ*XX$xm zlsjw}@>!|cS2`7`#jCE|$ygw2Aeyp_H-SqiehQa3sCtRYAFaLaa~XB}DOdKa4oZLG z-X`Mm?l#C+y8#XMboz)5be+6NS-Bau*X&WP@LDY9~^Q4eYGI+4!$Lb8dZ z^NCcCO0c<1{O-9%XjRcGO<5Rt`22~=tVh4i4BOGOQW+8IyTBJ86s5xgel`5cE|iI^ zQK@|=+Z-KPNwT)#lJE^l>bV$lax{;7`Wu7DaB?=pFT;{v4StV$o-|RXqo8A{i?2|w z`J#3tHnwz(>6xJ+D2luYtL4ocv#cIqopAm17JVIO+^cVInK!P<-eV}$N4ND7)kwB2 z==IMEHgrQok4zj_CB`L5YDYkIX>k~jmZuC6SE{f9J&e!*IFXF{La&G^#~pB1%wOV*{IvkK1yZwSx~&qn;3IkwZ$c5 z)r0xEsA&fH>)bp&DcjK9J)MaER$}*pzf(Xs(6zgj5R((2QrgjHME#QE~@732Ycq8xAt^3?nD%ZZ3a*%G~Nn zRk3e`BHC50TLEonwxX^ZO|+xiTNR&k=c8uGSiCAO1p+VwJ)70?_|{f03{FYSHd1VTiw+RJsr|+ zO;t9s#DFWQKcw=4lf(=`nJ!Cch3_U}gWEndX}){bHT>e^D#H42Oy&EFjt_dqqx|!D z*WcO)pvPik5_66DzSX=C%;H%7gn`%Y*#DyYR3%Uzv=BT^0WBu zZ~b3eMLnk)j?xC(!=8(RouwYvue6cl27ovW#)}sW&+>^;bF6T@IxV|hgP@QMQcETb z|3N7HDYUZsguX8ew6Ej)Ccx{W4a;!R%P`3zc|r#j)5(L^i*(ux9Eg+1(@X{H@Q92o zGEw+w%sRs={w;Gx!=5|S6($9PWSsowCJb|P|F$9e9q$4PEGj#Ll-~EBWfx#b&*;m_ z!?G7|>1BmwnQx)bzks!jrkbCJc$YYI@5;)s@iS5Jb_)DJ^?C0LGAhIerQ1FhAuv?_ zm=@$UJO$_z4pzKu1S#T^YEjkq*GVNhS>l^*zg^H)@P${QzHqkKQ4_kpE$m+mSu6`6 zJFKhJAclL&l;gvIAfJUj;p&t^;S)j=YX%J{OFs*sjo%?nlNNV zDDUsagC5WHi+)$XT@e3VcSN$lf}7eXb&|09bT?Qsm`iGdezyVN*;Xx-%9YvP&D9%Q zx=sm$-CwA<4>gLf(qB!h*o8zJ1r^#0p?uAD0=e;I1t~1djuUcwScrf)sd;{!wGIl529BXjbBZ2-@Qsoc6jGBp$ ztF%TeG8kx#!&l2@60(k;p4;UplFs!$i|c{fKt|Gy{&COJFX4z2)ynZiJ-o*n?_ayg zb;4;O)P3B=-FUaSe;q(a$02UV8#}IjA>&r9p6zv%TJ^kT)`+O@IcrWUN`zhGz(`|k zx|DUjH7kOL%A}nDkb?) zbmu4XX}fh%MjX(axV$qm^!2LcF(*Pm&D$AEWH!IcND|*yHZg@8{)NdqOww16^+V7L zNz@~GExkkTm;qUBW`A^)FdcJT&3eF|PxQ4p5W90|BZ#5hf4{#mMP~isl6`-_4c|{+ zbILv6knD5E#3;D{*LlpD&gO*vXGj+_us+-;^yC8BiShohnD1+YjxREX@JM!~!BQU> zP!b@{MNvQZVCZ1kb$+;<45I<1rm1-?8rU4lROX+7Ll%*rP#CbSpy|U49@}e>tnmDZ z6XJscA`-ntmxIgeQQiiMhEvi)6%IYHvPGA`h3!=O{H|Te+oi?z%|34-P}JF_RI1ra zQ9Ht zMcy^QY3n@|Pg|-R59k$VUhSn;6ToNa&i*^Z4|E#bRb1aWG_T|hgI}f@e_B6l;Ohl| zVK+YWj5>Y6Kk?%cfi}b^4)dPG?21wi#lbv7L z{J#Ph4ZX6QGUoTaj-k0)?CepR3sx`DK5Kh`v#}%-s|7flMO@pz+{$oYM_p0Q{d_a5 zhbzf4?m!a(WxLp~Bb@EPH1HrG9kifjI*8+KI+KXrlOoj@wnZh<$0LoQpp;~?wDlum z2CD)Vj%!9bQeK^{<)tRB`-G!P3^a4JGim?TOW;~;mkw`nd8byHtVH* zt&7s`9>NU<8NJ@Qqgkysw$!XoIoQho#Ct+MrL|V1yXhZ}@j{ z>%CPW?!C4}`}eN6qTWo^&d2UoN4ZjK%o3Y-C}Cp()&#xzQ|qJ~X4k)RAqhXbX+Z4Q zoCZUA!;3e!-U@$ES&LWB&4z66IT*0!`T4JY%?JbJidmea=oE_ze7e{-!Be^GZ?tz4 zthV-2z`K4JuPo=Zk$I?a=DUu&ihtPnbh<8?rizxQ0sZk0N9H<;xyO#NK zE4;;OyaCxp@xAh^lPS^t=(SSZy_y)UP< zNiF#+*(1FxOS1b!y4dNbPu^FTczGA6Jm0yEtjOCS1`5Ek&9vLz2DFRl)wj}WIyEqU zSZiz<(_4O;J$HfG$_TK_4YO%2(_>PbQTnMlfu@3A$r8Ue1Q|@1Y#bP&^A}0}GVJix z#JyNBra=b0Dmsx+?Si`{YSK!WTjKQ%>U+4QNH)=bh*L&9i6_d%nw|_ zz|`sY+JQ&n48{3NEH>@oi);IU>WiYta5?Hwp0)05BK7!x3dF2UIc?a?;}283}RDKA0IeaEbZH+YZgYfG zXGOTT6vL$UcP23OCXkke_rD!g$eD4DR7Zb_+hA;J-=WDwuBADS2qzO;M;HvCZp;e? zq!zB`K@^g#4OV)l)fex4^{neLSGcbw&;}O1*v5}WJkNx?LjyP7?edvdUKhfIgsU-I0- zGe>sS4@fv10e;x+7?sb0rMyKmFhT&@= zhUrNaMmo!y@fW%6Z!}%l!1_vH^lcXyPl@+y+vnd}ziO%?YKu3Hw1}&nB`|eV zE)1LTcQd&1gkq_p%GG>!2IU{+woe1Gaes!(8=>-=jEE#y!LsMEMO4B^`IFW6V(Slw zhPCAhn!jyKpPdMPX6T+AT>apfy4Ds|Lq*O!W{VZNK-$>V=s>*E-O(mOhZ$b~^p=i< z0+oS2SXq_F{2At)7S=iNQI&S_f{POccQ8%9%Wb2y-+com9Q#{ZAY9+o`0DYE6opW2 z#`-tZO{`AicL@9h1@_Zi_jG~PUOLh1rj3r>N&tKRlOTQ!p^HC?brzl+r*)&KN2;R> z9VJvZ`&AJ_P!P~mM3-V3On6(0 zTX?Lx4Psg6zO$jgu9|7^&oIznS5l|_{8c^4^&=_S_X{=w=10X+hRY(5VR>NPLty!J zQ9r3|$V%zuM`N@F^65JElfd4EhJG_+%il(@FP*r-3wO|(JE8(hTX~C@_3uYW>mYcV z>W|0+m!La+<|SO^OWKi2gs^q=wP(}&#=)5Gu9hGSv|7So|61QHp{?W}mz8jhe$bWg z>QXw;j(X|NXLRqIwLVu3)1G0F5}1FN*rywNb52-Dg^Ao&mmHS)S##6U2`~Z(?%x7Sj;u`u7+Wl&pvjn{svPnefW`ak(a^~S0JYo9*CtKR*HJBxTU+|S@UA&c^UHl165=qhMya>a}kP)UZ zp`_2zN+huPFh{TE8U6W`!_s9LcES|qtP-4VS!2yC%0DI?+hNiyVvH>GA+Mmbv6JES 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 Ls;3?y_2L6I6ubGEc5+jPRsJ2` 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∑rbH}?X|9uw-<+lu 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�{OoZ4Hc`Tjt!%$0g1pC}*?d|Z}$NvMNEbxHR@ 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 literal 0 HcmV?d00001 diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index caf8f913..ab4205f8 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -6,6 +6,8 @@ import { AntiAliasingKernel } from './antialiasingkernel'; import { BlitPass } from './blitpass'; import { Context } from './context'; import { DefaultFramebuffer } from './defaultframebuffer'; +import { FontFace } from './fontface'; +import { FontLoader } from './fontloader'; import { Framebuffer } from './framebuffer'; import { MouseEventProvider } from './mouseeventprovider'; import { NdcFillingTriangle } from './ndcfillingtriangle'; @@ -147,6 +149,9 @@ export class LabelRenderer extends Renderer { } protected onPrepare(): void { + + this.loadFont(); + const gl = this._context.gl; const gl2facade = this._context.gl2facade; @@ -197,6 +202,14 @@ export class LabelRenderer extends Renderer { this._blit.frame(); } + protected loadFont(): void { + const loader = new FontLoader(); + + const fontFace: FontFace = loader.load( + this.context, './data/opensansr144/opensansr144.fnt', false, () => { + }); + } + } From 668f67990f644b60ac780f4cfc6675ecd29129cf Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Thu, 7 Jun 2018 16:58:11 +0200 Subject: [PATCH 11/92] load font in onInitialize --- source/labelrenderer.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index ab4205f8..3a9f8d95 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -46,6 +46,8 @@ export class LabelRenderer extends Renderer { /* keyEventProvider: KeyEventProvider, */ /* touchEventProvider: TouchEventProvider */): boolean { + this.loadFont(context); + const gl = this._context.gl; const gl2facade = this._context.gl2facade; @@ -150,8 +152,6 @@ export class LabelRenderer extends Renderer { protected onPrepare(): void { - this.loadFont(); - const gl = this._context.gl; const gl2facade = this._context.gl2facade; @@ -202,14 +202,14 @@ export class LabelRenderer extends Renderer { this._blit.frame(); } - protected loadFont(): void { + protected loadFont(context: Context): void { const loader = new FontLoader(); const fontFace: FontFace = loader.load( - this.context, './data/opensansr144/opensansr144.fnt', false, () => { + context, './data/opensansr144/opensansr144.fnt', false, () => { + //TODO setup Labels }); } - } From aa5a43d3f65941b35b102ccd75ba027dc8f3dac2 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 8 Jun 2018 15:59:31 +0200 Subject: [PATCH 12/92] WIP: trying to typeset a label --- source/labelrenderer.ts | 88 +++++++++++++++++++++++++++++++++-- source/shaders/glyphquad.frag | 4 +- source/shaders/glyphquad.vert | 18 +++---- source/text.ts | 10 ++++ 4 files changed, 106 insertions(+), 14 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 3a9f8d95..fc9bca2b 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -1,13 +1,13 @@ import { assert } from './auxiliaries'; +import { mat4, vec3, vec4 } from 'gl-matrix'; + import { AccumulatePass } from './accumulatepass'; import { AntiAliasingKernel } from './antialiasingkernel'; import { BlitPass } from './blitpass'; import { Context } from './context'; import { DefaultFramebuffer } from './defaultframebuffer'; -import { FontFace } from './fontface'; -import { FontLoader } from './fontloader'; import { Framebuffer } from './framebuffer'; import { MouseEventProvider } from './mouseeventprovider'; import { NdcFillingTriangle } from './ndcfillingtriangle'; @@ -17,6 +17,14 @@ import { Invalidate, Renderer } from './renderer'; import { Shader } from './shader'; import { Texture2 } from './texture2'; +import { FontFace } from './fontface'; +import { FontLoader } from './fontloader'; +import { GlyphVertex, GlyphVertices } from './glyphvertices'; +import { Label } from './label'; +import { Text } from './text'; +import { Typesetter } from './typesetter'; + + import { TestNavigation } from './debug/testnavigation'; @@ -40,6 +48,7 @@ export class LabelRenderer extends Renderer { protected _testNavigation: TestNavigation; + protected _fontFace: FontFace; protected onInitialize(context: Context, callback: Invalidate, mouseEventProvider: MouseEventProvider, @@ -207,8 +216,81 @@ export class LabelRenderer extends Renderer { const fontFace: FontFace = loader.load( context, './data/opensansr144/opensansr144.fnt', false, () => { - //TODO setup Labels + this.setupScene(); + this.invalidate(); }); + + this._fontFace = fontFace; + } + + protected setupScene(): void { + + // create Label with Text + + this.prepareLabel('Hello World!'); + + // tell the Typesetter to typeset that Label with the loaded FontFace (using Glyph or Glyph Vertices) + // make a Geometry out of those vertices (or find another way of sending vertices to shader) + + } + + protected prepareLabel(/*userTransform: mat4,*/ str: string /*other params*/) { + + const testLabel: Label = new Label(); + + testLabel.text = new Text(str); + testLabel.fontFace = this._fontFace; + testLabel.transform = mat4.create(); + + // const margins: vec4 = config.margins; + + // // compute transform matrix + // let transform = mat4.create(); + + // // translate to lower left in NDC + // mat4.translate(transform, transform, vec3.fromValues(-1.0, -1.0, 0.0)); + + // // scale glyphs to NDC size + // // this._size was the viewport size in Haeley + // mat4.scale(transform, transform, vec3.fromValues(2.0 / this._size[0], 2.0 / this._size[1], 1.0)); + + // // scale glyphs to pixel size with respect to the displays ppi + // // mat4.scale(transform, transform, vec3.fromValues(config.ppiScale, config.ppiScale, config.ppiScale)); + + // // translate to origin in point space - scale origin within + // // margined extend (i.e., viewport with margined areas removed) + // let marginedExtent: vec2 = vec2.create(); + // vec2.sub(marginedExtent, vec2.fromValues(this._size[0] / config.ppiScale, this._size[1] / config.ppiScale), + // vec2.fromValues(margins[3] + margins[1], margins[2] + margins[0])); + + // let v3 = vec3.fromValues(0.5 * marginedExtent[0], 0.5 * marginedExtent[1], 0); + // vec3.add(v3, v3, vec3.fromValues(margins[3], margins[2], 0.0)); + // mat4.translate(transform, transform, v3); + + // sequence.additionalTransform = transform; + + const numGlyphs = testLabel.length; + + + // prepare vertex storage (values will be overridden by typesetter) + const vertices = new GlyphVertices(); + for (let i = 0; i < numGlyphs; ++i) { + + const vertex: GlyphVertex = { + origin: vec3.create(), + tangent: vec3.create(), + up: vec3.create(), + // vec2 lowerLeft and vec2 upperRight in glyph texture (uv) + uvRect: vec4.create(), + }; + vertices.push(vertex); + } + + + Typesetter.typeset(testLabel, vertices, 0); + + + return vertices; } } diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index c6817d85..ae517fa1 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -10,9 +10,9 @@ precision mediump float; #endif -// uniform sampler2D u_glyphs; +uniform sampler2D u_glyphs; -// varying vec2 v_texture_coord; +varying vec2 v_texture_coord; void main(void) { diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 7f9614bc..4787511a 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -6,8 +6,8 @@ precision lowp int; #if __VERSION__ == 100 #extension GL_EXT_draw_buffers : enable attribute vec2 a_vertex; +attribute vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] // attribute vec3 a_id; // encoded uint24 id in byte3 -// attribute vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] // attribute vec3 a_origin; // attribute vec3 a_tan; // attribute vec3 a_bitan; @@ -18,8 +18,8 @@ attribute vec2 a_vertex; // attribute vec4 a_transform4; #else layout(location = 0) in vec2 a_vertex; -// layout(location = 1) in vec3 a_id; // encoded uint24 id in byte3 -// layout(location = 2) in vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] +layout(location = 1) in vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] +// layout(location = 2) in vec3 a_id; // encoded uint24 id in byte3 // layout(location = 3) in vec3 a_origin; // layout(location = 4) in vec3 a_tan; // layout(location = 5) in vec3 a_bitan; @@ -43,14 +43,14 @@ void main(void) //TEXTURE COORDS - // float posX = a_texture_coord[0]; - // float posY = a_texture_coord[1]; + float posX = a_texture_coord[0]; + float posY = a_texture_coord[1]; - // float pos2X = a_texture_coord[2]; - // float pos2Y = a_texture_coord[3]; - // vec2 texExt = vec2(pos2X-posX, pos2Y-posY); + float pos2X = a_texture_coord[2]; + float pos2Y = a_texture_coord[3]; + vec2 texExt = vec2(pos2X-posX, pos2Y-posY); - // v_texture_coord = a_vertex * texExt + a_texture_coord.xy; + v_texture_coord = a_vertex * texExt + a_texture_coord.xy; //POSITIONING diff --git a/source/text.ts b/source/text.ts index 3f7d2a10..3429efbe 100644 --- a/source/text.ts +++ b/source/text.ts @@ -19,6 +19,16 @@ export class Text { /** @see {@link altered} */ protected _altered = false; + /** + * Constructs a Text to be used for a Label. + * @param str - the actual content of this Text. + * @param lineFeed - char for lineFeed, default is LF. + */ + constructor(str: string, lineFeed?: string) { + this._text = str; + + this._lineFeed = lineFeed !== undefined ? lineFeed : this._lineFeed; + } /** * Length of the text, i.e., number of characters within the text. From 3b5b4c7aa233eb6199e44fdc609464407fa90711 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 11 Jun 2018 11:13:09 +0200 Subject: [PATCH 13/92] Hello Glyph Texture! --- source/fontloader.ts | 10 +++++----- source/label.ts | 12 ++++++++++++ source/labelrenderer.ts | 28 +++++++++++++++++++++++----- source/shaders/glyphquad.frag | 5 +++-- source/shaders/glyphquad.vert | 18 ++++++++++-------- 5 files changed, 53 insertions(+), 20 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index 5ba7e9df..8a8a381c 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -108,17 +108,17 @@ export class FontLoader { const w: number = img.naturalWidth; const h: number = img.naturalHeight; - fontFace.glyphTexture.reformat(gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE); - fontFace.glyphTexture.resize(w, h); - fontFace.glyphTexture.data(img); + fontFace.glyphTexture.reformat(gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, false, false); + fontFace.glyphTexture.resize(w, h, false, false); + fontFace.glyphTexture.data(img, false, false); /** * mipmaps are usually a good idea, but: using Fonts based on Distance Fields and super sampling * (accumulation frames), the rendered Glyph looks more readable when not using mipmaps. * Maybe manually provide mipmaps; smaller than 8x8 could be blank (can't render glyphs that small?) */ - fontFace.glyphTexture.filter(gl.LINEAR, gl.LINEAR, true); - fontFace.glyphTexture.wrap(gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE, true); + fontFace.glyphTexture.filter(gl.LINEAR, gl.LINEAR, false, false); + fontFace.glyphTexture.wrap(gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE, false, false); fontFace.glyphTexture.unbind(); diff --git a/source/label.ts b/source/label.ts index 6edc3427..181bfc14 100644 --- a/source/label.ts +++ b/source/label.ts @@ -48,6 +48,18 @@ export class Label { any: false, color: false, resources: false, text: false, typesetting: false, transformation: false, }); + /** + * Constructs an unconfigured, empty label. + * @param text - Valid context to create the object for. + * @param identifier - Meaningful name for identification of this instances VAO and VBOs. + */ + constructor(text: Text, fontFace: FontFace) { + this._text = text; + this._fontFace = fontFace; + this._transform = mat4.create(); + } + + /** * Returns the character at the specified index. * @param pos - The zero-based index of the desired character. diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index fc9bca2b..c6a1a46c 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -50,6 +50,8 @@ export class LabelRenderer extends Renderer { protected _fontFace: FontFace; + protected _uGlyphAtlas: WebGLUniformLocation; + protected onInitialize(context: Context, callback: Invalidate, mouseEventProvider: MouseEventProvider, /* keyEventProvider: KeyEventProvider, */ @@ -85,6 +87,8 @@ export class LabelRenderer extends Renderer { this._uNdcOffset = this._program.uniform('u_ndcOffset'); this._uFrameNumber = this._program.uniform('u_frameNumber'); + this._uGlyphAtlas = this._program.uniform('u_glyphs'); + this._ndcTriangle = new NdcFillingTriangle(this._context); const aVertex = this._program.attribute('a_vertex', 0); this._ndcTriangle.initialize(aVertex); @@ -129,6 +133,7 @@ export class LabelRenderer extends Renderer { this._uNdcOffset = -1; this._uFrameNumber = -1; + this._uGlyphAtlas = -1; this._program.uninitialize(); this._ndcTriangle.uninitialize(); @@ -189,6 +194,14 @@ export class LabelRenderer extends Renderer { gl.viewport(0, 0, this._frameSize[0], this._frameSize[1]); + let wasBlendEnabled = false; + const oldBlendSRC: any = gl.getParameter(gl.BLEND_SRC_RGB); + const oldBlendDST: any = gl.getParameter(gl.BLEND_DST_RGB); + + wasBlendEnabled = gl.isEnabled(gl.BLEND); + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + this._program.bind(); const ndcOffset = this._ndcOffsetKernel.get(frameNumber); @@ -197,12 +210,21 @@ export class LabelRenderer extends Renderer { gl.uniform2fv(this._uNdcOffset, ndcOffset); gl.uniform1i(this._uFrameNumber, frameNumber); + this._fontFace.glyphTexture.bind(gl.TEXTURE0); + gl.uniform1i(this._uGlyphAtlas, 0); + this._intermediateFBO.clear(gl.COLOR_BUFFER_BIT, true, false); this._ndcTriangle.bind(); this._ndcTriangle.draw(); this._intermediateFBO.unbind(); this._accumulate.frame(frameNumber); + + this._fontFace.glyphTexture.unbind(gl.TEXTURE0); + gl.blendFunc(oldBlendSRC, oldBlendDST); + if (!wasBlendEnabled) { + gl.disable(gl.BLEND); + } } protected onSwap(): void { @@ -236,11 +258,7 @@ export class LabelRenderer extends Renderer { protected prepareLabel(/*userTransform: mat4,*/ str: string /*other params*/) { - const testLabel: Label = new Label(); - - testLabel.text = new Text(str); - testLabel.fontFace = this._fontFace; - testLabel.transform = mat4.create(); + const testLabel: Label = new Label(new Text(str), this._fontFace); // const margins: vec4 = config.margins; diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index ae517fa1..4a2a4d82 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -18,7 +18,7 @@ void main(void) { //requires blend: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // float d = texture(u_glyphs, v_texture_coord).r; + float d = texture(u_glyphs, v_texture_coord).r; /** * using if-statement and discard can slow down performance: * it's bad for IMR, TBR, TBDR and early-Z optimization @@ -28,7 +28,8 @@ void main(void) //if(d < 0.45) // discard; - vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green + // vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green + vec4 fc = vec4(d, d, d, 1.0); //debug texture //TODO mipmap access? // float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 4787511a..7f610069 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -6,7 +6,7 @@ precision lowp int; #if __VERSION__ == 100 #extension GL_EXT_draw_buffers : enable attribute vec2 a_vertex; -attribute vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] +// attribute vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] // attribute vec3 a_id; // encoded uint24 id in byte3 // attribute vec3 a_origin; // attribute vec3 a_tan; @@ -18,7 +18,7 @@ attribute vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] // attribute vec4 a_transform4; #else layout(location = 0) in vec2 a_vertex; -layout(location = 1) in vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] +// layout(location = 1) in vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] // layout(location = 2) in vec3 a_id; // encoded uint24 id in byte3 // layout(location = 3) in vec3 a_origin; // layout(location = 4) in vec3 a_tan; @@ -43,14 +43,16 @@ void main(void) //TEXTURE COORDS - float posX = a_texture_coord[0]; - float posY = a_texture_coord[1]; + // float posX = a_texture_coord[0]; + // float posY = a_texture_coord[1]; - float pos2X = a_texture_coord[2]; - float pos2Y = a_texture_coord[3]; - vec2 texExt = vec2(pos2X-posX, pos2Y-posY); + // float pos2X = a_texture_coord[2]; + // float pos2Y = a_texture_coord[3]; + // vec2 texExt = vec2(pos2X-posX, pos2Y-posY); - v_texture_coord = a_vertex * texExt + a_texture_coord.xy; + v_texture_coord = a_vertex.xy * 0.5 + 0.5; + + //v_texture_coord = a_vertex; // * texExt + a_texture_coord.xy; //POSITIONING From 99ed0cb759a8374f3584a54c8dc842e1447d59ee Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 11 Jun 2018 17:01:02 +0200 Subject: [PATCH 14/92] WIPcommit. idea: gather all vertex data in labelgeometry. --- source/LabelGeometry.ts | 79 +++++++++++++++++++++++++++++++++++ source/labelrenderer.ts | 15 ++++--- source/shaders/glyphquad.vert | 3 +- 3 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 source/LabelGeometry.ts diff --git a/source/LabelGeometry.ts b/source/LabelGeometry.ts new file mode 100644 index 00000000..16fc3897 --- /dev/null +++ b/source/LabelGeometry.ts @@ -0,0 +1,79 @@ + +import { assert } from './auxiliaries'; + +import { Buffer } from './buffer'; +import { Context } from './context'; +import { Geometry } from './geometry'; +import { Initializable } from './initializable'; + +/** + * Gathers vertices and other data needed for drawing all labels. + */ +export class LabelGeometry extends Geometry { + + /** + * Object constructor, requires a context and an identifier. + * @param context - Valid context to create the object for. + * @param identifier - Meaningful name for identification of this instance. + */ + constructor(context: Context, identifier?: string) { + super(context, identifier); + + /* Generate identifier from constructor name if none given. */ + identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; + + const vertexVBO = new Buffer(context, identifier + 'VBO'); + const indexBuffer = new Buffer(context, identifier + 'TexCoordBuffer'); + this._buffers.push(vertexVBO); + this._buffers.push(indexBuffer); + } + + /** + * Binds the vertex buffer object (VBO) to an attribute binding point of a given, pre-defined index. + */ + protected bindBuffers(indices: Array): void { + /* Please note the implicit bind in attribEnable */ + this._buffers[0].attribEnable(indices[0], 3, this.context.gl.FLOAT, false, 0, 0, true, false); + this._buffers[1].bind(); + } + + /** + * Unbinds the vertex buffer object (VBO) and disables the binding point. + */ + protected unbindBuffers(indices: Array): void { + /* Please note the implicit unbind in attribEnable is skipped */ + this._buffers[0].attribDisable(indices[0], true, true); + this._buffers[1].unbind(); + } + + /** + * Specifies/invokes the draw of all labels. + */ + @Initializable.assert_initialized() + draw(): void { + const gl = this.context.gl; + gl.drawElements(gl.TRIANGLE_STRIP, /* TODO */ 4, gl.UNSIGNED_BYTE, 0); + } + + /** + * Creates the vertex buffer object (VBO) and creates and initializes the buffer's data store. + * @param aVertex - Attribute binding point for vertices. + */ + initialize(aVertex: GLuint): boolean { + const gl = this.context.gl; + + // TODO: do not bind index to location 4 + const valid = super.initialize([gl.ARRAY_BUFFER, gl.ELEMENT_ARRAY_BUFFER], [aVertex, 8]); + + assert(this._buffers[0] !== undefined && this._buffers[0].object instanceof WebGLBuffer, + `expected valid WebGLBuffer`); + + assert(this._buffers[1] !== undefined && this._buffers[1].object instanceof WebGLBuffer, + `expected valid WebGLBuffer`); + + this._buffers[0].data(/*TODO vertices */, gl.STATIC_DRAW); + this._buffers[1].data(/*TODO tex coords*/, gl.STATIC_DRAW); + + return valid; + } +} diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index c6a1a46c..0726cc0b 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -213,6 +213,10 @@ export class LabelRenderer extends Renderer { this._fontFace.glyphTexture.bind(gl.TEXTURE0); gl.uniform1i(this._uGlyphAtlas, 0); + // TODO + // labelgeometry.bind(); + // labelgeometry.draw(); + this._intermediateFBO.clear(gl.COLOR_BUFFER_BIT, true, false); this._ndcTriangle.bind(); this._ndcTriangle.draw(); @@ -247,16 +251,15 @@ export class LabelRenderer extends Renderer { protected setupScene(): void { - // create Label with Text - - this.prepareLabel('Hello World!'); - + // create Label with Text and // tell the Typesetter to typeset that Label with the loaded FontFace (using Glyph or Glyph Vertices) - // make a Geometry out of those vertices (or find another way of sending vertices to shader) + const vertices = this.prepareLabel('Hello World!'); + // make a Geometry out of those vertices (or find another way of sending vertices to shader) + // TODO labelgeometry } - protected prepareLabel(/*userTransform: mat4,*/ str: string /*other params*/) { + protected prepareLabel(/*userTransform: mat4,*/ str: string /*other params*/): GlyphVertices { const testLabel: Label = new Label(new Text(str), this._fontFace); diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 7f610069..d566bdcc 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -50,10 +50,9 @@ void main(void) // float pos2Y = a_texture_coord[3]; // vec2 texExt = vec2(pos2X-posX, pos2Y-posY); + // v_texture_coord = a_vertex; // * texExt + a_texture_coord.xy; v_texture_coord = a_vertex.xy * 0.5 + 0.5; - //v_texture_coord = a_vertex; // * texExt + a_texture_coord.xy; - //POSITIONING //quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_vertex) TODO: is this up-to-date? From 622678c00f18c2c0c43b984e0bc91ef20c542c68 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 25 Jun 2018 15:31:06 +0200 Subject: [PATCH 15/92] WIP: typesetting seems to work, tex coords don't --- source/LabelGeometry.ts | 42 ++++++++++++---- source/labelrenderer.ts | 90 ++++++++++++++++++++++++++++------- source/shaders/glyphquad.frag | 18 +++---- source/shaders/glyphquad.vert | 30 +++--------- website/js/website.js | 2 +- 5 files changed, 123 insertions(+), 59 deletions(-) diff --git a/source/LabelGeometry.ts b/source/LabelGeometry.ts index 16fc3897..a1647653 100644 --- a/source/LabelGeometry.ts +++ b/source/LabelGeometry.ts @@ -11,6 +11,9 @@ import { Initializable } from './initializable'; */ export class LabelGeometry extends Geometry { + protected _vertices: Float32Array = new Float32Array(0); + protected _texCoords: Float32Array = new Float32Array(0); + /** * Object constructor, requires a context and an identifier. * @param context - Valid context to create the object for. @@ -23,9 +26,9 @@ export class LabelGeometry extends Geometry { identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; const vertexVBO = new Buffer(context, identifier + 'VBO'); - const indexBuffer = new Buffer(context, identifier + 'TexCoordBuffer'); + const texCoordBuffer = new Buffer(context, identifier + 'TexCoordBuffer'); this._buffers.push(vertexVBO); - this._buffers.push(indexBuffer); + this._buffers.push(texCoordBuffer); } /** @@ -52,28 +55,51 @@ export class LabelGeometry extends Geometry { @Initializable.assert_initialized() draw(): void { const gl = this.context.gl; - gl.drawElements(gl.TRIANGLE_STRIP, /* TODO */ 4, gl.UNSIGNED_BYTE, 0); + // gl.drawElements(gl.TRIANGLE_STRIP, /* TODO */ 4, gl.UNSIGNED_BYTE, 0); + const count = this._texCoords.length / 2; // because a texCoord has 4 values: ll, lr, ul, ur + // gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, count); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, count); } /** * Creates the vertex buffer object (VBO) and creates and initializes the buffer's data store. + * // TODO doesnt really initialize the data! * @param aVertex - Attribute binding point for vertices. */ initialize(aVertex: GLuint): boolean { + const gl = this.context.gl; - // TODO: do not bind index to location 4 - const valid = super.initialize([gl.ARRAY_BUFFER, gl.ELEMENT_ARRAY_BUFFER], [aVertex, 8]); + // TODO: do not bind index to location 4 // why not? + const valid = super.initialize([gl.ARRAY_BUFFER, gl.ARRAY_BUFFER], [aVertex, 8]); + + return valid; + } + + setVertices(data: Float32Array): void { assert(this._buffers[0] !== undefined && this._buffers[0].object instanceof WebGLBuffer, `expected valid WebGLBuffer`); + this._vertices = data; + + const gl = this.context.gl; + // TODO: is DYNAMIC_DRAW more appropriate? + this._buffers[0].data(this._vertices, gl.STATIC_DRAW); + + } + + setTexCoords(data: Float32Array): void { + assert(this._buffers[1] !== undefined && this._buffers[1].object instanceof WebGLBuffer, `expected valid WebGLBuffer`); - this._buffers[0].data(/*TODO vertices */, gl.STATIC_DRAW); - this._buffers[1].data(/*TODO tex coords*/, gl.STATIC_DRAW); + this._texCoords = data; - return valid; + const gl = this.context.gl; + // TODO: is DYNAMIC_DRAW more appropriate? + this._buffers[1].data(this._texCoords, gl.STATIC_DRAW); } + + } diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 0726cc0b..cee3f533 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -10,7 +10,7 @@ import { Context } from './context'; import { DefaultFramebuffer } from './defaultframebuffer'; import { Framebuffer } from './framebuffer'; import { MouseEventProvider } from './mouseeventprovider'; -import { NdcFillingTriangle } from './ndcfillingtriangle'; +// import { NdcFillingTriangle } from './ndcfillingtriangle'; import { Program } from './program'; import { Renderbuffer } from './renderbuffer'; import { Invalidate, Renderer } from './renderer'; @@ -21,10 +21,10 @@ import { FontFace } from './fontface'; import { FontLoader } from './fontloader'; import { GlyphVertex, GlyphVertices } from './glyphvertices'; import { Label } from './label'; +import { LabelGeometry } from './LabelGeometry'; import { Text } from './text'; import { Typesetter } from './typesetter'; - import { TestNavigation } from './debug/testnavigation'; @@ -36,7 +36,7 @@ export class LabelRenderer extends Renderer { protected _ndcOffsetKernel: AntiAliasingKernel; protected _uNdcOffset: WebGLUniformLocation; protected _uFrameNumber: WebGLUniformLocation; - protected _ndcTriangle: NdcFillingTriangle; + // protected _ndcTriangle: NdcFillingTriangle; protected _accumulate: AccumulatePass; protected _blit: BlitPass; @@ -49,7 +49,7 @@ export class LabelRenderer extends Renderer { protected _testNavigation: TestNavigation; protected _fontFace: FontFace; - + protected _labelGeometry: LabelGeometry; protected _uGlyphAtlas: WebGLUniformLocation; protected onInitialize(context: Context, callback: Invalidate, @@ -89,9 +89,13 @@ export class LabelRenderer extends Renderer { this._uGlyphAtlas = this._program.uniform('u_glyphs'); - this._ndcTriangle = new NdcFillingTriangle(this._context); + this._labelGeometry = new LabelGeometry(this._context); const aVertex = this._program.attribute('a_vertex', 0); - this._ndcTriangle.initialize(aVertex); + this._labelGeometry.initialize(aVertex); + + // this._ndcTriangle = new NdcFillingTriangle(this._context); + // const aVertex = this._program.attribute('a_vertex', 0); + // this._ndcTriangle.initialize(aVertex); this._ndcOffsetKernel = new AntiAliasingKernel(this._multiFrameNumber); @@ -108,7 +112,7 @@ export class LabelRenderer extends Renderer { /* Create and configure accumulation pass. */ this._accumulate = new AccumulatePass(this._context); - this._accumulate.initialize(this._ndcTriangle); + this._accumulate.initialize(/*this._ndcTriangle*/); this._accumulate.precision = this._framePrecision; this._accumulate.texture = this._colorRenderTexture; // this._accumulate.depthStencilAttachment = this._depthRenderbuffer; @@ -116,7 +120,7 @@ export class LabelRenderer extends Renderer { /* Create and configure blit pass. */ this._blit = new BlitPass(this._context); - this._blit.initialize(this._ndcTriangle); + this._blit.initialize(/*this._ndcTriangle*/); this._blit.readBuffer = gl2facade.COLOR_ATTACHMENT0; this._blit.drawBuffer = gl.BACK; this._blit.target = this._defaultFBO; @@ -136,7 +140,7 @@ export class LabelRenderer extends Renderer { this._uGlyphAtlas = -1; this._program.uninitialize(); - this._ndcTriangle.uninitialize(); + // this._ndcTriangle.uninitialize(); this._intermediateFBO.uninitialize(); this._defaultFBO.uninitialize(); @@ -213,13 +217,13 @@ export class LabelRenderer extends Renderer { this._fontFace.glyphTexture.bind(gl.TEXTURE0); gl.uniform1i(this._uGlyphAtlas, 0); - // TODO - // labelgeometry.bind(); - // labelgeometry.draw(); - this._intermediateFBO.clear(gl.COLOR_BUFFER_BIT, true, false); - this._ndcTriangle.bind(); - this._ndcTriangle.draw(); + + this._labelGeometry.bind(); + this._labelGeometry.draw(); + + // this._ndcTriangle.bind(); + // this._ndcTriangle.draw(); this._intermediateFBO.unbind(); this._accumulate.frame(frameNumber); @@ -253,10 +257,55 @@ export class LabelRenderer extends Renderer { // create Label with Text and // tell the Typesetter to typeset that Label with the loaded FontFace (using Glyph or Glyph Vertices) - const vertices = this.prepareLabel('Hello World!'); + const glyphVertices = this.prepareLabel('Hello World!'); // make a Geometry out of those vertices (or find another way of sending vertices to shader) // TODO labelgeometry + const vertices: Array = []; + const texCoords: Array = []; + + const l = glyphVertices.length; + + for (let i = 0; i < l; i++) { + const v = glyphVertices[i]; + + // ll + vertices.push(v.origin[0]); + vertices.push(v.origin[1]); + vertices.push(v.origin[2]); + texCoords.push(v.uvRect[0]); + texCoords.push(v.uvRect[1]); + + const lr = vec3.create(); + vec3.add(lr, v.origin, v.tangent); + vertices.push(lr[0]); + vertices.push(lr[1]); + vertices.push(lr[2]); + texCoords.push(v.uvRect[2]); + texCoords.push(v.uvRect[1]); + + const ul = vec3.create(); + vec3.add(ul, v.origin, v.up); + vertices.push(ul[0]); + vertices.push(ul[1]); + vertices.push(ul[2]); + texCoords.push(v.uvRect[0]); + texCoords.push(v.uvRect[3]); + + const ur = vec3.create(); + vec3.add(ur, lr, v.up); + vertices.push(ur[0]); + vertices.push(ur[1]); + vertices.push(ur[2]); + texCoords.push(v.uvRect[2]); + texCoords.push(v.uvRect[3]); + } + + console.log(this._frameSize); + + + this._labelGeometry.setVertices(Float32Array.from(vertices)); + this._labelGeometry.setTexCoords(Float32Array.from(texCoords)); } protected prepareLabel(/*userTransform: mat4,*/ str: string /*other params*/): GlyphVertices { @@ -307,12 +356,17 @@ export class LabelRenderer extends Renderer { vertices.push(vertex); } - Typesetter.typeset(testLabel, vertices, 0); - return vertices; } + + /** + * This is ugly, but it should do the trick for now. + * Later, we want to have a labelrenderpass and a labelpositionpass. + * The first one bakes the geometry, the second one adapts the placement regarding dynamic placement algorithms. + * For now, we will have both as a labelrenderer, and split it up later. + */ } diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index 4a2a4d82..e6bf92d2 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -25,15 +25,15 @@ void main(void) * https://stackoverflow.com/questions/8509051/is-discard-bad-for-program-performance-in-opengl * But it is necessary: overlapping glyphs (like in 'ft') should not fight each other */ - //if(d < 0.45) - // discard; - - // vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green - vec4 fc = vec4(d, d, d, 1.0); //debug texture - - //TODO mipmap access? - // float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur + // if(d < 0.45) + // discard; + + vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green + // vec4 fc = vec4(d, d, d, 1.0); //debug texture + + // TODO mipmap access? + float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur - //fragColor = vec4(fc.rgb, fc.a * a); + fragColor = vec4(fc.rgb, fc.a * a); fragColor = vec4(fc.rgb, 1.0); } diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index d566bdcc..1d507eb7 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -5,8 +5,8 @@ precision lowp int; #if __VERSION__ == 100 #extension GL_EXT_draw_buffers : enable -attribute vec2 a_vertex; -// attribute vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] +attribute vec3 a_vertex; +attribute vec2 a_texCoord; // // attribute vec3 a_id; // encoded uint24 id in byte3 // attribute vec3 a_origin; // attribute vec3 a_tan; @@ -17,8 +17,8 @@ attribute vec2 a_vertex; // attribute vec4 a_transform3; // attribute vec4 a_transform4; #else -layout(location = 0) in vec2 a_vertex; -// layout(location = 1) in vec4 a_texture_coord; // [ texture ll: vec2, ur: vec2 ] +layout(location = 0) in vec3 a_vertex; +layout(location = 1) in vec2 a_texCoord; // [ texture ll: vec2, ur: vec2 ] // layout(location = 2) in vec3 a_id; // encoded uint24 id in byte3 // layout(location = 3) in vec3 a_origin; // layout(location = 4) in vec3 a_tan; @@ -40,29 +40,13 @@ varying vec2 v_texture_coord; void main(void) { - - //TEXTURE COORDS - - // float posX = a_texture_coord[0]; - // float posY = a_texture_coord[1]; - - // float pos2X = a_texture_coord[2]; - // float pos2Y = a_texture_coord[3]; - // vec2 texExt = vec2(pos2X-posX, pos2Y-posY); - - // v_texture_coord = a_vertex; // * texExt + a_texture_coord.xy; - v_texture_coord = a_vertex.xy * 0.5 + 0.5; + v_texture_coord = a_texCoord; //POSITIONING - //quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_vertex) TODO: is this up-to-date? - - //vec4 vertex = vec4(a_vertex, 0.0, 1.0) * (vec4(a_tan, 1.0) + vec4(a_bitan, 1.0)) + vec4(a_origin, 0.0); - vec4 vertex = vec4(a_vertex, 0.0, 1.0); - - // mat4 transform = mat4(a_transform1, a_transform2, a_transform3, a_transform4); + vec4 vertex = vec4(0.003*a_vertex, 1.0); - // vertex = u_viewProjection * transform * vertex; + //vertex = u_viewProjection * vertex; ndcOffset(vertex, u_ndcOffset); gl_Position = vertex; diff --git a/website/js/website.js b/website/js/website.js index c202c04e..c7484488 100644 --- a/website/js/website.js +++ b/website/js/website.js @@ -10,7 +10,7 @@ window.onload = function () { canvas = new gloperate.Canvas('test-canvas'); context = canvas.context; canvas.controller.multiFrameNumber = 1024; - canvas.frameScale = [0.2, 0.2]; + canvas.frameScale = [1.0, 1.0]; renderer = new gloperate.LabelRenderer(); canvas.renderer = renderer; }; From 1899fb1d3ef663cf4ff9b10d932b91386e41954a Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 25 Jun 2018 17:08:20 +0200 Subject: [PATCH 16/92] WIP: changed image loading, still not working (stays black) --- source/LabelGeometry.ts | 11 ++++++-- source/fontloader.ts | 49 +++++++++++++++++++---------------- source/labelrenderer.ts | 3 --- source/shaders/glyphquad.frag | 13 +++++----- source/shaders/glyphquad.vert | 4 +-- 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/source/LabelGeometry.ts b/source/LabelGeometry.ts index a1647653..fb368d02 100644 --- a/source/LabelGeometry.ts +++ b/source/LabelGeometry.ts @@ -55,10 +55,17 @@ export class LabelGeometry extends Geometry { @Initializable.assert_initialized() draw(): void { const gl = this.context.gl; + const count = this._texCoords.length / 2; + // gl.drawElements(gl.TRIANGLE_STRIP, /* TODO */ 4, gl.UNSIGNED_BYTE, 0); - const count = this._texCoords.length / 2; // because a texCoord has 4 values: ll, lr, ul, ur // gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, count); - gl.drawArrays(gl.TRIANGLE_STRIP, 0, count); + + // TODO refactor this for performance! ( = only 1 draw call) + for (let i = 0; i < count; i = i + 4) { + gl.drawArrays(gl.TRIANGLE_STRIP, i, 4); + } + + // gl.drawArrays(gl.TRIANGLE_STRIP, 0, count); } /** diff --git a/source/fontloader.ts b/source/fontloader.ts index 8a8a381c..422104e2 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -5,6 +5,7 @@ import { Context } from './context'; import { FontFace } from './fontface'; import { Glyph } from './glyph'; import { GLfloat2, GLfloat4 } from './tuples'; +import { Texture2 } from './texture2'; type StringPairs = Map; @@ -93,37 +94,41 @@ export class FontLoader { const pngPath: string = path + file.split('.')[0] + '.png'; - const img = new Image(); - img.src = pngPath; - img.onload = () => { - onImageLoad(); + fontFace.glyphTexture.load(pngPath).then( - const gl = context.gl; + function fulfilled(img) { + onImageLoad(); + const gl = context.gl; - fontFace.glyphTexture.bind(); + fontFace.glyphTexture.bind(); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); - const w: number = img.naturalWidth; - const h: number = img.naturalHeight; + // const w: number = img.naturalWidth; + // const h: number = img.naturalHeight; - fontFace.glyphTexture.reformat(gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, false, false); - fontFace.glyphTexture.resize(w, h, false, false); - fontFace.glyphTexture.data(img, false, false); + fontFace.glyphTexture.reformat(gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, false, false); + // fontFace.glyphTexture.resize(w, h, false, false); + // fontFace.glyphTexture.data(img, false, false); - /** - * mipmaps are usually a good idea, but: using Fonts based on Distance Fields and super sampling - * (accumulation frames), the rendered Glyph looks more readable when not using mipmaps. - * Maybe manually provide mipmaps; smaller than 8x8 could be blank (can't render glyphs that small?) - */ - fontFace.glyphTexture.filter(gl.LINEAR, gl.LINEAR, false, false); - fontFace.glyphTexture.wrap(gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE, false, false); + /** + * mipmaps are usually a good idea, but: using Fonts based on Distance Fields and super sampling + * (accumulation frames), the rendered Glyph looks more readable when not using mipmaps. + * Maybe manually provide mipmaps; smaller than 8x8 could be blank (can't render glyphs that small?) + */ + fontFace.glyphTexture.filter(gl.LINEAR, gl.LINEAR, false, false); + fontFace.glyphTexture.wrap(gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE, false, false); - fontFace.glyphTexture.unbind(); + fontFace.glyphTexture.unbind(); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); - }; + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + }, + function rejected() { + console.error('ERROR: Could not load glyph Texture. Image was not found?'); + } + ); } protected handleChar(stream: Array, fontFace: FontFace): void { diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index cee3f533..371fb27d 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -301,9 +301,6 @@ export class LabelRenderer extends Renderer { texCoords.push(v.uvRect[3]); } - console.log(this._frameSize); - - this._labelGeometry.setVertices(Float32Array.from(vertices)); this._labelGeometry.setTexCoords(Float32Array.from(texCoords)); } diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index e6bf92d2..ea0c2f13 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -18,7 +18,8 @@ void main(void) { //requires blend: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - float d = texture(u_glyphs, v_texture_coord).r; + // float d = texture(u_glyphs, v_texture_coord).r; + vec3 texColor = texture(u_glyphs, v_texture_coord).rgb; /** * using if-statement and discard can slow down performance: * it's bad for IMR, TBR, TBDR and early-Z optimization @@ -29,11 +30,11 @@ void main(void) // discard; vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green - // vec4 fc = vec4(d, d, d, 1.0); //debug texture + // fc = vec4(d, d, d, 1.0); //debug texture + fc = vec4(texColor, 1.0); //debug texture // TODO mipmap access? - float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur - - fragColor = vec4(fc.rgb, fc.a * a); - fragColor = vec4(fc.rgb, 1.0); + //float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur + //fragColor = vec4(fc.rgb, fc.a * a); + fragColor = vec4(texColor, 1.0); } diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 1d507eb7..5c78ca64 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -43,8 +43,8 @@ void main(void) v_texture_coord = a_texCoord; //POSITIONING - - vec4 vertex = vec4(0.003*a_vertex, 1.0); + // magic numbers for debugging purpose, as there is no meaningful positioning yet. + vec4 vertex = vec4(0.002*a_vertex + vec3(-0.8,0,0), 1.0); //vertex = u_viewProjection * vertex; From fbb905ce621b8127b90658c160f7545d198b7207 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Tue, 26 Jun 2018 11:56:01 +0200 Subject: [PATCH 17/92] texture coordinates seem right now. --- source/LabelGeometry.ts | 9 +++++---- source/fontloader.ts | 27 +-------------------------- source/labelrenderer.ts | 3 ++- source/shaders/glyphquad.frag | 17 ++++++++--------- source/shaders/glyphquad.vert | 2 +- 5 files changed, 17 insertions(+), 41 deletions(-) diff --git a/source/LabelGeometry.ts b/source/LabelGeometry.ts index fb368d02..fbdf0d73 100644 --- a/source/LabelGeometry.ts +++ b/source/LabelGeometry.ts @@ -37,7 +37,7 @@ export class LabelGeometry extends Geometry { protected bindBuffers(indices: Array): void { /* Please note the implicit bind in attribEnable */ this._buffers[0].attribEnable(indices[0], 3, this.context.gl.FLOAT, false, 0, 0, true, false); - this._buffers[1].bind(); + this._buffers[1].attribEnable(indices[1], 2, this.context.gl.FLOAT, false, 0, 0, true, false); } /** @@ -46,7 +46,7 @@ export class LabelGeometry extends Geometry { protected unbindBuffers(indices: Array): void { /* Please note the implicit unbind in attribEnable is skipped */ this._buffers[0].attribDisable(indices[0], true, true); - this._buffers[1].unbind(); + this._buffers[1].attribDisable(indices[1], true, true); } /** @@ -72,13 +72,14 @@ export class LabelGeometry extends Geometry { * Creates the vertex buffer object (VBO) and creates and initializes the buffer's data store. * // TODO doesnt really initialize the data! * @param aVertex - Attribute binding point for vertices. + * @param aTexCoord - Attribute binding point for texture coordinates. */ - initialize(aVertex: GLuint): boolean { + initialize(aVertex: GLuint, aTexCoord: GLuint): boolean { const gl = this.context.gl; // TODO: do not bind index to location 4 // why not? - const valid = super.initialize([gl.ARRAY_BUFFER, gl.ARRAY_BUFFER], [aVertex, 8]); + const valid = super.initialize([gl.ARRAY_BUFFER, gl.ARRAY_BUFFER], [aVertex, aTexCoord]); return valid; } diff --git a/source/fontloader.ts b/source/fontloader.ts index 422104e2..dd17dfa3 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -97,33 +97,8 @@ export class FontLoader { fontFace.glyphTexture.load(pngPath).then( - function fulfilled(img) { + function fulfilled() { onImageLoad(); - const gl = context.gl; - - fontFace.glyphTexture.bind(); - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); - - // const w: number = img.naturalWidth; - // const h: number = img.naturalHeight; - - fontFace.glyphTexture.reformat(gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, false, false); - // fontFace.glyphTexture.resize(w, h, false, false); - // fontFace.glyphTexture.data(img, false, false); - - /** - * mipmaps are usually a good idea, but: using Fonts based on Distance Fields and super sampling - * (accumulation frames), the rendered Glyph looks more readable when not using mipmaps. - * Maybe manually provide mipmaps; smaller than 8x8 could be blank (can't render glyphs that small?) - */ - fontFace.glyphTexture.filter(gl.LINEAR, gl.LINEAR, false, false); - fontFace.glyphTexture.wrap(gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE, false, false); - - fontFace.glyphTexture.unbind(); - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); - }, function rejected() { console.error('ERROR: Could not load glyph Texture. Image was not found?'); diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 371fb27d..ea6efdff 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -91,7 +91,8 @@ export class LabelRenderer extends Renderer { this._labelGeometry = new LabelGeometry(this._context); const aVertex = this._program.attribute('a_vertex', 0); - this._labelGeometry.initialize(aVertex); + const aTexCoord = this._program.attribute('a_texCoord', 1); + this._labelGeometry.initialize(aVertex, aTexCoord); // this._ndcTriangle = new NdcFillingTriangle(this._context); // const aVertex = this._program.attribute('a_vertex', 0); diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index ea0c2f13..dfdb59bb 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -18,23 +18,22 @@ void main(void) { //requires blend: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // float d = texture(u_glyphs, v_texture_coord).r; - vec3 texColor = texture(u_glyphs, v_texture_coord).rgb; + float d = texture(u_glyphs, v_texture_coord).r; + /** * using if-statement and discard can slow down performance: * it's bad for IMR, TBR, TBDR and early-Z optimization * https://stackoverflow.com/questions/8509051/is-discard-bad-for-program-performance-in-opengl * But it is necessary: overlapping glyphs (like in 'ft') should not fight each other */ - // if(d < 0.45) - // discard; + if(d < 0.45) + discard; vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green - // fc = vec4(d, d, d, 1.0); //debug texture - fc = vec4(texColor, 1.0); //debug texture + // TODO mipmap access? - //float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur - //fragColor = vec4(fc.rgb, fc.a * a); - fragColor = vec4(texColor, 1.0); + float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur + fragColor = vec4(fc.rgb, fc.a * a); + } diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 5c78ca64..28e0df38 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -40,7 +40,7 @@ varying vec2 v_texture_coord; void main(void) { - v_texture_coord = a_texCoord; + v_texture_coord = vec2(a_texCoord.x, 1.0-a_texCoord.y); //POSITIONING // magic numbers for debugging purpose, as there is no meaningful positioning yet. From 549c57b7391cfea2a7f6390a56214a0a5ba27a20 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Tue, 26 Jun 2018 16:55:39 +0200 Subject: [PATCH 18/92] removed codes in comments --- source/labelrenderer.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index ea6efdff..d18802da 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -10,7 +10,6 @@ import { Context } from './context'; import { DefaultFramebuffer } from './defaultframebuffer'; import { Framebuffer } from './framebuffer'; import { MouseEventProvider } from './mouseeventprovider'; -// import { NdcFillingTriangle } from './ndcfillingtriangle'; import { Program } from './program'; import { Renderbuffer } from './renderbuffer'; import { Invalidate, Renderer } from './renderer'; @@ -27,7 +26,6 @@ import { Typesetter } from './typesetter'; import { TestNavigation } from './debug/testnavigation'; - export class LabelRenderer extends Renderer { protected _extensions = false; @@ -36,7 +34,6 @@ export class LabelRenderer extends Renderer { protected _ndcOffsetKernel: AntiAliasingKernel; protected _uNdcOffset: WebGLUniformLocation; protected _uFrameNumber: WebGLUniformLocation; - // protected _ndcTriangle: NdcFillingTriangle; protected _accumulate: AccumulatePass; protected _blit: BlitPass; @@ -76,11 +73,9 @@ export class LabelRenderer extends Renderer { const vert = new Shader(this._context, gl.VERTEX_SHADER, 'glyphquad.vert'); vert.initialize(require('./shaders/glyphquad.vert')); - const frag = new Shader(this._context, gl.FRAGMENT_SHADER, 'glyphquad.frag'); frag.initialize(require('./shaders/glyphquad.frag')); - this._program = new Program(this._context); this._program.initialize([vert, frag]); @@ -94,10 +89,6 @@ export class LabelRenderer extends Renderer { const aTexCoord = this._program.attribute('a_texCoord', 1); this._labelGeometry.initialize(aVertex, aTexCoord); - // this._ndcTriangle = new NdcFillingTriangle(this._context); - // const aVertex = this._program.attribute('a_vertex', 0); - // this._ndcTriangle.initialize(aVertex); - this._ndcOffsetKernel = new AntiAliasingKernel(this._multiFrameNumber); /* Create framebuffers, textures, and render buffers. */ @@ -113,7 +104,7 @@ export class LabelRenderer extends Renderer { /* Create and configure accumulation pass. */ this._accumulate = new AccumulatePass(this._context); - this._accumulate.initialize(/*this._ndcTriangle*/); + this._accumulate.initialize(); this._accumulate.precision = this._framePrecision; this._accumulate.texture = this._colorRenderTexture; // this._accumulate.depthStencilAttachment = this._depthRenderbuffer; @@ -121,7 +112,7 @@ export class LabelRenderer extends Renderer { /* Create and configure blit pass. */ this._blit = new BlitPass(this._context); - this._blit.initialize(/*this._ndcTriangle*/); + this._blit.initialize(); this._blit.readBuffer = gl2facade.COLOR_ATTACHMENT0; this._blit.drawBuffer = gl.BACK; this._blit.target = this._defaultFBO; @@ -141,8 +132,6 @@ export class LabelRenderer extends Renderer { this._uGlyphAtlas = -1; this._program.uninitialize(); - // this._ndcTriangle.uninitialize(); - this._intermediateFBO.uninitialize(); this._defaultFBO.uninitialize(); this._colorRenderTexture.uninitialize(); @@ -223,8 +212,6 @@ export class LabelRenderer extends Renderer { this._labelGeometry.bind(); this._labelGeometry.draw(); - // this._ndcTriangle.bind(); - // this._ndcTriangle.draw(); this._intermediateFBO.unbind(); this._accumulate.frame(frameNumber); From dd7cb9409c4417571a0569bd9c2944cd79d800a4 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Tue, 26 Jun 2018 17:22:17 +0200 Subject: [PATCH 19/92] introduced camera, fixes aspect ratio --- source/labelrenderer.ts | 18 ++++++++++++++++++ source/shaders/glyphquad.vert | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index d18802da..70126e1d 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -6,6 +6,7 @@ import { mat4, vec3, vec4 } from 'gl-matrix'; import { AccumulatePass } from './accumulatepass'; import { AntiAliasingKernel } from './antialiasingkernel'; import { BlitPass } from './blitpass'; +import { Camera } from './camera'; import { Context } from './context'; import { DefaultFramebuffer } from './defaultframebuffer'; import { Framebuffer } from './framebuffer'; @@ -38,6 +39,9 @@ export class LabelRenderer extends Renderer { protected _accumulate: AccumulatePass; protected _blit: BlitPass; + protected _camera: Camera; + protected _uViewProjection: WebGLUniformLocation; + protected _defaultFBO: DefaultFramebuffer; protected _colorRenderTexture: Texture2; protected _depthRenderbuffer: Renderbuffer; @@ -81,6 +85,7 @@ export class LabelRenderer extends Renderer { this._uNdcOffset = this._program.uniform('u_ndcOffset'); this._uFrameNumber = this._program.uniform('u_frameNumber'); + this._uViewProjection = this._program.uniform('u_viewProjection'); this._uGlyphAtlas = this._program.uniform('u_glyphs'); @@ -119,6 +124,13 @@ export class LabelRenderer extends Renderer { /* Create and configure test navigation. */ + this._camera = new Camera(); + this._camera.center = vec3.fromValues(0.0, 0.0, 0.0); + this._camera.up = vec3.fromValues(0.0, 1.0, 0.0); + this._camera.eye = vec3.fromValues(0.0, 0.0, 2.0); + this._camera.near = 0.1; + this._camera.far = 8.0; + this._testNavigation = new TestNavigation(() => this.invalidate(), mouseEventProvider); return true; @@ -170,8 +182,12 @@ export class LabelRenderer extends Renderer { this._intermediateFBO.initialize([[gl2facade.COLOR_ATTACHMENT0, this._colorRenderTexture] , [gl.DEPTH_ATTACHMENT, this._depthRenderbuffer]]); + this._camera.aspect = this._frameSize[0] / this._frameSize[1]; + } else if (this._altered.frameSize) { this._intermediateFBO.resize(this._frameSize[0], this._frameSize[1]); + this._camera.viewport = [this._frameSize[0], this._frameSize[1]]; + this._camera.aspect = this._frameSize[0] / this._frameSize[1]; } if (this._altered.clearColor) { @@ -204,6 +220,8 @@ export class LabelRenderer extends Renderer { gl.uniform2fv(this._uNdcOffset, ndcOffset); gl.uniform1i(this._uFrameNumber, frameNumber); + gl.uniformMatrix4fv(this._uViewProjection, gl.GL_FALSE, this._camera.viewProjection); + this._fontFace.glyphTexture.bind(gl.TEXTURE0); gl.uniform1i(this._uGlyphAtlas, 0); diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 28e0df38..3287e252 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -30,7 +30,7 @@ layout(location = 1) in vec2 a_texCoord; // [ texture ll: vec2, ur: vec2 ] // layout(location = 10) in vec4 a_transform4; #endif -// uniform mat4 u_viewProjection; +uniform mat4 u_viewProjection; uniform vec2 u_ndcOffset; varying vec2 v_texture_coord; @@ -46,7 +46,7 @@ void main(void) // magic numbers for debugging purpose, as there is no meaningful positioning yet. vec4 vertex = vec4(0.002*a_vertex + vec3(-0.8,0,0), 1.0); - //vertex = u_viewProjection * vertex; + vertex = u_viewProjection * vertex; ndcOffset(vertex, u_ndcOffset); gl_Position = vertex; From 57b827039043758ee10171dcd38b771ec2aa1018 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Tue, 26 Jun 2018 17:31:28 +0200 Subject: [PATCH 20/92] Deleted outdated comments. No functional changes. --- source/LabelGeometry.ts | 2 -- source/fontloader.ts | 2 +- source/labelrenderer.ts | 20 ++++++++------------ source/shaders/glyphquad.frag | 1 - source/shaders/glyphquad.vert | 22 ++-------------------- 5 files changed, 11 insertions(+), 36 deletions(-) diff --git a/source/LabelGeometry.ts b/source/LabelGeometry.ts index fbdf0d73..39ce5f64 100644 --- a/source/LabelGeometry.ts +++ b/source/LabelGeometry.ts @@ -94,7 +94,6 @@ export class LabelGeometry extends Geometry { const gl = this.context.gl; // TODO: is DYNAMIC_DRAW more appropriate? this._buffers[0].data(this._vertices, gl.STATIC_DRAW); - } setTexCoords(data: Float32Array): void { @@ -109,5 +108,4 @@ export class LabelGeometry extends Geometry { this._buffers[1].data(this._texCoords, gl.STATIC_DRAW); } - } diff --git a/source/fontloader.ts b/source/fontloader.ts index dd17dfa3..f29c057c 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -5,7 +5,6 @@ import { Context } from './context'; import { FontFace } from './fontface'; import { Glyph } from './glyph'; import { GLfloat2, GLfloat4 } from './tuples'; -import { Texture2 } from './texture2'; type StringPairs = Map; @@ -200,6 +199,7 @@ export class FontLoader { const fontFace = new FontFace(context); // asynchronous loading + // TODO: refactoring using Promise? xmlhttp.open('GET', filename, true); xmlhttp.onreadystatechange = () => { if (xmlhttp.readyState === 4) { diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 70126e1d..b2cd9d84 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -27,6 +27,12 @@ import { Typesetter } from './typesetter'; import { TestNavigation } from './debug/testnavigation'; +/** + * This is ugly, but it should do the trick for now: + * Later, we want to have a labelrenderpass and a labelpositionpass. + * The first one bakes the geometry, the second one adapts the placement regarding dynamic placement algorithms. + * For now, we will have both as a labelrenderer, and split it up later. + */ export class LabelRenderer extends Renderer { protected _extensions = false; @@ -262,11 +268,9 @@ export class LabelRenderer extends Renderer { protected setupScene(): void { // create Label with Text and - // tell the Typesetter to typeset that Label with the loaded FontFace (using Glyph or Glyph Vertices) + // tell the Typesetter to typeset that Label with the loaded FontFace const glyphVertices = this.prepareLabel('Hello World!'); - // make a Geometry out of those vertices (or find another way of sending vertices to shader) - // TODO labelgeometry const vertices: Array = []; const texCoords: Array = []; @@ -311,7 +315,7 @@ export class LabelRenderer extends Renderer { this._labelGeometry.setTexCoords(Float32Array.from(texCoords)); } - protected prepareLabel(/*userTransform: mat4,*/ str: string /*other params*/): GlyphVertices { + protected prepareLabel(/*userTransform: mat4,*/ str: string): GlyphVertices { const testLabel: Label = new Label(new Text(str), this._fontFace); @@ -344,7 +348,6 @@ export class LabelRenderer extends Renderer { const numGlyphs = testLabel.length; - // prepare vertex storage (values will be overridden by typesetter) const vertices = new GlyphVertices(); for (let i = 0; i < numGlyphs; ++i) { @@ -363,13 +366,6 @@ export class LabelRenderer extends Renderer { return vertices; } - - /** - * This is ugly, but it should do the trick for now. - * Later, we want to have a labelrenderpass and a labelpositionpass. - * The first one bakes the geometry, the second one adapts the placement regarding dynamic placement algorithms. - * For now, we will have both as a labelrenderer, and split it up later. - */ } diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index dfdb59bb..a86ee962 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -31,7 +31,6 @@ void main(void) vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green - // TODO mipmap access? float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur fragColor = vec4(fc.rgb, fc.a * a); diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 3287e252..636e8755 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -6,28 +6,10 @@ precision lowp int; #if __VERSION__ == 100 #extension GL_EXT_draw_buffers : enable attribute vec3 a_vertex; -attribute vec2 a_texCoord; // -// attribute vec3 a_id; // encoded uint24 id in byte3 -// attribute vec3 a_origin; -// attribute vec3 a_tan; -// attribute vec3 a_bitan; - -// attribute vec4 a_transform1; -// attribute vec4 a_transform2; -// attribute vec4 a_transform3; -// attribute vec4 a_transform4; +attribute vec2 a_texCoord; #else layout(location = 0) in vec3 a_vertex; -layout(location = 1) in vec2 a_texCoord; // [ texture ll: vec2, ur: vec2 ] -// layout(location = 2) in vec3 a_id; // encoded uint24 id in byte3 -// layout(location = 3) in vec3 a_origin; -// layout(location = 4) in vec3 a_tan; -// layout(location = 5) in vec3 a_bitan; - -// layout(location = 7) in vec4 a_transform1; -// layout(location = 8) in vec4 a_transform2; -// layout(location = 9) in vec4 a_transform3; -// layout(location = 10) in vec4 a_transform4; +layout(location = 1) in vec2 a_texCoord; #endif uniform mat4 u_viewProjection; From 0342bf2c2c80df5cb0de822b90a78272cb35b074 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Wed, 27 Jun 2018 10:30:34 +0200 Subject: [PATCH 21/92] build: copy data dir --- pugconfig.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pugconfig.js b/pugconfig.js index af67fc99..42d1dab0 100644 --- a/pugconfig.js +++ b/pugconfig.js @@ -18,7 +18,10 @@ const distDir = './build'; const entries = ['index.pug']; const assets = [ - [websiteDir, distDir, ['css/*.css', 'js/*.js', 'img/*.{svg,png}', 'fonts/*', '*.{svg,png,ico,xml,json}'], [], false]]; + [websiteDir, distDir, ['css/*.css', 'js/*.js', 'img/*.{svg,png}', 'fonts/*', '*.{svg,png,ico,xml,json}'], [], false], + ['./source/data/', distDir+'/data', ['*'], [], false], + ['./source/data/opensansr144', distDir+'/data/opensansr144', ['*'], [], false], + ['./source/data/verdana', distDir+'/data/verdana', ['*'], [], false]]; var build_pending = false; From 5536ae03ad0e4f48a9a2570708640e39bbd7d46a Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Wed, 27 Jun 2018 17:14:41 +0200 Subject: [PATCH 22/92] WIP: draw arrays instanced; still not working --- source/LabelGeometry.ts | 88 ++++++++++++++++++++++++++++------- source/labelrenderer.ts | 54 ++++++++++----------- source/shaders/glyphquad.vert | 34 ++++++++++---- 3 files changed, 122 insertions(+), 54 deletions(-) diff --git a/source/LabelGeometry.ts b/source/LabelGeometry.ts index 39ce5f64..a7afe68e 100644 --- a/source/LabelGeometry.ts +++ b/source/LabelGeometry.ts @@ -11,8 +11,13 @@ import { Initializable } from './initializable'; */ export class LabelGeometry extends Geometry { + private _bindIndices: Array; + protected _vertices: Float32Array = new Float32Array(0); protected _texCoords: Float32Array = new Float32Array(0); + protected _origins: Float32Array = new Float32Array(0); + protected _tans: Float32Array = new Float32Array(0); + protected _ups: Float32Array = new Float32Array(0); /** * Object constructor, requires a context and an identifier. @@ -26,18 +31,42 @@ export class LabelGeometry extends Geometry { identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; const vertexVBO = new Buffer(context, identifier + 'VBO'); - const texCoordBuffer = new Buffer(context, identifier + 'TexCoordBuffer'); this._buffers.push(vertexVBO); + const texCoordBuffer = new Buffer(context, identifier + 'TexCoordBuffer'); this._buffers.push(texCoordBuffer); + const originBuffer = new Buffer(context, identifier + 'OriginBuffer'); + this._buffers.push(originBuffer); + const tanBuffer = new Buffer(context, identifier + 'TanBuffer'); + this._buffers.push(tanBuffer); + const upBuffer = new Buffer(context, identifier + 'UpBuffer'); + this._buffers.push(upBuffer); } /** * Binds the vertex buffer object (VBO) to an attribute binding point of a given, pre-defined index. */ protected bindBuffers(indices: Array): void { + const gl = this.context.gl; + const gl2facade = this.context.gl2facade; + + this._bindIndices = indices; + /* Please note the implicit bind in attribEnable */ - this._buffers[0].attribEnable(indices[0], 3, this.context.gl.FLOAT, false, 0, 0, true, false); - this._buffers[1].attribEnable(indices[1], 2, this.context.gl.FLOAT, false, 0, 0, true, false); + // quadVertex + this._buffers[0].attribEnable(indices[0], 2, gl.FLOAT, false, 0, 0, true, false); + gl2facade.vertexAttribDivisor(indices[0], 0); + // texCoords + this._buffers[1].attribEnable(indices[1], 4, gl.FLOAT, false, 4 * 4, 0, true, false); + gl2facade.vertexAttribDivisor(indices[1], 1); + // origin + this._buffers[2].attribEnable(indices[2], 3, gl.FLOAT, false, 3 * 4, 0, true, false); + gl2facade.vertexAttribDivisor(indices[2], 1); + // tan + this._buffers[3].attribEnable(indices[3], 3, gl.FLOAT, false, 3 * 4, 0, true, false); + gl2facade.vertexAttribDivisor(indices[3], 1); + // up + this._buffers[4].attribEnable(indices[4], 3, gl.FLOAT, false, 3 * 4, 0, true, false); + gl2facade.vertexAttribDivisor(indices[4], 1); } /** @@ -45,8 +74,10 @@ export class LabelGeometry extends Geometry { */ protected unbindBuffers(indices: Array): void { /* Please note the implicit unbind in attribEnable is skipped */ - this._buffers[0].attribDisable(indices[0], true, true); - this._buffers[1].attribDisable(indices[1], true, true); + const l = this._buffers.length; + for (let i = 0; i < l; i++) { + this._buffers[i].attribDisable(indices[i], true, true); + } } /** @@ -55,15 +86,20 @@ export class LabelGeometry extends Geometry { @Initializable.assert_initialized() draw(): void { const gl = this.context.gl; - const count = this._texCoords.length / 2; + const count = this._origins.length / 3; // gl.drawElements(gl.TRIANGLE_STRIP, /* TODO */ 4, gl.UNSIGNED_BYTE, 0); // gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, count); - // TODO refactor this for performance! ( = only 1 draw call) - for (let i = 0; i < count; i = i + 4) { - gl.drawArrays(gl.TRIANGLE_STRIP, i, 4); - } + this.bindBuffers(this._bindIndices); + + this.context.gl2facade.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, count); + + this.unbindBuffers(this._bindIndices); + + // for (let i = 0; i < count; i = i + 4) { + // gl.drawArrays(gl.TRIANGLE_STRIP, i, 4); + // } // gl.drawArrays(gl.TRIANGLE_STRIP, 0, count); } @@ -71,29 +107,47 @@ export class LabelGeometry extends Geometry { /** * Creates the vertex buffer object (VBO) and creates and initializes the buffer's data store. * // TODO doesnt really initialize the data! - * @param aVertex - Attribute binding point for vertices. + * @param aQuadVertex - Attribute binding point for vertices. * @param aTexCoord - Attribute binding point for texture coordinates. + * @param aOrigin - Attribute binding point for glyph origin coordinates + * @param aTan - Attribute binding point for glyph tangent coordinates. + * @param aUp - Attribute binding point for glyph up-vector coordinates. */ - initialize(aVertex: GLuint, aTexCoord: GLuint): boolean { + initialize(aQuadVertex: GLuint, aTexCoord: GLuint, aOrigin: GLuint, aTan: GLuint, aUp: GLuint): boolean { const gl = this.context.gl; // TODO: do not bind index to location 4 // why not? - const valid = super.initialize([gl.ARRAY_BUFFER, gl.ARRAY_BUFFER], [aVertex, aTexCoord]); + const valid = super.initialize( + [gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER] + , [aQuadVertex, aTexCoord, aOrigin, aTan, aUp]); + + // These vertices are equal for all quads. There actual position will be changed using + // origin, tan(gent) and up(-vector). + this._vertices = Float32Array.from([0, 0, 0, 1, 1, 0, 1, 1]); return valid; } - setVertices(data: Float32Array): void { + setGlyphCoords(dataOrigin: Float32Array, dataTan: Float32Array, dataUp: Float32Array): void { - assert(this._buffers[0] !== undefined && this._buffers[0].object instanceof WebGLBuffer, + assert(this._buffers[2] !== undefined && this._buffers[0].object instanceof WebGLBuffer, `expected valid WebGLBuffer`); + assert(this._buffers[3] !== undefined && this._buffers[0].object instanceof WebGLBuffer, + `expected valid WebGLBuffer`); + assert(this._buffers[4] !== undefined && this._buffers[0].object instanceof WebGLBuffer, + `expected valid WebGLBuffer`); + - this._vertices = data; + this._origins = dataOrigin; + this._tans = dataTan; + this._ups = dataUp; const gl = this.context.gl; // TODO: is DYNAMIC_DRAW more appropriate? - this._buffers[0].data(this._vertices, gl.STATIC_DRAW); + this._buffers[2].data(this._origins, gl.STATIC_DRAW); + this._buffers[3].data(this._tans, gl.STATIC_DRAW); + this._buffers[4].data(this._ups, gl.STATIC_DRAW); } setTexCoords(data: Float32Array): void { diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index b2cd9d84..7d7bd329 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -96,9 +96,13 @@ export class LabelRenderer extends Renderer { this._uGlyphAtlas = this._program.uniform('u_glyphs'); this._labelGeometry = new LabelGeometry(this._context); - const aVertex = this._program.attribute('a_vertex', 0); + const aVertex = this._program.attribute('a_quadVertex', 0); const aTexCoord = this._program.attribute('a_texCoord', 1); - this._labelGeometry.initialize(aVertex, aTexCoord); + const aOrigin = this._program.attribute('a_origin', 2); + const aTan = this._program.attribute('a_tan', 3); + const aUp = this._program.attribute('a_up', 4); + + this._labelGeometry.initialize(aVertex, aTexCoord, aOrigin, aTan, aUp); this._ndcOffsetKernel = new AntiAliasingKernel(this._multiFrameNumber); @@ -235,6 +239,7 @@ export class LabelRenderer extends Renderer { this._labelGeometry.bind(); this._labelGeometry.draw(); + this._labelGeometry.unbind(); this._intermediateFBO.unbind(); @@ -271,47 +276,38 @@ export class LabelRenderer extends Renderer { // tell the Typesetter to typeset that Label with the loaded FontFace const glyphVertices = this.prepareLabel('Hello World!'); - const vertices: Array = []; + const origins: Array = []; + const tans: Array = []; + const ups: Array = []; const texCoords: Array = []; const l = glyphVertices.length; for (let i = 0; i < l; i++) { + // TODO: shouldn't there be an easier way to achieve this? + // concat doesn't work as vec3 apparently is not an Array. + const v = glyphVertices[i]; - // ll - vertices.push(v.origin[0]); - vertices.push(v.origin[1]); - vertices.push(v.origin[2]); - texCoords.push(v.uvRect[0]); - texCoords.push(v.uvRect[1]); + origins.push(v.origin[0]); + origins.push(v.origin[1]); + origins.push(v.origin[2]); - const lr = vec3.create(); - vec3.add(lr, v.origin, v.tangent); - vertices.push(lr[0]); - vertices.push(lr[1]); - vertices.push(lr[2]); - texCoords.push(v.uvRect[2]); - texCoords.push(v.uvRect[1]); + tans.push(v.tangent[0]); + tans.push(v.tangent[1]); + tans.push(v.tangent[2]); - const ul = vec3.create(); - vec3.add(ul, v.origin, v.up); - vertices.push(ul[0]); - vertices.push(ul[1]); - vertices.push(ul[2]); - texCoords.push(v.uvRect[0]); - texCoords.push(v.uvRect[3]); + ups.push(v.up[0]); + ups.push(v.up[1]); + ups.push(v.up[2]); - const ur = vec3.create(); - vec3.add(ur, lr, v.up); - vertices.push(ur[0]); - vertices.push(ur[1]); - vertices.push(ur[2]); + texCoords.push(v.uvRect[0]); + texCoords.push(v.uvRect[1]); texCoords.push(v.uvRect[2]); texCoords.push(v.uvRect[3]); } - this._labelGeometry.setVertices(Float32Array.from(vertices)); + this._labelGeometry.setGlyphCoords(Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); this._labelGeometry.setTexCoords(Float32Array.from(texCoords)); } diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 636e8755..bde0478c 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -5,11 +5,17 @@ precision lowp int; #if __VERSION__ == 100 #extension GL_EXT_draw_buffers : enable -attribute vec3 a_vertex; -attribute vec2 a_texCoord; +attribute vec2 a_quadVertex; +attribute vec4 a_texCoord; // [ texture ll: vec2, ur: vec2 ] +attribute vec3 a_origin; +attribute vec3 a_tan; +attribute vec3 a_up; #else -layout(location = 0) in vec3 a_vertex; -layout(location = 1) in vec2 a_texCoord; +layout(location = 0) in vec2 a_quadVertex; +layout(location = 1) in vec4 a_texCoord; // [ texture ll: vec2, ur: vec2 ] +layout(location = 2) in vec3 a_origin; +layout(location = 3) in vec3 a_tan; +layout(location = 4) in vec3 a_up; #endif uniform mat4 u_viewProjection; @@ -17,18 +23,30 @@ uniform vec2 u_ndcOffset; varying vec2 v_texture_coord; - @import ./ndcoffset; void main(void) { - v_texture_coord = vec2(a_texCoord.x, 1.0-a_texCoord.y); + //TEXTURE COORDS + + float posX = a_texCoord[0]; + float posY = a_texCoord[1]; + + float pos2X = a_texCoord[2]; + float pos2Y = a_texCoord[3]; + vec2 texExt = vec2(pos2X-posX, pos2Y-posY); + + v_texture_coord = a_quadVertex * texExt + a_texCoord.xy; + //v_texture_coord = vec2(a_texCoord.x, 1.0-a_texCoord.y); //POSITIONING // magic numbers for debugging purpose, as there is no meaningful positioning yet. - vec4 vertex = vec4(0.002*a_vertex + vec3(-0.8,0,0), 1.0); + // vec4 vertex = vec4(0.002*a_vertex + vec3(-0.8,0,0), 1.0); + + //quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) - vertex = u_viewProjection * vertex; + vec4 vertex = vec4(a_quadVertex, 0.0, 1.0) * (vec4(a_tan, 1.0) + vec4(a_up, 1.0)) + vec4(a_origin, 0.0); + vertex = u_viewProjection * ( vertex * 0.002 + vec4(-0.8, 0, 0, 0) ); ndcOffset(vertex, u_ndcOffset); gl_Position = vertex; From 52ae73daab82a07bba87c8953988876a5a949f3a Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Thu, 28 Jun 2018 11:43:21 +0200 Subject: [PATCH 23/92] fixed: draw arrays instanced. --- source/LabelGeometry.ts | 1 + source/labelrenderer.ts | 7 ++++++- source/shaders/glyphquad.vert | 15 +++++++-------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/source/LabelGeometry.ts b/source/LabelGeometry.ts index a7afe68e..c2195f95 100644 --- a/source/LabelGeometry.ts +++ b/source/LabelGeometry.ts @@ -125,6 +125,7 @@ export class LabelGeometry extends Geometry { // These vertices are equal for all quads. There actual position will be changed using // origin, tan(gent) and up(-vector). this._vertices = Float32Array.from([0, 0, 0, 1, 1, 0, 1, 1]); + this._buffers[0].data(this._vertices, gl.STATIC_DRAW); return valid; } diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 7d7bd329..86fe2bda 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -222,6 +222,9 @@ export class LabelRenderer extends Renderer { gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + // gl.enable(gl.POLYGON_OFFSET_FILL); + // gl.polygonOffset(-1, 1); // avoid z-fighting with other geometry + this._program.bind(); const ndcOffset = this._ndcOffsetKernel.get(frameNumber); @@ -250,6 +253,8 @@ export class LabelRenderer extends Renderer { if (!wasBlendEnabled) { gl.disable(gl.BLEND); } + + // gl.disable(gl.POLYGON_OFFSET_FILL); } protected onSwap(): void { @@ -307,8 +312,8 @@ export class LabelRenderer extends Renderer { texCoords.push(v.uvRect[3]); } - this._labelGeometry.setGlyphCoords(Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); this._labelGeometry.setTexCoords(Float32Array.from(texCoords)); + this._labelGeometry.setGlyphCoords(Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); } protected prepareLabel(/*userTransform: mat4,*/ str: string): GlyphVertices { diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index bde0478c..5e7cb355 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -28,25 +28,24 @@ varying vec2 v_texture_coord; void main(void) { //TEXTURE COORDS + // flip y coordinates float posX = a_texCoord[0]; float posY = a_texCoord[1]; float pos2X = a_texCoord[2]; float pos2Y = a_texCoord[3]; - vec2 texExt = vec2(pos2X-posX, pos2Y-posY); + vec2 texExt = vec2(pos2X-posX, posY-pos2Y); - v_texture_coord = a_quadVertex * texExt + a_texCoord.xy; - //v_texture_coord = vec2(a_texCoord.x, 1.0-a_texCoord.y); + v_texture_coord = a_quadVertex * texExt + vec2(a_texCoord[0], 1.0-a_texCoord[1]); //POSITIONING - // magic numbers for debugging purpose, as there is no meaningful positioning yet. - // vec4 vertex = vec4(0.002*a_vertex + vec3(-0.8,0,0), 1.0); + // quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) - //quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) - vec4 vertex = vec4(a_quadVertex, 0.0, 1.0) * (vec4(a_tan, 1.0) + vec4(a_up, 1.0)) + vec4(a_origin, 0.0); - vertex = u_viewProjection * ( vertex * 0.002 + vec4(-0.8, 0, 0, 0) ); + vec4 vertex = a_quadVertex.x*vec4(a_tan, 1.0) + a_quadVertex.y*vec4(a_up, 1.0) + vec4(a_origin, 0.0); + // magic numbers for debugging purpose, as there is no meaningful positioning yet. + vertex = u_viewProjection * ( vec4(vec3(vertex * 0.001), 1.0) + vec4(-0.8, 0, 0, 0) ); ndcOffset(vertex, u_ndcOffset); gl_Position = vertex; From d7a343c72df98168312f00a9a2cce7906fafd704 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Thu, 28 Jun 2018 14:43:21 +0200 Subject: [PATCH 24/92] doc; cleaned up unused / unnecessary code --- source/LabelGeometry.ts | 30 +++++++++++------------------- source/shaders/glyphquad.frag | 2 +- source/shaders/glyphquad.vert | 3 +-- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/source/LabelGeometry.ts b/source/LabelGeometry.ts index c2195f95..d99a0a3c 100644 --- a/source/LabelGeometry.ts +++ b/source/LabelGeometry.ts @@ -11,8 +11,6 @@ import { Initializable } from './initializable'; */ export class LabelGeometry extends Geometry { - private _bindIndices: Array; - protected _vertices: Float32Array = new Float32Array(0); protected _texCoords: Float32Array = new Float32Array(0); protected _origins: Float32Array = new Float32Array(0); @@ -49,8 +47,6 @@ export class LabelGeometry extends Geometry { const gl = this.context.gl; const gl2facade = this.context.gl2facade; - this._bindIndices = indices; - /* Please note the implicit bind in attribEnable */ // quadVertex this._buffers[0].attribEnable(indices[0], 2, gl.FLOAT, false, 0, 0, true, false); @@ -88,25 +84,11 @@ export class LabelGeometry extends Geometry { const gl = this.context.gl; const count = this._origins.length / 3; - // gl.drawElements(gl.TRIANGLE_STRIP, /* TODO */ 4, gl.UNSIGNED_BYTE, 0); - // gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, count); - - this.bindBuffers(this._bindIndices); - this.context.gl2facade.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, count); - - this.unbindBuffers(this._bindIndices); - - // for (let i = 0; i < count; i = i + 4) { - // gl.drawArrays(gl.TRIANGLE_STRIP, i, 4); - // } - - // gl.drawArrays(gl.TRIANGLE_STRIP, 0, count); } /** * Creates the vertex buffer object (VBO) and creates and initializes the buffer's data store. - * // TODO doesnt really initialize the data! * @param aQuadVertex - Attribute binding point for vertices. * @param aTexCoord - Attribute binding point for texture coordinates. * @param aOrigin - Attribute binding point for glyph origin coordinates @@ -117,7 +99,6 @@ export class LabelGeometry extends Geometry { const gl = this.context.gl; - // TODO: do not bind index to location 4 // why not? const valid = super.initialize( [gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER] , [aQuadVertex, aTexCoord, aOrigin, aTan, aUp]); @@ -130,6 +111,13 @@ export class LabelGeometry extends Geometry { return valid; } + /** + * Use this method to set (or update) the glyph coordinates, e.g. after typesetting or after the calculations + * of a placement algorithm. + * @param dataOrigin xyz-coordinates of the lower left corner of every glyph + * @param dataTan tangent vector for every glyph (direction along base line) + * @param dataUp up vector for every glyph (orthogonal to its tangent vector) + */ setGlyphCoords(dataOrigin: Float32Array, dataTan: Float32Array, dataUp: Float32Array): void { assert(this._buffers[2] !== undefined && this._buffers[0].object instanceof WebGLBuffer, @@ -151,6 +139,10 @@ export class LabelGeometry extends Geometry { this._buffers[4].data(this._ups, gl.STATIC_DRAW); } + /** + * Use this method to set (or update) the texture coordinates for every glyph, e.g. after typesetting. + * @param data The texture coordinates for every glyph, format: ll.x, ll.y, ur.x, ur.y + */ setTexCoords(data: Float32Array): void { assert(this._buffers[1] !== undefined && this._buffers[1].object instanceof WebGLBuffer, diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index a86ee962..953fde2a 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -29,7 +29,7 @@ void main(void) if(d < 0.45) discard; - vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green + vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green, TODO: font color as vertex attrib or uniform? // TODO mipmap access? float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 5e7cb355..c8e79b06 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -28,7 +28,7 @@ varying vec2 v_texture_coord; void main(void) { //TEXTURE COORDS - // flip y coordinates + // flip y-coordinates float posX = a_texCoord[0]; float posY = a_texCoord[1]; @@ -42,7 +42,6 @@ void main(void) //POSITIONING // quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) - vec4 vertex = a_quadVertex.x*vec4(a_tan, 1.0) + a_quadVertex.y*vec4(a_up, 1.0) + vec4(a_origin, 0.0); // magic numbers for debugging purpose, as there is no meaningful positioning yet. vertex = u_viewProjection * ( vec4(vec3(vertex * 0.001), 1.0) + vec4(-0.8, 0, 0, 0) ); From 6ffbff6c66fb8ffcccb1c2a955f232f11145c6bc Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Thu, 28 Jun 2018 15:45:48 +0200 Subject: [PATCH 25/92] avoid division by zero when converting from vec4 to vec3 --- source/gl-matrix-extensions.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/gl-matrix-extensions.ts b/source/gl-matrix-extensions.ts index 33bb8126..6c2b57a7 100644 --- a/source/gl-matrix-extensions.ts +++ b/source/gl-matrix-extensions.ts @@ -226,6 +226,11 @@ namespace gl_matrix_extensions { * @returns - Three component vector based on x. */ export function fromVec4(x: vec4): vec3 { + if (x[3] === 0) { + // avoid division by 0 + return vec3.fromValues(x[0], x[1], x[2]); + } + return vec3.fromValues(x[0] / x[3], x[1] / x[3], x[2] / x[3]); } From a897ab8477bf8c363b80f8a2a3e53a3b68e1c8b1 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 29 Jun 2018 11:45:03 +0200 Subject: [PATCH 26/92] 3D-label, kind of. usertransform doesn't work yet --- source/label.ts | 26 +++++++++++++++++-- source/labelrenderer.ts | 49 +++++++++++++++++++++-------------- source/shaders/glyphquad.vert | 6 ++--- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/source/label.ts b/source/label.ts index 181bfc14..3e0443e4 100644 --- a/source/label.ts +++ b/source/label.ts @@ -1,5 +1,5 @@ -import { mat4 } from 'gl-matrix'; +import { mat4, vec3 } from 'gl-matrix'; import { ChangeLookup } from './changelookup'; import { Color } from './color'; @@ -29,6 +29,9 @@ export class Label { /** @see {@link lineWidth} */ protected _lineWidth = 0.0; + /** @see {@link fontSize} */ + protected _fontSize = 50.0; + /** @see {@link fontFace} */ protected _fontFace: FontFace; @@ -45,7 +48,7 @@ export class Label { /** @see {@link altered} */ protected readonly _altered = Object.assign(new ChangeLookup(), { - any: false, color: false, resources: false, text: false, typesetting: false, transformation: false, + any: false, color: false, resources: false, text: false, typesetting: false, transform: false, }); /** @@ -199,6 +202,21 @@ export class Label { return this._lineWidth; } + /** + * Font Size in pt or px // TODO unit?? + */ + set fontSize(newSize: number) { + if (this._fontSize === newSize) { + return; + } + this._altered.alter('typesetting'); + this._altered.alter('transform'); + this._fontSize = newSize; + } + get fontSize(): number { + return this._fontSize; + } + /** * Font face used for typesetting, transformation, and rendering. */ @@ -256,6 +274,10 @@ export class Label { this._transform = transform; } get transform(): mat4 { + + const s = this._fontSize / this._fontFace.size; + mat4.scale(this._transform, this._transform, vec3.fromValues(s, s, s)); + return this._transform; } diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 86fe2bda..4304896a 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -1,7 +1,7 @@ import { assert } from './auxiliaries'; -import { mat4, vec3, vec4 } from 'gl-matrix'; +import { mat4, vec2, vec3, vec4 } from 'gl-matrix'; import { AccumulatePass } from './accumulatepass'; import { AntiAliasingKernel } from './antialiasingkernel'; @@ -320,32 +320,41 @@ export class LabelRenderer extends Renderer { const testLabel: Label = new Label(new Text(str), this._fontFace); - // const margins: vec4 = config.margins; + // TODO meaningful margins from label.margins or config.margins ? + const margins: vec4 = vec4.create(); + // TODO meaningful ppiScale from label.ppiScale or config.ppiScale ? + const ppiScale = 1; - // // compute transform matrix - // let transform = mat4.create(); + // compute transform matrix + const transform = mat4.create(); - // // translate to lower left in NDC - // mat4.translate(transform, transform, vec3.fromValues(-1.0, -1.0, 0.0)); + // translate to lower left in NDC + mat4.translate(transform, transform, vec3.fromValues(-1.0, -1.0, 0.0)); - // // scale glyphs to NDC size - // // this._size was the viewport size in Haeley - // mat4.scale(transform, transform, vec3.fromValues(2.0 / this._size[0], 2.0 / this._size[1], 1.0)); + // scale glyphs to NDC size + // this._frameSize should be the viewport size + mat4.scale(transform, transform, vec3.fromValues(2.0 / this._frameSize[0], 2.0 / this._frameSize[1], 1.0)); - // // scale glyphs to pixel size with respect to the displays ppi - // // mat4.scale(transform, transform, vec3.fromValues(config.ppiScale, config.ppiScale, config.ppiScale)); + // scale glyphs to pixel size with respect to the displays ppi + mat4.scale(transform, transform, vec3.fromValues(ppiScale, ppiScale, ppiScale)); - // // translate to origin in point space - scale origin within - // // margined extend (i.e., viewport with margined areas removed) - // let marginedExtent: vec2 = vec2.create(); - // vec2.sub(marginedExtent, vec2.fromValues(this._size[0] / config.ppiScale, this._size[1] / config.ppiScale), - // vec2.fromValues(margins[3] + margins[1], margins[2] + margins[0])); + // translate to origin in point space - scale origin within + // margined extend (i.e., viewport with margined areas removed) + const marginedExtent: vec2 = vec2.create(); + vec2.sub(marginedExtent, vec2.fromValues( + this._frameSize[0] / ppiScale, this._frameSize[1] / ppiScale), + vec2.fromValues(margins[3] + margins[1], margins[2] + margins[0])); - // let v3 = vec3.fromValues(0.5 * marginedExtent[0], 0.5 * marginedExtent[1], 0); - // vec3.add(v3, v3, vec3.fromValues(margins[3], margins[2], 0.0)); - // mat4.translate(transform, transform, v3); + const v3 = vec3.fromValues(0.5 * marginedExtent[0], 0.5 * marginedExtent[1], 0); + vec3.add(v3, v3, vec3.fromValues(margins[3], margins[2], 0.0)); + mat4.translate(transform, transform, v3); - // sequence.additionalTransform = transform; + // let userTransform = mat4.create(); + // mat4.translate(userTransform, userTransform, vec3.fromValues(-1, 0.0, 0)); + // mat4.rotateZ(userTransform, userTransform, Math.PI * 0.3); + + // testLabel.transform = mat4.mul(testLabel.transform, userTransform, transform); + testLabel.transform = transform; const numGlyphs = testLabel.length; diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index c8e79b06..64534816 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -42,9 +42,9 @@ void main(void) //POSITIONING // quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) - vec4 vertex = a_quadVertex.x*vec4(a_tan, 1.0) + a_quadVertex.y*vec4(a_up, 1.0) + vec4(a_origin, 0.0); - // magic numbers for debugging purpose, as there is no meaningful positioning yet. - vertex = u_viewProjection * ( vec4(vec3(vertex * 0.001), 1.0) + vec4(-0.8, 0, 0, 0) ); + vec4 vertex = vec4(a_quadVertex, 0.0, 1.0) * (vec4(a_tan, 1.0) + vec4(a_up, 1.0)) + vec4(a_origin, 0.0); + + vertex = u_viewProjection * vertex; ndcOffset(vertex, u_ndcOffset); gl_Position = vertex; From 1851cd046e6296db17035c15020c9494f65e9119 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 29 Jun 2018 14:36:30 +0200 Subject: [PATCH 27/92] fix: transform for multiple labels (still seem kind of stretched) --- source/glyphvertices.ts | 4 ++-- source/labelrenderer.ts | 20 ++++++++++++-------- source/shaders/glyphquad.frag | 2 +- source/shaders/glyphquad.vert | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/source/glyphvertices.ts b/source/glyphvertices.ts index c62be1e0..f221cb66 100644 --- a/source/glyphvertices.ts +++ b/source/glyphvertices.ts @@ -35,9 +35,9 @@ export interface GlyphVertex { */ export class GlyphVertices extends Array { - optimize() { + // optimize() { - } + // } } diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 4304896a..439eadfd 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -279,7 +279,15 @@ export class LabelRenderer extends Renderer { // create Label with Text and // tell the Typesetter to typeset that Label with the loaded FontFace - const glyphVertices = this.prepareLabel('Hello World!'); + + const userTransform = mat4.create(); + mat4.translate(userTransform, userTransform, vec3.fromValues(-1, 0.0, 0)); + mat4.rotateX(userTransform, userTransform, Math.PI * -0.4); + mat4.rotateY(userTransform, userTransform, Math.PI * 0.2); + mat4.rotateZ(userTransform, userTransform, Math.PI * 0.5); + let glyphVertices = this.prepareLabel('Hello Transform!', userTransform); + + glyphVertices = glyphVertices.concat(this.prepareLabel('Hello World!')); const origins: Array = []; const tans: Array = []; @@ -316,7 +324,7 @@ export class LabelRenderer extends Renderer { this._labelGeometry.setGlyphCoords(Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); } - protected prepareLabel(/*userTransform: mat4,*/ str: string): GlyphVertices { + protected prepareLabel(str: string, userTransform?: mat4): GlyphVertices { const testLabel: Label = new Label(new Text(str), this._fontFace); @@ -349,12 +357,8 @@ export class LabelRenderer extends Renderer { vec3.add(v3, v3, vec3.fromValues(margins[3], margins[2], 0.0)); mat4.translate(transform, transform, v3); - // let userTransform = mat4.create(); - // mat4.translate(userTransform, userTransform, vec3.fromValues(-1, 0.0, 0)); - // mat4.rotateZ(userTransform, userTransform, Math.PI * 0.3); - - // testLabel.transform = mat4.mul(testLabel.transform, userTransform, transform); - testLabel.transform = transform; + testLabel.transform = mat4.mul(testLabel.transform, + userTransform !== undefined ? userTransform : mat4.create(), transform); const numGlyphs = testLabel.length; diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index 953fde2a..dd82f118 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -27,7 +27,7 @@ void main(void) * But it is necessary: overlapping glyphs (like in 'ft') should not fight each other */ if(d < 0.45) - discard; + discard; vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green, TODO: font color as vertex attrib or uniform? diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 64534816..e6b70e1c 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -42,7 +42,7 @@ void main(void) //POSITIONING // quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) - vec4 vertex = vec4(a_quadVertex, 0.0, 1.0) * (vec4(a_tan, 1.0) + vec4(a_up, 1.0)) + vec4(a_origin, 0.0); + vec4 vertex = vec4(a_origin + a_quadVertex.x*a_tan + a_quadVertex.y*a_up, 1.0); vertex = u_viewProjection * vertex; From 22b6df3241cccf82158976e77f87e2c2ffbc54bb Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 29 Jun 2018 14:37:57 +0200 Subject: [PATCH 28/92] deleted unused+duplicated+commented code. --- source/labelrenderer.ts | 85 ----------------------------------------- 1 file changed, 85 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 439eadfd..88ca0ba1 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -381,88 +381,3 @@ export class LabelRenderer extends Renderer { return vertices; } } - - -// protected _superSampling: SuperSampling; - - -// export enum Sampling { -// None = 'none', -// Grid2 = 'grid2', -// Grid3 = 'grid3', -// Grid4 = 'grid4', -// Quincunx = 'quincunx', -// RGSS = 'rgss', -// Rooks8 = 'rooks8', -// } - - -// get superSampling(): SuperSampling { -// return this._superSampling; -// } - -// set superSampling(superSampling: SuperSampling) { -// this._superSampling = superSampling; -// } - - -// // numDepictable -// // Extent(): number { -// // let count = 0; -// // for (let c of this._text) { - -// // /** -// // * let number = "h".charCodeAt(0); //(returns number = 104) -// // * let char = String.fromCharCode(number); //(returns char = "h") -// // */ - -// // if (this._fontFace.depictable(c.charCodeAt(0))) { -// // ++count; -// // } -// // } -// // return count; -// // } - - -// protected _additionalTransform: mat4; -// protected _transformValid: boolean; -// protected _transform: mat4; - -/** TODO GlyphSequenceConfig --> LabelConfig */ -// // public setFromConfig(config: GlyphSequenceConfig) { -// // this.wordWrap = config.wordWrap; -// // this.lineWidth = config.lineWidth; -// // this.alignment = config.alignment; -// // this.lineAnchor = config.anchor; -// // this.fontColor = config.fontColor; -// // this.fontFace = config.fontFace; -// // this.fontSize = config.fontSize; -// // } - -// // get additionalTransform(): mat4 { -// // return this._additionalTransform; -// // } - -// // set additionalTransform(additionalTransform: mat4) { -// // this._transformValid = false; -// // this._additionalTransform = additionalTransform; -// // } - -// // get transform(): mat4 { -// // if (!this._transformValid) { -// // this.computeTransform(); -// // this._transformValid = true; -// // } -// // return this._transform; -// // } - -// // public computeTransform(): void { -// // //assert(this._fontFace); - -// // this._transform = mat4.create(); -// // mat4.multiply(this._transform, this._transform, this._additionalTransform); - -// // let s = this._fontSize / this._fontFace.size; - -// // mat4.scale(this._transform, this._transform, vec3.fromValues(s, s, s)) -// // } From 0d2462a93328857e5b0d61782e01aea0d6202643 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 29 Jun 2018 15:12:51 +0200 Subject: [PATCH 29/92] font color black, different example transform. --- source/labelrenderer.ts | 3 ++- source/shaders/glyphquad.frag | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 88ca0ba1..610f0e79 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -281,10 +281,11 @@ export class LabelRenderer extends Renderer { // tell the Typesetter to typeset that Label with the loaded FontFace const userTransform = mat4.create(); - mat4.translate(userTransform, userTransform, vec3.fromValues(-1, 0.0, 0)); + mat4.scale(userTransform, userTransform, vec3.fromValues(1.2, 1.2, 1.2)); mat4.rotateX(userTransform, userTransform, Math.PI * -0.4); mat4.rotateY(userTransform, userTransform, Math.PI * 0.2); mat4.rotateZ(userTransform, userTransform, Math.PI * 0.5); + mat4.translate(userTransform, userTransform, vec3.fromValues(-1, 0.0, 0)); let glyphVertices = this.prepareLabel('Hello Transform!', userTransform); glyphVertices = glyphVertices.concat(this.prepareLabel('Hello World!')); diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index dd82f118..6acbe0bd 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -29,7 +29,7 @@ void main(void) if(d < 0.45) discard; - vec4 fc = vec4(0.0, 1.0, 0.0, 1.0); //debug color green, TODO: font color as vertex attrib or uniform? + vec4 fc = vec4(0.0, 0.0, 0.0, 1.0); //black. TODO: font color as vertex attrib or uniform? // TODO mipmap access? float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur From b0fad3a4ac32e8f3b24b3d91445b3c1d384d4534 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Fri, 29 Jun 2018 17:02:54 +0200 Subject: [PATCH 30/92] format new line --- source/renderer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/source/renderer.ts b/source/renderer.ts index feae9321..28440ba6 100644 --- a/source/renderer.ts +++ b/source/renderer.ts @@ -15,7 +15,6 @@ import { GLclampf4, GLfloat2, GLsizei2, tuple2 } from './tuples'; import { Wizard } from './wizard'; - // export interface IdCallback { (id: number, x?: number, y?: number): void; } From 1fe0e069f7c650ef4812520be9e6bc4972163a5f Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 29 Jun 2018 17:12:06 +0200 Subject: [PATCH 31/92] merge fix: wizard precision byte --- source/fontface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/fontface.ts b/source/fontface.ts index 9fbf1977..ef89b6a3 100644 --- a/source/fontface.ts +++ b/source/fontface.ts @@ -66,7 +66,7 @@ export class FontFace { identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; this._glyphTexture = new Texture2(context, identifier + 'GlyphAtlas'); - const internalFormat = Wizard.queryInternalTextureFormat(context, gl.RGBA, 'byte'); + const internalFormat = Wizard.queryInternalTextureFormat(context, gl.RGBA, Wizard.Precision.byte); this._glyphTexture.initialize(1, 1, internalFormat[0], gl.RGBA, internalFormat[1]); } From 42f00eb14f3bb751a6099e6b6425fe078d351a59 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 29 Jun 2018 17:14:56 +0200 Subject: [PATCH 32/92] another merge fix: bad merged lines (syntax error) --- pugconfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pugconfig.js b/pugconfig.js index 805eb715..f2068332 100644 --- a/pugconfig.js +++ b/pugconfig.js @@ -22,7 +22,7 @@ const assets = [ ['./source', buildDir, ['data/{colorbrewer,smithwalt}.json'], [], false], ['./source/data/', buildDir+'/data', ['*'], [], false], ['./source/data/opensansr144', buildDir+'/data/opensansr144', ['*'], [], false], - ['./source/data/verdana', buildDir+'/data/verdana', ['*'], [], false]]; + ['./source/data/verdana', buildDir+'/data/verdana', ['*'], [], false], ['./node_modules/rxjs/bundles/', `${buildDir}/js`, ['rxjs.umd.min.js'], [], false]]; From 5366de929fd354ef8cd9bb330b989148223b1527 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Sat, 30 Jun 2018 19:01:39 +0200 Subject: [PATCH 33/92] Rename LabelGeometry.ts to labelgeometry.ts --- source/{LabelGeometry.ts => labelgeometry.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename source/{LabelGeometry.ts => labelgeometry.ts} (100%) diff --git a/source/LabelGeometry.ts b/source/labelgeometry.ts similarity index 100% rename from source/LabelGeometry.ts rename to source/labelgeometry.ts From 61f80fadd11439cc3803536bcf9a7ed8bd1e732b Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 2 Jul 2018 10:00:38 +0200 Subject: [PATCH 34/92] refactoring --- source/labelrenderer.ts | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 610f0e79..5c4b4ece 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -298,27 +298,12 @@ export class LabelRenderer extends Renderer { const l = glyphVertices.length; for (let i = 0; i < l; i++) { - // TODO: shouldn't there be an easier way to achieve this? - // concat doesn't work as vec3 apparently is not an Array. - const v = glyphVertices[i]; - origins.push(v.origin[0]); - origins.push(v.origin[1]); - origins.push(v.origin[2]); - - tans.push(v.tangent[0]); - tans.push(v.tangent[1]); - tans.push(v.tangent[2]); - - ups.push(v.up[0]); - ups.push(v.up[1]); - ups.push(v.up[2]); - - texCoords.push(v.uvRect[0]); - texCoords.push(v.uvRect[1]); - texCoords.push(v.uvRect[2]); - texCoords.push(v.uvRect[3]); + origins.push.apply(origins, v.origin); + tans.push.apply(tans, v.tangent); + ups.push.apply(ups, v.up); + texCoords.push.apply(texCoords, v.uvRect); } this._labelGeometry.setTexCoords(Float32Array.from(texCoords)); From bda4ae427ab25c5e15ee9910885a1d32112d89ea Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 2 Jul 2018 13:48:19 +0200 Subject: [PATCH 35/92] refactoring: fetchAsync+Promise instead of raw xmlhttp --- source/fontloader.ts | 57 ++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index f29c057c..dfa7410f 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -2,6 +2,7 @@ import { assert } from './auxiliaries'; import { Context } from './context'; +import { fetchAsync } from './fetch'; import { FontFace } from './fontface'; import { Glyph } from './glyph'; import { GLfloat2, GLfloat4 } from './tuples'; @@ -193,42 +194,36 @@ export class FontLoader { * @param onImageLoad Callback is called when the glyph atlas is loaded. */ load(context: Context, filename: string, headless: boolean, onImageLoad: (() => void)): FontFace { - const xmlhttp = new XMLHttpRequest(); - - // fontface: Objects and arrays are passed by reference (JS) const fontFace = new FontFace(context); - // asynchronous loading - // TODO: refactoring using Promise? - xmlhttp.open('GET', filename, true); - xmlhttp.onreadystatechange = () => { - if (xmlhttp.readyState === 4) { - if (xmlhttp.status === 200 || xmlhttp.status === 0) { - const text = xmlhttp.responseText; - - const lines = text.split('\n'); - - for (const l of lines) { - let line = l.split(' '); - const identifier = line[0]; - line = line.slice(1); - - if (identifier === 'info') { - this.handleInfo(line, fontFace); - } else if (identifier === 'common') { - this.handleCommon(line, fontFace); - } else if (identifier === 'page' && !headless) { - this.handlePage(line, fontFace, filename, context, onImageLoad); - } else if (identifier === 'char') { - this.handleChar(line, fontFace); - } else if (identifier === 'kerning') { - this.handleKerning(line, fontFace); - } + fetchAsync(filename, (text) => text).then( + (text) => { + // promise fulfilled + const lines = text.split('\n'); + + for (const l of lines) { + let line = l.split(' '); + const identifier = line[0]; + line = line.slice(1); + + if (identifier === 'info') { + this.handleInfo(line, fontFace); + } else if (identifier === 'common') { + this.handleCommon(line, fontFace); + } else if (identifier === 'page' && !headless) { + this.handlePage(line, fontFace, filename, context, onImageLoad); + } else if (identifier === 'char') { + this.handleChar(line, fontFace); + } else if (identifier === 'kerning') { + this.handleKerning(line, fontFace); } } + }, + (text) => { + // promise rejected + console.error('ERROR: Could not load font file. filename is: ' + filename); } - }; - xmlhttp.send(); + ); // TODO: assert? throw exception? // if (headless || fontFace.glyphTexture) { From 197bca63f9608781ba9bc4ac6106245e86a4682a Mon Sep 17 00:00:00 2001 From: Benjamin Wasty Date: Sat, 30 Jun 2018 10:49:13 +0200 Subject: [PATCH 36/92] export labeling classes --- source/webgl-operate.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/webgl-operate.ts b/source/webgl-operate.ts index ce046b7b..d85fd4b1 100644 --- a/source/webgl-operate.ts +++ b/source/webgl-operate.ts @@ -13,6 +13,13 @@ export import fetch = root_fetch; import * as root_raymath from './raymath'; export import ray_math = root_raymath; +export { FontFace } from './fontface'; +export { FontLoader } from './fontloader'; +export { GlyphVertex, GlyphVertices } from './glyphvertices'; +export { Label } from './label'; +export { LabelGeometry } from './labelgeometry'; +export { Text } from './text'; +export { Typesetter } from './typesetter'; // /* DEBUG facilities */ From 81c4ad8905585c1ae3ffd3e5429481328f851776 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 2 Jul 2018 14:45:13 +0200 Subject: [PATCH 37/92] updated changes from testrenderer to labelrenderer --- source/labelrenderer.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 5c4b4ece..d3c6f570 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -173,10 +173,6 @@ export class LabelRenderer extends Renderer { return false; } - if (this._altered.multiFrameNumber) { - this._ndcOffsetKernel.width = this._multiFrameNumber; - } - return redraw; } @@ -199,6 +195,13 @@ export class LabelRenderer extends Renderer { this._camera.viewport = [this._frameSize[0], this._frameSize[1]]; this._camera.aspect = this._frameSize[0] / this._frameSize[1]; } + if (this._altered.multiFrameNumber) { + this._ndcOffsetKernel.width = this._multiFrameNumber; + } + + if (this._altered.framePrecision) { + this._accumulate.precision = this._framePrecision; + } if (this._altered.clearColor) { this._intermediateFBO.clearColor(this._clearColor); From 5faa58125ee2d3d466b81e70ab0cd4c205a9e723 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 2 Jul 2018 14:53:52 +0200 Subject: [PATCH 38/92] comment/doc about frame resizes that affect labels using pt sizes (as font size or as threshold for readability) --- source/labelrenderer.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index d3c6f570..e3e395fe 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -194,6 +194,12 @@ export class LabelRenderer extends Renderer { this._intermediateFBO.resize(this._frameSize[0], this._frameSize[1]); this._camera.viewport = [this._frameSize[0], this._frameSize[1]]; this._camera.aspect = this._frameSize[0] / this._frameSize[1]; + /* TODO + * update the geometry of the labels that use pt sizes (e.g. labels in screen space) + * and/or update: labels that get too small (to be readable) should not be rendered anymore + * (a.k.a. threshold for readability) + */ + // this.setupScene(); } if (this._altered.multiFrameNumber) { this._ndcOffsetKernel.width = this._multiFrameNumber; From 59dba23cca08a7ad7d6d95d1cb6f9fee42c458ca Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 2 Jul 2018 16:41:03 +0200 Subject: [PATCH 39/92] using Promise less archaic and verbose. --- source/fontloader.ts | 53 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index dfa7410f..6322478c 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -193,37 +193,36 @@ export class FontLoader { * @param headless * @param onImageLoad Callback is called when the glyph atlas is loaded. */ - load(context: Context, filename: string, headless: boolean, onImageLoad: (() => void)): FontFace { + async load(context: Context, filename: string, headless: boolean, onImageLoad: (() => void)): Promise { const fontFace = new FontFace(context); - fetchAsync(filename, (text) => text).then( - (text) => { - // promise fulfilled - const lines = text.split('\n'); - - for (const l of lines) { - let line = l.split(' '); - const identifier = line[0]; - line = line.slice(1); - - if (identifier === 'info') { - this.handleInfo(line, fontFace); - } else if (identifier === 'common') { - this.handleCommon(line, fontFace); - } else if (identifier === 'page' && !headless) { - this.handlePage(line, fontFace, filename, context, onImageLoad); - } else if (identifier === 'char') { - this.handleChar(line, fontFace); - } else if (identifier === 'kerning') { - this.handleKerning(line, fontFace); - } + try { + const text = await fetchAsync(filename, (text) => text); + // promise fulfilled + const lines = text.split('\n'); + + for (const l of lines) { + let line = l.split(' '); + const identifier = line[0]; + line = line.slice(1); + + if (identifier === 'info') { + this.handleInfo(line, fontFace); + } else if (identifier === 'common') { + this.handleCommon(line, fontFace); + } else if (identifier === 'page' && !headless) { + this.handlePage(line, fontFace, filename, context, onImageLoad); + } else if (identifier === 'char') { + this.handleChar(line, fontFace); + } else if (identifier === 'kerning') { + this.handleKerning(line, fontFace); } - }, - (text) => { - // promise rejected - console.error('ERROR: Could not load font file. filename is: ' + filename); } - ); + + } catch (e) { + // promise rejected + console.error('ERROR: Could not load font file. filename is: ' + filename); + } // TODO: assert? throw exception? // if (headless || fontFace.glyphTexture) { From c3ceaf56f00035ec103918f379a9ac9e7ae5ea37 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Tue, 3 Jul 2018 12:25:07 +0200 Subject: [PATCH 40/92] load fontFace completely asynchronously using Promises --- source/fontloader.ts | 20 ++++++-------------- source/labelrenderer.ts | 16 ++++++++++------ 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index 6322478c..7777779c 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -84,8 +84,7 @@ export class FontLoader { } protected handlePage( - stream: Array, fontFace: FontFace, filename: string, context: Context - , onImageLoad: (() => void)): void { + stream: Array, fontFace: FontFace, filename: string, context: Context): Promise { const pairs = this.readKeyValuePairs(stream, ['file']); @@ -94,16 +93,7 @@ export class FontLoader { const pngPath: string = path + file.split('.')[0] + '.png'; - - fontFace.glyphTexture.load(pngPath).then( - - function fulfilled() { - onImageLoad(); - }, - function rejected() { - console.error('ERROR: Could not load glyph Texture. Image was not found?'); - } - ); + return fontFace.glyphTexture.load(pngPath); } protected handleChar(stream: Array, fontFace: FontFace): void { @@ -193,7 +183,7 @@ export class FontLoader { * @param headless * @param onImageLoad Callback is called when the glyph atlas is loaded. */ - async load(context: Context, filename: string, headless: boolean, onImageLoad: (() => void)): Promise { + async load(context: Context, filename: string, headless: boolean): Promise { const fontFace = new FontFace(context); try { @@ -201,6 +191,7 @@ export class FontLoader { // promise fulfilled const lines = text.split('\n'); + const promises = []; for (const l of lines) { let line = l.split(' '); const identifier = line[0]; @@ -211,13 +202,14 @@ export class FontLoader { } else if (identifier === 'common') { this.handleCommon(line, fontFace); } else if (identifier === 'page' && !headless) { - this.handlePage(line, fontFace, filename, context, onImageLoad); + promises.push(this.handlePage(line, fontFace, filename, context)); } else if (identifier === 'char') { this.handleChar(line, fontFace); } else if (identifier === 'kerning') { this.handleKerning(line, fontFace); } } + await Promise.all(promises); } catch (e) { // promise rejected diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index e3e395fe..267a456f 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -21,7 +21,7 @@ import { FontFace } from './fontface'; import { FontLoader } from './fontloader'; import { GlyphVertex, GlyphVertices } from './glyphvertices'; import { Label } from './label'; -import { LabelGeometry } from './LabelGeometry'; +import { LabelGeometry } from './labelgeometry'; import { Text } from './text'; import { Typesetter } from './typesetter'; @@ -275,12 +275,16 @@ export class LabelRenderer extends Renderer { protected loadFont(context: Context): void { const loader = new FontLoader(); - const fontFace: FontFace = loader.load( - context, './data/opensansr144/opensansr144.fnt', false, () => { - this.setupScene(); - this.invalidate(); - }); + // This is a placeholder until the 'real' fontFace is loaded asynchronously by the fontLoader + const fontFace: FontFace = new FontFace(context); + loader.load(context, './data/opensansr144/opensansr144.fnt', false).then( + (fontFace) => { + this._fontFace = fontFace; + this.setupScene(); + this.invalidate(true); + }, + ); this._fontFace = fontFace; } From 509aef6386aaa3242f08e4075a584167233a3b2f Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Tue, 14 Aug 2018 17:56:39 +0200 Subject: [PATCH 41/92] fix: label scale was bad. plus: navigation! --- source/label.ts | 4 ++-- source/labelrenderer.ts | 44 +++++++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/source/label.ts b/source/label.ts index 3e0443e4..7e4cf0eb 100644 --- a/source/label.ts +++ b/source/label.ts @@ -30,7 +30,7 @@ export class Label { protected _lineWidth = 0.0; /** @see {@link fontSize} */ - protected _fontSize = 50.0; + protected _fontSize = 20.0; /** @see {@link fontFace} */ @@ -300,7 +300,7 @@ export class Label { /** * Intended for resetting alteration status. */ - reset() { + reset(): void { this._altered.reset(); } diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 267a456f..ed7eef47 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -11,6 +11,7 @@ import { Context } from './context'; import { DefaultFramebuffer } from './defaultframebuffer'; import { Framebuffer } from './framebuffer'; import { MouseEventProvider } from './mouseeventprovider'; +import { Navigation } from './navigation'; import { Program } from './program'; import { Renderbuffer } from './renderbuffer'; import { Invalidate, Renderer } from './renderer'; @@ -54,6 +55,7 @@ export class LabelRenderer extends Renderer { protected _intermediateFBO: Framebuffer; protected _testNavigation: TestNavigation; + protected _navigation: Navigation; protected _fontFace: FontFace; protected _labelGeometry: LabelGeometry; @@ -141,7 +143,9 @@ export class LabelRenderer extends Renderer { this._camera.near = 0.1; this._camera.far = 8.0; - this._testNavigation = new TestNavigation(() => this.invalidate(), mouseEventProvider); + // Initialize navigation + this._navigation = new Navigation(callback, mouseEventProvider); + this._navigation.camera = this._camera; return true; } @@ -164,16 +168,12 @@ export class LabelRenderer extends Renderer { protected onUpdate(): boolean { - this._testNavigation.update(); - const redraw = this._testNavigation.altered; - this._testNavigation.reset(); + this._ndcOffsetKernel = new AntiAliasingKernel(this._multiFrameNumber); - if (!redraw && !this._altered.any) { - return false; - } + this._navigation.update(); - return redraw; + return this._altered.any || this._camera.altered; } protected onPrepare(): void { @@ -201,6 +201,11 @@ export class LabelRenderer extends Renderer { */ // this.setupScene(); } + + if (this._altered.canvasSize) { + this._camera.aspect = this._canvasSize[0] / this._canvasSize[1]; + } + if (this._altered.multiFrameNumber) { this._ndcOffsetKernel.width = this._multiFrameNumber; } @@ -215,13 +220,21 @@ export class LabelRenderer extends Renderer { this._accumulate.update(); + if (this._camera.altered) { + this._program.bind(); + gl.uniformMatrix4fv(this._uViewProjection, gl.GL_FALSE, this._camera.viewProjection); + this._program.unbind(); + } + this._altered.reset(); + this._camera.altered = false; } protected onFrame(frameNumber: number): void { const gl = this._context.gl; gl.viewport(0, 0, this._frameSize[0], this._frameSize[1]); + this._camera.viewport = [this._frameSize[0], this._frameSize[1]]; let wasBlendEnabled = false; const oldBlendSRC: any = gl.getParameter(gl.BLEND_SRC_RGB); @@ -292,13 +305,10 @@ export class LabelRenderer extends Renderer { // create Label with Text and // tell the Typesetter to typeset that Label with the loaded FontFace - const userTransform = mat4.create(); mat4.scale(userTransform, userTransform, vec3.fromValues(1.2, 1.2, 1.2)); - mat4.rotateX(userTransform, userTransform, Math.PI * -0.4); - mat4.rotateY(userTransform, userTransform, Math.PI * 0.2); mat4.rotateZ(userTransform, userTransform, Math.PI * 0.5); - mat4.translate(userTransform, userTransform, vec3.fromValues(-1, 0.0, 0)); + mat4.translate(userTransform, userTransform, vec3.fromValues(-0.1, 0.0, 0.3)); let glyphVertices = this.prepareLabel('Hello Transform!', userTransform); glyphVertices = glyphVertices.concat(this.prepareLabel('Hello World!')); @@ -325,7 +335,7 @@ export class LabelRenderer extends Renderer { protected prepareLabel(str: string, userTransform?: mat4): GlyphVertices { - const testLabel: Label = new Label(new Text(str), this._fontFace); + const label: Label = new Label(new Text(str), this._fontFace); // TODO meaningful margins from label.margins or config.margins ? const margins: vec4 = vec4.create(); @@ -336,8 +346,8 @@ export class LabelRenderer extends Renderer { const transform = mat4.create(); // translate to lower left in NDC + mat4.scale(transform, transform, vec3.fromValues(1.0, this._frameSize[1] / this._frameSize[0], 1.0)); mat4.translate(transform, transform, vec3.fromValues(-1.0, -1.0, 0.0)); - // scale glyphs to NDC size // this._frameSize should be the viewport size mat4.scale(transform, transform, vec3.fromValues(2.0 / this._frameSize[0], 2.0 / this._frameSize[1], 1.0)); @@ -356,10 +366,10 @@ export class LabelRenderer extends Renderer { vec3.add(v3, v3, vec3.fromValues(margins[3], margins[2], 0.0)); mat4.translate(transform, transform, v3); - testLabel.transform = mat4.mul(testLabel.transform, + label.transform = mat4.mul(label.transform, userTransform !== undefined ? userTransform : mat4.create(), transform); - const numGlyphs = testLabel.length; + const numGlyphs = label.length; // prepare vertex storage (values will be overridden by typesetter) const vertices = new GlyphVertices(); @@ -375,7 +385,7 @@ export class LabelRenderer extends Renderer { vertices.push(vertex); } - Typesetter.typeset(testLabel, vertices, 0); + Typesetter.typeset(label, vertices, 0); return vertices; } From c22fd5d5064ea5a1e3d6a1008eabe928571aa192 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 17 Aug 2018 11:53:20 +0200 Subject: [PATCH 42/92] userTransform for label --- source/label.ts | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/source/label.ts b/source/label.ts index 7e4cf0eb..05610581 100644 --- a/source/label.ts +++ b/source/label.ts @@ -45,10 +45,13 @@ export class Label { /** @see {@link transform} */ protected _transform: mat4; + /** @see {@link userTransform} */ + protected _userTransform: mat4; /** @see {@link altered} */ protected readonly _altered = Object.assign(new ChangeLookup(), { - any: false, color: false, resources: false, text: false, typesetting: false, transform: false, + any: false, color: false, resources: false, text: false, typesetting: false, + transform: false, userTransform: false, }); /** @@ -60,6 +63,7 @@ export class Label { this._text = text; this._fontFace = fontFace; this._transform = mat4.create(); + this._userTransform = mat4.create(); } @@ -196,7 +200,7 @@ export class Label { /** * Width of a single line (in pt or w.r.t. font face scaling in world space respectively). The width of the line - * is not intended to be set explicitly, but implicitly vie transformations/label placement. + * is not intended to be set explicitly, but implicitly via transformations/label placement. */ get lineWidth(): number { return this._lineWidth; @@ -276,11 +280,25 @@ export class Label { get transform(): mat4 { const s = this._fontSize / this._fontFace.size; - mat4.scale(this._transform, this._transform, vec3.fromValues(s, s, s)); - return this._transform; + const t: mat4 = mat4.create(); + mat4.scale(t, this._transform, vec3.fromValues(s, s, s)); + + return t; + } + + /** + * This just stores a transform for the user. The user takes care of using this appropriately + * (e.g., for calculations to the final transform). + */ + set userTransform(t: mat4) { + this._altered.alter('userTransform'); + this._userTransform = t; } + get userTransform(): mat4 { + return this._userTransform; + } toString(): string { if (this._text instanceof Text) { From 5efe053966a6129a88eccd430c8d300c989a45b0 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 17 Aug 2018 15:15:15 +0200 Subject: [PATCH 43/92] fix: line anchor was not applied to the last glyph of each label --- source/typesetter.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/typesetter.ts b/source/typesetter.ts index bd931a3d..a0b33257 100644 --- a/source/typesetter.ts +++ b/source/typesetter.ts @@ -72,7 +72,7 @@ export class Typesetter { * @param pen - In/out parameter: pen (typesetting position) to be used and adjusted. * @param extent - In/out parameter: extent to be adjusted. */ - protected static backward(label: Label, index: number, begin: number, pen: vec2, extent: vec2) { + protected static backward(label: Label, index: number, begin: number, pen: vec2, extent: vec2): void { while (index > begin) { const precedingGlyph = label.fontFace.glyph(label.charCodeAt(index)); if (precedingGlyph.depictable()) { @@ -95,7 +95,7 @@ export class Typesetter { * @param end - Vertex index to stop alignment at. */ protected static transformAlignment(pen: vec2, alignment: Label.Alignment, - vertices: GlyphVertices | undefined, begin: number, end: number) { + vertices: GlyphVertices | undefined, begin: number, end: number): void { if (vertices === undefined || alignment === Label.Alignment.Left) { return; } @@ -121,7 +121,7 @@ export class Typesetter { * @todo Apply once at the beginning! Initial offset! */ protected static transformLineAnchor(label: Label, - vertices: GlyphVertices | undefined, begin: number, end: number) { + vertices: GlyphVertices | undefined, begin: number, end: number): void { if (vertices === undefined) { return; } @@ -148,7 +148,7 @@ export class Typesetter { return; } - for (let i = begin; i < end; ++i) { + for (let i = begin; i <= end; ++i) { vertices[i].origin[1] -= offset; } } @@ -161,7 +161,9 @@ export class Typesetter { * @param glyph - Glyph that is to be rendered/configured. * @param vertex - Associated vertex to store data required for rendering. */ - protected static transformGlyph(fontFace: FontFace, pen: vec2, glyph: Glyph, vertex: GlyphVertex | undefined) { + protected static transformGlyph(fontFace: FontFace, pen: vec2, glyph: Glyph, + vertex: GlyphVertex | undefined): void { + if (vertex === undefined || glyph.depictable() === false) { return; } From 6850c12266f089f19090f3ece2fca4cfe7605234 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Fri, 17 Aug 2018 15:18:23 +0200 Subject: [PATCH 44/92] missing return typedefs --- source/fontface.ts | 2 +- source/glyph.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/fontface.ts b/source/fontface.ts index ef89b6a3..064fa81b 100644 --- a/source/fontface.ts +++ b/source/fontface.ts @@ -234,7 +234,7 @@ export class FontFace { * Add a glyph to the font face's set of glyphs. If the glyph already exists, the existing glyph remains. * @param glyph - The glyph to add to the set of glyphs. */ - addGlyph(glyph: Glyph) { + addGlyph(glyph: Glyph): void { assert(!(this._glyphs.get(glyph.index)), 'expected glyph to not already exist'); this._glyphs.set(glyph.index, glyph); } diff --git a/source/glyph.ts b/source/glyph.ts index cb817d6e..40590c7b 100644 --- a/source/glyph.ts +++ b/source/glyph.ts @@ -68,7 +68,7 @@ export class Glyph { * negative value but is not enforced to be in terms of assertion or clamping. If kerning data for the subsequent * glyph is already available it will be updated to the provided value. */ - setKerning(subsequentIndex: GLsizei, kerning: GLfloat) { + setKerning(subsequentIndex: GLsizei, kerning: GLfloat): void { this._kernings.set(subsequentIndex, kerning); } @@ -134,7 +134,7 @@ export class Glyph { * @param yOffset - The glyphs vertical offset w.r.t. the font's topmost descendends, without the font's top * padding in pt. */ - setBearing(fontBase: GLfloat, xOffset: GLfloat, yOffset: GLfloat) { + setBearing(fontBase: GLfloat, xOffset: GLfloat, yOffset: GLfloat): void { this._bearing[0] = xOffset; this._bearing[1] = fontBase - yOffset; } From f7c28c5f8a531b86990045e314f9ac15cafdfe60 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Mon, 20 Aug 2018 13:55:20 +0200 Subject: [PATCH 45/92] different units possible for font size (world and px) --- source/label.ts | 33 ++++++++++++++++++-- source/labelrenderer.ts | 67 ++++++++++++++++++++++------------------- 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/source/label.ts b/source/label.ts index 05610581..4ca41a33 100644 --- a/source/label.ts +++ b/source/label.ts @@ -30,8 +30,10 @@ export class Label { protected _lineWidth = 0.0; /** @see {@link fontSize} */ - protected _fontSize = 20.0; + protected _fontSize = 0.05; + /** @see {@link fontSizeUnit} */ + protected _fontSizeUnit: Label.SpaceUnit = Label.SpaceUnit.World; /** @see {@link fontFace} */ protected _fontFace: FontFace; @@ -207,7 +209,8 @@ export class Label { } /** - * Font Size in pt or px // TODO unit?? + * The currently used font size. + * (@see {@link fontSizeUnit}) */ set fontSize(newSize: number) { if (this._fontSize === newSize) { @@ -221,6 +224,22 @@ export class Label { return this._fontSize; } + /** + * This unit is used for the font size. + * (@see {@link fontSize}) + */ + set fontSizeUnit(newUnit: Label.SpaceUnit) { + if (this._fontSizeUnit === newUnit) { + return; + } + this._altered.alter('typesetting'); + this._altered.alter('transform'); + this._fontSizeUnit = newUnit; + } + get fontSizeUnit(): Label.SpaceUnit { + return this._fontSizeUnit; + } + /** * Font face used for typesetting, transformation, and rendering. */ @@ -279,7 +298,7 @@ export class Label { } get transform(): mat4 { - const s = this._fontSize / this._fontFace.size; + const s = this.fontSize / this._fontFace.size; const t: mat4 = mat4.create(); mat4.scale(t, this._transform, vec3.fromValues(s, s, s)); @@ -341,4 +360,12 @@ export namespace Label { Bottom = 'bottom', } + /** + * This unit is used for the font size. + */ + export enum SpaceUnit { + World = 'world', //abstract world unit + Px = 'px', // screen pixel + } + } diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index ed7eef47..c677f590 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -337,37 +337,42 @@ export class LabelRenderer extends Renderer { const label: Label = new Label(new Text(str), this._fontFace); - // TODO meaningful margins from label.margins or config.margins ? - const margins: vec4 = vec4.create(); - // TODO meaningful ppiScale from label.ppiScale or config.ppiScale ? - const ppiScale = 1; - - // compute transform matrix - const transform = mat4.create(); - - // translate to lower left in NDC - mat4.scale(transform, transform, vec3.fromValues(1.0, this._frameSize[1] / this._frameSize[0], 1.0)); - mat4.translate(transform, transform, vec3.fromValues(-1.0, -1.0, 0.0)); - // scale glyphs to NDC size - // this._frameSize should be the viewport size - mat4.scale(transform, transform, vec3.fromValues(2.0 / this._frameSize[0], 2.0 / this._frameSize[1], 1.0)); - - // scale glyphs to pixel size with respect to the displays ppi - mat4.scale(transform, transform, vec3.fromValues(ppiScale, ppiScale, ppiScale)); - - // translate to origin in point space - scale origin within - // margined extend (i.e., viewport with margined areas removed) - const marginedExtent: vec2 = vec2.create(); - vec2.sub(marginedExtent, vec2.fromValues( - this._frameSize[0] / ppiScale, this._frameSize[1] / ppiScale), - vec2.fromValues(margins[3] + margins[1], margins[2] + margins[0])); - - const v3 = vec3.fromValues(0.5 * marginedExtent[0], 0.5 * marginedExtent[1], 0); - vec3.add(v3, v3, vec3.fromValues(margins[3], margins[2], 0.0)); - mat4.translate(transform, transform, v3); - - label.transform = mat4.mul(label.transform, - userTransform !== undefined ? userTransform : mat4.create(), transform); + const uT = userTransform !== undefined ? userTransform : mat4.create(); + + label.transform = uT; + + if (label.fontSizeUnit === Label.SpaceUnit.Px) { + // TODO meaningful margins from label.margins or config.margins ? + const margins: vec4 = vec4.create(); + // TODO meaningful ppiScale from label.ppiScale or config.ppiScale ? + const ppiScale = 1; + + // compute transform matrix + const transform = mat4.create(); + + // translate to lower left in NDC + mat4.scale(transform, transform, vec3.fromValues(1.0, this._frameSize[1] / this._frameSize[0], 1.0)); + mat4.translate(transform, transform, vec3.fromValues(-1.0, -1.0, 0.0)); + // scale glyphs to NDC size + // this._frameSize should be the viewport size + mat4.scale(transform, transform, vec3.fromValues(2.0 / this._frameSize[0], 2.0 / this._frameSize[1], 1.0)); + + // scale glyphs to pixel size with respect to the displays ppi + mat4.scale(transform, transform, vec3.fromValues(ppiScale, ppiScale, ppiScale)); + + // translate to origin in point space - scale origin within + // margined extend (i.e., viewport with margined areas removed) + const marginedExtent: vec2 = vec2.create(); + vec2.sub(marginedExtent, vec2.fromValues( + this._frameSize[0] / ppiScale, this._frameSize[1] / ppiScale), + vec2.fromValues(margins[3] + margins[1], margins[2] + margins[0])); + + const v3 = vec3.fromValues(0.5 * marginedExtent[0], 0.5 * marginedExtent[1], 0); + vec3.add(v3, v3, vec3.fromValues(margins[3], margins[2], 0.0)); + mat4.translate(transform, transform, v3); + + label.transform = mat4.mul(label.transform, uT, transform); + } const numGlyphs = label.length; From d27d99c0febab80f72625a0273bd733729239a14 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Tue, 21 Aug 2018 14:33:28 +0200 Subject: [PATCH 46/92] label stores its current extent --- source/label.ts | 19 ++++++++++++++++++- source/typesetter.ts | 6 +++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/source/label.ts b/source/label.ts index 4ca41a33..2d56bed2 100644 --- a/source/label.ts +++ b/source/label.ts @@ -50,6 +50,9 @@ export class Label { /** @see {@link userTransform} */ protected _userTransform: mat4; + /** @see {@link extent} */ + protected _extent: [number, number]; + /** @see {@link altered} */ protected readonly _altered = Object.assign(new ChangeLookup(), { any: false, color: false, resources: false, text: false, typesetting: false, @@ -66,6 +69,7 @@ export class Label { this._fontFace = fontFace; this._transform = mat4.create(); this._userTransform = mat4.create(); + this._extent = [0, 0]; } @@ -319,6 +323,19 @@ export class Label { return this._userTransform; } + /** + * The typesetter sets this extent after typesetting and applying the transform. + */ + set extent(e: [number, number]) { + this._extent = e; + } + /** + * Returns the width and height of the typset label. Both are zero if not typeset yet. + */ + get extent(): [number, number] { + return this._extent; + } + toString(): string { if (this._text instanceof Text) { return this._text.text; @@ -364,7 +381,7 @@ export namespace Label { * This unit is used for the font size. */ export enum SpaceUnit { - World = 'world', //abstract world unit + World = 'world', // abstract world unit Px = 'px', // screen pixel } diff --git a/source/typesetter.ts b/source/typesetter.ts index a0b33257..4bf4eca9 100644 --- a/source/typesetter.ts +++ b/source/typesetter.ts @@ -282,7 +282,11 @@ export class Typesetter { Typesetter.transformLineAnchor(label, vertices, iBegin, iEnd - 1); Typesetter.transformVertex(label.transform, vertices, iBegin, vertexIndex); - return Typesetter.transformExtent(label.transform, extent); + + const labelExtent = Typesetter.transformExtent(label.transform, extent); + label.extent = labelExtent; + return labelExtent; + } } From 13c0fe57c0cb7141e85a1640d49b02b5fae7fdf7 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Wed, 22 Aug 2018 11:08:50 +0200 Subject: [PATCH 47/92] deleted unused code (POLYGON_OFFSET_FILL) --- source/labelrenderer.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index c677f590..2d807087 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -244,9 +244,6 @@ export class LabelRenderer extends Renderer { gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - // gl.enable(gl.POLYGON_OFFSET_FILL); - // gl.polygonOffset(-1, 1); // avoid z-fighting with other geometry - this._program.bind(); const ndcOffset = this._ndcOffsetKernel.get(frameNumber); @@ -275,8 +272,6 @@ export class LabelRenderer extends Renderer { if (!wasBlendEnabled) { gl.disable(gl.BLEND); } - - // gl.disable(gl.POLYGON_OFFSET_FILL); } protected onSwap(): void { From d9e30f8a04408a9beaec5815e844d83b4d57c8a3 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Tue, 11 Sep 2018 16:12:32 +0200 Subject: [PATCH 48/92] warn if not all required keys were provided by font file --- source/fontloader.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/source/fontloader.ts b/source/fontloader.ts index 7777779c..c3938274 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -171,6 +171,7 @@ export class FontLoader { } if (!valid) { + console.warn('Not all required keys are provided! Mandatory keys: ' + mandatoryKeys); return new Map(); // typescript does not allow writing `new StringPairs()` } else { return pairs; From 7f41f110cc7eb72965031fc349cf752804a0ca58 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Tue, 11 Sep 2018 16:12:49 +0200 Subject: [PATCH 49/92] fix comment --- source/glyph.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/glyph.ts b/source/glyph.ts index 40590c7b..85c01193 100644 --- a/source/glyph.ts +++ b/source/glyph.ts @@ -131,7 +131,7 @@ export class Glyph { * The horizontal bearing equals the xOffset: bearingX = xOffset - left padding: * @param fontBase - The font face's (FontFace) base-to-top distance in pt. * @param xOffset - The glyphs horizontal offset without left padding. - * @param yOffset - The glyphs vertical offset w.r.t. the font's topmost descendends, without the font's top + * @param yOffset - The glyphs vertical offset w.r.t. the font's topmost ascenders, without the font's top * padding in pt. */ setBearing(fontBase: GLfloat, xOffset: GLfloat, yOffset: GLfloat): void { From 7db0c9762c931f025c2293c88c03a54efb3165f9 Mon Sep 17 00:00:00 2001 From: "anne.gropler" Date: Thu, 20 Sep 2018 16:40:06 +0200 Subject: [PATCH 50/92] WIP: created new class --- source/labelrenderer.ts | 12 ++++++++ source/position2dlabel.ts | 62 +++++++++++++++++++++++++++++++++++++++ source/webgl-operate.ts | 1 + 3 files changed, 75 insertions(+) create mode 100644 source/position2dlabel.ts diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 2d807087..059b2107 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -23,6 +23,7 @@ import { FontLoader } from './fontloader'; import { GlyphVertex, GlyphVertices } from './glyphvertices'; import { Label } from './label'; import { LabelGeometry } from './labelgeometry'; +import { Position2DLabel } from './position2Dlabel'; import { Text } from './text'; import { Typesetter } from './typesetter'; @@ -298,6 +299,16 @@ export class LabelRenderer extends Renderer { protected setupScene(): void { + /** New scene; as OpenLL */ + + const pos2Dlabel = new Position2DLabel(new Text('Hello Position 2D!'), this._fontFace); + pos2Dlabel.fontSizeUnit = Label.SpaceUnit.Px; + + pos2Dlabel.setPosition(0, 0); + pos2Dlabel.setDirection(1, 1); // expected: from ll to ur + + /** Old scene; */ + // create Label with Text and // tell the Typesetter to typeset that Label with the loaded FontFace const userTransform = mat4.create(); @@ -328,6 +339,7 @@ export class LabelRenderer extends Renderer { this._labelGeometry.setGlyphCoords(Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); } + protected prepareLabel(str: string, userTransform?: mat4): GlyphVertices { const label: Label = new Label(new Text(str), this._fontFace); diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts new file mode 100644 index 00000000..4482cc58 --- /dev/null +++ b/source/position2dlabel.ts @@ -0,0 +1,62 @@ + +import { mat4, vec3, vec2 } from 'gl-matrix'; + +import { ChangeLookup } from './changelookup'; +import { Color } from './color'; +import { FontFace } from './fontface'; +import { Label } from './label'; +import { Text } from './text'; + + +/** + * @todo + */ +export class Position2DLabel extends Label { + + /** @see {@link fontSizeUnit} */ + /** @todo allow only px or pt, not World */ + // protected _fontSizeUnit: Label.SpaceUnit = Label.SpaceUnit.World; + + protected _position: vec2; + protected _direction: vec2; + + /** + * Constructs an unconfigured, empty label. + * @param text - Valid context to create the object for. + * @param identifier - Meaningful name for identification of this instances VAO and VBOs. + */ + constructor(text: Text, fontFace: FontFace) { + super(text, fontFace); + } + + + /** + * Sets the 2D position of the label's reference point (i.e. lower left corner for horizontal alignment) + */ + set position(xy: vec2) { + this._position = vec2.clone(xy); + } + + get position(): vec2 { + return this._position; + } + + /** position parameters as specified in OpenLL */ + setPosition(x: number, y: number, unit?: Label.SpaceUnit): void { + // todo: assert that SpaceUnit is px or pt; transform to NDC + this._position = vec2.fromValues(x, y); + } + + set direction(xy: vec2) { + this._direction = vec2.clone(xy); + } + + get direction(): vec2 { + return this._direction; + } + + setDirection(x: number, y: number): void { + this._direction = vec2.fromValues(x, y); + } +} + diff --git a/source/webgl-operate.ts b/source/webgl-operate.ts index d85fd4b1..1898e85d 100644 --- a/source/webgl-operate.ts +++ b/source/webgl-operate.ts @@ -17,6 +17,7 @@ export { FontFace } from './fontface'; export { FontLoader } from './fontloader'; export { GlyphVertex, GlyphVertices } from './glyphvertices'; export { Label } from './label'; +export { Position2DLabel } from './position2dlabel'; export { LabelGeometry } from './labelgeometry'; export { Text } from './text'; export { Typesetter } from './typesetter'; From 4c5184f3682dfe3e16b085759a7374c720173566 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Tue, 2 Oct 2018 11:53:10 +0200 Subject: [PATCH 51/92] Position2Dlabel ready to use --- source/label.ts | 19 ++++++++- source/labelrenderer.ts | 74 ++++++++++++++++++++++++++--------- source/position2dlabel.ts | 62 ++++++++++++++++++++++++++++- source/shaders/glyphquad.vert | 6 +-- 4 files changed, 138 insertions(+), 23 deletions(-) diff --git a/source/label.ts b/source/label.ts index 2d56bed2..7ecbf57a 100644 --- a/source/label.ts +++ b/source/label.ts @@ -1,9 +1,10 @@ -import { mat4, vec3 } from 'gl-matrix'; +import { mat4, vec3, vec4 } from 'gl-matrix'; import { ChangeLookup } from './changelookup'; import { Color } from './color'; import { FontFace } from './fontface'; +import { GlyphVertex, GlyphVertices } from './glyphvertices'; import { Text } from './text'; @@ -72,6 +73,22 @@ export class Label { this._extent = [0, 0]; } + protected prepareVertexStorage(): GlyphVertices { + const vertices = new GlyphVertices(); + const numGlyphs = this.length; + for (let i = 0; i < numGlyphs; ++i) { + + const vertex: GlyphVertex = { + origin: vec3.create(), + tangent: vec3.create(), + up: vec3.create(), + // vec2 lowerLeft and vec2 upperRight in glyph texture (uv) + uvRect: vec4.create(), + }; + vertices.push(vertex); + } + return vertices; + } /** * Returns the character at the specified index. diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 059b2107..d6a5425e 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -23,7 +23,7 @@ import { FontLoader } from './fontloader'; import { GlyphVertex, GlyphVertices } from './glyphvertices'; import { Label } from './label'; import { LabelGeometry } from './labelgeometry'; -import { Position2DLabel } from './position2Dlabel'; +import { Position2DLabel } from './position2dlabel'; import { Text } from './text'; import { Typesetter } from './typesetter'; @@ -59,7 +59,8 @@ export class LabelRenderer extends Renderer { protected _navigation: Navigation; protected _fontFace: FontFace; - protected _labelGeometry: LabelGeometry; + protected _2DLabelGeometry: LabelGeometry; + protected _3DLabelGeometry: LabelGeometry; protected _uGlyphAtlas: WebGLUniformLocation; protected onInitialize(context: Context, callback: Invalidate, @@ -98,14 +99,16 @@ export class LabelRenderer extends Renderer { this._uGlyphAtlas = this._program.uniform('u_glyphs'); - this._labelGeometry = new LabelGeometry(this._context); + this._2DLabelGeometry = new LabelGeometry(this._context); + this._3DLabelGeometry = new LabelGeometry(this._context); const aVertex = this._program.attribute('a_quadVertex', 0); const aTexCoord = this._program.attribute('a_texCoord', 1); const aOrigin = this._program.attribute('a_origin', 2); const aTan = this._program.attribute('a_tan', 3); const aUp = this._program.attribute('a_up', 4); - this._labelGeometry.initialize(aVertex, aTexCoord, aOrigin, aTan, aUp); + this._2DLabelGeometry.initialize(aVertex, aTexCoord, aOrigin, aTan, aUp); + this._3DLabelGeometry.initialize(aVertex, aTexCoord, aOrigin, aTan, aUp); this._ndcOffsetKernel = new AntiAliasingKernel(this._multiFrameNumber); @@ -159,6 +162,9 @@ export class LabelRenderer extends Renderer { this._uGlyphAtlas = -1; this._program.uninitialize(); + this._2DLabelGeometry.uninitialize(); + this._3DLabelGeometry.uninitialize(); + this._intermediateFBO.uninitialize(); this._defaultFBO.uninitialize(); this._colorRenderTexture.uninitialize(); @@ -260,9 +266,15 @@ export class LabelRenderer extends Renderer { this._intermediateFBO.clear(gl.COLOR_BUFFER_BIT, true, false); - this._labelGeometry.bind(); - this._labelGeometry.draw(); - this._labelGeometry.unbind(); + this._3DLabelGeometry.bind(); + this._3DLabelGeometry.draw(); + this._3DLabelGeometry.unbind(); + + gl.uniformMatrix4fv(this._uViewProjection, gl.GL_FALSE, mat4.create()); + + this._2DLabelGeometry.bind(); + this._2DLabelGeometry.draw(); + this._2DLabelGeometry.unbind(); this._intermediateFBO.unbind(); @@ -303,11 +315,36 @@ export class LabelRenderer extends Renderer { const pos2Dlabel = new Position2DLabel(new Text('Hello Position 2D!'), this._fontFace); pos2Dlabel.fontSizeUnit = Label.SpaceUnit.Px; + pos2Dlabel.fontSize = 50; + + pos2Dlabel.setPosition(0, 0.5); + pos2Dlabel.setDirection(1, 0); // expected: from ll to lr + + let glyphVertices = pos2Dlabel.typeset(this._frameSize); + + // fill buffers + let origins: Array = []; + let tans: Array = []; + let ups: Array = []; + let texCoords: Array = []; + + let l = glyphVertices.length; + + for (let i = 0; i < l; i++) { + const v = glyphVertices[i]; + + origins.push.apply(origins, v.origin); + tans.push.apply(tans, v.tangent); + ups.push.apply(ups, v.up); + texCoords.push.apply(texCoords, v.uvRect); + } + + this._2DLabelGeometry.setTexCoords(Float32Array.from(texCoords)); + this._2DLabelGeometry.setGlyphCoords( + Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); - pos2Dlabel.setPosition(0, 0); - pos2Dlabel.setDirection(1, 1); // expected: from ll to ur - /** Old scene; */ + /** Old scene; soon to be "position3DLabel" */ // create Label with Text and // tell the Typesetter to typeset that Label with the loaded FontFace @@ -315,16 +352,16 @@ export class LabelRenderer extends Renderer { mat4.scale(userTransform, userTransform, vec3.fromValues(1.2, 1.2, 1.2)); mat4.rotateZ(userTransform, userTransform, Math.PI * 0.5); mat4.translate(userTransform, userTransform, vec3.fromValues(-0.1, 0.0, 0.3)); - let glyphVertices = this.prepareLabel('Hello Transform!', userTransform); + glyphVertices = this.prepareLabel('Hello Transform!', userTransform); glyphVertices = glyphVertices.concat(this.prepareLabel('Hello World!')); - const origins: Array = []; - const tans: Array = []; - const ups: Array = []; - const texCoords: Array = []; + origins = []; + tans = []; + ups = []; + texCoords = []; - const l = glyphVertices.length; + l = glyphVertices.length; for (let i = 0; i < l; i++) { const v = glyphVertices[i]; @@ -335,8 +372,9 @@ export class LabelRenderer extends Renderer { texCoords.push.apply(texCoords, v.uvRect); } - this._labelGeometry.setTexCoords(Float32Array.from(texCoords)); - this._labelGeometry.setGlyphCoords(Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); + this._3DLabelGeometry.setTexCoords(Float32Array.from(texCoords)); + this._3DLabelGeometry.setGlyphCoords( + Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); } diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index 4482cc58..1e303ac5 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -1,11 +1,13 @@ -import { mat4, vec3, vec2 } from 'gl-matrix'; +import { mat4, vec2, vec3, vec4 } from 'gl-matrix'; import { ChangeLookup } from './changelookup'; import { Color } from './color'; import { FontFace } from './fontface'; +import { GlyphVertices } from './glyphvertices'; import { Label } from './label'; import { Text } from './text'; +import { Typesetter } from './typesetter'; /** @@ -27,14 +29,69 @@ export class Position2DLabel extends Label { */ constructor(text: Text, fontFace: FontFace) { super(text, fontFace); + this._position = vec2.fromValues(0, 0); + this._direction = vec2.fromValues(1, 0); } + protected updateTransform(): void { + + const newTransform = mat4.create(); + + const angle = vec2.angle(vec2.fromValues(1, 0), this._direction); + mat4.rotateZ(newTransform, newTransform, angle); + mat4.translate(newTransform, newTransform, vec3.fromValues(this._position[0], this._position[1], 0)); + + this.transform = newTransform; + } + + typeset(frameSize: [number, number]): GlyphVertices { + // label.transform is set through .position and .direction + + // TODO assert: this.fontSizeUnit === Label.SpaceUnit.Px + + // TODO meaningful margins from label.margins or config.margins ? + const margins: vec4 = vec4.create(); + // TODO meaningful ppiScale from label.ppiScale or config.ppiScale ? + const ppiScale = 1; + + // compute transform matrix + const transform = mat4.create(); + + // translate to lower left in NDC + // mat4.scale(transform, transform, vec3.fromValues(1.0, frameSize[1] / frameSize[0], 1.0)); + mat4.translate(transform, transform, vec3.fromValues(-1.0, -1.0, 0.0)); + // scale glyphs to NDC size + // this._frameSize should be the viewport size + mat4.scale(transform, transform, vec3.fromValues(2.0 / frameSize[0], 2.0 / frameSize[1], 1.0)); + + // scale glyphs to pixel size with respect to the displays ppi + mat4.scale(transform, transform, vec3.fromValues(ppiScale, ppiScale, ppiScale)); + + // translate to origin in point space - scale origin within + // margined extend (i.e., viewport with margined areas removed) + const marginedExtent: vec2 = vec2.create(); + vec2.sub(marginedExtent, vec2.fromValues( + frameSize[0] / ppiScale, frameSize[1] / ppiScale), + vec2.fromValues(margins[3] + margins[1], margins[2] + margins[0])); + + const v3 = vec3.fromValues(0.5 * marginedExtent[0], 0.5 * marginedExtent[1], 0); + vec3.add(v3, v3, vec3.fromValues(margins[3], margins[2], 0.0)); + mat4.translate(transform, transform, v3); + + this.transform = mat4.mul(this.transform, this.transform, transform); + + const vertices = this.prepareVertexStorage(); + Typesetter.typeset(this, vertices, 0); + + return vertices; + } /** * Sets the 2D position of the label's reference point (i.e. lower left corner for horizontal alignment) */ set position(xy: vec2) { this._position = vec2.clone(xy); + this.updateTransform(); } get position(): vec2 { @@ -45,10 +102,12 @@ export class Position2DLabel extends Label { setPosition(x: number, y: number, unit?: Label.SpaceUnit): void { // todo: assert that SpaceUnit is px or pt; transform to NDC this._position = vec2.fromValues(x, y); + this.updateTransform(); } set direction(xy: vec2) { this._direction = vec2.clone(xy); + this.updateTransform(); } get direction(): vec2 { @@ -57,6 +116,7 @@ export class Position2DLabel extends Label { setDirection(x: number, y: number): void { this._direction = vec2.fromValues(x, y); + this.updateTransform(); } } diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index e6b70e1c..5039aa59 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -4,14 +4,14 @@ precision lowp int; @import ./facade.vert; #if __VERSION__ == 100 -#extension GL_EXT_draw_buffers : enable -attribute vec2 a_quadVertex; +#extension GL_EXT_draw_buffers : enable +attribute vec2 a_quadVertex; attribute vec4 a_texCoord; // [ texture ll: vec2, ur: vec2 ] attribute vec3 a_origin; attribute vec3 a_tan; attribute vec3 a_up; #else -layout(location = 0) in vec2 a_quadVertex; +layout(location = 0) in vec2 a_quadVertex; layout(location = 1) in vec4 a_texCoord; // [ texture ll: vec2, ur: vec2 ] layout(location = 2) in vec3 a_origin; layout(location = 3) in vec3 a_tan; From 850f918b15ec8d398cbd3d75207d1ad581fa37f1 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Tue, 2 Oct 2018 13:40:33 +0200 Subject: [PATCH 52/92] fix: order of applied transforms for 2D label --- source/labelrenderer.ts | 4 ++-- source/position2dlabel.ts | 28 ++++++++++------------------ 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index d6a5425e..5dba880a 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -317,8 +317,8 @@ export class LabelRenderer extends Renderer { pos2Dlabel.fontSizeUnit = Label.SpaceUnit.Px; pos2Dlabel.fontSize = 50; - pos2Dlabel.setPosition(0, 0.5); - pos2Dlabel.setDirection(1, 0); // expected: from ll to lr + pos2Dlabel.setPosition(100, 0); // position values in px, since fontSizeUnit is set to SpaceUni.Px + pos2Dlabel.setDirection(1, 0.5); let glyphVertices = pos2Dlabel.typeset(this._frameSize); diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index 1e303ac5..813b17d9 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -33,20 +33,7 @@ export class Position2DLabel extends Label { this._direction = vec2.fromValues(1, 0); } - protected updateTransform(): void { - - const newTransform = mat4.create(); - - const angle = vec2.angle(vec2.fromValues(1, 0), this._direction); - mat4.rotateZ(newTransform, newTransform, angle); - mat4.translate(newTransform, newTransform, vec3.fromValues(this._position[0], this._position[1], 0)); - - this.transform = newTransform; - } - typeset(frameSize: [number, number]): GlyphVertices { - // label.transform is set through .position and .direction - // TODO assert: this.fontSizeUnit === Label.SpaceUnit.Px // TODO meaningful margins from label.margins or config.margins ? @@ -78,7 +65,16 @@ export class Position2DLabel extends Label { vec3.add(v3, v3, vec3.fromValues(margins[3], margins[2], 0.0)); mat4.translate(transform, transform, v3); - this.transform = mat4.mul(this.transform, this.transform, transform); + + // apply user tranformations (position, direction) + + // TODO only returns positive angle! + const angle = vec2.angle(vec2.fromValues(1, 0), this._direction); + + mat4.translate(transform, transform, vec3.fromValues(this._position[0], this._position[1], 0)); + mat4.rotateZ(transform, transform, angle); + + this.transform = transform; const vertices = this.prepareVertexStorage(); Typesetter.typeset(this, vertices, 0); @@ -91,7 +87,6 @@ export class Position2DLabel extends Label { */ set position(xy: vec2) { this._position = vec2.clone(xy); - this.updateTransform(); } get position(): vec2 { @@ -102,12 +97,10 @@ export class Position2DLabel extends Label { setPosition(x: number, y: number, unit?: Label.SpaceUnit): void { // todo: assert that SpaceUnit is px or pt; transform to NDC this._position = vec2.fromValues(x, y); - this.updateTransform(); } set direction(xy: vec2) { this._direction = vec2.clone(xy); - this.updateTransform(); } get direction(): vec2 { @@ -116,7 +109,6 @@ export class Position2DLabel extends Label { setDirection(x: number, y: number): void { this._direction = vec2.fromValues(x, y); - this.updateTransform(); } } From abd645b09e6b6ca24cfb1ff1a38f9fab5f38b57a Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Tue, 2 Oct 2018 14:12:56 +0200 Subject: [PATCH 53/92] fix: direction for position2dlabel now uses signed angles; fix: cannot assign SpaceUnit.World to position2dlabel anymore --- source/labelrenderer.ts | 7 +++---- source/position2dlabel.ts | 28 ++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 5dba880a..050ca3b1 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -314,11 +314,10 @@ export class LabelRenderer extends Renderer { /** New scene; as OpenLL */ const pos2Dlabel = new Position2DLabel(new Text('Hello Position 2D!'), this._fontFace); - pos2Dlabel.fontSizeUnit = Label.SpaceUnit.Px; - pos2Dlabel.fontSize = 50; + pos2Dlabel.fontSize = 40; - pos2Dlabel.setPosition(100, 0); // position values in px, since fontSizeUnit is set to SpaceUni.Px - pos2Dlabel.setDirection(1, 0.5); + pos2Dlabel.setPosition(-100, 0); // position values in px, since fontSizeUnit is set to SpaceUni.Px + pos2Dlabel.setDirection(0.5, -0.5); let glyphVertices = pos2Dlabel.typeset(this._frameSize); diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index 813b17d9..e8095481 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -15,10 +15,6 @@ import { Typesetter } from './typesetter'; */ export class Position2DLabel extends Label { - /** @see {@link fontSizeUnit} */ - /** @todo allow only px or pt, not World */ - // protected _fontSizeUnit: Label.SpaceUnit = Label.SpaceUnit.World; - protected _position: vec2; protected _direction: vec2; @@ -31,6 +27,8 @@ export class Position2DLabel extends Label { super(text, fontFace); this._position = vec2.fromValues(0, 0); this._direction = vec2.fromValues(1, 0); + + this._fontSizeUnit = Label.SpaceUnit.Px; } typeset(frameSize: [number, number]): GlyphVertices { @@ -67,11 +65,16 @@ export class Position2DLabel extends Label { // apply user tranformations (position, direction) + mat4.translate(transform, transform, vec3.fromValues(this._position[0], this._position[1], 0)); - // TODO only returns positive angle! - const angle = vec2.angle(vec2.fromValues(1, 0), this._direction); + const n: vec2 = vec2.fromValues(1, 0); + let angle = vec2.angle(n, this._direction); + + // perp dot product for signed angle + if (n[0] * this._direction[1] - n[1] * this._direction[0] < 0) { + angle = -angle; + } - mat4.translate(transform, transform, vec3.fromValues(this._position[0], this._position[1], 0)); mat4.rotateZ(transform, transform, angle); this.transform = transform; @@ -95,7 +98,7 @@ export class Position2DLabel extends Label { /** position parameters as specified in OpenLL */ setPosition(x: number, y: number, unit?: Label.SpaceUnit): void { - // todo: assert that SpaceUnit is px or pt; transform to NDC + // todo: assert that SpaceUnit is px or pt; transform to NDC? this._position = vec2.fromValues(x, y); } @@ -110,5 +113,14 @@ export class Position2DLabel extends Label { setDirection(x: number, y: number): void { this._direction = vec2.fromValues(x, y); } + + /** + * This unit is used for the font size. + * This method overrides the super.fontSizeUnit, since a position2dlabel only allows px, not World. + * (@see {@link fontSize}) + */ + set fontSizeUnit(newUnit: Label.SpaceUnit) { + console.warn('New SpaceUnit not set; only allowed SpaceUnit is Px for this label.'); + } } From 600b532f1a15d09724985c6567949b90aa769b52 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Mon, 8 Oct 2018 09:55:05 +0200 Subject: [PATCH 54/92] typo fix --- source/labelrenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 050ca3b1..81ee277b 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -316,7 +316,7 @@ export class LabelRenderer extends Renderer { const pos2Dlabel = new Position2DLabel(new Text('Hello Position 2D!'), this._fontFace); pos2Dlabel.fontSize = 40; - pos2Dlabel.setPosition(-100, 0); // position values in px, since fontSizeUnit is set to SpaceUni.Px + pos2Dlabel.setPosition(-100, 0); // position values in px, since fontSizeUnit is set to SpaceUnit.Px pos2Dlabel.setDirection(0.5, -0.5); let glyphVertices = pos2Dlabel.typeset(this._frameSize); From ef18084f4d76635e18a48c18e2084c2f66a2605a Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Mon, 8 Oct 2018 16:53:49 +0200 Subject: [PATCH 55/92] Label in 3D space implemented, following the example of the OpenLL API --- source/labelrenderer.ts | 50 +++++++++++++---- source/position2dlabel.ts | 8 +-- source/position3dlabel.ts | 114 ++++++++++++++++++++++++++++++++++++++ source/webgl-operate.ts | 1 + 4 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 source/position3dlabel.ts diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 81ee277b..5ed39ad1 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -24,6 +24,7 @@ import { GlyphVertex, GlyphVertices } from './glyphvertices'; import { Label } from './label'; import { LabelGeometry } from './labelgeometry'; import { Position2DLabel } from './position2dlabel'; +import { Position3DLabel } from './position3dlabel'; import { Text } from './text'; import { Typesetter } from './typesetter'; @@ -311,7 +312,7 @@ export class LabelRenderer extends Renderer { protected setupScene(): void { - /** New scene; as OpenLL */ + /** OpenLL 2D Labels */ const pos2Dlabel = new Position2DLabel(new Text('Hello Position 2D!'), this._fontFace); pos2Dlabel.fontSize = 40; @@ -343,18 +344,46 @@ export class LabelRenderer extends Renderer { Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); - /** Old scene; soon to be "position3DLabel" */ + /** This is de deprecated way */ - // create Label with Text and - // tell the Typesetter to typeset that Label with the loaded FontFace - const userTransform = mat4.create(); - mat4.scale(userTransform, userTransform, vec3.fromValues(1.2, 1.2, 1.2)); - mat4.rotateZ(userTransform, userTransform, Math.PI * 0.5); - mat4.translate(userTransform, userTransform, vec3.fromValues(-0.1, 0.0, 0.3)); + // // create Label with Text and + // // tell the Typesetter to typeset that Label with the loaded FontFace + // const userTransform = mat4.create(); + // mat4.scale(userTransform, userTransform, vec3.fromValues(1.2, 1.2, 1.2)); + // mat4.rotateZ(userTransform, userTransform, Math.PI * 0.5); + // mat4.translate(userTransform, userTransform, vec3.fromValues(-0.1, 0.0, 0.3)); - glyphVertices = this.prepareLabel('Hello Transform!', userTransform); - glyphVertices = glyphVertices.concat(this.prepareLabel('Hello World!')); + // glyphVertices = this.prepareLabel('Hello Transform!', userTransform); + // glyphVertices = glyphVertices.concat(this.prepareLabel('Hello World!')); + + /** OpenLL 3D Labels */ + const pos3Dlabel = new Position3DLabel(new Text('Hello Position 3D!'), this._fontFace); + pos3Dlabel.fontSize = 0.1; + + // position values in world, since fontSizeUnit is set to SpaceUnit.World + pos3Dlabel.setPosition(0, 0.1, -0.5); + pos3Dlabel.setDirection(0, 1, 0); + pos3Dlabel.setUp(-1, 0, 0); + + glyphVertices = pos3Dlabel.typeset(); + + const shadowPos3Dlabel = new Position3DLabel(new Text('Hello Position Shadow'), this._fontFace); + shadowPos3Dlabel.setPosition(0, 0.1, -0.5); + shadowPos3Dlabel.fontSize = 0.1; + shadowPos3Dlabel.setDirection(0, 1, 0); + shadowPos3Dlabel.setUp(0, 0, -1); + + glyphVertices = glyphVertices.concat(shadowPos3Dlabel.typeset()); + + const anotherPos3Dlabel = new Position3DLabel(new Text('Yet another 3D Label'), this._fontFace); + anotherPos3Dlabel.setPosition(0.2, -0.1, 0); + anotherPos3Dlabel.setDirection(-1, 0, 0); + anotherPos3Dlabel.setUp(0, -1, 0); + glyphVertices = glyphVertices.concat(anotherPos3Dlabel.typeset()); + + + // fill buffers origins = []; tans = []; ups = []; @@ -377,6 +406,7 @@ export class LabelRenderer extends Renderer { } + /** THIS WILL BE DEPRECATED SOON: use subclasses of Label instead. */ protected prepareLabel(str: string, userTransform?: mat4): GlyphVertices { const label: Label = new Label(new Text(str), this._fontFace); diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index e8095481..ac0101d4 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -19,7 +19,7 @@ export class Position2DLabel extends Label { protected _direction: vec2; /** - * Constructs an unconfigured, empty label. + * Constructs a pre-configured 2D-label with given text * @param text - Valid context to create the object for. * @param identifier - Meaningful name for identification of this instances VAO and VBOs. */ @@ -32,7 +32,7 @@ export class Position2DLabel extends Label { } typeset(frameSize: [number, number]): GlyphVertices { - // TODO assert: this.fontSizeUnit === Label.SpaceUnit.Px + // TODO assert: this.fontSizeUnit === Label.SpaceUnit.Px or, later, === Label.SpaceUnit.Pt // TODO meaningful margins from label.margins or config.margins ? const margins: vec4 = vec4.create(); @@ -103,7 +103,7 @@ export class Position2DLabel extends Label { } set direction(xy: vec2) { - this._direction = vec2.clone(xy); + this._direction = vec2.normalize(this._direction, xy); } get direction(): vec2 { @@ -111,7 +111,7 @@ export class Position2DLabel extends Label { } setDirection(x: number, y: number): void { - this._direction = vec2.fromValues(x, y); + this.direction = vec2.fromValues(x, y); } /** diff --git a/source/position3dlabel.ts b/source/position3dlabel.ts new file mode 100644 index 00000000..55c5e19d --- /dev/null +++ b/source/position3dlabel.ts @@ -0,0 +1,114 @@ + +import { mat4, quat, vec2, vec3, vec4 } from 'gl-matrix'; + +import { ChangeLookup } from './changelookup'; +import { Color } from './color'; +import { FontFace } from './fontface'; +import { GlyphVertices } from './glyphvertices'; +import { Label } from './label'; +import { Text } from './text'; +import { Typesetter } from './typesetter'; + + +/** + * @todo + */ +export class Position3DLabel extends Label { + + protected _position: vec3; + protected _direction: vec3; + protected _up: vec3; + + /** + * Constructs a pre-configured 3D-label with given text. + * @param text - Valid context to create the object for. + * @param identifier - Meaningful name for identification of this instances VAO and VBOs. + */ + constructor(text: Text, fontFace: FontFace) { + super(text, fontFace); + this._position = vec3.fromValues(0, 0, 0); + this._direction = vec3.fromValues(1, 0, 0); + this._up = vec3.fromValues(0, 1, 0); + + this._fontSizeUnit = Label.SpaceUnit.World; + } + + typeset(): GlyphVertices { + // TODO assert: this.fontSizeUnit === Label.SpaceUnit.World + + const transform = mat4.create(); + + // default values; TODO: store default values in a constant, bc they are also needed for construction + const direction: vec3 = vec3.fromValues(1, 0, 0); + const up: vec3 = vec3.fromValues(0, 1, 0); + const newN = vec3.create(); + + // apply user tranformations (position, direction) + + mat4.translate(transform, mat4.create(), vec3.fromValues(this._position[0], this._position[1], 0)); + + vec3.cross(newN, this._direction, this._up); + + const rotation = mat4.fromValues(this.direction[0], this.direction[1], this.direction[2], 0, + this.up[0], this.up[1], this.up[2], 0, + newN[0], newN[1], newN[2], 0, + 0, 0, 0, 1); + + this.transform = mat4.mul(this.transform, transform, rotation); + + const vertices = this.prepareVertexStorage(); + Typesetter.typeset(this, vertices, 0); + + return vertices; + } + + /** + * Sets the 2D position of the label's reference point (i.e. lower left corner for horizontal alignment) + */ + set position(xyz: vec3) { + this._position = vec3.clone(xyz); + } + + get position(): vec3 { + return this._position; + } + + /** position parameters as specified in OpenLL */ + setPosition(x: number, y: number, z: number): void { + this._position = vec3.fromValues(x, y, z); + } + + set direction(xyz: vec3) { + this._direction = vec3.normalize(this._direction, xyz); + } + + get direction(): vec3 { + return this._direction; + } + + setDirection(x: number, y: number, z: number): void { + this.direction = vec3.fromValues(x, y, z); + } + + set up(xyz: vec3) { + this._up = vec3.normalize(this._up, xyz); + } + + get up(): vec3 { + return this._up; + } + + setUp(x: number, y: number, z: number): void { + this.up = vec3.fromValues(x, y, z); + } + + /** + * This unit is used for the font size. + * This method overrides the super.fontSizeUnit, since a position3dlabel only allows World, not px nor pt. + * (@see {@link fontSize}) + */ + set fontSizeUnit(newUnit: Label.SpaceUnit) { + console.warn('New SpaceUnit not set; only allowed SpaceUnit is World for this label.'); + } +} + diff --git a/source/webgl-operate.ts b/source/webgl-operate.ts index 1898e85d..67253d57 100644 --- a/source/webgl-operate.ts +++ b/source/webgl-operate.ts @@ -18,6 +18,7 @@ export { FontLoader } from './fontloader'; export { GlyphVertex, GlyphVertices } from './glyphvertices'; export { Label } from './label'; export { Position2DLabel } from './position2dlabel'; +export { Position3DLabel } from './position3dlabel'; export { LabelGeometry } from './labelgeometry'; export { Text } from './text'; export { Typesetter } from './typesetter'; From ca2800e9e1f68c903849bf63f7dc1df2a702e2c8 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Tue, 9 Oct 2018 10:23:39 +0200 Subject: [PATCH 56/92] documentation; removed unused code --- source/labelrenderer.ts | 83 +-------------------------------------- source/position2dlabel.ts | 10 +++-- source/position3dlabel.ts | 30 ++++++++------ 3 files changed, 24 insertions(+), 99 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 5ed39ad1..160ceef2 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -1,7 +1,7 @@ import { assert } from './auxiliaries'; -import { mat4, vec2, vec3, vec4 } from 'gl-matrix'; +import { mat4, vec3 } from 'gl-matrix'; import { AccumulatePass } from './accumulatepass'; import { AntiAliasingKernel } from './antialiasingkernel'; @@ -20,13 +20,10 @@ import { Texture2 } from './texture2'; import { FontFace } from './fontface'; import { FontLoader } from './fontloader'; -import { GlyphVertex, GlyphVertices } from './glyphvertices'; -import { Label } from './label'; import { LabelGeometry } from './labelgeometry'; import { Position2DLabel } from './position2dlabel'; import { Position3DLabel } from './position3dlabel'; import { Text } from './text'; -import { Typesetter } from './typesetter'; import { TestNavigation } from './debug/testnavigation'; @@ -343,20 +340,6 @@ export class LabelRenderer extends Renderer { this._2DLabelGeometry.setGlyphCoords( Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); - - /** This is de deprecated way */ - - // // create Label with Text and - // // tell the Typesetter to typeset that Label with the loaded FontFace - // const userTransform = mat4.create(); - // mat4.scale(userTransform, userTransform, vec3.fromValues(1.2, 1.2, 1.2)); - // mat4.rotateZ(userTransform, userTransform, Math.PI * 0.5); - // mat4.translate(userTransform, userTransform, vec3.fromValues(-0.1, 0.0, 0.3)); - - // glyphVertices = this.prepareLabel('Hello Transform!', userTransform); - // glyphVertices = glyphVertices.concat(this.prepareLabel('Hello World!')); - - /** OpenLL 3D Labels */ const pos3Dlabel = new Position3DLabel(new Text('Hello Position 3D!'), this._fontFace); pos3Dlabel.fontSize = 0.1; @@ -404,68 +387,4 @@ export class LabelRenderer extends Renderer { this._3DLabelGeometry.setGlyphCoords( Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); } - - - /** THIS WILL BE DEPRECATED SOON: use subclasses of Label instead. */ - protected prepareLabel(str: string, userTransform?: mat4): GlyphVertices { - - const label: Label = new Label(new Text(str), this._fontFace); - - const uT = userTransform !== undefined ? userTransform : mat4.create(); - - label.transform = uT; - - if (label.fontSizeUnit === Label.SpaceUnit.Px) { - // TODO meaningful margins from label.margins or config.margins ? - const margins: vec4 = vec4.create(); - // TODO meaningful ppiScale from label.ppiScale or config.ppiScale ? - const ppiScale = 1; - - // compute transform matrix - const transform = mat4.create(); - - // translate to lower left in NDC - mat4.scale(transform, transform, vec3.fromValues(1.0, this._frameSize[1] / this._frameSize[0], 1.0)); - mat4.translate(transform, transform, vec3.fromValues(-1.0, -1.0, 0.0)); - // scale glyphs to NDC size - // this._frameSize should be the viewport size - mat4.scale(transform, transform, vec3.fromValues(2.0 / this._frameSize[0], 2.0 / this._frameSize[1], 1.0)); - - // scale glyphs to pixel size with respect to the displays ppi - mat4.scale(transform, transform, vec3.fromValues(ppiScale, ppiScale, ppiScale)); - - // translate to origin in point space - scale origin within - // margined extend (i.e., viewport with margined areas removed) - const marginedExtent: vec2 = vec2.create(); - vec2.sub(marginedExtent, vec2.fromValues( - this._frameSize[0] / ppiScale, this._frameSize[1] / ppiScale), - vec2.fromValues(margins[3] + margins[1], margins[2] + margins[0])); - - const v3 = vec3.fromValues(0.5 * marginedExtent[0], 0.5 * marginedExtent[1], 0); - vec3.add(v3, v3, vec3.fromValues(margins[3], margins[2], 0.0)); - mat4.translate(transform, transform, v3); - - label.transform = mat4.mul(label.transform, uT, transform); - } - - const numGlyphs = label.length; - - // prepare vertex storage (values will be overridden by typesetter) - const vertices = new GlyphVertices(); - for (let i = 0; i < numGlyphs; ++i) { - - const vertex: GlyphVertex = { - origin: vec3.create(), - tangent: vec3.create(), - up: vec3.create(), - // vec2 lowerLeft and vec2 upperRight in glyph texture (uv) - uvRect: vec4.create(), - }; - vertices.push(vertex); - } - - Typesetter.typeset(label, vertices, 0); - - return vertices; - } } diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index ac0101d4..b9b7fb7c 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -1,8 +1,6 @@ import { mat4, vec2, vec3, vec4 } from 'gl-matrix'; -import { ChangeLookup } from './changelookup'; -import { Color } from './color'; import { FontFace } from './fontface'; import { GlyphVertices } from './glyphvertices'; import { Label } from './label'; @@ -11,7 +9,8 @@ import { Typesetter } from './typesetter'; /** - * @todo + * A Label that can be positioned in 2D space. + * The unit for positions, size and transformations, is pixel (px). */ export class Position2DLabel extends Label { @@ -86,7 +85,7 @@ export class Position2DLabel extends Label { } /** - * Sets the 2D position of the label's reference point (i.e. lower left corner for horizontal alignment) + * Sets the 2D position of the label's reference point (i.e. lower left corner for horizontal alignment). */ set position(xy: vec2) { this._position = vec2.clone(xy); @@ -102,6 +101,9 @@ export class Position2DLabel extends Label { this._position = vec2.fromValues(x, y); } + /** + * Sets the 2D direction of the label, i.e., the direction of the baseline. + */ set direction(xy: vec2) { this._direction = vec2.normalize(this._direction, xy); } diff --git a/source/position3dlabel.ts b/source/position3dlabel.ts index 55c5e19d..a1ff342b 100644 --- a/source/position3dlabel.ts +++ b/source/position3dlabel.ts @@ -1,8 +1,6 @@ -import { mat4, quat, vec2, vec3, vec4 } from 'gl-matrix'; +import { mat4, vec3 } from 'gl-matrix'; -import { ChangeLookup } from './changelookup'; -import { Color } from './color'; import { FontFace } from './fontface'; import { GlyphVertices } from './glyphvertices'; import { Label } from './label'; @@ -11,7 +9,8 @@ import { Typesetter } from './typesetter'; /** - * @todo + * A Label that can be positioned in 3D space. + * The unit for positions, size and transformations, is the abstract World Unit. */ export class Position3DLabel extends Label { @@ -37,21 +36,17 @@ export class Position3DLabel extends Label { // TODO assert: this.fontSizeUnit === Label.SpaceUnit.World const transform = mat4.create(); - - // default values; TODO: store default values in a constant, bc they are also needed for construction - const direction: vec3 = vec3.fromValues(1, 0, 0); - const up: vec3 = vec3.fromValues(0, 1, 0); - const newN = vec3.create(); + const normal = vec3.create(); // apply user tranformations (position, direction) mat4.translate(transform, mat4.create(), vec3.fromValues(this._position[0], this._position[1], 0)); - vec3.cross(newN, this._direction, this._up); + vec3.cross(normal, this._direction, this._up); const rotation = mat4.fromValues(this.direction[0], this.direction[1], this.direction[2], 0, this.up[0], this.up[1], this.up[2], 0, - newN[0], newN[1], newN[2], 0, + normal[0], normal[1], normal[2], 0, 0, 0, 0, 1); this.transform = mat4.mul(this.transform, transform, rotation); @@ -63,7 +58,7 @@ export class Position3DLabel extends Label { } /** - * Sets the 2D position of the label's reference point (i.e. lower left corner for horizontal alignment) + * Sets the 3D position of the label's reference point (i.e. lower left corner for horizontal alignment). */ set position(xyz: vec3) { this._position = vec3.clone(xyz); @@ -78,6 +73,9 @@ export class Position3DLabel extends Label { this._position = vec3.fromValues(x, y, z); } + /** + * Sets the 3D direction of the label, i.e., the direction of the baseline. + */ set direction(xyz: vec3) { this._direction = vec3.normalize(this._direction, xyz); } @@ -86,10 +84,15 @@ export class Position3DLabel extends Label { return this._direction; } + /** direction parameters as specified in OpenLL */ setDirection(x: number, y: number, z: number): void { this.direction = vec3.fromValues(x, y, z); } + /** + * Sets the up-vector of the label. + * It should be orthogonal to the direction to ensure that the label is not skewed. + */ set up(xyz: vec3) { this._up = vec3.normalize(this._up, xyz); } @@ -98,13 +101,14 @@ export class Position3DLabel extends Label { return this._up; } + /** up-vector parameters as specified in OpenLL */ setUp(x: number, y: number, z: number): void { this.up = vec3.fromValues(x, y, z); } /** * This unit is used for the font size. - * This method overrides the super.fontSizeUnit, since a position3dlabel only allows World, not px nor pt. + * This method overrides the super.fontSizeUnit, since a position3dlabel only allows World, not Px nor Pt. * (@see {@link fontSize}) */ set fontSizeUnit(newUnit: Label.SpaceUnit) { From 1cb2cf22b2dfe404bb19fda4c4a33f7c4d44b1d1 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Tue, 9 Oct 2018 12:22:18 +0200 Subject: [PATCH 57/92] fix: z-coordinate for 3D-Label was ignored --- source/position3dlabel.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/position3dlabel.ts b/source/position3dlabel.ts index a1ff342b..0a1edb25 100644 --- a/source/position3dlabel.ts +++ b/source/position3dlabel.ts @@ -40,7 +40,8 @@ export class Position3DLabel extends Label { // apply user tranformations (position, direction) - mat4.translate(transform, mat4.create(), vec3.fromValues(this._position[0], this._position[1], 0)); + mat4.translate(transform, mat4.create(), + vec3.fromValues(this._position[0], this._position[1], this._position[2])); vec3.cross(normal, this._direction, this._up); From 7e76c9368ba55da6d400d214f7137d64dc37b779 Mon Sep 17 00:00:00 2001 From: Daniel Limberger Date: Tue, 9 Oct 2018 14:23:55 +0200 Subject: [PATCH 58/92] Update pugconfig.js --- pugconfig.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pugconfig.js b/pugconfig.js index f2068332..d325dec9 100644 --- a/pugconfig.js +++ b/pugconfig.js @@ -20,9 +20,9 @@ const entries = ['index.pug']; const assets = [ [websiteDir, buildDir, ['css/*.css', 'js/*.js', 'img/*.{svg,png}', 'fonts/*', '*.{svg,png,ico,xml,json}'], [], false], ['./source', buildDir, ['data/{colorbrewer,smithwalt}.json'], [], false], - ['./source/data/', buildDir+'/data', ['*'], [], false], - ['./source/data/opensansr144', buildDir+'/data/opensansr144', ['*'], [], false], - ['./source/data/verdana', buildDir+'/data/verdana', ['*'], [], false], + ['./source/data/', `${buildDir}/data`, ['*'], [], false], + ['./source/data/opensansr144', `${buildDir}/data/opensansr144`, ['*'], [], false], + ['./source/data/verdana', `${buildDir}/data/verdana`, ['*'], [], false], ['./node_modules/rxjs/bundles/', `${buildDir}/js`, ['rxjs.umd.min.js'], [], false]]; From 9568e0a59a5990cad8812b5e0c338c826e1028b1 Mon Sep 17 00:00:00 2001 From: Daniel Limberger Date: Tue, 9 Oct 2018 14:25:10 +0200 Subject: [PATCH 59/92] Update pugconfig.js --- pugconfig.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pugconfig.js b/pugconfig.js index d325dec9..25421535 100644 --- a/pugconfig.js +++ b/pugconfig.js @@ -20,10 +20,11 @@ const entries = ['index.pug']; const assets = [ [websiteDir, buildDir, ['css/*.css', 'js/*.js', 'img/*.{svg,png}', 'fonts/*', '*.{svg,png,ico,xml,json}'], [], false], ['./source', buildDir, ['data/{colorbrewer,smithwalt}.json'], [], false], + ['./node_modules/rxjs/bundles/', `${buildDir}/js`, ['rxjs.umd.min.js'], [], false], ['./source/data/', `${buildDir}/data`, ['*'], [], false], ['./source/data/opensansr144', `${buildDir}/data/opensansr144`, ['*'], [], false], ['./source/data/verdana', `${buildDir}/data/verdana`, ['*'], [], false], - ['./node_modules/rxjs/bundles/', `${buildDir}/js`, ['rxjs.umd.min.js'], [], false]]; +]; var build_pending = false; From 848d8e5026c06ba6a825dd4b794c4f047aebd93d Mon Sep 17 00:00:00 2001 From: Daniel Limberger Date: Tue, 9 Oct 2018 15:49:49 +0200 Subject: [PATCH 60/92] Update gl-matrix-extensions.ts --- source/gl-matrix-extensions.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/gl-matrix-extensions.ts b/source/gl-matrix-extensions.ts index 6c2b57a7..9f3c4fef 100644 --- a/source/gl-matrix-extensions.ts +++ b/source/gl-matrix-extensions.ts @@ -217,7 +217,8 @@ namespace gl_matrix_extensions { } /** - * Constructs a vec3 from a vec4 with division by the w component applied. + * Constructs a vec3 from a vec4 with division by the w component applied. If the w component is zero, division + * skipped. * ``` * const v4: vec4 = vec4.fromValues(2, 4, 6, 2); * const v3: vec3 = fromVec4(v4); // v3 is [1, 2, 3] @@ -227,10 +228,8 @@ namespace gl_matrix_extensions { */ export function fromVec4(x: vec4): vec3 { if (x[3] === 0) { - // avoid division by 0 return vec3.fromValues(x[0], x[1], x[2]); } - return vec3.fromValues(x[0] / x[3], x[1] / x[3], x[2] / x[3]); } From 2654bf6d1cbe071ef0b1f4864c6ff54d0198682d Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 10:58:00 +0200 Subject: [PATCH 61/92] documentation of FontLoader, WIP: refactoring --- source/fontloader.ts | 149 ++++++++++++++++++++++++++++------------ source/labelrenderer.ts | 3 +- 2 files changed, 106 insertions(+), 46 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index c3938274..5d9d6f63 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -1,5 +1,5 @@ -import { assert } from './auxiliaries'; +import { logIf, LogLevel } from './auxiliaries'; import { Context } from './context'; import { fetchAsync } from './fetch'; @@ -17,13 +17,14 @@ function directoryPath(path: string): string { return ''; } - return path.substr(0, pos + 1); // Add trailing slash + /* Add trailing slash */ + return path.substr(0, pos + 1); } function stripped(input: string, blacklist: Array): string { let result: string = input; - // first param for erase: + /* first param for erase: */ blacklist.forEach((element) => { if (result.includes(element)) { result = result.replace(RegExp(element, 'g'), ''); @@ -40,41 +41,54 @@ function stripped(input: string, blacklist: Array): string { * * Example: * ``` - * let loader = new FontLoader(); - * let fontFace: FontFace = loader.load(this.context, 'font/opensansr144/opensansr144.fnt', false, callbackFunction); + * let fontFace: FontFace = FontLoader.load(this.context, 'font/yourFont.fnt', false, callbackFunction); * ``` */ export class FontLoader { - constructor() { - } - - // fontface: Objects and arrays are passed by reference - protected handleInfo(stream: Array, fontFace: FontFace): void { + /** + * Parses the info fields for padding values and stores them in the font face + * @param stream The stream of the 'info' identifier. + * @param fontFace The font face in which the padding is stored. + */ + protected static processInfo(stream: Array, fontFace: FontFace): void { const pairs = this.readKeyValuePairs(stream, ['padding']); const values = pairs.get('padding')!.split(','); - assert(values.length === 4, `handleInfo: expected 4 values for padding`); + logIf(values.length !== 4, LogLevel.Error, + `expected 4 values for padding, given ${values} (${values.length})`); const padding: GLfloat4 = [ - parseFloat(values[2]), // top - parseFloat(values[1]), // right - parseFloat(values[3]), // bottom - parseFloat(values[0]), // left + /* top */ + parseFloat(values[2]), + /* right */ + parseFloat(values[1]), + /* bottom */ + parseFloat(values[3]), + /* left */ + parseFloat(values[0]), ]; fontFace.glyphTexturePadding = padding; } - protected handleCommon(stream: Array, fontFace: FontFace): void { + /** + * Parses the common fields for lineHeight, base, ascent, descent, scaleW and scaleH to store them + * in the font face. + * @param stream The stream of the 'common' identifier. + * @param fontFace The font face in which the parsed values are stored. + */ + protected static processCommon(stream: Array, fontFace: FontFace): void { const pairs = this.readKeyValuePairs(stream, ['lineHeight', 'base', 'ascent', 'descent', 'scaleW', 'scaleH']); fontFace.base = parseFloat(pairs.get('base')!); fontFace.ascent = parseFloat(pairs.get('ascent')!); fontFace.descent = parseFloat(pairs.get('descent')!); - assert(fontFace.size > 0.0, `fontFace.size is not greater than 0.`); + logIf(fontFace.size > 0.0, LogLevel.Error, + `expected fontFace.size to be greater than 0, given ${fontFace.size}`); + fontFace.lineHeight = parseFloat(pairs.get('lineHeight')!); fontFace.glyphTextureExtent = [ @@ -83,8 +97,15 @@ export class FontLoader { ]; } - protected handlePage( - stream: Array, fontFace: FontFace, filename: string, context: Context): Promise { + /** + * Parses a page to load the associated png-file, i.e., the glyph atlas. + * @param stream The stream of the 'page' identifier. + * @param fontFace The font face in which the loaded glyph texture is stored. + * @param filename The file name to find the png-file. + * @returns Promise for handling image load status. + */ + protected static processPage( + stream: Array, fontFace: FontFace, filename: string): Promise { const pairs = this.readKeyValuePairs(stream, ['file']); @@ -96,12 +117,20 @@ export class FontLoader { return fontFace.glyphTexture.load(pngPath); } - protected handleChar(stream: Array, fontFace: FontFace): void { + /** + * Parses the char fields for character id, x, y, width, height, xoffset, yoffset, xadvance to + * store them in the font face. Relies on fontFace.base and fontFace.glyphTextureExtent, so + * execute processCommon() first. + * @param stream The stream of the 'char' identifier. + * @param fontFace The font face in which the loaded glyph texture is stored. + */ + protected static processChar(stream: Array, fontFace: FontFace): void { const pairs = this.readKeyValuePairs(stream, ['id', 'x', 'y', 'width', 'height', 'xoffset', 'yoffset', 'xadvance']); const index: number = parseInt(pairs.get('id')!, 10); - assert(index > 0, `handleChar: glyph index is not greater than 0.`); + logIf(index > 0.0, LogLevel.Error, + `expected glyph index to be greater than 0, given ${index}`); const glyph = new Glyph(); @@ -136,21 +165,34 @@ export class FontLoader { fontFace.addGlyph(glyph); } - protected handleKerning(stream: Array, fontFace: FontFace): void { + /** + * Parses the kerning fields for first and second character and the amount, to store them in the font face. + * @param stream The stream of the 'kerning' identifier. + * @param fontFace The font face in which the kerning tuples are stored. + */ + protected static processKerning(stream: Array, fontFace: FontFace): void { const pairs = this.readKeyValuePairs(stream, ['first', 'second', 'amount']); const first: number = parseInt(pairs.get('first')!, 10); - assert(first > 0, `handleKerning: first is not greater than 0.`); + logIf(first > 0.0, LogLevel.Error, + `expected kerning's first to be greater than 0, given ${first}`); const second: number = parseInt(pairs.get('second')!, 10); - assert(second > 0, `handleKerning: second is not greater than 0.`); + logIf(second > 0.0, LogLevel.Error, + `expected kerning's first to be greater than 0, given ${second}`); const kerning: number = parseFloat(pairs.get('amount')!); fontFace.setKerning(first, second, kerning); } - protected readKeyValuePairs(stream: Array, mandatoryKeys: Array): StringPairs { + /** + * Parses to find key-value pairs for given mandatory keys. + * @param stream The stream from which the pairs should be read. + * @param mandatoryKeys The found pairs are only valid if the mandatory keys are found. + * @returns key-value pairs, or an empty map if not all mandatory keys are found. + */ + protected static readKeyValuePairs(stream: Array, mandatoryKeys: Array): StringPairs { let key: string; let value: string; @@ -164,7 +206,7 @@ export class FontLoader { pairs.set(key, value); } - // check if all required keys are provided + /* check if all required keys are provided */ let valid = true; for (const mandatoryKey of mandatoryKeys) { valid = valid && pairs.has(mandatoryKey); @@ -172,24 +214,27 @@ export class FontLoader { if (!valid) { console.warn('Not all required keys are provided! Mandatory keys: ' + mandatoryKeys); - return new Map(); // typescript does not allow writing `new StringPairs()` + /* typescript does not allow writing `new StringPairs()` */ + return new Map(); } else { return pairs; } } /** - * @param context - * @param filename path to a .fnt file - * @param headless + * Asynchronously loads a fnt-file and a png-file of the same name, to create a font face from them. + * @param context The WebGL rendering context. + * @param filename The path to the fnt-file. + * @param headless Boolean for headless mode. * @param onImageLoad Callback is called when the glyph atlas is loaded. */ - async load(context: Context, filename: string, headless: boolean): Promise { + static async load(context: Context, filename: string, headless: boolean): Promise { const fontFace = new FontFace(context); try { - const text = await fetchAsync(filename, (text) => text); - // promise fulfilled + const text = await fetchAsync(filename, '', (text) => text); + + /* promise fulfilled */ const lines = text.split('\n'); const promises = []; @@ -198,22 +243,38 @@ export class FontLoader { const identifier = line[0]; line = line.slice(1); - if (identifier === 'info') { - this.handleInfo(line, fontFace); - } else if (identifier === 'common') { - this.handleCommon(line, fontFace); - } else if (identifier === 'page' && !headless) { - promises.push(this.handlePage(line, fontFace, filename, context)); - } else if (identifier === 'char') { - this.handleChar(line, fontFace); - } else if (identifier === 'kerning') { - this.handleKerning(line, fontFace); + switch (identifier) { + case 'info': { + this.processInfo(line, fontFace); + break; + } + case 'common': { + this.processCommon(line, fontFace); + break; + } + case 'page': { + if (!headless) { + promises.push(this.processPage(line, fontFace, filename)); + } + break; + } + case 'char': { + this.processChar(line, fontFace); + break; + } + case 'kerning': { + this.processKerning(line, fontFace); + break; + } + default: { + break; + } } } await Promise.all(promises); } catch (e) { - // promise rejected + /* promise rejected */ console.error('ERROR: Could not load font file. filename is: ' + filename); } diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 160ceef2..953dba18 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -292,12 +292,11 @@ export class LabelRenderer extends Renderer { } protected loadFont(context: Context): void { - const loader = new FontLoader(); // This is a placeholder until the 'real' fontFace is loaded asynchronously by the fontLoader const fontFace: FontFace = new FontFace(context); - loader.load(context, './data/opensansr144/opensansr144.fnt', false).then( + FontLoader.load(context, './data/opensansr144/opensansr144.fnt', false).then( (fontFace) => { this._fontFace = fontFace; this.setupScene(); From a5dc2ab2cd3932bcd8eb5e03d68098c8add6474a Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 11:06:47 +0200 Subject: [PATCH 62/92] log instead of console --- source/fontloader.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index 5d9d6f63..d214e3ef 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -1,5 +1,5 @@ -import { logIf, LogLevel } from './auxiliaries'; +import { log, logIf, LogLevel } from './auxiliaries'; import { Context } from './context'; import { fetchAsync } from './fetch'; @@ -213,7 +213,7 @@ export class FontLoader { } if (!valid) { - console.warn('Not all required keys are provided! Mandatory keys: ' + mandatoryKeys); + log(LogLevel.Warning, `Not all required keys are provided! Mandatory keys: ${mandatoryKeys}`); /* typescript does not allow writing `new StringPairs()` */ return new Map(); } else { @@ -275,7 +275,7 @@ export class FontLoader { } catch (e) { /* promise rejected */ - console.error('ERROR: Could not load font file. filename is: ' + filename); + log(LogLevel.Error, `Could not load font file. filename is: ${filename}`); } // TODO: assert? throw exception? From 8cc2904a22f481875f6f5bacfa4478811b7721ec Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 11:08:54 +0200 Subject: [PATCH 63/92] rename: setBearing --> bearingFromFontBaseAndOffset --- source/fontloader.ts | 2 +- source/glyph.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index d214e3ef..680c8e3b 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -155,7 +155,7 @@ export class FontLoader { glyph.subTextureExtent[0] = extent[0] * extentScale[0]; glyph.subTextureExtent[1] = extent[1] * extentScale[1]; - glyph.setBearing(fontFace.base, + glyph.bearingFromFontBaseAndOffset(fontFace.base, parseFloat(pairs.get('xoffset')!), parseFloat(pairs.get('yoffset')!), ); diff --git a/source/glyph.ts b/source/glyph.ts index 85c01193..0ca88658 100644 --- a/source/glyph.ts +++ b/source/glyph.ts @@ -134,7 +134,7 @@ export class Glyph { * @param yOffset - The glyphs vertical offset w.r.t. the font's topmost ascenders, without the font's top * padding in pt. */ - setBearing(fontBase: GLfloat, xOffset: GLfloat, yOffset: GLfloat): void { + bearingFromFontBaseAndOffset(fontBase: GLfloat, xOffset: GLfloat, yOffset: GLfloat): void { this._bearing[0] = xOffset; this._bearing[1] = fontBase - yOffset; } From 09ad8027ae815847f43ad8679f38bb45963652a2 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 11:15:07 +0200 Subject: [PATCH 64/92] for of --> forEach --- source/fontloader.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index 680c8e3b..1c789a8a 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -208,9 +208,7 @@ export class FontLoader { /* check if all required keys are provided */ let valid = true; - for (const mandatoryKey of mandatoryKeys) { - valid = valid && pairs.has(mandatoryKey); - } + mandatoryKeys.forEach((key) => valid = valid && pairs.has(key)); if (!valid) { log(LogLevel.Warning, `Not all required keys are provided! Mandatory keys: ${mandatoryKeys}`); From 1cb501af96a380247d6f577f3b04dbebb632925c Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 11:21:08 +0200 Subject: [PATCH 65/92] fix: logIfs had bad condition --- source/fontloader.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index 1c789a8a..a053929b 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -86,7 +86,7 @@ export class FontLoader { fontFace.ascent = parseFloat(pairs.get('ascent')!); fontFace.descent = parseFloat(pairs.get('descent')!); - logIf(fontFace.size > 0.0, LogLevel.Error, + logIf(fontFace.size <= 0.0, LogLevel.Error, `expected fontFace.size to be greater than 0, given ${fontFace.size}`); fontFace.lineHeight = parseFloat(pairs.get('lineHeight')!); @@ -129,7 +129,7 @@ export class FontLoader { ['id', 'x', 'y', 'width', 'height', 'xoffset', 'yoffset', 'xadvance']); const index: number = parseInt(pairs.get('id')!, 10); - logIf(index > 0.0, LogLevel.Error, + logIf(index <= 0.0, LogLevel.Error, `expected glyph index to be greater than 0, given ${index}`); const glyph = new Glyph(); @@ -174,11 +174,11 @@ export class FontLoader { const pairs = this.readKeyValuePairs(stream, ['first', 'second', 'amount']); const first: number = parseInt(pairs.get('first')!, 10); - logIf(first > 0.0, LogLevel.Error, + logIf(first < 0.0, LogLevel.Error, `expected kerning's first to be greater than 0, given ${first}`); const second: number = parseInt(pairs.get('second')!, 10); - logIf(second > 0.0, LogLevel.Error, + logIf(second < 0.0, LogLevel.Error, `expected kerning's first to be greater than 0, given ${second}`); const kerning: number = parseFloat(pairs.get('amount')!); From f87879b242717d69d75a53afbdbe06372a93a1b8 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 11:36:03 +0200 Subject: [PATCH 66/92] documentation for idea on how to treat multiple glyph atlas pages --- source/fontloader.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/fontloader.ts b/source/fontloader.ts index a053929b..6a14f345 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -112,6 +112,11 @@ export class FontLoader { const path = directoryPath(filename); const file = stripped(pairs.get('file')!, ['"', '\r']); + /** For multiple pages, the filename should be different. + * Idea: Provide a name for the font file, and a template for the page file name + * where the page number is replaced, e.g.: + * `foo/bar/-.png` + */ const pngPath: string = path + file.split('.')[0] + '.png'; return fontFace.glyphTexture.load(pngPath); From 16afadd27e24004d44dd31e08baf0bccde28e0f3 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 12:03:09 +0200 Subject: [PATCH 67/92] // --> /* */ --- source/fontface.ts | 2 +- source/fontloader.ts | 4 +--- source/label.ts | 9 ++++++--- source/labelgeometry.ts | 21 ++++++++++++--------- source/labelrenderer.ts | 16 ++++++++-------- source/position2dlabel.ts | 33 +++++++++++++++++++-------------- source/position3dlabel.ts | 9 +++++++-- source/shaders/glyphquad.frag | 13 +++++++------ source/shaders/glyphquad.vert | 15 +++++++++------ source/typesetter.ts | 4 ++-- 10 files changed, 72 insertions(+), 54 deletions(-) diff --git a/source/fontface.ts b/source/fontface.ts index 064fa81b..6f742c66 100644 --- a/source/fontface.ts +++ b/source/fontface.ts @@ -76,7 +76,7 @@ export class FontFace { * @return - The font size in pt (ascent + descent). */ get size(): number { - // Note: this._descent is usually negative. + /* Note: this._descent is usually negative. */ return this._ascent - this._descent; } diff --git a/source/fontloader.ts b/source/fontloader.ts index 6a14f345..5db8dda9 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -246,6 +246,7 @@ export class FontLoader { const identifier = line[0]; line = line.slice(1); + /* tslint:disable-next-line:switch-default */ switch (identifier) { case 'info': { this.processInfo(line, fontFace); @@ -269,9 +270,6 @@ export class FontLoader { this.processKerning(line, fontFace); break; } - default: { - break; - } } } await Promise.all(promises); diff --git a/source/label.ts b/source/label.ts index 7ecbf57a..80cf61a5 100644 --- a/source/label.ts +++ b/source/label.ts @@ -82,7 +82,7 @@ export class Label { origin: vec3.create(), tangent: vec3.create(), up: vec3.create(), - // vec2 lowerLeft and vec2 upperRight in glyph texture (uv) + /* vec2 lowerLeft and vec2 upperRight in glyph texture (uv) */ uvRect: vec4.create(), }; vertices.push(vertex); @@ -398,8 +398,11 @@ export namespace Label { * This unit is used for the font size. */ export enum SpaceUnit { - World = 'world', // abstract world unit - Px = 'px', // screen pixel + /* abstract world unit */ + World = 'world', + /* screen pixel */ + Px = 'px', + /** @todo Pt for point unit */ } } diff --git a/source/labelgeometry.ts b/source/labelgeometry.ts index d99a0a3c..29294c10 100644 --- a/source/labelgeometry.ts +++ b/source/labelgeometry.ts @@ -48,19 +48,20 @@ export class LabelGeometry extends Geometry { const gl2facade = this.context.gl2facade; /* Please note the implicit bind in attribEnable */ - // quadVertex + + /* quadVertex */ this._buffers[0].attribEnable(indices[0], 2, gl.FLOAT, false, 0, 0, true, false); gl2facade.vertexAttribDivisor(indices[0], 0); - // texCoords + /* texCoords */ this._buffers[1].attribEnable(indices[1], 4, gl.FLOAT, false, 4 * 4, 0, true, false); gl2facade.vertexAttribDivisor(indices[1], 1); - // origin + /* origin */ this._buffers[2].attribEnable(indices[2], 3, gl.FLOAT, false, 3 * 4, 0, true, false); gl2facade.vertexAttribDivisor(indices[2], 1); - // tan + /* tan */ this._buffers[3].attribEnable(indices[3], 3, gl.FLOAT, false, 3 * 4, 0, true, false); gl2facade.vertexAttribDivisor(indices[3], 1); - // up + /* up */ this._buffers[4].attribEnable(indices[4], 3, gl.FLOAT, false, 3 * 4, 0, true, false); gl2facade.vertexAttribDivisor(indices[4], 1); } @@ -103,8 +104,10 @@ export class LabelGeometry extends Geometry { [gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER] , [aQuadVertex, aTexCoord, aOrigin, aTan, aUp]); - // These vertices are equal for all quads. There actual position will be changed using - // origin, tan(gent) and up(-vector). + /** + * These vertices are equal for all quads. There actual position will be changed using + * origin, tan(gent) and up(-vector). + */ this._vertices = Float32Array.from([0, 0, 0, 1, 1, 0, 1, 1]); this._buffers[0].data(this._vertices, gl.STATIC_DRAW); @@ -133,7 +136,7 @@ export class LabelGeometry extends Geometry { this._ups = dataUp; const gl = this.context.gl; - // TODO: is DYNAMIC_DRAW more appropriate? + /** @todo is DYNAMIC_DRAW more appropriate? */ this._buffers[2].data(this._origins, gl.STATIC_DRAW); this._buffers[3].data(this._tans, gl.STATIC_DRAW); this._buffers[4].data(this._ups, gl.STATIC_DRAW); @@ -151,7 +154,7 @@ export class LabelGeometry extends Geometry { this._texCoords = data; const gl = this.context.gl; - // TODO: is DYNAMIC_DRAW more appropriate? + /** @todo is DYNAMIC_DRAW more appropriate? */ this._buffers[1].data(this._texCoords, gl.STATIC_DRAW); } diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 953dba18..a034e481 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -145,7 +145,7 @@ export class LabelRenderer extends Renderer { this._camera.near = 0.1; this._camera.far = 8.0; - // Initialize navigation + /* Initialize navigation */ this._navigation = new Navigation(callback, mouseEventProvider); this._navigation.camera = this._camera; @@ -199,7 +199,7 @@ export class LabelRenderer extends Renderer { this._intermediateFBO.resize(this._frameSize[0], this._frameSize[1]); this._camera.viewport = [this._frameSize[0], this._frameSize[1]]; this._camera.aspect = this._frameSize[0] / this._frameSize[1]; - /* TODO + /** @todo * update the geometry of the labels that use pt sizes (e.g. labels in screen space) * and/or update: labels that get too small (to be readable) should not be rendered anymore * (a.k.a. threshold for readability) @@ -293,7 +293,7 @@ export class LabelRenderer extends Renderer { protected loadFont(context: Context): void { - // This is a placeholder until the 'real' fontFace is loaded asynchronously by the fontLoader + /* This is a placeholder until the 'real' fontFace is loaded asynchronously by the fontLoader */ const fontFace: FontFace = new FontFace(context); FontLoader.load(context, './data/opensansr144/opensansr144.fnt', false).then( @@ -313,12 +313,13 @@ export class LabelRenderer extends Renderer { const pos2Dlabel = new Position2DLabel(new Text('Hello Position 2D!'), this._fontFace); pos2Dlabel.fontSize = 40; - pos2Dlabel.setPosition(-100, 0); // position values in px, since fontSizeUnit is set to SpaceUnit.Px + /* position values in px, since fontSizeUnit is set to SpaceUnit.Px */ + pos2Dlabel.setPosition(-100, 0); pos2Dlabel.setDirection(0.5, -0.5); let glyphVertices = pos2Dlabel.typeset(this._frameSize); - // fill buffers + /* fill buffers */ let origins: Array = []; let tans: Array = []; let ups: Array = []; @@ -343,7 +344,7 @@ export class LabelRenderer extends Renderer { const pos3Dlabel = new Position3DLabel(new Text('Hello Position 3D!'), this._fontFace); pos3Dlabel.fontSize = 0.1; - // position values in world, since fontSizeUnit is set to SpaceUnit.World + /* position values in world, since fontSizeUnit is set to SpaceUnit.World */ pos3Dlabel.setPosition(0, 0.1, -0.5); pos3Dlabel.setDirection(0, 1, 0); pos3Dlabel.setUp(-1, 0, 0); @@ -364,8 +365,7 @@ export class LabelRenderer extends Renderer { anotherPos3Dlabel.setUp(0, -1, 0); glyphVertices = glyphVertices.concat(anotherPos3Dlabel.typeset()); - - // fill buffers + /* fill buffers */ origins = []; tans = []; ups = []; diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index b9b7fb7c..8752bac1 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -30,29 +30,34 @@ export class Position2DLabel extends Label { this._fontSizeUnit = Label.SpaceUnit.Px; } + /** + * Applies its position and direction, then prepares the vertex storage so that + * the Typesetter can typeset this label. + * @param frameSize The width and height of the frame, so that sizes can be calculated to use pixel units. + * @returns The transformed glyph vertices. + */ typeset(frameSize: [number, number]): GlyphVertices { - // TODO assert: this.fontSizeUnit === Label.SpaceUnit.Px or, later, === Label.SpaceUnit.Pt + /** @todo assert: this.fontSizeUnit === Label.SpaceUnit.Px or, later, === Label.SpaceUnit.Pt */ - // TODO meaningful margins from label.margins or config.margins ? + /** @todo meaningful margins from label.margins or config.margins ? */ const margins: vec4 = vec4.create(); - // TODO meaningful ppiScale from label.ppiScale or config.ppiScale ? + /** @todo meaningful ppiScale from label.ppiScale or config.ppiScale ? */ const ppiScale = 1; - // compute transform matrix + /* compute transform matrix */ const transform = mat4.create(); - // translate to lower left in NDC - // mat4.scale(transform, transform, vec3.fromValues(1.0, frameSize[1] / frameSize[0], 1.0)); + /* translate to lower left in NDC */ mat4.translate(transform, transform, vec3.fromValues(-1.0, -1.0, 0.0)); - // scale glyphs to NDC size - // this._frameSize should be the viewport size + /* scale glyphs to NDC size, this._frameSize should be the viewport size */ mat4.scale(transform, transform, vec3.fromValues(2.0 / frameSize[0], 2.0 / frameSize[1], 1.0)); - // scale glyphs to pixel size with respect to the displays ppi + /* scale glyphs to pixel size with respect to the displays ppi */ mat4.scale(transform, transform, vec3.fromValues(ppiScale, ppiScale, ppiScale)); - // translate to origin in point space - scale origin within - // margined extend (i.e., viewport with margined areas removed) + /* translate to origin in point space - scale origin within margined extend + * (i.e., viewport with margined areas removed) + */ const marginedExtent: vec2 = vec2.create(); vec2.sub(marginedExtent, vec2.fromValues( frameSize[0] / ppiScale, frameSize[1] / ppiScale), @@ -63,13 +68,13 @@ export class Position2DLabel extends Label { mat4.translate(transform, transform, v3); - // apply user tranformations (position, direction) + /* apply user tranformations (position, direction) */ mat4.translate(transform, transform, vec3.fromValues(this._position[0], this._position[1], 0)); const n: vec2 = vec2.fromValues(1, 0); let angle = vec2.angle(n, this._direction); - // perp dot product for signed angle + /* perp dot product for signed angle */ if (n[0] * this._direction[1] - n[1] * this._direction[0] < 0) { angle = -angle; } @@ -97,7 +102,7 @@ export class Position2DLabel extends Label { /** position parameters as specified in OpenLL */ setPosition(x: number, y: number, unit?: Label.SpaceUnit): void { - // todo: assert that SpaceUnit is px or pt; transform to NDC? + /** @todo assert that SpaceUnit is px or pt; transform to NDC? */ this._position = vec2.fromValues(x, y); } diff --git a/source/position3dlabel.ts b/source/position3dlabel.ts index 0a1edb25..9860fcdd 100644 --- a/source/position3dlabel.ts +++ b/source/position3dlabel.ts @@ -32,13 +32,18 @@ export class Position3DLabel extends Label { this._fontSizeUnit = Label.SpaceUnit.World; } + /** + * Applies its position, direction and up-vector, then prepares the vertex storage so that + * the Typesetter can typeset this label. + * @returns The transformed glyph vertices. + */ typeset(): GlyphVertices { - // TODO assert: this.fontSizeUnit === Label.SpaceUnit.World + /** @todo assert: this.fontSizeUnit === Label.SpaceUnit.World */ const transform = mat4.create(); const normal = vec3.create(); - // apply user tranformations (position, direction) + /* apply user tranformations (position, direction) */ mat4.translate(transform, mat4.create(), vec3.fromValues(this._position[0], this._position[1], this._position[2])); diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index 6acbe0bd..64d1a51e 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -5,7 +5,7 @@ precision mediump float; #if __VERSION__ == 100 #define fragColor gl_FragColor #extension GL_OES_standard_derivatives : enable -#else +#else layout(location = 0) out vec4 fragColor; #endif @@ -16,7 +16,7 @@ varying vec2 v_texture_coord; void main(void) { - //requires blend: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + /* requires blend: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); */ float d = texture(u_glyphs, v_texture_coord).r; @@ -29,10 +29,11 @@ void main(void) if(d < 0.45) discard; - vec4 fc = vec4(0.0, 0.0, 0.0, 1.0); //black. TODO: font color as vertex attrib or uniform? + /** black. @todo font color as vertex attrib or uniform */ + vec4 fc = vec4(0.0, 0.0, 0.0, 1.0); - // TODO mipmap access? - float a = step(0.5, d); //simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur + /** @todo mipmap access? */ + /* simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur */ + float a = step(0.5, d); fragColor = vec4(fc.rgb, fc.a * a); - } diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 5039aa59..96cf2375 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -6,13 +6,15 @@ precision lowp int; #if __VERSION__ == 100 #extension GL_EXT_draw_buffers : enable attribute vec2 a_quadVertex; -attribute vec4 a_texCoord; // [ texture ll: vec2, ur: vec2 ] +/* [ texture ll: vec2, ur: vec2 ] */ +attribute vec4 a_texCoord; attribute vec3 a_origin; attribute vec3 a_tan; attribute vec3 a_up; #else layout(location = 0) in vec2 a_quadVertex; -layout(location = 1) in vec4 a_texCoord; // [ texture ll: vec2, ur: vec2 ] +/* [ texture ll: vec2, ur: vec2 ]*/ +layout(location = 1) in vec4 a_texCoord; layout(location = 2) in vec3 a_origin; layout(location = 3) in vec3 a_tan; layout(location = 4) in vec3 a_up; @@ -27,8 +29,9 @@ varying vec2 v_texture_coord; void main(void) { - //TEXTURE COORDS - // flip y-coordinates + /* TEXTURE COORDS */ + + /* flip y-coordinates */ float posX = a_texCoord[0]; float posY = a_texCoord[1]; @@ -39,8 +42,8 @@ void main(void) v_texture_coord = a_quadVertex * texExt + vec2(a_texCoord[0], 1.0-a_texCoord[1]); - //POSITIONING - // quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) + /* POSITIONING*/ + /* quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) */ vec4 vertex = vec4(a_origin + a_quadVertex.x*a_tan + a_quadVertex.y*a_up, 1.0); diff --git a/source/typesetter.ts b/source/typesetter.ts index 4bf4eca9..16efafd5 100644 --- a/source/typesetter.ts +++ b/source/typesetter.ts @@ -31,7 +31,7 @@ export class Typesetter { if (index < safeForwardIndex) { return false; } - // tslint:disable-next-line:prefer-const + /* tslint:disable-next-line:prefer-const */ let forwardWidth = 0.0; safeForwardIndex = Typesetter.forward(label, index, forwardWidth); return forwardWidth <= lineWidth && (pen[0] + forwardWidth) > lineWidth; @@ -242,7 +242,7 @@ export class Typesetter { const iEnd: number = label.length; /* Index used to reduce the number of wordwrap forward passes. */ - // tslint:disable-next-line:prefer-const + /* tslint:disable-next-line:prefer-const */ let safeForwardIndex = iBegin; let feedVertexIndex: number = vertexIndex; From f45b10a2a372e3ccb6678ceaf73b5411dbd5635d Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 15:43:07 +0200 Subject: [PATCH 68/92] use path module instead of homebrew function --- source/fontloader.ts | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index 5db8dda9..1738b59a 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -7,19 +7,10 @@ import { FontFace } from './fontface'; import { Glyph } from './glyph'; import { GLfloat2, GLfloat4 } from './tuples'; +import Path = require('path'); -type StringPairs = Map; - -function directoryPath(path: string): string { - const pos = path.lastIndexOf('/'); - - if (pos < 0) { - return ''; - } - /* Add trailing slash */ - return path.substr(0, pos + 1); -} +type StringPairs = Map; function stripped(input: string, blacklist: Array): string { let result: string = input; @@ -108,10 +99,10 @@ export class FontLoader { stream: Array, fontFace: FontFace, filename: string): Promise { const pairs = this.readKeyValuePairs(stream, ['file']); - - const path = directoryPath(filename); const file = stripped(pairs.get('file')!, ['"', '\r']); + const path = Path.dirname(filename) + `/`; + /** For multiple pages, the filename should be different. * Idea: Provide a name for the font file, and a template for the page file name * where the page number is replaced, e.g.: From a326c592b10d114252a74182b807d235fdaf1000 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 16:11:17 +0200 Subject: [PATCH 69/92] error handling for when not all mandatory keys are provided --- source/fontloader.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index 1738b59a..a6e93ae4 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -206,13 +206,9 @@ export class FontLoader { let valid = true; mandatoryKeys.forEach((key) => valid = valid && pairs.has(key)); - if (!valid) { - log(LogLevel.Warning, `Not all required keys are provided! Mandatory keys: ${mandatoryKeys}`); - /* typescript does not allow writing `new StringPairs()` */ - return new Map(); - } else { - return pairs; - } + logIf(!valid, LogLevel.Error, `Not all required keys are provided! Mandatory keys: ${mandatoryKeys}`); + + return pairs; } /** From 26d51277bf54c2176f4e089f6134b3316ca69b58 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 17:06:13 +0200 Subject: [PATCH 70/92] shorter try-catch block and more early-returns for fontloader loading-steps --- source/fontloader.ts | 188 +++++++++++++++++++++++++++---------------- 1 file changed, 118 insertions(+), 70 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index a6e93ae4..d5d409d7 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -41,14 +41,23 @@ export class FontLoader { * Parses the info fields for padding values and stores them in the font face * @param stream The stream of the 'info' identifier. * @param fontFace The font face in which the padding is stored. + * @returns success */ - protected static processInfo(stream: Array, fontFace: FontFace): void { - const pairs = this.readKeyValuePairs(stream, ['padding']); + protected static processInfo(stream: Array, fontFace: FontFace): boolean { + + const pairs: StringPairs = new Map(); + const success = this.readKeyValuePairs(stream, ['padding'], pairs); + + if (!success) { + return false; + } const values = pairs.get('padding')!.split(','); - logIf(values.length !== 4, LogLevel.Error, - `expected 4 values for padding, given ${values} (${values.length})`); + if (values.length !== 4) { + log(LogLevel.Error, `expected 4 values for padding, given ${values} (${values.length})`); + return false; + } const padding: GLfloat4 = [ /* top */ @@ -62,6 +71,8 @@ export class FontLoader { ]; fontFace.glyphTexturePadding = padding; + + return true; } /** @@ -69,16 +80,26 @@ export class FontLoader { * in the font face. * @param stream The stream of the 'common' identifier. * @param fontFace The font face in which the parsed values are stored. + * @returns success */ - protected static processCommon(stream: Array, fontFace: FontFace): void { - const pairs = this.readKeyValuePairs(stream, ['lineHeight', 'base', 'ascent', 'descent', 'scaleW', 'scaleH']); + protected static processCommon(stream: Array, fontFace: FontFace): boolean { + + const pairs: StringPairs = new Map(); + const success = this.readKeyValuePairs(stream, + ['lineHeight', 'base', 'ascent', 'descent', 'scaleW', 'scaleH'], pairs); + + if (!success) { + return false; + } fontFace.base = parseFloat(pairs.get('base')!); fontFace.ascent = parseFloat(pairs.get('ascent')!); fontFace.descent = parseFloat(pairs.get('descent')!); - logIf(fontFace.size <= 0.0, LogLevel.Error, - `expected fontFace.size to be greater than 0, given ${fontFace.size}`); + if (fontFace.size <= 0.0) { + log(LogLevel.Error, `expected fontFace.size to be greater than 0, given ${fontFace.size}`); + return false; + } fontFace.lineHeight = parseFloat(pairs.get('lineHeight')!); @@ -86,6 +107,8 @@ export class FontLoader { parseFloat(pairs.get('scaleW')!), parseFloat(pairs.get('scaleH')!), ]; + + return true; } /** @@ -98,7 +121,13 @@ export class FontLoader { protected static processPage( stream: Array, fontFace: FontFace, filename: string): Promise { - const pairs = this.readKeyValuePairs(stream, ['file']); + const pairs: StringPairs = new Map(); + const success = this.readKeyValuePairs(stream, ['file'], pairs); + + if (!success) { + log(LogLevel.Error, `Could not read texture filename from fnt file.`); + } + const file = stripped(pairs.get('file')!, ['"', '\r']); const path = Path.dirname(filename) + `/`; @@ -119,10 +148,16 @@ export class FontLoader { * execute processCommon() first. * @param stream The stream of the 'char' identifier. * @param fontFace The font face in which the loaded glyph texture is stored. + * @returns success */ - protected static processChar(stream: Array, fontFace: FontFace): void { - const pairs = this.readKeyValuePairs(stream, - ['id', 'x', 'y', 'width', 'height', 'xoffset', 'yoffset', 'xadvance']); + protected static processChar(stream: Array, fontFace: FontFace): boolean { + const pairs: StringPairs = new Map(); + const success = this.readKeyValuePairs(stream, + ['id', 'x', 'y', 'width', 'height', 'xoffset', 'yoffset', 'xadvance'], pairs); + + if (!success) { + return false; + } const index: number = parseInt(pairs.get('id')!, 10); logIf(index <= 0.0, LogLevel.Error, @@ -159,56 +194,70 @@ export class FontLoader { glyph.advance = parseFloat(pairs.get('xadvance')!); fontFace.addGlyph(glyph); + + return true; } /** * Parses the kerning fields for first and second character and the amount, to store them in the font face. * @param stream The stream of the 'kerning' identifier. * @param fontFace The font face in which the kerning tuples are stored. + * @returns success */ - protected static processKerning(stream: Array, fontFace: FontFace): void { - const pairs = this.readKeyValuePairs(stream, ['first', 'second', 'amount']); + protected static processKerning(stream: Array, fontFace: FontFace): boolean { + const pairs: StringPairs = new Map(); + const success = this.readKeyValuePairs(stream, ['first', 'second', 'amount'], pairs); + + if (!success) { + return false; + } const first: number = parseInt(pairs.get('first')!, 10); - logIf(first < 0.0, LogLevel.Error, - `expected kerning's first to be greater than 0, given ${first}`); + if (first <= 0.0) { + log(LogLevel.Error, `expected kerning's first to be greater than 0, given ${first}`); + return false; + } const second: number = parseInt(pairs.get('second')!, 10); - logIf(second < 0.0, LogLevel.Error, - `expected kerning's first to be greater than 0, given ${second}`); + if (second <= 0.0) { + log(LogLevel.Error, `expected kerning's second to be greater than 0, given ${second}`); + return false; + } const kerning: number = parseFloat(pairs.get('amount')!); fontFace.setKerning(first, second, kerning); + + return true; } /** * Parses to find key-value pairs for given mandatory keys. * @param stream The stream from which the pairs should be read. * @param mandatoryKeys The found pairs are only valid if the mandatory keys are found. - * @returns key-value pairs, or an empty map if not all mandatory keys are found. + * @param result key-value pairs, or undefined if not all mandatory keys are found. + * @returns success */ - protected static readKeyValuePairs(stream: Array, mandatoryKeys: Array): StringPairs { + protected static readKeyValuePairs(stream: Array, mandatoryKeys: Array, + resultPairs: StringPairs): boolean { let key: string; let value: string; - const pairs: StringPairs = new Map(); - for (const s of stream) { const pair = s.split('='); key = pair[0]; value = pair[1]; - pairs.set(key, value); + resultPairs.set(key, value); } /* check if all required keys are provided */ let valid = true; - mandatoryKeys.forEach((key) => valid = valid && pairs.has(key)); + mandatoryKeys.forEach((key) => valid = valid && resultPairs.has(key)); logIf(!valid, LogLevel.Error, `Not all required keys are provided! Mandatory keys: ${mandatoryKeys}`); - return pairs; + return valid; } /** @@ -221,56 +270,55 @@ export class FontLoader { static async load(context: Context, filename: string, headless: boolean): Promise { const fontFace = new FontFace(context); + let text; try { - const text = await fetchAsync(filename, '', (text) => text); - - /* promise fulfilled */ - const lines = text.split('\n'); - - const promises = []; - for (const l of lines) { - let line = l.split(' '); - const identifier = line[0]; - line = line.slice(1); - - /* tslint:disable-next-line:switch-default */ - switch (identifier) { - case 'info': { - this.processInfo(line, fontFace); - break; - } - case 'common': { - this.processCommon(line, fontFace); - break; - } - case 'page': { - if (!headless) { - promises.push(this.processPage(line, fontFace, filename)); - } - break; - } - case 'char': { - this.processChar(line, fontFace); - break; - } - case 'kerning': { - this.processKerning(line, fontFace); - break; - } - } - } - await Promise.all(promises); - + text = await fetchAsync(filename, '', (text) => text); } catch (e) { /* promise rejected */ log(LogLevel.Error, `Could not load font file. filename is: ${filename}`); } - // TODO: assert? throw exception? - // if (headless || fontFace.glyphTexture) { - return fontFace; - // } else { - // return null; - // } + /* promise fulfilled */ + const lines = text.split('\n'); + + const promises = []; + let valid = true; + for (const l of lines) { + if (!valid) { + break; + } + let line = l.split(' '); + const identifier = line[0]; + line = line.slice(1); + + /* tslint:disable-next-line:switch-default */ + switch (identifier) { + case 'info': + valid = this.processInfo(line, fontFace); + break; + case 'common': + valid = this.processCommon(line, fontFace); + break; + case 'page': + if (!headless) { + promises.push(this.processPage(line, fontFace, filename)); + } + break; + case 'char': + valid = this.processChar(line, fontFace); + break; + case 'kerning': + valid = this.processKerning(line, fontFace); + break; + } + } + await Promise.all(promises); + + + if (valid) { + return fontFace; + } else { + return new FontFace(context, `invalid`); + } } } From cc8e2b98ec4cd0de8eef8cf6a594a8617cb787a9 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 17:15:49 +0200 Subject: [PATCH 71/92] removed unused code --- source/glyphvertices.ts | 85 ----------------------------------------- 1 file changed, 85 deletions(-) diff --git a/source/glyphvertices.ts b/source/glyphvertices.ts index f221cb66..c82f2258 100644 --- a/source/glyphvertices.ts +++ b/source/glyphvertices.ts @@ -40,88 +40,3 @@ export class GlyphVertices extends Array { // } } - - -// protected _superSampling: SuperSampling; - - -// export enum Sampling { -// None = 'none', -// Grid2 = 'grid2', -// Grid3 = 'grid3', -// Grid4 = 'grid4', -// Quincunx = 'quincunx', -// RGSS = 'rgss', -// Rooks8 = 'rooks8', -// } - - -// get superSampling(): SuperSampling { -// return this._superSampling; -// } - -// set superSampling(superSampling: SuperSampling) { -// this._superSampling = superSampling; -// } - - -// // numDepictable -// // Extent(): number { -// // let count = 0; -// // for (let c of this._text) { - -// // /** -// // * let number = "h".charCodeAt(0); //(returns number = 104) -// // * let char = String.fromCharCode(number); //(returns char = "h") -// // */ - -// // if (this._fontFace.depictable(c.charCodeAt(0))) { -// // ++count; -// // } -// // } -// // return count; -// // } - - -// protected _additionalTransform: mat4; -// protected _transformValid: boolean; -// protected _transform: mat4; - -/** TODO GlyphSequenceConfig --> LabelConfig ( if at all ) */ -// // public setFromConfig(config: GlyphSequenceConfig) { -// // this.wordWrap = config.wordWrap; -// // this.lineWidth = config.lineWidth; -// // this.alignment = config.alignment; -// // this.lineAnchor = config.anchor; -// // this.fontColor = config.fontColor; -// // this.fontFace = config.fontFace; -// // this.fontSize = config.fontSize; -// // } - -// // get additionalTransform(): mat4 { -// // return this._additionalTransform; -// // } - -// // set additionalTransform(additionalTransform: mat4) { -// // this._transformValid = false; -// // this._additionalTransform = additionalTransform; -// // } - -// // get transform(): mat4 { -// // if (!this._transformValid) { -// // this.computeTransform(); -// // this._transformValid = true; -// // } -// // return this._transform; -// // } - -// // public computeTransform(): void { -// // //assert(this._fontFace); - -// // this._transform = mat4.create(); -// // mat4.multiply(this._transform, this._transform, this._additionalTransform); - -// // let s = this._fontSize / this._fontFace.size; - -// // mat4.scale(this._transform, this._transform, vec3.fromValues(s, s, s)) -// // } From 97f8fb6efbce9c1c1b8fbe88c82af4e325b8da31 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 17:21:39 +0200 Subject: [PATCH 72/92] template strings for identifier naming --- source/fontface.ts | 2 +- source/geometry.ts | 2 +- source/labelgeometry.ts | 11 ++++++----- source/labelrenderer.ts | 1 + source/ndcfillingrectangle.ts | 2 +- source/ndcfillingtriangle.ts | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/source/fontface.ts b/source/fontface.ts index 6f742c66..a7658a3e 100644 --- a/source/fontface.ts +++ b/source/fontface.ts @@ -65,7 +65,7 @@ export class FontFace { const gl = context.gl; identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; - this._glyphTexture = new Texture2(context, identifier + 'GlyphAtlas'); + this._glyphTexture = new Texture2(context, `${identifier}GlyphAtlas`); const internalFormat = Wizard.queryInternalTextureFormat(context, gl.RGBA, Wizard.Precision.byte); this._glyphTexture.initialize(1, 1, internalFormat[0], gl.RGBA, internalFormat[1]); } diff --git a/source/geometry.ts b/source/geometry.ts index 82900c52..3366a63a 100644 --- a/source/geometry.ts +++ b/source/geometry.ts @@ -34,7 +34,7 @@ export abstract class Geometry extends Initializable implements Bindable { super(); identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; - this._vertexArray = new VertexArray(context, identifier + 'VAO'); + this._vertexArray = new VertexArray(context, `${identifier}VAO`); } diff --git a/source/labelgeometry.ts b/source/labelgeometry.ts index 29294c10..4ce522be 100644 --- a/source/labelgeometry.ts +++ b/source/labelgeometry.ts @@ -6,6 +6,7 @@ import { Context } from './context'; import { Geometry } from './geometry'; import { Initializable } from './initializable'; + /** * Gathers vertices and other data needed for drawing all labels. */ @@ -28,15 +29,15 @@ export class LabelGeometry extends Geometry { /* Generate identifier from constructor name if none given. */ identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; - const vertexVBO = new Buffer(context, identifier + 'VBO'); + const vertexVBO = new Buffer(context, `${identifier}VBO`); this._buffers.push(vertexVBO); - const texCoordBuffer = new Buffer(context, identifier + 'TexCoordBuffer'); + const texCoordBuffer = new Buffer(context, `${identifier}TexCoordBuffer`); this._buffers.push(texCoordBuffer); - const originBuffer = new Buffer(context, identifier + 'OriginBuffer'); + const originBuffer = new Buffer(context, `${identifier}OriginBuffer`); this._buffers.push(originBuffer); - const tanBuffer = new Buffer(context, identifier + 'TanBuffer'); + const tanBuffer = new Buffer(context, `${identifier}TangentBuffer`); this._buffers.push(tanBuffer); - const upBuffer = new Buffer(context, identifier + 'UpBuffer'); + const upBuffer = new Buffer(context, `${identifier}UpBuffer`); this._buffers.push(upBuffer); } diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index a034e481..b947dbd3 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -27,6 +27,7 @@ import { Text } from './text'; import { TestNavigation } from './debug/testnavigation'; + /** * This is ugly, but it should do the trick for now: * Later, we want to have a labelrenderpass and a labelpositionpass. diff --git a/source/ndcfillingrectangle.ts b/source/ndcfillingrectangle.ts index f66fc0ac..a89709cc 100644 --- a/source/ndcfillingrectangle.ts +++ b/source/ndcfillingrectangle.ts @@ -48,7 +48,7 @@ export class NdcFillingRectangle extends Geometry { /* Generate identifier from constructor name if none given. */ identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; - const vertexVBO = new Buffer(context, identifier + 'VBO'); + const vertexVBO = new Buffer(context, `${identifier}VBO`); this._buffers.push(vertexVBO); } diff --git a/source/ndcfillingtriangle.ts b/source/ndcfillingtriangle.ts index cc43d41c..c7aafa10 100644 --- a/source/ndcfillingtriangle.ts +++ b/source/ndcfillingtriangle.ts @@ -55,7 +55,7 @@ export class NdcFillingTriangle extends Geometry { /* Generate identifier from constructor name if none given. */ identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; - const vertexVBO = new Buffer(context, identifier + 'VBO'); + const vertexVBO = new Buffer(context, `${identifier}VBO`); this._buffers.push(vertexVBO); } From a5138ee65f2b0f3cecd1fd578114f0d2c0f1c4c2 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 17:30:56 +0200 Subject: [PATCH 73/92] renaming: tan --> tangent; and alike. --- source/labelgeometry.ts | 33 ++++++++++++++++----------------- source/labelrenderer.ts | 18 +++++++++--------- source/shaders/glyphquad.vert | 6 +++--- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/source/labelgeometry.ts b/source/labelgeometry.ts index 4ce522be..cf69ae37 100644 --- a/source/labelgeometry.ts +++ b/source/labelgeometry.ts @@ -15,7 +15,7 @@ export class LabelGeometry extends Geometry { protected _vertices: Float32Array = new Float32Array(0); protected _texCoords: Float32Array = new Float32Array(0); protected _origins: Float32Array = new Float32Array(0); - protected _tans: Float32Array = new Float32Array(0); + protected _tangents: Float32Array = new Float32Array(0); protected _ups: Float32Array = new Float32Array(0); /** @@ -35,8 +35,8 @@ export class LabelGeometry extends Geometry { this._buffers.push(texCoordBuffer); const originBuffer = new Buffer(context, `${identifier}OriginBuffer`); this._buffers.push(originBuffer); - const tanBuffer = new Buffer(context, `${identifier}TangentBuffer`); - this._buffers.push(tanBuffer); + const tangentBuffer = new Buffer(context, `${identifier}TangentBuffer`); + this._buffers.push(tangentBuffer); const upBuffer = new Buffer(context, `${identifier}UpBuffer`); this._buffers.push(upBuffer); } @@ -59,7 +59,7 @@ export class LabelGeometry extends Geometry { /* origin */ this._buffers[2].attribEnable(indices[2], 3, gl.FLOAT, false, 3 * 4, 0, true, false); gl2facade.vertexAttribDivisor(indices[2], 1); - /* tan */ + /* tangent */ this._buffers[3].attribEnable(indices[3], 3, gl.FLOAT, false, 3 * 4, 0, true, false); gl2facade.vertexAttribDivisor(indices[3], 1); /* up */ @@ -94,20 +94,20 @@ export class LabelGeometry extends Geometry { * @param aQuadVertex - Attribute binding point for vertices. * @param aTexCoord - Attribute binding point for texture coordinates. * @param aOrigin - Attribute binding point for glyph origin coordinates - * @param aTan - Attribute binding point for glyph tangent coordinates. + * @param aTangent - Attribute binding point for glyph tangent coordinates. * @param aUp - Attribute binding point for glyph up-vector coordinates. */ - initialize(aQuadVertex: GLuint, aTexCoord: GLuint, aOrigin: GLuint, aTan: GLuint, aUp: GLuint): boolean { + initialize(aQuadVertex: GLuint, aTexCoord: GLuint, aOrigin: GLuint, aTangent: GLuint, aUp: GLuint): boolean { const gl = this.context.gl; const valid = super.initialize( [gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER, gl.ARRAY_BUFFER] - , [aQuadVertex, aTexCoord, aOrigin, aTan, aUp]); + , [aQuadVertex, aTexCoord, aOrigin, aTangent, aUp]); /** * These vertices are equal for all quads. There actual position will be changed using - * origin, tan(gent) and up(-vector). + * origin, tangent and up(-vector). */ this._vertices = Float32Array.from([0, 0, 0, 1, 1, 0, 1, 1]); this._buffers[0].data(this._vertices, gl.STATIC_DRAW); @@ -118,11 +118,11 @@ export class LabelGeometry extends Geometry { /** * Use this method to set (or update) the glyph coordinates, e.g. after typesetting or after the calculations * of a placement algorithm. - * @param dataOrigin xyz-coordinates of the lower left corner of every glyph - * @param dataTan tangent vector for every glyph (direction along base line) - * @param dataUp up vector for every glyph (orthogonal to its tangent vector) + * @param origins xyz-coordinates of the lower left corner of every glyph + * @param tangents tangent vector for every glyph (direction along base line) + * @param ups up vector for every glyph (orthogonal to its tangent vector) */ - setGlyphCoords(dataOrigin: Float32Array, dataTan: Float32Array, dataUp: Float32Array): void { + setGlyphCoords(origins: Float32Array, tangents: Float32Array, ups: Float32Array): void { assert(this._buffers[2] !== undefined && this._buffers[0].object instanceof WebGLBuffer, `expected valid WebGLBuffer`); @@ -131,15 +131,14 @@ export class LabelGeometry extends Geometry { assert(this._buffers[4] !== undefined && this._buffers[0].object instanceof WebGLBuffer, `expected valid WebGLBuffer`); - - this._origins = dataOrigin; - this._tans = dataTan; - this._ups = dataUp; + this._origins = origins; + this._tangents = tangents; + this._ups = ups; const gl = this.context.gl; /** @todo is DYNAMIC_DRAW more appropriate? */ this._buffers[2].data(this._origins, gl.STATIC_DRAW); - this._buffers[3].data(this._tans, gl.STATIC_DRAW); + this._buffers[3].data(this._tangents, gl.STATIC_DRAW); this._buffers[4].data(this._ups, gl.STATIC_DRAW); } diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index b947dbd3..544f399c 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -103,11 +103,11 @@ export class LabelRenderer extends Renderer { const aVertex = this._program.attribute('a_quadVertex', 0); const aTexCoord = this._program.attribute('a_texCoord', 1); const aOrigin = this._program.attribute('a_origin', 2); - const aTan = this._program.attribute('a_tan', 3); + const aTangent = this._program.attribute('a_tangent', 3); const aUp = this._program.attribute('a_up', 4); - this._2DLabelGeometry.initialize(aVertex, aTexCoord, aOrigin, aTan, aUp); - this._3DLabelGeometry.initialize(aVertex, aTexCoord, aOrigin, aTan, aUp); + this._2DLabelGeometry.initialize(aVertex, aTexCoord, aOrigin, aTangent, aUp); + this._3DLabelGeometry.initialize(aVertex, aTexCoord, aOrigin, aTangent, aUp); this._ndcOffsetKernel = new AntiAliasingKernel(this._multiFrameNumber); @@ -322,7 +322,7 @@ export class LabelRenderer extends Renderer { /* fill buffers */ let origins: Array = []; - let tans: Array = []; + let tangents: Array = []; let ups: Array = []; let texCoords: Array = []; @@ -332,14 +332,14 @@ export class LabelRenderer extends Renderer { const v = glyphVertices[i]; origins.push.apply(origins, v.origin); - tans.push.apply(tans, v.tangent); + tangents.push.apply(tangents, v.tangent); ups.push.apply(ups, v.up); texCoords.push.apply(texCoords, v.uvRect); } this._2DLabelGeometry.setTexCoords(Float32Array.from(texCoords)); this._2DLabelGeometry.setGlyphCoords( - Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); + Float32Array.from(origins), Float32Array.from(tangents), Float32Array.from(ups)); /** OpenLL 3D Labels */ const pos3Dlabel = new Position3DLabel(new Text('Hello Position 3D!'), this._fontFace); @@ -368,7 +368,7 @@ export class LabelRenderer extends Renderer { /* fill buffers */ origins = []; - tans = []; + tangents = []; ups = []; texCoords = []; @@ -378,13 +378,13 @@ export class LabelRenderer extends Renderer { const v = glyphVertices[i]; origins.push.apply(origins, v.origin); - tans.push.apply(tans, v.tangent); + tangents.push.apply(tangents, v.tangent); ups.push.apply(ups, v.up); texCoords.push.apply(texCoords, v.uvRect); } this._3DLabelGeometry.setTexCoords(Float32Array.from(texCoords)); this._3DLabelGeometry.setGlyphCoords( - Float32Array.from(origins), Float32Array.from(tans), Float32Array.from(ups)); + Float32Array.from(origins), Float32Array.from(tangents), Float32Array.from(ups)); } } diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 96cf2375..b30c2be1 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -9,14 +9,14 @@ attribute vec2 a_quadVertex; /* [ texture ll: vec2, ur: vec2 ] */ attribute vec4 a_texCoord; attribute vec3 a_origin; -attribute vec3 a_tan; +attribute vec3 a_tangent; attribute vec3 a_up; #else layout(location = 0) in vec2 a_quadVertex; /* [ texture ll: vec2, ur: vec2 ]*/ layout(location = 1) in vec4 a_texCoord; layout(location = 2) in vec3 a_origin; -layout(location = 3) in vec3 a_tan; +layout(location = 3) in vec3 a_tangent; layout(location = 4) in vec3 a_up; #endif @@ -45,7 +45,7 @@ void main(void) /* POSITIONING*/ /* quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) */ - vec4 vertex = vec4(a_origin + a_quadVertex.x*a_tan + a_quadVertex.y*a_up, 1.0); + vec4 vertex = vec4(a_origin + a_quadVertex.x*a_tangent + a_quadVertex.y*a_up, 1.0); vertex = u_viewProjection * vertex; From 01b8983adf8e54e99670329d386cadc7564d9fb4 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 17:31:58 +0200 Subject: [PATCH 74/92] renaming: data --> texCoords --- source/labelgeometry.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/labelgeometry.ts b/source/labelgeometry.ts index cf69ae37..27e7ac1c 100644 --- a/source/labelgeometry.ts +++ b/source/labelgeometry.ts @@ -144,14 +144,14 @@ export class LabelGeometry extends Geometry { /** * Use this method to set (or update) the texture coordinates for every glyph, e.g. after typesetting. - * @param data The texture coordinates for every glyph, format: ll.x, ll.y, ur.x, ur.y + * @param texCoords The texture coordinates for every glyph, format: ll.x, ll.y, ur.x, ur.y */ - setTexCoords(data: Float32Array): void { + setTexCoords(texCoords: Float32Array): void { assert(this._buffers[1] !== undefined && this._buffers[1].object instanceof WebGLBuffer, `expected valid WebGLBuffer`); - this._texCoords = data; + this._texCoords = texCoords; const gl = this.context.gl; /** @todo is DYNAMIC_DRAW more appropriate? */ From 443abfebf3d4307c1836d73d81dc035887bd1862 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 17:38:33 +0200 Subject: [PATCH 75/92] explicitly todo added --- source/labelrenderer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 544f399c..fb22fce5 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -33,6 +33,7 @@ import { TestNavigation } from './debug/testnavigation'; * Later, we want to have a labelrenderpass and a labelpositionpass. * The first one bakes the geometry, the second one adapts the placement regarding dynamic placement algorithms. * For now, we will have both as a labelrenderer, and split it up later. + * @todo implement label render pass */ export class LabelRenderer extends Renderer { From aca8dcce2010208497d92743a0c1ce6f4462054c Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Wed, 10 Oct 2018 17:51:17 +0200 Subject: [PATCH 76/92] documentation --- source/label.ts | 1 - source/position2dlabel.ts | 17 ++++++++++++++--- source/position3dlabel.ts | 28 ++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/source/label.ts b/source/label.ts index 80cf61a5..f721cd9e 100644 --- a/source/label.ts +++ b/source/label.ts @@ -335,7 +335,6 @@ export class Label { this._altered.alter('userTransform'); this._userTransform = t; } - get userTransform(): mat4 { return this._userTransform; } diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index 8752bac1..d9f50d72 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -95,12 +95,17 @@ export class Position2DLabel extends Label { set position(xy: vec2) { this._position = vec2.clone(xy); } - get position(): vec2 { return this._position; } - /** position parameters as specified in OpenLL */ + /** + * Sets 2D position parameters as specified in OpenLL. + * Position is the label's reference point (i.e. lower left corner for horizontal alignment). + * @param x x coordinate of the 2D position + * @param y y coordinate of the 2D position + * @param unit the unit to interpret the coordinates + */ setPosition(x: number, y: number, unit?: Label.SpaceUnit): void { /** @todo assert that SpaceUnit is px or pt; transform to NDC? */ this._position = vec2.fromValues(x, y); @@ -112,11 +117,16 @@ export class Position2DLabel extends Label { set direction(xy: vec2) { this._direction = vec2.normalize(this._direction, xy); } - get direction(): vec2 { return this._direction; } + /** + * Sets the 2D direction parameters as specified in OpenLL. + * The labels's direction is the direction of its baseline. + * @param x x coordinate of the 2D direction vector. + * @param y y coordinate of the 2D direction vector. + */ setDirection(x: number, y: number): void { this.direction = vec2.fromValues(x, y); } @@ -125,6 +135,7 @@ export class Position2DLabel extends Label { * This unit is used for the font size. * This method overrides the super.fontSizeUnit, since a position2dlabel only allows px, not World. * (@see {@link fontSize}) + * @param newUnit unused, since there is only one allowed unit (Px) for this kind of label */ set fontSizeUnit(newUnit: Label.SpaceUnit) { console.warn('New SpaceUnit not set; only allowed SpaceUnit is Px for this label.'); diff --git a/source/position3dlabel.ts b/source/position3dlabel.ts index 9860fcdd..80500151 100644 --- a/source/position3dlabel.ts +++ b/source/position3dlabel.ts @@ -69,12 +69,17 @@ export class Position3DLabel extends Label { set position(xyz: vec3) { this._position = vec3.clone(xyz); } - get position(): vec3 { return this._position; } - /** position parameters as specified in OpenLL */ + /** + * Sets 3D position parameters as specified in OpenLL. + * Position is the label's reference point (i.e. lower left corner for horizontal alignment). + * @param x x coordinate of 3D position + * @param y y coordinate of 3D position + * @param z z coordinate of 3D position + */ setPosition(x: number, y: number, z: number): void { this._position = vec3.fromValues(x, y, z); } @@ -85,12 +90,17 @@ export class Position3DLabel extends Label { set direction(xyz: vec3) { this._direction = vec3.normalize(this._direction, xyz); } - get direction(): vec3 { return this._direction; } - /** direction parameters as specified in OpenLL */ + /** + * Sets the 3D direction parameters as specified in OpenLL. + * The labels's direction is the direction of its baseline. + * @param x x coordinate of the 3D direction vector. + * @param y y coordinate of the 3D direction vector. + * @param z z coordinate of the 3D direction vector. + */ setDirection(x: number, y: number, z: number): void { this.direction = vec3.fromValues(x, y, z); } @@ -102,12 +112,17 @@ export class Position3DLabel extends Label { set up(xyz: vec3) { this._up = vec3.normalize(this._up, xyz); } - get up(): vec3 { return this._up; } - /** up-vector parameters as specified in OpenLL */ + /** + * Sets the 3D up-vector parameters as specified in OpenLL. + * It should be orthogonal to the direction to ensure that the label is not skewed. + * @param x x coordinate of the 3D up vector. + * @param y y coordinate of the 3D up vector. + * @param z z coordinate of the 3D up vector. + */ setUp(x: number, y: number, z: number): void { this.up = vec3.fromValues(x, y, z); } @@ -116,6 +131,7 @@ export class Position3DLabel extends Label { * This unit is used for the font size. * This method overrides the super.fontSizeUnit, since a position3dlabel only allows World, not Px nor Pt. * (@see {@link fontSize}) + * @param newUnit unused, since there is only one allowed unit (World) for this kind of label */ set fontSizeUnit(newUnit: Label.SpaceUnit) { console.warn('New SpaceUnit not set; only allowed SpaceUnit is World for this label.'); From 516c74d76b54bc1f6fd4f753f2274939fff08956 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 13:44:09 +0200 Subject: [PATCH 77/92] useful error handling for fontloader's Promises --- source/data/opensansr144/opensansr144.fnt | 2 +- source/fontloader.ts | 119 ++++++++++------------ 2 files changed, 57 insertions(+), 64 deletions(-) diff --git a/source/data/opensansr144/opensansr144.fnt b/source/data/opensansr144/opensansr144.fnt index 84785432..c09ecb7a 100644 --- a/source/data/opensansr144/opensansr144.fnt +++ b/source/data/opensansr144/opensansr144.fnt @@ -1,6 +1,6 @@ info face=opensansr144 size=144 bold=0 italic=0 charset= unicode= stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=0,0 outline=0 common lineHeight=196.1 base=135 ascent=110 descent=-34 scaleW=2048 scaleH=2048 pages=1 packed=0 -page id=0 file="opensansr144.2048.2048.r.ub.raw" +page id=0 file="opensansr144.png" chars count=217 char id=33 x=0 y=0 width=25 height=112 xoffset=14.69 yoffset=31.29 xadvance=38.46 page=0 chnl=15 char id=34 x=25 y=0 width=47 height=45 xoffset=13.35 yoffset=31.29 xadvance=57.73 page=0 chnl=15 diff --git a/source/fontloader.ts b/source/fontloader.ts index d5d409d7..a4c5f011 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -12,19 +12,6 @@ import Path = require('path'); type StringPairs = Map; -function stripped(input: string, blacklist: Array): string { - let result: string = input; - - /* first param for erase: */ - blacklist.forEach((element) => { - if (result.includes(element)) { - result = result.replace(RegExp(element, 'g'), ''); - } - }); - return result; -} - - /** * Loads the png image that displays the glyph atlas, that was prepared using a Distance Field Transform. It also * loads all needed data to use that image from a fnt-file with the same name as the png image. It thus creates @@ -37,26 +24,32 @@ function stripped(input: string, blacklist: Array): string { */ export class FontLoader { + /** + * False when the current loading process gets invalid. + */ + protected static _valid: boolean; + /** * Parses the info fields for padding values and stores them in the font face * @param stream The stream of the 'info' identifier. * @param fontFace The font face in which the padding is stored. - * @returns success */ - protected static processInfo(stream: Array, fontFace: FontFace): boolean { + protected static processInfo(stream: Array, fontFace: FontFace): void { const pairs: StringPairs = new Map(); const success = this.readKeyValuePairs(stream, ['padding'], pairs); if (!success) { - return false; + this._valid = false; + return; } const values = pairs.get('padding')!.split(','); if (values.length !== 4) { - log(LogLevel.Error, `expected 4 values for padding, given ${values} (${values.length})`); - return false; + log(LogLevel.Warning, `expected 4 values for padding, given ${values} (${values.length})`); + this._valid = false; + return; } const padding: GLfloat4 = [ @@ -71,8 +64,6 @@ export class FontLoader { ]; fontFace.glyphTexturePadding = padding; - - return true; } /** @@ -80,16 +71,16 @@ export class FontLoader { * in the font face. * @param stream The stream of the 'common' identifier. * @param fontFace The font face in which the parsed values are stored. - * @returns success */ - protected static processCommon(stream: Array, fontFace: FontFace): boolean { + protected static processCommon(stream: Array, fontFace: FontFace): void { const pairs: StringPairs = new Map(); const success = this.readKeyValuePairs(stream, ['lineHeight', 'base', 'ascent', 'descent', 'scaleW', 'scaleH'], pairs); if (!success) { - return false; + this._valid = false; + return; } fontFace.base = parseFloat(pairs.get('base')!); @@ -97,8 +88,9 @@ export class FontLoader { fontFace.descent = parseFloat(pairs.get('descent')!); if (fontFace.size <= 0.0) { - log(LogLevel.Error, `expected fontFace.size to be greater than 0, given ${fontFace.size}`); - return false; + log(LogLevel.Warning, `expected fontFace.size to be greater than 0, given ${fontFace.size}`); + this._valid = false; + return; } fontFace.lineHeight = parseFloat(pairs.get('lineHeight')!); @@ -107,8 +99,6 @@ export class FontLoader { parseFloat(pairs.get('scaleW')!), parseFloat(pairs.get('scaleH')!), ]; - - return true; } /** @@ -119,27 +109,25 @@ export class FontLoader { * @returns Promise for handling image load status. */ protected static processPage( - stream: Array, fontFace: FontFace, filename: string): Promise { + stream: Array, fontFace: FontFace, filepath: string): Promise { const pairs: StringPairs = new Map(); const success = this.readKeyValuePairs(stream, ['file'], pairs); if (!success) { - log(LogLevel.Error, `Could not read texture filename from fnt file.`); + log(LogLevel.Warning, `Could not read texture filename from fnt file.`); + this._valid = false; } - const file = stripped(pairs.get('file')!, ['"', '\r']); - - const path = Path.dirname(filename) + `/`; + const path = Path.dirname(filepath) + `/`; + const filename = Path.basename(filepath, `.fnt`); - /** For multiple pages, the filename should be different. - * Idea: Provide a name for the font file, and a template for the page file name - * where the page number is replaced, e.g.: - * `foo/bar/-.png` - */ - const pngPath: string = path + file.split('.')[0] + '.png'; + const pngPath: string = path + filename + `.png`; - return fontFace.glyphTexture.load(pngPath); + return fontFace.glyphTexture.load(pngPath).catch((error) => { + log(LogLevel.Warning, `${error}. Could not load glyphTexture: ${pngPath}`); + this._valid = false; + }); } /** @@ -148,19 +136,19 @@ export class FontLoader { * execute processCommon() first. * @param stream The stream of the 'char' identifier. * @param fontFace The font face in which the loaded glyph texture is stored. - * @returns success */ - protected static processChar(stream: Array, fontFace: FontFace): boolean { + protected static processChar(stream: Array, fontFace: FontFace): void { const pairs: StringPairs = new Map(); const success = this.readKeyValuePairs(stream, ['id', 'x', 'y', 'width', 'height', 'xoffset', 'yoffset', 'xadvance'], pairs); if (!success) { - return false; + this._valid = false; + return; } const index: number = parseInt(pairs.get('id')!, 10); - logIf(index <= 0.0, LogLevel.Error, + logIf(index <= 0.0, LogLevel.Warning, `expected glyph index to be greater than 0, given ${index}`); const glyph = new Glyph(); @@ -194,41 +182,41 @@ export class FontLoader { glyph.advance = parseFloat(pairs.get('xadvance')!); fontFace.addGlyph(glyph); - - return true; } /** * Parses the kerning fields for first and second character and the amount, to store them in the font face. * @param stream The stream of the 'kerning' identifier. * @param fontFace The font face in which the kerning tuples are stored. - * @returns success */ - protected static processKerning(stream: Array, fontFace: FontFace): boolean { + protected static processKerning(stream: Array, fontFace: FontFace): void { const pairs: StringPairs = new Map(); const success = this.readKeyValuePairs(stream, ['first', 'second', 'amount'], pairs); if (!success) { - return false; + this._valid = false; + return; } const first: number = parseInt(pairs.get('first')!, 10); if (first <= 0.0) { - log(LogLevel.Error, `expected kerning's first to be greater than 0, given ${first}`); - return false; + log(LogLevel.Warning, `expected kerning's first to be greater than 0, given ${first}`); + this._valid = false; + return; } const second: number = parseInt(pairs.get('second')!, 10); if (second <= 0.0) { - log(LogLevel.Error, `expected kerning's second to be greater than 0, given ${second}`); - return false; + log(LogLevel.Warning, `expected kerning's second to be greater than 0, given ${second}`); + this._valid = false; + return; } const kerning: number = parseFloat(pairs.get('amount')!); fontFace.setKerning(first, second, kerning); - return true; + return; } /** @@ -255,7 +243,9 @@ export class FontLoader { let valid = true; mandatoryKeys.forEach((key) => valid = valid && resultPairs.has(key)); - logIf(!valid, LogLevel.Error, `Not all required keys are provided! Mandatory keys: ${mandatoryKeys}`); + if (!valid) { + log(LogLevel.Warning, `Not all required keys are provided! Mandatory keys: ${mandatoryKeys}`); + } return valid; } @@ -270,21 +260,23 @@ export class FontLoader { static async load(context: Context, filename: string, headless: boolean): Promise { const fontFace = new FontFace(context); + this._valid = true; + let text; try { text = await fetchAsync(filename, '', (text) => text); } catch (e) { /* promise rejected */ - log(LogLevel.Error, `Could not load font file. filename is: ${filename}`); + log(LogLevel.Warning, `Could not load font file. filename is: ${filename}`); + this._valid = false; } /* promise fulfilled */ const lines = text.split('\n'); const promises = []; - let valid = true; for (const l of lines) { - if (!valid) { + if (!this._valid) { break; } let line = l.split(' '); @@ -294,10 +286,10 @@ export class FontLoader { /* tslint:disable-next-line:switch-default */ switch (identifier) { case 'info': - valid = this.processInfo(line, fontFace); + this.processInfo(line, fontFace); break; case 'common': - valid = this.processCommon(line, fontFace); + this.processCommon(line, fontFace); break; case 'page': if (!headless) { @@ -305,19 +297,20 @@ export class FontLoader { } break; case 'char': - valid = this.processChar(line, fontFace); + this.processChar(line, fontFace); break; case 'kerning': - valid = this.processKerning(line, fontFace); + this.processKerning(line, fontFace); break; } } - await Promise.all(promises); + await Promise.all(promises); - if (valid) { + if (this._valid) { return fontFace; } else { + log(LogLevel.Warning, `No valid FontFace created.`); return new FontFace(context, `invalid`); } } From 9ed6b96602e37fe0958771853cc338947790434e Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 13:50:57 +0200 Subject: [PATCH 78/92] console.warn --> log(LogLevel.Warning, ...) --- source/position2dlabel.ts | 3 ++- source/position3dlabel.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index d9f50d72..d88894f8 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -1,6 +1,7 @@ import { mat4, vec2, vec3, vec4 } from 'gl-matrix'; +import { log, LogLevel } from './auxiliaries'; import { FontFace } from './fontface'; import { GlyphVertices } from './glyphvertices'; import { Label } from './label'; @@ -138,7 +139,7 @@ export class Position2DLabel extends Label { * @param newUnit unused, since there is only one allowed unit (Px) for this kind of label */ set fontSizeUnit(newUnit: Label.SpaceUnit) { - console.warn('New SpaceUnit not set; only allowed SpaceUnit is Px for this label.'); + log(LogLevel.Warning, `New SpaceUnit ${newUnit} not set; only allowed SpaceUnit is Px for this label.`); } } diff --git a/source/position3dlabel.ts b/source/position3dlabel.ts index 80500151..32979192 100644 --- a/source/position3dlabel.ts +++ b/source/position3dlabel.ts @@ -1,6 +1,7 @@ import { mat4, vec3 } from 'gl-matrix'; +import { log, LogLevel } from './auxiliaries'; import { FontFace } from './fontface'; import { GlyphVertices } from './glyphvertices'; import { Label } from './label'; @@ -134,7 +135,7 @@ export class Position3DLabel extends Label { * @param newUnit unused, since there is only one allowed unit (World) for this kind of label */ set fontSizeUnit(newUnit: Label.SpaceUnit) { - console.warn('New SpaceUnit not set; only allowed SpaceUnit is World for this label.'); + log(LogLevel.Warning, `New SpaceUnit ${newUnit} not set; only allowed SpaceUnit is World for this label.`); } } From 16bd40316731ceeb0914c6daf91134de1e5ac0a1 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 13:58:26 +0200 Subject: [PATCH 79/92] doc refinement --- source/fontloader.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/fontloader.ts b/source/fontloader.ts index a4c5f011..f5ea0bea 100644 --- a/source/fontloader.ts +++ b/source/fontloader.ts @@ -131,9 +131,9 @@ export class FontLoader { } /** - * Parses the char fields for character id, x, y, width, height, xoffset, yoffset, xadvance to - * store them in the font face. Relies on fontFace.base and fontFace.glyphTextureExtent, so - * execute processCommon() first. + * Parses the char fields for character id (codepoint), x, y, width, height, xoffset, yoffset, xadvance to + * store them in the font face as instances of Glyph. + * This relies on fontFace.base and fontFace.glyphTextureExtent, so execute processCommon() first. * @param stream The stream of the 'char' identifier. * @param fontFace The font face in which the loaded glyph texture is stored. */ From 7d8face65bdb7e8b5fbc5a48e6c29909b7ced0d9 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 14:14:41 +0200 Subject: [PATCH 80/92] constructor for GlyphVertices --- source/glyphvertices.ts | 16 ++++++++++++++++ source/label.ts | 17 ++++------------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/source/glyphvertices.ts b/source/glyphvertices.ts index c82f2258..fc67ae53 100644 --- a/source/glyphvertices.ts +++ b/source/glyphvertices.ts @@ -35,6 +35,22 @@ export interface GlyphVertex { */ export class GlyphVertices extends Array { + constructor(numGlyphs: number) { + super(); + + for (let i = 0; i < numGlyphs; ++i) { + + const vertex: GlyphVertex = { + origin: vec3.create(), + tangent: vec3.create(), + up: vec3.create(), + /* vec2 lowerLeft and vec2 upperRight in glyph texture (uv) */ + uvRect: vec4.create(), + }; + this.push(vertex); + } + } + // optimize() { // } diff --git a/source/label.ts b/source/label.ts index f721cd9e..e002e261 100644 --- a/source/label.ts +++ b/source/label.ts @@ -73,20 +73,11 @@ export class Label { this._extent = [0, 0]; } + /** + * Creates an Array of glyph vertices with given length, ready to be used in the Typesetter. + */ protected prepareVertexStorage(): GlyphVertices { - const vertices = new GlyphVertices(); - const numGlyphs = this.length; - for (let i = 0; i < numGlyphs; ++i) { - - const vertex: GlyphVertex = { - origin: vec3.create(), - tangent: vec3.create(), - up: vec3.create(), - /* vec2 lowerLeft and vec2 upperRight in glyph texture (uv) */ - uvRect: vec4.create(), - }; - vertices.push(vertex); - } + const vertices = new GlyphVertices(this.length); return vertices; } From fcd0cbf71528abfd6fba3b77f60ed111ff54193b Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 14:19:03 +0200 Subject: [PATCH 81/92] doc --- source/label.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/label.ts b/source/label.ts index e002e261..ac939e70 100644 --- a/source/label.ts +++ b/source/label.ts @@ -114,6 +114,10 @@ export class Label { } + /** + * Gets the kerning value before (i.e., left in left-to-right writing systems) the given glyph index. + * @param index index of the glyph in this label + */ kerningBefore(index: number): number { if (index < 1 || index > this.length) { return NaN; @@ -121,6 +125,10 @@ export class Label { return this._fontFace.kerning(this.charCodeAt(index - 1), this.charCodeAt(index)); } + /** + * Gets the kerning value after (i.e., right in left-to-right writing systems) the given glyph index. + * @param index index of the glyph in this label + */ kerningAfter(index: number): number { if (index < 0 || index > this.length - 1) { return NaN; From 38e6dc77f23795e866b9d3d2d504d37f8a95b9db Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 14:24:49 +0200 Subject: [PATCH 82/92] comment line breaks --- source/position2dlabel.ts | 18 ++++++++---------- source/position3dlabel.ts | 26 ++++++++++++-------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index d88894f8..969f393b 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -10,8 +10,7 @@ import { Typesetter } from './typesetter'; /** - * A Label that can be positioned in 2D space. - * The unit for positions, size and transformations, is pixel (px). + * A Label that can be positioned in 2D space. The unit for positions, size and transformations, is pixel (px). */ export class Position2DLabel extends Label { @@ -32,8 +31,8 @@ export class Position2DLabel extends Label { } /** - * Applies its position and direction, then prepares the vertex storage so that - * the Typesetter can typeset this label. + * Applies its position and direction, then prepares the vertex storage so that the Typesetter can typeset this + * label. * @param frameSize The width and height of the frame, so that sizes can be calculated to use pixel units. * @returns The transformed glyph vertices. */ @@ -101,8 +100,8 @@ export class Position2DLabel extends Label { } /** - * Sets 2D position parameters as specified in OpenLL. - * Position is the label's reference point (i.e. lower left corner for horizontal alignment). + * Sets 2D position parameters as specified in OpenLL. Position is the label's reference point (i.e. lower left + * corner for horizontal alignment). * @param x x coordinate of the 2D position * @param y y coordinate of the 2D position * @param unit the unit to interpret the coordinates @@ -123,8 +122,7 @@ export class Position2DLabel extends Label { } /** - * Sets the 2D direction parameters as specified in OpenLL. - * The labels's direction is the direction of its baseline. + * Sets the 2D direction parameters as specified in OpenLL. The labels's direction is the direction of its baseline. * @param x x coordinate of the 2D direction vector. * @param y y coordinate of the 2D direction vector. */ @@ -133,8 +131,8 @@ export class Position2DLabel extends Label { } /** - * This unit is used for the font size. - * This method overrides the super.fontSizeUnit, since a position2dlabel only allows px, not World. + * This unit is used for the font size. This method overrides the super.fontSizeUnit, since a position2dlabel only + * allows px, not World. * (@see {@link fontSize}) * @param newUnit unused, since there is only one allowed unit (Px) for this kind of label */ diff --git a/source/position3dlabel.ts b/source/position3dlabel.ts index 32979192..257eea21 100644 --- a/source/position3dlabel.ts +++ b/source/position3dlabel.ts @@ -10,8 +10,8 @@ import { Typesetter } from './typesetter'; /** - * A Label that can be positioned in 3D space. - * The unit for positions, size and transformations, is the abstract World Unit. + * A Label that can be positioned in 3D space. The unit for positions, size and transformations, is the abstract World + * Unit. */ export class Position3DLabel extends Label { @@ -34,8 +34,8 @@ export class Position3DLabel extends Label { } /** - * Applies its position, direction and up-vector, then prepares the vertex storage so that - * the Typesetter can typeset this label. + * Applies its position, direction and up-vector, then prepares the vertex storage so that the Typesetter can + * typeset this label. * @returns The transformed glyph vertices. */ typeset(): GlyphVertices { @@ -75,8 +75,8 @@ export class Position3DLabel extends Label { } /** - * Sets 3D position parameters as specified in OpenLL. - * Position is the label's reference point (i.e. lower left corner for horizontal alignment). + * Sets 3D position parameters as specified in OpenLL. Position is the label's reference point (i.e. lower left + * corner for horizontal alignment). * @param x x coordinate of 3D position * @param y y coordinate of 3D position * @param z z coordinate of 3D position @@ -96,8 +96,7 @@ export class Position3DLabel extends Label { } /** - * Sets the 3D direction parameters as specified in OpenLL. - * The labels's direction is the direction of its baseline. + * Sets the 3D direction parameters as specified in OpenLL. The labels's direction is the direction of its baseline. * @param x x coordinate of the 3D direction vector. * @param y y coordinate of the 3D direction vector. * @param z z coordinate of the 3D direction vector. @@ -107,8 +106,7 @@ export class Position3DLabel extends Label { } /** - * Sets the up-vector of the label. - * It should be orthogonal to the direction to ensure that the label is not skewed. + * Sets the up-vector of the label. It should be orthogonal to the direction to ensure that the label is not skewed. */ set up(xyz: vec3) { this._up = vec3.normalize(this._up, xyz); @@ -118,8 +116,8 @@ export class Position3DLabel extends Label { } /** - * Sets the 3D up-vector parameters as specified in OpenLL. - * It should be orthogonal to the direction to ensure that the label is not skewed. + * Sets the 3D up-vector parameters as specified in OpenLL. It should be orthogonal to the direction to ensure that + * the label is not skewed. * @param x x coordinate of the 3D up vector. * @param y y coordinate of the 3D up vector. * @param z z coordinate of the 3D up vector. @@ -129,8 +127,8 @@ export class Position3DLabel extends Label { } /** - * This unit is used for the font size. - * This method overrides the super.fontSizeUnit, since a position3dlabel only allows World, not Px nor Pt. + * This unit is used for the font size. This method overrides the super.fontSizeUnit, since a position3dlabel only + * allows World, not Px nor Pt. * (@see {@link fontSize}) * @param newUnit unused, since there is only one allowed unit (World) for this kind of label */ From 8d230390065a69abef49727eadb22ed114026e90 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 14:43:10 +0200 Subject: [PATCH 83/92] doc for label class --- source/label.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/source/label.ts b/source/label.ts index ac939e70..a47d966e 100644 --- a/source/label.ts +++ b/source/label.ts @@ -83,7 +83,8 @@ export class Label { /** * Returns the character at the specified index. - * @param pos - The zero-based index of the desired character. + * @param index - The zero-based index of the desired character. + * @returns character at the specified index */ charAt(index: number): string { if (this._text instanceof Text) { @@ -93,9 +94,10 @@ export class Label { } /** - * Returns the Unicode value of the character at the specified location. + * Returns the Unicode value (codepoint) of the character at the specified location. * @param index - The zero-based index of the desired character. If there is no character at the specified index, * NaN is returned. + * @returns codepoint of the char at given index or NaN */ charCodeAt(index: number): number { if (this._text instanceof Text) { @@ -108,6 +110,7 @@ export class Label { * Returns, whether or not the character at a given index is equal to the default or the text's line feed character. * @param index - The zero-based index of the desired character. If there is no character at the specified index, * NaN is returned. + * @returns true if char at given index equals the text's line feed character */ lineFeedAt(index: number): boolean { return this.charAt(index) === this.lineFeed; @@ -117,6 +120,7 @@ export class Label { /** * Gets the kerning value before (i.e., left in left-to-right writing systems) the given glyph index. * @param index index of the glyph in this label + * @returns kerning value before glyph at given index */ kerningBefore(index: number): number { if (index < 1 || index > this.length) { @@ -128,6 +132,7 @@ export class Label { /** * Gets the kerning value after (i.e., right in left-to-right writing systems) the given glyph index. * @param index index of the glyph in this label + * @returns kerning value after glyph at given index */ kerningAfter(index: number): number { if (index < 0 || index > this.length - 1) { @@ -137,9 +142,10 @@ export class Label { } /** - * Returns the advancement of a specified glyph. + * Returns the advancement of the glyph at given index. * @param index - The zero-based index of the desired character. If there is no character at the specified index, * NaN is returned. + * @returns advancement of the glyph at given index or NaN */ advance(index: number): number { if (index < 0 || index > this.length) { @@ -351,6 +357,10 @@ export class Label { return this._extent; } + /** + * Convenience getter to the label's text as string. + * @returns the label's text as string + */ toString(): string { if (this._text instanceof Text) { return this._text.text; From 0c5b0ac445c1bab0b9e45c2b06ef8b9e71f5c840 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 15:51:56 +0200 Subject: [PATCH 84/92] doc for labelgeometry --- source/labelgeometry.ts | 57 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/source/labelgeometry.ts b/source/labelgeometry.ts index 27e7ac1c..4872f490 100644 --- a/source/labelgeometry.ts +++ b/source/labelgeometry.ts @@ -8,14 +8,61 @@ import { Initializable } from './initializable'; /** - * Gathers vertices and other data needed for drawing all labels. + * Gathers vertices and other data needed for drawing all labels using the glyphquad-shaders. + * + * Example usage: + * + * const labelGeometry = new LabelGeometry(this._context); + * labelGeometry = new LabelGeometry(this._context); + * const aVertex = this._program.attribute('a_quadVertex', 0); + * const aTexCoord = this._program.attribute('a_texCoord', 1); + * const aOrigin = this._program.attribute('a_origin', 2); + * const aTangent = this._program.attribute('a_tangent', 3); + * const aUp = this._program.attribute('a_up', 4); + * + * labelGeometry.initialize(aVertex, aTexCoord, aOrigin, aTangent, aUp); + * labelGeometry.initialize(aVertex, aTexCoord, aOrigin, aTangent, aUp); + * + * .... + * + * labelGeometry.setTexCoords(Float32Array.from(texCoords)); + * labelGeometry.setGlyphCoords(Float32Array.from(origins), Float32Array.from(tangents), Float32Array.from(ups)); + * + * .... + * + * labelGeometry.bind(); + * labelGeometry.draw(); + * labelGeometry.unbind(); */ export class LabelGeometry extends Geometry { + /** + * These 2D vertices are equal for all quads, used for instanced rendering. Their actual position will be changed + * in the vertex shader, based on origins, tangents and up-vector attributes. + * 2-------4 + * | \ | + * | \ | + * 1-------3 + */ protected _vertices: Float32Array = new Float32Array(0); + /** + * Texture coordinates (uv) for every glyph, format: ll.x, ll.y, ur.x, ur.y + */ protected _texCoords: Float32Array = new Float32Array(0); + /** + * The coordinates of the lower left corner of every glyph. Its interpretation depends on the shader, + * usually it's a 3-component vector in world space. + */ protected _origins: Float32Array = new Float32Array(0); + /** + * The tangent vector for every glyph (direction along base line). Its interpretation depends on the shader, + * usually it's a 3-component vector in world space. + */ protected _tangents: Float32Array = new Float32Array(0); + /** + * The up vector for every glyph (orthogonal to its tangent vector). Its interpretation depends on the shader, + * usually it's a 3-component vector in world space. + */ protected _ups: Float32Array = new Float32Array(0); /** @@ -43,6 +90,7 @@ export class LabelGeometry extends Geometry { /** * Binds the vertex buffer object (VBO) to an attribute binding point of a given, pre-defined index. + * @param indices indices of buffers to bind */ protected bindBuffers(indices: Array): void { const gl = this.context.gl; @@ -69,6 +117,7 @@ export class LabelGeometry extends Geometry { /** * Unbinds the vertex buffer object (VBO) and disables the binding point. + * @param indices indices of buffers to unbind */ protected unbindBuffers(indices: Array): void { /* Please note the implicit unbind in attribEnable is skipped */ @@ -117,8 +166,9 @@ export class LabelGeometry extends Geometry { /** * Use this method to set (or update) the glyph coordinates, e.g. after typesetting or after the calculations - * of a placement algorithm. - * @param origins xyz-coordinates of the lower left corner of every glyph + * of a placement algorithm. The actuall interpretation of those buffers depends on the shader, + * usually they are 3-component vector in world space (provided as flat array.) + * @param origins coordinates of the lower left corner of every glyph * @param tangents tangent vector for every glyph (direction along base line) * @param ups up vector for every glyph (orthogonal to its tangent vector) */ @@ -157,5 +207,4 @@ export class LabelGeometry extends Geometry { /** @todo is DYNAMIC_DRAW more appropriate? */ this._buffers[1].data(this._texCoords, gl.STATIC_DRAW); } - } From 6972569ccee4420690803e17f39f5071d54ae3a7 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 16:14:04 +0200 Subject: [PATCH 85/92] assert for webgl extension requirement --- source/labelgeometry.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/labelgeometry.ts b/source/labelgeometry.ts index 4872f490..75edbbc9 100644 --- a/source/labelgeometry.ts +++ b/source/labelgeometry.ts @@ -73,6 +73,8 @@ export class LabelGeometry extends Geometry { constructor(context: Context, identifier?: string) { super(context, identifier); + assert(context.isWebGL2 || context.supportsInstancedArrays, `Support for Instanced Arrays is required.`); + /* Generate identifier from constructor name if none given. */ identifier = identifier !== undefined && identifier !== `` ? identifier : this.constructor.name; From df919b300c31b9676b6c21cc9967ea09b03ce14b Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 16:14:14 +0200 Subject: [PATCH 86/92] doc for labelrenderer --- source/labelrenderer.ts | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index fb22fce5..69dc02ad 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -63,6 +63,13 @@ export class LabelRenderer extends Renderer { protected _3DLabelGeometry: LabelGeometry; protected _uGlyphAtlas: WebGLUniformLocation; + /** + * Initializes and sets up rendering passes, navigation, loads a font face and links shaders with program. + * @param context valid context to create the object for. + * @param identifier meaningful name for identification of this instance. + * @param mouseEventProvider required for mouse interaction + * @returns whether initialization was successful + */ protected onInitialize(context: Context, callback: Invalidate, mouseEventProvider: MouseEventProvider, /* keyEventProvider: KeyEventProvider, */ @@ -154,6 +161,9 @@ export class LabelRenderer extends Renderer { return true; } + /** + * Uninitializes Buffers, Textures and and Programm. + */ protected onUninitialize(): void { super.uninitialize(); @@ -173,7 +183,14 @@ export class LabelRenderer extends Renderer { this._blit.uninitialize(); } - + /** + * This is invoked in order to check if rendering of a frame is required by means of implementation specific + * evaluation (e.g., lazy non continuous rendering). Regardless of the return value a new frame (preparation, + * frame, swap) might be invoked anyway, e.g., when update is forced or canvas or context properties have changed + * or the renderer was invalidated @see{@link invalidate}. + * Updates the navigaten and the AntiAliasingKernel. + * @returns whether to redraw + */ protected onUpdate(): boolean { this._ndcOffsetKernel = new AntiAliasingKernel(this._multiFrameNumber); @@ -183,6 +200,10 @@ export class LabelRenderer extends Renderer { return this._altered.any || this._camera.altered; } + /** + * This is invoked in order to prepare rendering of one or more frames, regarding multi-frame rendering and + * camera-updates. + */ protected onPrepare(): void { const gl = this._context.gl; @@ -237,6 +258,10 @@ export class LabelRenderer extends Renderer { this._camera.altered = false; } + /** + * After (1) update and (2) preparation are invoked, a frame is invoked. Renders both 2D and 3D labels. + * @param frameNumber for intermediate frames in accumulation rendering + */ protected onFrame(frameNumber: number): void { const gl = this._context.gl; @@ -287,12 +312,19 @@ export class LabelRenderer extends Renderer { } } + /** + * After (1) update, (2) preparation, and (3) frame are invoked, a swap is invoked for multi-frame rendering. + */ protected onSwap(): void { this._blit.framebuffer = this._accumulate.framebuffer ? this._accumulate.framebuffer : this._blit.framebuffer = this._intermediateFBO; this._blit.frame(); } + /** + * Loads font files and creates a fontface. + * @param context valid context to create the FontFace for. + */ protected loadFont(context: Context): void { /* This is a placeholder until the 'real' fontFace is loaded asynchronously by the fontLoader */ @@ -308,6 +340,9 @@ export class LabelRenderer extends Renderer { this._fontFace = fontFace; } + /** + * Sets up an example scene with 2D and 3D labels and sets the corresponding data on LabelGeometries. + */ protected setupScene(): void { /** OpenLL 2D Labels */ From 6404aebf5e8154354ba67fba0a943d8027670304 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 16:21:51 +0200 Subject: [PATCH 87/92] floating point notation for non integer semantic --- source/labelrenderer.ts | 18 +++++++++--------- source/position2dlabel.ts | 8 ++++---- source/position3dlabel.ts | 8 ++++---- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/source/labelrenderer.ts b/source/labelrenderer.ts index 69dc02ad..db0a659b 100644 --- a/source/labelrenderer.ts +++ b/source/labelrenderer.ts @@ -382,24 +382,24 @@ export class LabelRenderer extends Renderer { pos3Dlabel.fontSize = 0.1; /* position values in world, since fontSizeUnit is set to SpaceUnit.World */ - pos3Dlabel.setPosition(0, 0.1, -0.5); - pos3Dlabel.setDirection(0, 1, 0); - pos3Dlabel.setUp(-1, 0, 0); + pos3Dlabel.setPosition(0.0, 0.1, -0.5); + pos3Dlabel.setDirection(0.0, 1.0, 0.0); + pos3Dlabel.setUp(-1.0, 0.0, 0.0); glyphVertices = pos3Dlabel.typeset(); const shadowPos3Dlabel = new Position3DLabel(new Text('Hello Position Shadow'), this._fontFace); - shadowPos3Dlabel.setPosition(0, 0.1, -0.5); + shadowPos3Dlabel.setPosition(0.0, 0.1, -0.5); shadowPos3Dlabel.fontSize = 0.1; - shadowPos3Dlabel.setDirection(0, 1, 0); - shadowPos3Dlabel.setUp(0, 0, -1); + shadowPos3Dlabel.setDirection(0.0, 1.0, 0.0); + shadowPos3Dlabel.setUp(0.0, 0.0, -1.0); glyphVertices = glyphVertices.concat(shadowPos3Dlabel.typeset()); const anotherPos3Dlabel = new Position3DLabel(new Text('Yet another 3D Label'), this._fontFace); - anotherPos3Dlabel.setPosition(0.2, -0.1, 0); - anotherPos3Dlabel.setDirection(-1, 0, 0); - anotherPos3Dlabel.setUp(0, -1, 0); + anotherPos3Dlabel.setPosition(0.2, -0.1, 0.0); + anotherPos3Dlabel.setDirection(-1.0, 0.0, 0.0); + anotherPos3Dlabel.setUp(0.0, -1.0, 0.0); glyphVertices = glyphVertices.concat(anotherPos3Dlabel.typeset()); /* fill buffers */ diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index 969f393b..c2f7e5a1 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -24,8 +24,8 @@ export class Position2DLabel extends Label { */ constructor(text: Text, fontFace: FontFace) { super(text, fontFace); - this._position = vec2.fromValues(0, 0); - this._direction = vec2.fromValues(1, 0); + this._position = vec2.fromValues(0.0, 0.0); + this._direction = vec2.fromValues(1.0, 0.0); this._fontSizeUnit = Label.SpaceUnit.Px; } @@ -71,11 +71,11 @@ export class Position2DLabel extends Label { /* apply user tranformations (position, direction) */ mat4.translate(transform, transform, vec3.fromValues(this._position[0], this._position[1], 0)); - const n: vec2 = vec2.fromValues(1, 0); + const n: vec2 = vec2.fromValues(1.0, 0.0); let angle = vec2.angle(n, this._direction); /* perp dot product for signed angle */ - if (n[0] * this._direction[1] - n[1] * this._direction[0] < 0) { + if (n[0] * this._direction[1] - n[1] * this._direction[0] < 0.0) { angle = -angle; } diff --git a/source/position3dlabel.ts b/source/position3dlabel.ts index 257eea21..eee57424 100644 --- a/source/position3dlabel.ts +++ b/source/position3dlabel.ts @@ -26,9 +26,9 @@ export class Position3DLabel extends Label { */ constructor(text: Text, fontFace: FontFace) { super(text, fontFace); - this._position = vec3.fromValues(0, 0, 0); - this._direction = vec3.fromValues(1, 0, 0); - this._up = vec3.fromValues(0, 1, 0); + this._position = vec3.fromValues(0.0, 0.0, 0.0); + this._direction = vec3.fromValues(1.0, 0.0, 0.0); + this._up = vec3.fromValues(0.0, 1.0, 0.0); this._fontSizeUnit = Label.SpaceUnit.World; } @@ -54,7 +54,7 @@ export class Position3DLabel extends Label { const rotation = mat4.fromValues(this.direction[0], this.direction[1], this.direction[2], 0, this.up[0], this.up[1], this.up[2], 0, normal[0], normal[1], normal[2], 0, - 0, 0, 0, 1); + 0.0, 0.0, 0.0, 1.0); this.transform = mat4.mul(this.transform, transform, rotation); From c684b4c88f79ca2b300fbe5cf3a034a588530f98 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 16:35:29 +0200 Subject: [PATCH 88/92] don't discard invisible glyph fragments --- source/shaders/glyphquad.frag | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index 64d1a51e..0e18f03f 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -21,16 +21,17 @@ void main(void) float d = texture(u_glyphs, v_texture_coord).r; /** - * using if-statement and discard can slow down performance: + * Don't discard fragments, as we might need them for an id-buffer for clicking-interaction. + * Furthermore, using if-statement and discard can slow down performance: * it's bad for IMR, TBR, TBDR and early-Z optimization * https://stackoverflow.com/questions/8509051/is-discard-bad-for-program-performance-in-opengl - * But it is necessary: overlapping glyphs (like in 'ft') should not fight each other + * */ - if(d < 0.45) - discard; + // if(d < 0.45) + // discard; /** black. @todo font color as vertex attrib or uniform */ - vec4 fc = vec4(0.0, 0.0, 0.0, 1.0); + vec4 fc = vec4(1.0, 0.0, 0.0, 1.0); /** @todo mipmap access? */ /* simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur */ From f1a79f4583adf88e2886f534fb773837091ca5ee Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 16:43:05 +0200 Subject: [PATCH 89/92] format --- source/shaders/glyphquad.frag | 2 +- source/shaders/glyphquad.vert | 36 +++++++++++++++-------------------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/source/shaders/glyphquad.frag b/source/shaders/glyphquad.frag index 0e18f03f..b5c6008d 100644 --- a/source/shaders/glyphquad.frag +++ b/source/shaders/glyphquad.frag @@ -31,7 +31,7 @@ void main(void) // discard; /** black. @todo font color as vertex attrib or uniform */ - vec4 fc = vec4(1.0, 0.0, 0.0, 1.0); + vec4 fc = vec4(0.0, 0.0, 0.0, 1.0); /** @todo mipmap access? */ /* simplest aastep; when using multiframe sampling, smoothstep is not necessary and will add too much blur */ diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index b30c2be1..2661d40a 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -4,20 +4,20 @@ precision lowp int; @import ./facade.vert; #if __VERSION__ == 100 -#extension GL_EXT_draw_buffers : enable -attribute vec2 a_quadVertex; -/* [ texture ll: vec2, ur: vec2 ] */ -attribute vec4 a_texCoord; -attribute vec3 a_origin; -attribute vec3 a_tangent; -attribute vec3 a_up; + #extension GL_EXT_draw_buffers : enable + attribute vec2 a_quadVertex; + /* [ texture ll: vec2, ur: vec2 ] */ + attribute vec4 a_texCoord; + attribute vec3 a_origin; + attribute vec3 a_tangent; + attribute vec3 a_up; #else -layout(location = 0) in vec2 a_quadVertex; -/* [ texture ll: vec2, ur: vec2 ]*/ -layout(location = 1) in vec4 a_texCoord; -layout(location = 2) in vec3 a_origin; -layout(location = 3) in vec3 a_tangent; -layout(location = 4) in vec3 a_up; + layout(location = 0) in vec2 a_quadVertex; + /* [ texture ll: vec2, ur: vec2 ]*/ + layout(location = 1) in vec4 a_texCoord; + layout(location = 2) in vec3 a_origin; + layout(location = 3) in vec3 a_tangent; + layout(location = 4) in vec3 a_up; #endif uniform mat4 u_viewProjection; @@ -32,15 +32,9 @@ void main(void) /* TEXTURE COORDS */ /* flip y-coordinates */ + vec2 texExt = vec2(a_texCoord[2] - a_texCoord[0], a_texCoord[1] - a_texCoord[3]); - float posX = a_texCoord[0]; - float posY = a_texCoord[1]; - - float pos2X = a_texCoord[2]; - float pos2Y = a_texCoord[3]; - vec2 texExt = vec2(pos2X-posX, posY-pos2Y); - - v_texture_coord = a_quadVertex * texExt + vec2(a_texCoord[0], 1.0-a_texCoord[1]); + v_texture_coord = a_quadVertex * texExt + vec2(a_texCoord[0], 1.0 - a_texCoord[1]); /* POSITIONING*/ /* quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) */ From eda05c1a5bc5dcb2f8b5cb5b1a2546f9cdf341cd Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 16:57:50 +0200 Subject: [PATCH 90/92] doc for typesetter --- source/typesetter.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/source/typesetter.ts b/source/typesetter.ts index 16efafd5..947b34b1 100644 --- a/source/typesetter.ts +++ b/source/typesetter.ts @@ -20,6 +20,15 @@ export class Typesetter { protected static readonly DELIMITERS: string = '\x0A ,.-/()[]<>'; + /** + * Returns if newline should be applied for next word (or next glyph if word exceeds the line width) + * @param label the label that has wordWrap enabled + * @param pen horizontal and vertical position at which typesetting takes place/arrived. + * @param glyph current glyph + * @param index current index for char in this label + * @param safeForwardIndex used to reduce the number of wordwrap forward passes + * @returns whether or not typesetting should go on a new line + */ protected static wordWrap(label: Label, pen: vec2, glyph: Glyph, index: number, safeForwardIndex: number): boolean { assert(label.wordWrap, `expected wordWrap to be enabled for label, given ${label}`); const lineWidth = label.lineWidth; @@ -231,6 +240,12 @@ export class Typesetter { } + /** + * Typesets the given label, transforming the vertices in-world, ready to be rendered. + * @param label the label that shall be typeset + * @param vertices the glyphvertices, a prepared (optionally empty) vertex storage + * @param begin vertex index to start the typesetting (usually 0) + */ static typeset(label: Label, vertices?: GlyphVertices, begin?: number): GLfloat2 { /* Horizontal and vertical position at which typesetting takes place/arrived. */ const pen = vec2.create(); @@ -286,7 +301,5 @@ export class Typesetter { const labelExtent = Typesetter.transformExtent(label.transform, extent); label.extent = labelExtent; return labelExtent; - } - } From f3b26b689cdb28e67527b8d5f384ec04b1bdb2fc Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 17:51:42 +0200 Subject: [PATCH 91/92] MAD optimization in shader + doc --- source/shaders/glyphquad.vert | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/source/shaders/glyphquad.vert b/source/shaders/glyphquad.vert index 2661d40a..bacce6cf 100644 --- a/source/shaders/glyphquad.vert +++ b/source/shaders/glyphquad.vert @@ -37,9 +37,16 @@ void main(void) v_texture_coord = a_quadVertex * texExt + vec2(a_texCoord[0], 1.0 - a_texCoord[1]); /* POSITIONING*/ - /* quad data: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex) */ - - vec4 vertex = vec4(a_origin + a_quadVertex.x*a_tangent + a_quadVertex.y*a_up, 1.0); + /* quad data as flat array: [0, 0, 0, 1, 1, 0, 1, 1] (a_quadVertex), which translates to ll, lr, ul, ur corners. + * 2-------4 + * | \ | + * | \ | + * 1-------3 + * The current vertex is calculated based on the current quad corners and the tangent / up attributes. + * The following lines are optimized for MAD optimization. + */ + vec3 tangentDirection = a_origin + a_quadVertex.x * a_tangent; + vec4 vertex = vec4(tangentDirection + a_quadVertex.y * a_up, 1.0); vertex = u_viewProjection * vertex; From 8d807733b44b2c6747ec31d40b187f202906a427 Mon Sep 17 00:00:00 2001 From: Anne Gropler Date: Thu, 11 Oct 2018 17:54:57 +0200 Subject: [PATCH 92/92] format; remove double-assignment --- source/label.ts | 4 ++-- source/position2dlabel.ts | 2 +- source/position3dlabel.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/label.ts b/source/label.ts index a47d966e..2871e164 100644 --- a/source/label.ts +++ b/source/label.ts @@ -1,10 +1,10 @@ -import { mat4, vec3, vec4 } from 'gl-matrix'; +import { mat4, vec3 } from 'gl-matrix'; import { ChangeLookup } from './changelookup'; import { Color } from './color'; import { FontFace } from './fontface'; -import { GlyphVertex, GlyphVertices } from './glyphvertices'; +import { GlyphVertices } from './glyphvertices'; import { Text } from './text'; diff --git a/source/position2dlabel.ts b/source/position2dlabel.ts index c2f7e5a1..24b59bd6 100644 --- a/source/position2dlabel.ts +++ b/source/position2dlabel.ts @@ -115,7 +115,7 @@ export class Position2DLabel extends Label { * Sets the 2D direction of the label, i.e., the direction of the baseline. */ set direction(xy: vec2) { - this._direction = vec2.normalize(this._direction, xy); + vec2.normalize(this._direction, xy); } get direction(): vec2 { return this._direction; diff --git a/source/position3dlabel.ts b/source/position3dlabel.ts index eee57424..e2ecfcfe 100644 --- a/source/position3dlabel.ts +++ b/source/position3dlabel.ts @@ -89,7 +89,7 @@ export class Position3DLabel extends Label { * Sets the 3D direction of the label, i.e., the direction of the baseline. */ set direction(xyz: vec3) { - this._direction = vec3.normalize(this._direction, xyz); + vec3.normalize(this._direction, xyz); } get direction(): vec3 { return this._direction;