001package com.hfg.svg.path;
002
003import com.hfg.exception.ProgrammingException;
004import com.hfg.util.StringBuilderPlus;
005import com.hfg.util.collection.CollectionUtil;
006
007import java.awt.geom.Path2D;
008import java.awt.geom.Point2D;
009import java.lang.reflect.Constructor;
010import java.util.ArrayList;
011import java.util.HashMap;
012import java.util.List;
013import java.util.Map;
014
015//------------------------------------------------------------------------------
016/**
017 * Base representation of an SVG (Scalable Vector Graphics) path command.
018 *
019 * @author J. Alex Taylor, hairyfatguy.com
020 */
021//------------------------------------------------------------------------------
022// com.hfg XML/HTML Coding Library
023//
024// This library is free software; you can redistribute it and/or
025// modify it under the terms of the GNU Lesser General Public
026// License as published by the Free Software Foundation; either
027// version 2.1 of the License, or (at your option) any later version.
028//
029// This library is distributed in the hope that it will be useful,
030// but WITHOUT ANY WARRANTY; without even the implied warranty of
031// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
032// Lesser General Public License for more details.
033//
034// You should have received a copy of the GNU Lesser General Public
035// License along with this library; if not, write to the Free Software
036// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
037//
038// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
039// jataylor@hairyfatguy.com
040//------------------------------------------------------------------------------
041
042public abstract class SvgPathCmd
043{
044   private char            mCmdChar;
045   private List<Float>     mRawNumbers;
046   private boolean         mIsRelative;
047   private Point2D.Float   mStartingPoint;
048   private int             mNumSteps;
049
050   private static Map<Character, Constructor> sCmdMap = new HashMap<>(25);
051
052   static
053   {
054      try
055      {
056         sCmdMap.put('A', SvgPathEllipticalArcCmd.class.getConstructor());
057         sCmdMap.put('C', SvgPathCurveToCmd.class.getConstructor());
058         sCmdMap.put('H', SvgPathHorizLineToCmd.class.getConstructor());
059         sCmdMap.put('L', SvgPathLineToCmd.class.getConstructor());
060         sCmdMap.put('M', SvgPathMoveToCmd.class.getConstructor());
061         sCmdMap.put('Q', SvgPathQuadCurveToCmd.class.getConstructor());
062         sCmdMap.put('S', SvgPathSmoothCurveToCmd.class.getConstructor());
063         sCmdMap.put('T', SvgPathSmoothQuadCurveToCmd.class.getConstructor());
064         sCmdMap.put('V', SvgPathVertLineToCmd.class.getConstructor());
065         sCmdMap.put('Z', SvgPathClosePathCmd.class.getConstructor());
066      }
067      catch (NoSuchMethodException e)
068      {
069         throw new ProgrammingException(e);
070      }
071   }
072
073   //---------------------------------------------------------------------------
074   protected SvgPathCmd(char inCmdChar)
075   {
076      mCmdChar = inCmdChar;
077   }
078
079   //---------------------------------------------------------------------------
080   public static SvgPathCmd allocate(char inCmdChar)
081   {
082      SvgPathCmd pathCmd = null;
083
084      Constructor cmdClassConstructor = sCmdMap.get(Character.toUpperCase(inCmdChar));
085      if (cmdClassConstructor != null)
086      {
087         try
088         {
089            pathCmd = (SvgPathCmd) cmdClassConstructor.newInstance();
090         }
091         catch (Exception e)
092         {
093            throw new ProgrammingException(e);
094         }
095
096         if (Character.isLowerCase(inCmdChar))
097         {
098            pathCmd.mIsRelative = true;
099         }
100      }
101
102      return pathCmd;
103   }
104
105   //---------------------------------------------------------------------------
106   public char getCmdChar()
107   {
108      return (mIsRelative ? Character.toLowerCase(mCmdChar) : mCmdChar);
109   }
110
111   //---------------------------------------------------------------------------
112   public SvgPathCmd setIsRelative(boolean inValue)
113   {
114      mIsRelative = inValue;
115      return this;
116   }
117
118   //---------------------------------------------------------------------------
119   public boolean isRelative()
120   {
121      return mIsRelative;
122   }
123
124   //---------------------------------------------------------------------------
125   public SvgPathCmd setRawNumbers(List<Float> inValue)
126   {
127      mRawNumbers = (inValue != null ? new ArrayList<>(inValue) : null);
128      return this;
129   }
130
131   //---------------------------------------------------------------------------
132   public List<Float> getRawNumbers()
133   {
134      return mRawNumbers;
135   }
136
137   //---------------------------------------------------------------------------
138   public SvgPathCmd setStartingPoint(Point2D.Float inValue)
139   {
140      mStartingPoint = inValue;
141      return this;
142   }
143
144   //---------------------------------------------------------------------------
145   public Point2D.Float getStartingPoint()
146   {
147      return mStartingPoint;
148   }
149
150   //---------------------------------------------------------------------------
151   protected SvgPathCmd setNumSteps(int inValue)
152   {
153      mNumSteps = inValue;
154      return this;
155   }
156
157   //---------------------------------------------------------------------------
158   public int getNumSteps()
159   {
160      return mNumSteps;
161   }
162
163   //---------------------------------------------------------------------------
164   public abstract Point2D.Float draw(Path2D.Float inPolyline);
165
166   //---------------------------------------------------------------------------
167   @Override
168   public String toString()
169   {
170      StringBuilderPlus buffer = new StringBuilderPlus().setDelimiter(" ");
171      if (CollectionUtil.hasValues(mRawNumbers))
172      {
173         for (Float num : mRawNumbers)
174         {
175            buffer.delimitedAppend(num.floatValue() == num.intValue() ? num.intValue() + "" : num + "");
176         }
177      }
178
179      buffer.insert(0, getCmdChar());
180
181      return buffer.toString();
182   }
183}