-/*\r
-Copyright 2006 Jerry Huxtable\r
-\r
-Licensed under the Apache License, Version 2.0 (the "License");\r
-you may not use this file except in compliance with the License.\r
-You may obtain a copy of the License at\r
-\r
- http://www.apache.org/licenses/LICENSE-2.0\r
-\r
-Unless required by applicable law or agreed to in writing, software\r
-distributed under the License is distributed on an "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-See the License for the specific language governing permissions and\r
-limitations under the License.\r
- */\r
-\r
-package org.simantics.scenegraph.utils;\r
-\r
-import java.awt.Font;\r
-import java.awt.Shape;\r
-import java.awt.Stroke;\r
-import java.awt.font.FontRenderContext;\r
-import java.awt.font.GlyphVector;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.FlatteningPathIterator;\r
-import java.awt.geom.GeneralPath;\r
-import java.awt.geom.PathIterator;\r
-import java.awt.geom.Point2D;\r
-\r
-/**\r
- * Taken from http://www.jhlabs.com/java/java2d/strokes/\r
- */\r
-public class TextStroke implements Stroke {\r
- private final String text;\r
- private final Font font;\r
- private boolean stretchToFit = false;\r
- private boolean repeat = false;\r
- private final AffineTransform t = new AffineTransform();\r
-\r
- private static final float FLATNESS = 1;\r
-\r
- public TextStroke( String text, Font font ) {\r
- this( text, font, true, false );\r
- }\r
-\r
- public TextStroke( String text, Font font, boolean stretchToFit, boolean repeat ) {\r
- this.text = text;\r
- this.font = font;\r
- this.stretchToFit = stretchToFit;\r
- this.repeat = repeat;\r
- }\r
-\r
- @Override\r
- public Shape createStrokedShape( Shape shape ) {\r
- FontRenderContext frc = new FontRenderContext(null, true, true);\r
- GlyphVector glyphVector = font.createGlyphVector(frc, text);\r
-\r
- GeneralPath result = new GeneralPath();\r
- PathIterator it = new FlatteningPathIterator( shape.getPathIterator( null ), FLATNESS );\r
- float points[] = new float[6];\r
- float moveX = 0, moveY = 0;\r
- float lastX = 0, lastY = 0;\r
- float thisX = 0, thisY = 0;\r
- int type = 0;\r
- @SuppressWarnings("unused")\r
- boolean first = false;\r
- float next = 0;\r
- int currentChar = 0;\r
- int length = glyphVector.getNumGlyphs();\r
-\r
- if ( length == 0 )\r
- return result;\r
-\r
- float factor = stretchToFit ? measurePathLength( shape )/(float)glyphVector.getLogicalBounds().getWidth() : 1.0f;\r
- float nextAdvance = 0;\r
-\r
- while ( currentChar < length && !it.isDone() ) {\r
- type = it.currentSegment( points );\r
- switch( type ){\r
- case PathIterator.SEG_MOVETO:\r
- moveX = lastX = points[0];\r
- moveY = lastY = points[1];\r
- result.moveTo( moveX, moveY );\r
- first = true;\r
- nextAdvance = glyphVector.getGlyphMetrics( currentChar ).getAdvance() * 0.5f;\r
- next = nextAdvance;\r
- break;\r
-\r
- case PathIterator.SEG_CLOSE:\r
- points[0] = moveX;\r
- points[1] = moveY;\r
- // Fall into....\r
-\r
- case PathIterator.SEG_LINETO:\r
- thisX = points[0];\r
- thisY = points[1];\r
- float dx = thisX-lastX;\r
- float dy = thisY-lastY;\r
- float distance = (float)Math.sqrt( dx*dx + dy*dy );\r
- if ( distance >= next ) {\r
- float r = 1.0f/distance;\r
- float angle = (float)Math.atan2( dy, dx );\r
- while ( currentChar < length && distance >= next ) {\r
- Shape glyph = glyphVector.getGlyphOutline( currentChar );\r
- Point2D p = glyphVector.getGlyphPosition(currentChar);\r
- float px = (float)p.getX();\r
- float py = (float)p.getY();\r
- float x = lastX + next*dx*r;\r
- float y = lastY + next*dy*r;\r
- float advance = nextAdvance;\r
- nextAdvance = currentChar < length-1 ? glyphVector.getGlyphMetrics(currentChar+1).getAdvance() * 0.5f : 0;\r
- t.setToTranslation( x, y );\r
- t.rotate( angle );\r
- t.translate( -px-advance, -py );\r
- result.append( t.createTransformedShape( glyph ), false );\r
- next += (advance+nextAdvance) * factor;\r
- currentChar++;\r
- if ( repeat )\r
- currentChar %= length;\r
- }\r
- }\r
- next -= distance;\r
- first = false;\r
- lastX = thisX;\r
- lastY = thisY;\r
- break;\r
- }\r
- it.next();\r
- }\r
-\r
- return result;\r
- }\r
-\r
- public float measurePathLength( Shape shape ) {\r
- PathIterator it = new FlatteningPathIterator( shape.getPathIterator( null ), FLATNESS );\r
- float points[] = new float[6];\r
- float moveX = 0, moveY = 0;\r
- float lastX = 0, lastY = 0;\r
- float thisX = 0, thisY = 0;\r
- int type = 0;\r
- float total = 0;\r
-\r
- while ( !it.isDone() ) {\r
- type = it.currentSegment( points );\r
- switch( type ){\r
- case PathIterator.SEG_MOVETO:\r
- moveX = lastX = points[0];\r
- moveY = lastY = points[1];\r
- break;\r
-\r
- case PathIterator.SEG_CLOSE:\r
- points[0] = moveX;\r
- points[1] = moveY;\r
- // Fall into....\r
-\r
- case PathIterator.SEG_LINETO:\r
- thisX = points[0];\r
- thisY = points[1];\r
- float dx = thisX-lastX;\r
- float dy = thisY-lastY;\r
- total += (float)Math.sqrt( dx*dx + dy*dy );\r
- lastX = thisX;\r
- lastY = thisY;\r
- break;\r
- }\r
- it.next();\r
- }\r
-\r
- return total;\r
- }\r
-\r
+/*
+Copyright 2006 Jerry Huxtable
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ */
+
+package org.simantics.scenegraph.utils;
+
+import java.awt.Font;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.FlatteningPathIterator;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+
+/**
+ * Taken from http://www.jhlabs.com/java/java2d/strokes/
+ */
+public class TextStroke implements Stroke {
+ private final String text;
+ private final Font font;
+ private boolean stretchToFit = false;
+ private boolean repeat = false;
+ private final AffineTransform t = new AffineTransform();
+
+ private static final float FLATNESS = 1;
+
+ public TextStroke( String text, Font font ) {
+ this( text, font, true, false );
+ }
+
+ public TextStroke( String text, Font font, boolean stretchToFit, boolean repeat ) {
+ this.text = text;
+ this.font = font;
+ this.stretchToFit = stretchToFit;
+ this.repeat = repeat;
+ }
+
+ @Override
+ public Shape createStrokedShape( Shape shape ) {
+ FontRenderContext frc = new FontRenderContext(null, true, true);
+ GlyphVector glyphVector = font.createGlyphVector(frc, text);
+
+ GeneralPath result = new GeneralPath();
+ PathIterator it = new FlatteningPathIterator( shape.getPathIterator( null ), FLATNESS );
+ float points[] = new float[6];
+ float moveX = 0, moveY = 0;
+ float lastX = 0, lastY = 0;
+ float thisX = 0, thisY = 0;
+ int type = 0;
+ @SuppressWarnings("unused")
+ boolean first = false;
+ float next = 0;
+ int currentChar = 0;
+ int length = glyphVector.getNumGlyphs();
+
+ if ( length == 0 )
+ return result;
+
+ float factor = stretchToFit ? measurePathLength( shape )/(float)glyphVector.getLogicalBounds().getWidth() : 1.0f;
+ float nextAdvance = 0;
+
+ while ( currentChar < length && !it.isDone() ) {
+ type = it.currentSegment( points );
+ switch( type ){
+ case PathIterator.SEG_MOVETO:
+ moveX = lastX = points[0];
+ moveY = lastY = points[1];
+ result.moveTo( moveX, moveY );
+ first = true;
+ nextAdvance = glyphVector.getGlyphMetrics( currentChar ).getAdvance() * 0.5f;
+ next = nextAdvance;
+ break;
+
+ case PathIterator.SEG_CLOSE:
+ points[0] = moveX;
+ points[1] = moveY;
+ // Fall into....
+
+ case PathIterator.SEG_LINETO:
+ thisX = points[0];
+ thisY = points[1];
+ float dx = thisX-lastX;
+ float dy = thisY-lastY;
+ float distance = (float)Math.sqrt( dx*dx + dy*dy );
+ if ( distance >= next ) {
+ float r = 1.0f/distance;
+ float angle = (float)Math.atan2( dy, dx );
+ while ( currentChar < length && distance >= next ) {
+ Shape glyph = glyphVector.getGlyphOutline( currentChar );
+ Point2D p = glyphVector.getGlyphPosition(currentChar);
+ float px = (float)p.getX();
+ float py = (float)p.getY();
+ float x = lastX + next*dx*r;
+ float y = lastY + next*dy*r;
+ float advance = nextAdvance;
+ nextAdvance = currentChar < length-1 ? glyphVector.getGlyphMetrics(currentChar+1).getAdvance() * 0.5f : 0;
+ t.setToTranslation( x, y );
+ t.rotate( angle );
+ t.translate( -px-advance, -py );
+ result.append( t.createTransformedShape( glyph ), false );
+ next += (advance+nextAdvance) * factor;
+ currentChar++;
+ if ( repeat )
+ currentChar %= length;
+ }
+ }
+ next -= distance;
+ first = false;
+ lastX = thisX;
+ lastY = thisY;
+ break;
+ }
+ it.next();
+ }
+
+ return result;
+ }
+
+ public float measurePathLength( Shape shape ) {
+ PathIterator it = new FlatteningPathIterator( shape.getPathIterator( null ), FLATNESS );
+ float points[] = new float[6];
+ float moveX = 0, moveY = 0;
+ float lastX = 0, lastY = 0;
+ float thisX = 0, thisY = 0;
+ int type = 0;
+ float total = 0;
+
+ while ( !it.isDone() ) {
+ type = it.currentSegment( points );
+ switch( type ){
+ case PathIterator.SEG_MOVETO:
+ moveX = lastX = points[0];
+ moveY = lastY = points[1];
+ break;
+
+ case PathIterator.SEG_CLOSE:
+ points[0] = moveX;
+ points[1] = moveY;
+ // Fall into....
+
+ case PathIterator.SEG_LINETO:
+ thisX = points[0];
+ thisY = points[1];
+ float dx = thisX-lastX;
+ float dy = thisY-lastY;
+ total += (float)Math.sqrt( dx*dx + dy*dy );
+ lastX = thisX;
+ lastY = thisY;
+ break;
+ }
+ it.next();
+ }
+
+ return total;
+ }
+
}
\ No newline at end of file