001package com.hfg.svg.path;
002
003import java.awt.*;
004import java.awt.geom.GeneralPath;
005import java.awt.geom.Path2D;
006import java.awt.geom.Point2D;
007import java.util.ArrayList;
008import java.util.Arrays;
009import java.util.List;
010
011//------------------------------------------------------------------------------
012/**
013 * Object representation of an SVG (Scalable Vector Graphics) path moveTo ('M' or 'm') command.
014 *
015 * @author J. Alex Taylor, hairyfatguy.com
016 */
017//------------------------------------------------------------------------------
018// com.hfg XML/HTML Coding Library
019//
020// This library is free software; you can redistribute it and/or
021// modify it under the terms of the GNU Lesser General Public
022// License as published by the Free Software Foundation; either
023// version 2.1 of the License, or (at your option) any later version.
024//
025// This library is distributed in the hope that it will be useful,
026// but WITHOUT ANY WARRANTY; without even the implied warranty of
027// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
028// Lesser General Public License for more details.
029//
030// You should have received a copy of the GNU Lesser General Public
031// License along with this library; if not, write to the Free Software
032// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
033//
034// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
035// jataylor@hairyfatguy.com
036//------------------------------------------------------------------------------
037
038public class SvgPathMoveToCmd extends SvgPathCmd
039{
040
041   //---------------------------------------------------------------------------
042   public SvgPathMoveToCmd()
043   {
044      super('M');
045   }
046
047
048   //---------------------------------------------------------------------------
049   @Override
050   public SvgPathMoveToCmd setIsRelative(boolean inValue)
051   {
052      return (SvgPathMoveToCmd) super.setIsRelative(inValue);
053   }
054
055   //---------------------------------------------------------------------------
056   @Override
057   public SvgPathMoveToCmd setRawNumbers(List<Float> inValue)
058   {
059      if (inValue.size()%2 != 0)
060      {
061         throw new SvgPathDataException("An even number of numbers must be given to a moveTo path command!");
062      }
063
064      setNumSteps(inValue.size()/2);
065
066      super.setRawNumbers(inValue);
067      return this;
068   }
069
070
071   //---------------------------------------------------------------------------
072   public SvgPathMoveToCmd setRawNumbers(Float... inValues)
073   {
074      if (inValues.length%2 != 0)
075      {
076         throw new SvgPathDataException("An even number of numbers must be given to a moveTo path command!");
077      }
078
079      setNumSteps(inValues.length/2);
080
081      super.setRawNumbers(Arrays.asList(inValues));
082      return this;
083   }
084
085   //--------------------------------------------------------------------------
086   public SvgPathMoveToCmd addPoint(Point inValue)
087   {
088      if (inValue != null)
089      {
090         List<Float> rawNumbers = getRawNumbers();
091         if (null == rawNumbers)
092         {
093            rawNumbers = new ArrayList<>(10);
094         }
095
096         rawNumbers.add((float) inValue.getX());
097         rawNumbers.add((float) inValue.getY());
098
099         setRawNumbers(rawNumbers);
100         setNumSteps(getNumSteps() + 1);
101      }
102
103      return this;
104   }
105
106   //--------------------------------------------------------------------------
107   public SvgPathMoveToCmd addPoint(Point2D inValue)
108   {
109      if (inValue != null)
110      {
111         List<Float> rawNumbers = getRawNumbers();
112         if (null == rawNumbers)
113         {
114            rawNumbers = new ArrayList<>(10);
115         }
116
117         rawNumbers.add((float) inValue.getX());
118         rawNumbers.add((float) inValue.getY());
119
120         setRawNumbers(rawNumbers);
121         setNumSteps(getNumSteps() + 1);
122      }
123
124      return this;
125   }
126
127   //--------------------------------------------------------------------------
128   // From http://www.w3.org/TR/SVG/paths.html
129   //
130   // Start a new sub-path at the given (x,y) coordinate. M (uppercase) indicates that absolute coordinates will follow;
131   // m (lowercase) indicates that relative coordinates will follow. If a moveto is followed by multiple pairs of coordinates,
132   // the subsequent pairs are treated as implicit lineto commands. Hence, implicit lineto commands will be relative if
133   // the moveto is relative, and absolute if the moveto is absolute. If a relative moveto (m) appears as the first
134   // element of the path, then it is treated as a pair of absolute coordinates. In this case, subsequent pairs of
135   // coordinates are treated as relative even though the initial moveto is interpreted as an absolute moveto.
136   public Point2D.Float draw(Path2D.Float inPolyline)
137   {
138      List<Float> rawNumbers = getRawNumbers();
139      int numIndex = 0;
140
141      Point2D.Float currentPoint = getStartingPoint();
142
143      while (numIndex < rawNumbers.size() - 1)
144      {
145         Point2D.Float point = new Point2D.Float(rawNumbers.get(numIndex++), rawNumbers.get(numIndex++));
146
147         if (2 == numIndex)
148         {
149            // First point.
150            if (isRelative()
151                && getStartingPoint() != null)
152            {
153               point.setLocation(point.getX() + currentPoint.getX(), point.getY() + currentPoint.getY());
154            }
155
156            inPolyline.moveTo(point.getX(), point.getY());
157         }
158         else
159         {
160            // Subsequent x,y pairs are treated as implicit lineTo commands.
161
162            if (isRelative())
163            {
164               point.setLocation(point.getX() + currentPoint.getX(), point.getY() + currentPoint.getY());
165            }
166
167            inPolyline.lineTo(point.getX(), point.getY());
168         }
169
170         currentPoint = point;
171      }
172
173      // Return the last point.
174      return currentPoint;
175   }
176}