/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.ui.text2d; import java.awt.Font; import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.Vector; import org.openmali.spatial.bodies.Frustum; import org.openmali.vecmath2.Colorf; import org.openmali.vecmath2.Point2f; import org.openmali.vecmath2.Tuple2f; import org.openmali.vecmath2.Vector3f; import org.xith3d.scenegraph.UpdatableNode; import org.xith3d.scenegraph.Transform3D; import org.xith3d.scenegraph.TransformGroup; import org.xith3d.scenegraph.TransparencyAttributes; import org.xith3d.scenegraph.View; /** * As there is no ( or no dynamic ) text node for Xith3D here is mine.<br /> * * While the Text2D version org.xith3d.text.Text2D by Terje Wiesener uses a single face with a static texture * this class uses a seperate Shape3d for every char it uses. Those shapes are stored in a static HashMap to prevent * some load when using many text objects. </br> * * <ul>features: * <li>- fast text and color changes</li> * <li>- multiline support</li> * <li>- horizontal and vertical text aligning</li> * <li>- Java2D font support</li> * </ul> * * Thanks to Terje Wiesener ( who wrote the other Text2D implementation ), i borrowed the render stuff from his work :) * * @see <a href="http://192.18.37.44/forums/index.php?topic=10106.0">First Announcement (old version)</a> * @see <a href="http://192.18.37.44/forums/index.php?topic=10638.0">Secondary Announcement (new version)</a> * * @author Florian Hofmann (aka Goliat) * @author Amos Wenger (aka BlueSky) * @author Marvin Froehlich (aka Qudus) * * @version 2.0 */ public class Text2D extends TransformGroup implements UpdatableNode { private String text; private Font font; private Colorf color = new Colorf(); private TextAlignment align; private Tuple2f size; private Vector3f tmpVec = new Vector3f(); private boolean dirty = false; private List< Line2D > lines; private List< Line2D > unmodLines; private TransparencyAttributes cachedTA = null; /** * @return an unmodifiable List of all contained Lines */ public List< Line2D > getLines() { return ( unmodLines ); } /** * Sets the color of current text </br> * has a small bug: while changing text rapidly the color doesn't change correctly when setColor is called before setText * * @param r red value * @param g green value * @param b blue value */ public void setColor( float r, float g, float b ) { for ( int i = 0; i < lines.size(); i++ ) { lines.get( i ).setColor( r, g, b ); } this.color.set( r, g, b ); } /** * Sets the color of current text </br> * (this should be called after setText if both are called once per frame) * * @see #setColor( float, float, float ) * * @param color new Color to set */ public void setColor( Colorf color ) { setColor( color.getRed(), color.getGreen(), color.getBlue() ); } public Colorf getColor() { return ( color ); } public float getRedValue() { return ( color.getRed() ); } public float getBlueValue() { return ( color.getGreen() ); } public float getGreenValue() { return ( color.getBlue() ); } public void setTransparency( float transparency ) { if ( cachedTA != null ) { cachedTA.setTransparency( transparency ); return; } cachedTA = new TransparencyAttributes( TransparencyAttributes.BLENDED, transparency ); for ( int i = 0; i < lines.size(); i++ ) { lines.get( i ).setTransparency( cachedTA ); } } public float getTransparency() { if ( cachedTA == null ) return ( 0.0f ); return ( cachedTA.getTransparency() ); } public Tuple2f getSize() { return ( size ); } /** * Changes/sets the text's font * * @param font new font to use */ public void setFont( Font font ) { this.font = font; dirty = true; } /** * Changes/sets the text's font name * * @param fontname new font to use */ public void setFont( String fontname ) { if ( this.font != null ) { this.font = new Font( fontname, this.font.getStyle(), this.font.getSize() ); } else { this.font = new Font( fontname, Font.PLAIN, 12 ); } dirty = true; } public Font getFont() { return ( font ); } /** * Sets new fontflags @see java.awt.Font. * * @param fontFlags new flags */ public void setFontFlags( int fontFlags ) { if ( this.font != null ) { this.font = new Font( this.font.getName(), fontFlags, this.font.getSize() ); } else { this.font = new Font( "Monospace", fontFlags, 12 ); } dirty = true; } /** * Sets new resolution. * * @param resolution new resolution */ public void setResolution( int resolution ) { if ( this.font != null ) { this.font = new Font( this.font.getName(), this.font.getStyle(), resolution ); } else { this.font = new Font( "Monospace", Font.PLAIN, resolution ); } dirty = true; } /** * Sets new text alignment. * * @param alignment new text alignment */ public void setAlignment( TextAlignment alignment ) { this.align = alignment; dirty = true; } /** * @return the current text alignment */ public TextAlignment getAlignment() { return ( this.align ); } /** * {@inheritDoc} */ public boolean update( View view, Frustum frustum, long nanoTime, long nanoStep ) { if ( dirty ) { this.removeAllChildren(); lines.clear(); setText( this.text ); dirty = false; } return ( true ); } /** * Set new text (this is finally done in an efficient way). * * @param newText new text */ public void setText( String newText ) { TextAlignment lineAlign = null; if ( align.isLeftAligned() ) lineAlign = TextAlignment.TOP_LEFT; else if ( align.isHCenterAligned() ) lineAlign = TextAlignment.TOP_CENTER; else if ( align.isRightAligned() ) lineAlign = TextAlignment.TOP_RIGHT; // create a StringTokenizer to seperate the lines StringTokenizer lineTokens = new StringTokenizer( newText, "\n\f" ); Transform3D t3d; Line2D currLine; float offsetY = 0.0f; int index = 0; while ( lineTokens.hasMoreTokens() ) { if ( lines.size() <= index ) { currLine = new Line2D( lineTokens.nextToken(), color, font, lineAlign ); lines.add( currLine ); this.addChild( currLine ); t3d = currLine.getTransform(); t3d.getTranslation( tmpVec ); tmpVec.setY( offsetY ); t3d.setTranslation( tmpVec ); currLine.setTransform( t3d ); offsetY -= currLine.getSize().getY(); } else { currLine = lines.get( index ); t3d = currLine.getTransform(); t3d.getTranslation( tmpVec ); tmpVec.setY( offsetY ); t3d.setTranslation( tmpVec ); currLine.setTransform( t3d ); currLine.setText( lineTokens.nextToken() ); offsetY -= currLine.getSize().getY(); } index++; } // remove the unnecessary lines for ( int i = lines.size() - 1; i >= index; i-- ) { this.removeChild( lines.get( i ) ); lines.remove( i ); } // calculate size if ( newText.length() == 0 ) { size.set( 0f, 0f ); } else { float width = 0f; float height = 0f; for ( int i = 0; i < lines.size(); i++ ) { final Line2D line = lines.get( i ); if ( line.getSize().getX() > width ) { width = line.getSize().getX(); } height += line.getSize().getY(); } size.set( width, height ); } // adjust translation by (vertical) alignment t3d = this.getTransform(); if ( align.isVCenterAligned() ) { t3d.setTranslation( 0f, ( size.getY() / 2.0f ), 0f ); } else if ( align.isBottomAligned() ) { t3d.setTranslation( 0f, size.getY(), 0f ); } this.setTransform( t3d ); this.text = newText; } /** * Create a new Text Object with all options * * @param text the initial text to set * @param r the foreground color to use * @param g the foreground color to use * @param b the foreground color to use * @param font the font to use </br> as the font size is fixed on to the texture it's just necessary to give the fontname which can be any font that Font can handle * @param alignFlags align flags to use */ public Text2D( String text, float r, float g, float b, Font font, TextAlignment alignFlags ) { super(); this.color.set( r, g, b ); this.font = font; this.align = alignFlags; this.size = new Point2f(); this.lines = new Vector< Line2D >( 1 ); this.unmodLines = Collections.unmodifiableList( lines ); setText( text ); } /** * Create a new Text Object with all options * * @param text the initial text to set * @param font the font to use </br> as the font size is fixed on to the texture it's just necessary to give the fontname which can be any font that Font can handle * @param color the foreground color to use * @param alignFlags align flags to use */ public Text2D( String text, Colorf color, Font font, TextAlignment alignFlags ) { this( text, color.getRed(), color.getGreen(), color.getBlue(), font, alignFlags ); } /** * Create a new Text Object with all options * * @param text the initial text to set * @param r the foreground color to use * @param g the foreground color to use * @param b the foreground color to use * @param fontname the font to use </br> as the font size is fixed on to the texture it's just necessary to give the fontname which can be any font that Font can handle * @param fontFlags font flags to use @see java.awt.Font * @param resolution font resolution </br> every letter has a fixed geometry size of 1x1.5. The resolution is the width of the texture * @param alignFlags align flags to use */ public Text2D( String text, float r, float g, float b, String fontname, int fontFlags, int resolution, TextAlignment alignFlags ) { this( text, r, g, b, new Font( fontname, fontFlags, resolution ), alignFlags ); } /** * Create a new Text Object with all options * * @param text the initial text to set * @param fontname the font to use </br> as the font size is fixed on to the texture it's just necessary to give the fontname which can be any font that Font can handle * @param fontFlags font flags to use @see java.awt.Font * @param resolution font resolution </br> every letter has a fixed geometry size of 1x1.5. The resolution is the width of the texture * @param color the foreground color to use * @param alignFlags align flags to use */ public Text2D( String text, Colorf color, String fontname, int fontFlags, int resolution, TextAlignment alignFlags ) { this( text, color.getRed(), color.getGreen(), color.getBlue(), fontname, fontFlags, resolution, alignFlags ); } /** * Creates a new Text2D object * * @param text new text * @param color color */ public Text2D( String text, Colorf color ) { this( text, color, "Monospace", Font.BOLD, 12, TextAlignment.CENTER_CENTER ); } /** * Create a text object with alignflags * * @param text new text * @param alignFlags new alignFlags */ public Text2D( String text, TextAlignment alignFlags ) { this( text, 1.0f, 1.0f, 1.0f, "Monospace", Font.BOLD, 12, alignFlags ); } /** * Create a new Text Object with standard Attributes * * @param text first Text */ public Text2D( String text ) { this( text, 1.0f, 1.0f, 1.0f, "Monospace", Font.BOLD, 12, TextAlignment.CENTER_CENTER ); } }