Text terminal rendering design
- From: Brendan Guild <dont@xxxxxxx>
- Date: Wed, 16 May 2007 00:16:55 GMT
I'm casually working on a project to create an OO text terminal
library. The purpose of it is similar to PDCurses and I intend to use
PDCurses at the lowest level of implementation, but because I am
increasing the abstraction beyond what little abstraction PDCurses
offers, I also have the possibility for more interesting
implementations. Unfortunately, I have bumped into an annoying flaw
in my design that is preventing me from implementing a fun effect
that I have imagined.
My current design can be partially illustrated as follows:
0..* displays 1
[Terminal] ------------------>[Symbol]
+put() +ascii
+init() +unicode
+shutdown() +background
1 | +foreground
| uses symbols | 1
| from |
| |
0..*| |
v 0..* made by |
[SymbolFactory]<---------------------
The three major classes are Terminal, Symbol, and SymbolFactory. All
three are abstract with many potential implementations.
The Terminal class is the objects responsible for the low level
operations needed to present symbols to the user. It has a 2D array
of Symbols and methods that allow a client to modify that array and
display the array to the UI. In an actual implementation the Symbol
objects might not be stored, just used to update a low-level
operating system terminal using PDCurses or something similar.
The each Terminal has an associated SymbolFactory object. The
SymbolFactory class has a variety of methods for creating and
modifying Symbol objects. It produces Symbols of the specific
subclass that the Terminal is expecting, performing conversion
operations if necessary. For example, the SymbolFactory class has a
setColor(Symbol, Color) method that returns a Symbol. The expected
behaviour is to produce a symbol that looks the same as the given
symbol but in the given color. If the given symbol is not the
subclass of Symbol that this factory is expecting, the factory will
perform a conversion before returning the requested symbol in the
given color.
The Symbol class represents a symbol to be displayed. It has methods
for converting the symbol to the closest ASCII and Unicode
representations, as well as getting the foreground and background
color of the symbol. This allows a SymbolFactory that is aware of
ASCII or Unicode character sets to easily convert this symbol to the
required format. The Symbol class also provides a method that reports
the SymbolFactory that created it. This allows a factory or terminal
to guess the subclass and nature of any given Symbol object if it was
created by a known factory. For example, there is a singleton
subclass of SymbolFactory called LineFactory for line drawing that
only produces Symbols of the class LineSymbol and LineSymbol has
methods that allow a Terminal to determine the geometry of the
intended line and draw it, after testing that the factory of the
given symbol is LineFactory.
That is the abstract design. Now here is the specific case that is
causing trouble:
[Terminal] [Symbol]
A A
| 0..* displays 1 |
[GraphicTerminal] ------------------> [GraphicSymbol]
| 0..* 1 | | 1
| | |
| | | originates
| uses 1 0..* drawn by | | from
------------> [Drawer] <----------------- |
+draw(Symbol,x.y,w,h) | 0..*
v
[Symbol]
The idea is that not all terminals have to be implemented by an
operating system text terminal; we can use operating system drawing
commands instead to represent any arbitrary symbol. The possibilities
are too numerous for any one implementation of GraphicTerminal to
know how to draw every symbol, so it has a collection of Drawer
objects that each know how to draw a subset of the symbols that the
terminal may encounter. The factory assigns each symbol the
appropriate Drawer object so that the Drawer knows how to draw the
corresponding Symbol and then wraps the Symbol and Drawer together
into a pair called a GraphicSymbol.
The best part of this design is that we can give a GraphicSymbol to a
PDCurses terminal object and it will automatically translate the
symbol to an appropriate ASCII code and display something useful. I
can create terminal tools that look graphical when graphics are
available and are fast and text-based when that is desired. So
obviously I want to take this idea even further, but my wonderful
abstract design suddenly fails me.
I'd like to create larger symbols that are spread over a rectangular
area of the terminal corresponding to several ordinary symbols. I can
do this by creating a subclass of GraphicSymbol that holds the
overall symbol, as well as coordinates for the particular part of the
symbol that each object represents. For example, if I wanted to draw
a 4x4 'A' I would create four BigSymbol objects, (0,0), (0,1), (1,0),
(1,1), each having a reference to the symbol 'A' and a Drawer that
knows how to draw a quarter of a symbol. That's not the problem.
The problem is that the pixels of the graphical drawing area of the
terminal is not necessarily divisible among the symbols that are to
be drawn. For example, a screen might be 1024 pixels wide and we
might want the terminal to be 80 symbols wide, giving us 12.8 pixels
per symbol in width. The GraphicTerminal accommodates this by
sometimes telling the Drawer to draw a symbol 12 pixels wide and
sometimes telling it to draw 13 pixels wide. This is ordinarly not a
problem for any of the participants, but in the case of BigSymbol it
is a big problem.
In order for BigSymbol to correctly draw the symbol in pieces it has
to know the overall size of the symbol. Ordinary symbols are given
that automatically when they are drawn but BigSymbol is only given
the size of each piece. When it is drawing the first piece it can
only guess at the size of the next piece. The guess will be accurate
within one pixel, but that is not enough to avoid unwanted artifacts
in the image. I need something to keep track of big picture for
drawing larger symbols, but there seems to be no room in my current
design for this.
.
- Follow-Ups:
- Re: Text terminal rendering design
- From: H. S. Lahman
- Re: Text terminal rendering design
- Prev by Date: Re: Whose Fish?
- Next by Date: Re: SOA vs OO
- Previous by thread: Re: One object passing members to another class, modifying values
- Next by thread: Re: Text terminal rendering design
- Index(es):
Relevant Pages
|