/** @file SymbolLine.java * * @author marco corvi * @date dec 2012 * * @brief TopoDroid drawing: line symbol * -------------------------------------------------------- * Copyright This sowftare is distributed under GPL-3.0 or later * See the file COPYING. * -------------------------------------------------------- */ package com.topodroid.DistoX; import java.io.File; import java.io.FileReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import android.graphics.Path; import android.graphics.Paint; import android.graphics.PathEffect; import android.graphics.ComposePathEffect; import android.graphics.DashPathEffect; import android.graphics.PathDashPathEffect; import android.graphics.PathDashPathEffect.Style; import android.graphics.Matrix; // import android.util.Log; public class SymbolLine extends Symbol { String mName; // local name Paint mPaint; // forward paint Paint mRevPaint; // reverse paint boolean mHasEffect; Path mPath; boolean mStyleStraight; int mStyleX; // X times (one out of how many point to use) String mGroup; // group of this line (null if no group) @Override public String getName() { return mName; } @Override public String getThName( ) { return mThName; } @Override public Paint getPaint() { return mPaint; } @Override public Path getPath() { return mPath; } // @Override public boolean isOrientable() { return false; } // @Override public boolean isEnabled() { return mEnabled; } // @Override public void setEnabled( boolean enabled ) { mEnabled = enabled; } // @Override public void toggleEnabled() { mEnabled = ! mEnabled; } // @Override public boolean setAngle( float angle ) { } // @Override public int getAngle() { return 0; } public String getGroup() { return mGroup; } // width = 1; // no effect SymbolLine( String name, String th_name, String fname, String group, int color ) { super( th_name, fname ); init( name, group, color, 1 ); makePath(); } // no effect SymbolLine( String name, String th_name, String fname, String group, int color, float width ) { super( th_name, fname ); init( name, group, color, width ); makePath(); } SymbolLine( String name, String th_name, String fname, String group, int color, float width, PathEffect effect_dir, PathEffect effect_rev ) { super( th_name, fname ); init( name, group, color, width ); mPaint.setPathEffect( effect_dir ); mRevPaint.setPathEffect( effect_rev ); makePath(); } private void init( String name, String group, int color, float width ) { mName = name; mGroup = group; mPaint = new Paint(); mPaint.setDither(true); mPaint.setColor( color ); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth( width * TDSetting.mLineThickness ); mRevPaint = new Paint (mPaint ); mHasEffect = false; mStyleStraight = false; mStyleX = 1; } SymbolLine( String filepath, String fname, String locale, String iso ) { super( null, fname ); mStyleStraight = false; mStyleX = 1; readFile( filepath, locale, iso ); makePath(); } void makePath() { mPath = new Path(); mPath.moveTo(-50, 0 ); mPath.lineTo( 50, 0 ); } private int kval; private float nextFloat( String[] vals, int s, float unit ) throws NumberFormatException { ++kval; while ( kval < s && vals[kval].length() == 0 ) ++kval; if ( kval < s ) { return Float.parseFloat( vals[kval] ) * unit; } throw new NumberFormatException(); } private int nextInt( String[] vals, int s ) throws NumberFormatException { ++kval; while ( kval < s && vals[kval].length() == 0 ) ++kval; if ( kval < s ) { return Integer.parseInt( vals[kval] ); } throw new NumberFormatException(); } /** create a symbol reading it from a file * The file syntax is * symbol line * name NAME * th_name THERION_NAME * group GROUP_NAME * color 0xHHHHHH_COLOR 0xAA_ALPHA * width WIDTH * dash x1 y1 x2 y2 ... * style straight | xN * effect * command: moveTo lineTo cubicTo addCircle * endeffect * endsymbol */ void readFile( String filename, String locale, String iso ) { // Log.v( TopoDroidApp.TAG, "load line file " + filename ); float unit = TDSetting.mUnit * TDSetting.mLineThickness; String name = null; String th_name = null; String group = null; mHasEffect = false; int color = 0; int alpha = 0xcc; float width = 1; Path path_dir = null; Path path_rev = null; DashPathEffect dash = null; PathDashPathEffect effect = null; PathDashPathEffect rev_effect = null; float xmin=0, xmax=0; try { // FileReader fr = new FileReader( filename ); FileInputStream fr = new FileInputStream( filename ); BufferedReader br = new BufferedReader( new InputStreamReader( fr, iso ) ); String line; while ( (line = br.readLine()) != null ) { line.trim(); String[] vals = line.split(" "); int s = vals.length; for (int k=0; k<s; ++k ) { if ( vals[k].startsWith( "#" ) ) break; if ( vals[k].length() == 0 ) continue; if ( vals[k].equals("symbol") ) { name = null; th_name = null; group = null; color = 0x00000000; } else if ( vals[k].equals("name") || vals[k].equals(locale) ) { ++k; while ( k < s && vals[k].length() == 0 ) ++k; if ( k < s ) { name = (new String( vals[k].getBytes(iso) )).replace("_", " "); } } else if ( vals[k].equals("th_name") ) { ++k; while ( k < s && vals[k].length() == 0 ) ++k; if ( k < s ) { th_name = vals[k]; } } else if ( vals[k].equals("group") ) { ++k; while ( k < s && vals[k].length() == 0 ) ++k; if ( k < s ) { group = vals[k]; } } else if ( vals[k].equals("csurvey") ) { // syntax: // csurvey <layer> <type> <category> <pen> try { kval = k; mCsxLayer = nextInt( vals, s ); mCsxType = nextInt( vals, s ); mCsxCategory = nextInt( vals, s ); mCsxPen = nextInt( vals, s ); // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // mCsxLayer = Integer.parseInt( vals[k] ); // } // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // mCsxType = Integer.parseInt( vals[k] ); // } // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // mCsxCategory = Integer.parseInt( vals[k] ); // } // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // mCsxPen = Integer.parseInt( vals[k] ); // } } catch ( NumberFormatException e ) { TDLog.Error( filename + " parse csurvey error: " + line ); } } else if ( vals[k].equals("color") ) { ++k; while ( k < s && vals[k].length() == 0 ) ++k; if ( k < s ) { color = Integer.decode( vals[k] ); } ++k; while ( k < s && vals[k].length() == 0 ) ++k; if ( k < s ) { alpha = Integer.decode( vals[k] ); } } else if ( vals[k].equals("width") ) { try { kval = k; width = nextInt( vals, s ) * TDSetting.mLineThickness; // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // width = Integer.parseInt( vals[k] ) * TDSetting.mLineThickness; // } } catch ( NumberFormatException e ) { TDLog.Error( filename + " parse width error: " + line ); } } else if ( vals[k].equals("dash") ) { ++k; while ( k < s && vals[k].length() == 0 ) ++k; if ( k < s ) { int k1 = k; int ndash = 0; while ( k1 < s ) { while ( k1 < s && vals[k1].length() == 0 ) ++k1; ++ ndash; ++k1; } ndash = ndash - (ndash % 2); if ( ndash > 0 ) { try { float[] x = new float[ndash]; x[0] = Float.parseFloat( vals[k] ) * unit; kval = k; for (int n=1; n<ndash; ++n ) { x[n] = nextFloat( vals, s, unit ); // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // x[n] = Float.parseFloat( vals[k] ) * unit; } dash = new DashPathEffect( x, 0 ); } catch ( NumberFormatException e ) { TDLog.Error( filename + " parse dash error: " + line ); } } } } else if ( vals[k].equals("style") ) { // STYLE for ( ++ k; k < s; ++k ) { if ( vals[k].length() == 0 ) continue; if ( vals[k].equals("straight") ) { mStyleStraight = true; } else if ( vals[k].startsWith("x") ) { try { mStyleX = Integer.parseInt( vals[k].substring(1) ); if ( mStyleX <= 0 ) mStyleX = ItemDrawer.POINT_MAX; // FIXME INT_MAX } catch ( NumberFormatException e ) { } } } } else if ( vals[k].equals("effect") ) { path_dir = new Path(); path_rev = new Path(); // path_dir.moveTo(0,0); // path_rev.moveTo(0,0); boolean moved_to = false; while ( (line = br.readLine() ) != null ) { line.trim(); vals = line.split(" "); s = vals.length; k = 0; while ( k < s && vals[k].length() == 0 ) ++k; if ( k < s ) { if ( vals[k].equals("moveTo") ) { try { // if ( ! moved_to ) { kval = k; float x = nextFloat( vals, s, unit ); float y = nextFloat( vals, s, unit ); path_dir.moveTo( x, y ); path_rev.moveTo( x, -y ); if ( ! moved_to ) { xmin = xmax = x; moved_to = true; } // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // float x = Float.parseFloat( vals[k] ) * unit; // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // float y = Float.parseFloat( vals[k] ) * unit; // path_dir.moveTo( x, y ); // path_rev.moveTo( x, -y ); // xmin = xmax = x; // moved_to = true; // } // } // } } catch ( NumberFormatException e ) { TDLog.Error( filename + " parse moveTo point error: " + line ); } } else if ( vals[k].equals("lineTo") ) { try { kval = k; float x = nextFloat( vals, s, unit ); float y = nextFloat( vals, s, unit ); path_dir.lineTo( x, y ); path_rev.lineTo( x, -y ); if ( x < xmin ) xmin = x; else if ( x > xmax ) xmax = x; // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // float x = Float.parseFloat( vals[k] ) * unit; // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // float y = Float.parseFloat( vals[k] ) * unit; // path_dir.lineTo( x, y ); // path_rev.lineTo( x, -y ); // if ( x < xmin ) xmin = x; else if ( x > xmax ) xmax = x; // } // } } catch ( NumberFormatException e ) { TDLog.Error( filename + " parse lineTo point error: " + line ); } } else if ( vals[k].equals("cubicTo") ) { try { kval = k; float x1 = nextFloat( vals, s, unit ); float y1 = nextFloat( vals, s, unit ); float x2 = nextFloat( vals, s, unit ); float y2 = nextFloat( vals, s, unit ); float x3 = nextFloat( vals, s, unit ); float y3 = nextFloat( vals, s, unit ); path_dir.cubicTo( x1, y1, x2, y2, x3, y3 ); path_rev.cubicTo( x1, -y1, x2, -y2, x3, -y3 ); if ( x1 < xmin ) xmin = x1; else if ( x1 > xmax ) xmax = x1; if ( x2 < xmin ) xmin = x2; else if ( x2 > xmax ) xmax = x2; if ( x3 < xmin ) xmin = x3; else if ( x3 > xmax ) xmax = x3; // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // float x1 = Float.parseFloat( vals[k] ) * unit; // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // float y1 = Float.parseFloat( vals[k] ) * unit; // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // float x2 = Float.parseFloat( vals[k] ) * unit; // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // float y2 = Float.parseFloat( vals[k] ) * unit; // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // float x3 = Float.parseFloat( vals[k] ) * unit; // ++k; while ( k < s && vals[k].length() == 0 ) ++k; // if ( k < s ) { // float y3 = Float.parseFloat( vals[k] ) * unit; // path_dir.cubicTo( x1, y1, x2, y2, x3, y3 ); // path_rev.cubicTo( x1, -y1, x2, -y2, x3, -y3 ); // if ( x1 < xmin ) xmin = x1; else if ( x1 > xmax ) xmax = x1; // if ( x2 < xmin ) xmin = x2; else if ( x2 > xmax ) xmax = x2; // if ( x3 < xmin ) xmin = x3; else if ( x3 > xmax ) xmax = x3; // } // } // } // } // } // } } catch ( NumberFormatException e ) { TDLog.Error( filename + " parse lineTo point error: " + line ); } } else if ( vals[k].equals("addCircle") ) { try { kval = k; float x = nextFloat( vals, s, unit ); float y = nextFloat( vals, s, unit ); float r = nextFloat( vals, s, unit ); path_dir.addCircle( x, y, r, Path.Direction.CCW ); path_rev.addCircle( x, -y, r, Path.Direction.CCW ); if ( x-r < xmin ) xmin = x-r; if ( x+r > xmax ) xmax = x+r; } catch ( NumberFormatException e ) { TDLog.Error( filename + " parse lineTo point error: " + line ); } } else if ( vals[k].equals("endeffect") ) { // path_dir.close(); // path_rev.close(); effect = new PathDashPathEffect( path_dir, (xmax-xmin), 0, PathDashPathEffect.Style.MORPH ); rev_effect = new PathDashPathEffect( path_rev, (xmax-xmin), 0, PathDashPathEffect.Style.MORPH ); break; } } } } else if ( vals[k].equals("endsymbol") ) { if ( name == null ) { } else if ( th_name == null ) { } else { mName = name; mThName = th_name; mGroup = group; mPaint = new Paint(); mPaint.setDither(true); mPaint.setColor( color ); mPaint.setAlpha( alpha ); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND); mPaint.setStrokeCap(Paint.Cap.ROUND); mRevPaint = new Paint( mPaint ); if ( effect != null ) { mHasEffect = true; // mPaint.setStrokeWidth( 4 ); // mRevPaint.setStrokeWidth( 4 ); if ( dash != null ) { mPaint.setPathEffect( new ComposePathEffect( effect, dash ) ); mRevPaint.setPathEffect( new ComposePathEffect( rev_effect, dash ) ); } else { mPaint.setPathEffect( effect ); mRevPaint.setPathEffect( rev_effect ); } } else if ( dash != null ) { mPaint.setPathEffect( dash ); mRevPaint.setPathEffect( dash ); // } else { // mPaint.setStrokeWidth( width * TDSetting.mLineThickness ); // mRevPaint.setStrokeWidth( width * TDSetting.mLineThickness ); } mPaint.setStrokeWidth( width * TDSetting.mLineThickness ); mRevPaint.setStrokeWidth( width * TDSetting.mLineThickness ); } } } } } catch ( FileNotFoundException e ) { // FIXME } catch( IOException e ) { // FIXME } } }