Source code for pi3d.util.String

from __future__ import absolute_import, division, print_function, unicode_literals

import sys
if sys.version_info[0] == 3:
  unichr = chr
  text_type = str
else:
  text_type = unicode
import logging

from pi3d.constants import opengles, GL_ARRAY_BUFFER

from pi3d.Buffer import Buffer
from pi3d.Shape import Shape
from pi3d.util import Utility

LOGGER = logging.getLogger(__name__)
DOTS_PER_INCH = 72.0
DEFAULT_FONT_SIZE = 0.24
DEFAULT_FONT_SCALE = DEFAULT_FONT_SIZE / DOTS_PER_INCH
GAP = 1.0

_NORMALS = [[0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [0.0, 0.0, -1.0]]

[docs]class String(Shape): """Shape used for writing text on screen. It is a flat, one sided rectangualar plane""" def __init__(self, camera=None, light=None, font=None, string=None, x=0.0, y=0.0, z=1.0, sx=DEFAULT_FONT_SCALE, sy=DEFAULT_FONT_SCALE, is_3d=True, size=DEFAULT_FONT_SIZE, rx=0.0, ry=0.0, rz=0.0, justify="C"): """Standard Shape constructor without the facility to change z scale or any of the offset values. Additional keyword arguments: *font* Pngfont or Font class object. *string* of ASCI characters in range(32, 128) plus 10 = \n Line Feed *sx, sy* These change the actual vertex positions of the shape rather than being used as scaling factors. This is to avoid distortion when the string is drawn using an orthographic camera *is_3d* alters the values of sx and sy to give reasonable sizes with 2D or 3D drawing *size* approximate size of the characters in inches - obviously for 3D drawing of strings this will depend on camera fov, display size and how far away the string is placed *justify* default C for central, can be R for right or L for left """ if not is_3d: sy = sx = size * 4.0 super(String, self).__init__(camera, light, "", x, y, z, rx, ry, rz, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0) LOGGER.debug("Creating string ...") self.verts = [] self.texcoords = [] self.norms = [] self.inds = [] temp_verts = [] xoff = 0.0 yoff = 0.0 lines = 0 if not isinstance(string, text_type): string = string.decode('utf-8') nlines = string.count("\n") + 1 def make_verts(): #local function to justify each line if justify.upper() == "C": cx = xoff / 2.0 elif justify.upper() == "L": cx = 0.0 else: cx = xoff for j in temp_verts: self.verts.append([(j[0] - cx) * sx, (j[1] + nlines * font.height * GAP / 2.0 - yoff) * sy, j[2]]) default = font.glyph_table.get(unichr(0), None) for i, c in enumerate(string): if c == '\n': make_verts() yoff += font.height * GAP xoff = 0.0 temp_verts = [] lines += 1 continue #don't attempt to draw this character! glyph = font.glyph_table.get(c, default) if not glyph: continue w, h, texc, verts = glyph[0:4] for j in verts: temp_verts.append((j[0]+xoff, j[1], j[2])) xoff += w for j in texc: self.texcoords.append(j) self.norms.extend(_NORMALS) # Take Into account unprinted \n characters stv = 4 * (i - lines) self.inds.extend([[stv, stv + 2, stv + 1], [stv, stv + 3, stv + 2]]) make_verts() self.buf = [] self.buf.append(Buffer(self, self.verts, self.texcoords, self.inds, self.norms)) self.buf[0].textures = [font] self.buf[0].unib[1] = -1.0 self.string = string #for later use in quick_change() method self.maxlen = len(string) self.font = font
[docs] def quick_change(self, new_string): """Method for quickly changing some characters within a previously generated String. i.e. for changing digits in a readout etc. NB: 1. if you use a variable width font there will be some distortion as characters are stretched or squashed to the original character's dimensions. 2. there is no account made of new line characters (TODO) 3. you must make the original string long enough to fit any additional characters you add to new_string 4. you must make sure the Font as used for the String.__init__ contains all the glyphs you may need for subsequent changes. """ import ctypes if new_string != self.string: new_string = new_string + ' ' * (len(self.string) - len(new_string)) trunc_string = new_string[:self.maxlen] #chop to length for i, c in enumerate(trunc_string): if c != self.string[i]: stride = 8 offset = 6 texc = self.font.glyph_table[c][2] for j, tc in enumerate(texc): #patch values directly into array_buffer for k in [0, 1]: self.buf[0].array_buffer[(i * 4 + j),(offset + k)] = tc[k] uvmod = True self.buf[0]._select() #then just call glBufferData opengles.glBufferSubData(GL_ARRAY_BUFFER, 0, self.buf[0].array_buffer.nbytes, self.buf[0].array_buffer.ctypes.data_as(ctypes.POINTER(ctypes.c_float))) self.string = new_string