001/*
002 * To change this template, choose Tools | Templates
003 * and open the template in the editor.
004 */
005package armyc2.c2sd.graphics2d;
006import java.util.ArrayList;
007import armyc2.c2sd.JavaLineArray.POINT2;
008import armyc2.c2sd.JavaLineArray.ref;
009import armyc2.c2sd.JavaLineArray.arraysupport;
010import armyc2.c2sd.JavaLineArray.lineutility;
011import armyc2.c2sd.JavaLineArray.TacticalLines;
012
013/**
014 *
015 * @author Michael Deutch
016 */
017public class BasicStroke implements Stroke, Shape{
018
019    /**
020     * Joins path segments by extending their outside edges until they meet.
021     */
022    public final static int JOIN_MITER = 0;
023    /**
024     * Joins path segments by rounding off the corner at a radius of half the
025     * line width.
026     */
027    public final static int JOIN_ROUND = 1;
028    /**
029     * Joins path segments by connecting the outer corners of their wide
030     * outlines with a straight segment.
031     */
032    public final static int JOIN_BEVEL = 2;
033    /**
034     * Ends unclosed subpaths and dash segments with no added decoration.
035     */
036    public final static int CAP_BUTT = 0;
037    /**
038     * Ends unclosed subpaths and dash segments with a round decoration that has
039     * a radius equal to half of the width of the pen.
040     */
041    public final static int CAP_ROUND = 1;
042    /**
043     * Ends unclosed subpaths and dash segments with a square projection that
044     * extends beyond the end of the segment to a distance equal to half of the
045     * line width.
046     */
047    public final static int CAP_SQUARE = 2;
048    float width;
049    int join;
050    int cap;
051    float miterlimit;
052    float dash[];
053    float dash_phase;
054
055    public BasicStroke(float width, int cap, int join, float miterlimit,
056            float dash[], float dash_phase) {
057        if (width < 0.0f) {
058            throw new IllegalArgumentException("negative width");
059        }
060        if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {
061            throw new IllegalArgumentException("illegal end cap value");
062        }
063        if (join == JOIN_MITER) {
064            if (miterlimit < 1.0f) {
065                throw new IllegalArgumentException("miter limit < 1");
066            }
067        } else if (join != JOIN_ROUND && join != JOIN_BEVEL) {
068            throw new IllegalArgumentException("illegal line join value");
069        }
070        if (dash != null) {
071            if (dash_phase < 0.0f) {
072                throw new IllegalArgumentException("negative dash phase");
073            }
074            boolean allzero = true;
075            int n=dash.length;
076            //for (int i = 0; i < dash.length; i++) 
077            for (int i = 0; i < n; i++) 
078            {
079                float d = dash[i];
080                if (d > 0.0) {
081                    allzero = false;
082                } else if (d < 0.0) {
083                    throw new IllegalArgumentException("negative dash length");
084                }
085            }
086            if (allzero) {
087                throw new IllegalArgumentException("dash lengths all zero");
088            }
089        }
090        this.width = width;
091        this.cap = cap;
092        this.join = join;
093        this.miterlimit = miterlimit;
094        if (dash != null) {
095            this.dash = (float[]) dash.clone();
096        }
097        this.dash_phase = dash_phase;
098    }
099
100    public BasicStroke(float width, int cap, int join, float miterlimit) {
101        this(width, cap, join, miterlimit, null, 0.0f);
102    }
103
104    public BasicStroke(float width, int cap, int join) {
105        this(width, cap, join, 10.0f, null, 0.0f);
106    }
107
108    /**
109     * Constructs a solid <code>BasicStroke</code> with the specified line width
110     * and with default values for the cap and join styles.
111     *
112     * @param width
113     * the width of the <code>BasicStroke</code>
114     * @throws IllegalArgumentException
115     * if <code>width</code> is negative
116     */
117    public BasicStroke(float width) {
118        this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
119    }
120
121    /**
122     * Constructs a new <code>BasicStroke</code> with defaults for all
123     * attributes. The default attributes are a solid line of width 1.0,
124     * CAP_SQUARE, JOIN_MITER, a miter limit of 10.0.
125     */
126    public BasicStroke() {
127        this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
128    }
129
130    /**
131     * Returns a <code>Shape</code> whose interior defines the stroked outline
132     * of a specified <code>Shape</code>.
133     *
134     * @param s
135     * the <code>Shape</code> boundary be stroked
136     * @return the <code>Shape</code> of the stroked outline.
137     */
138    public Shape createStrokedShape(Shape s) {
139        /*
140         * sun.java2d.pipe.RenderingEngine re =
141         * sun.java2d.pipe.RenderingEngine.getInstance(); return
142         * re.createStrokedShape(s, width, cap, join, miterlimit, dash,
143         * dash_phase);
144         */
145        return null;
146    }
147    public Shape createStrokedShape(Polygon poly) {
148        /*
149         * sun.java2d.pipe.RenderingEngine re =
150         * sun.java2d.pipe.RenderingEngine.getInstance(); return
151         * re.createStrokedShape(s, width, cap, join, miterlimit, dash,
152         * dash_phase);
153         */
154        ArrayList<POINT2>pts=poly.getPathIterator(null).getPoints();
155        int j=0;
156        GeneralPath gp=new GeneralPath();
157        POINT2 pt=null;
158        POINT2[]ptsx=new POINT2[pts.size()];
159        int n=pts.size();
160        //for(j=0;j<pts.size();j++)
161        for(j=0;j<n;j++)
162        {
163            pt=pts.get(j);
164            ptsx[j]=pt;
165        }
166        
167        pts=GetInteriorPoints(ptsx,pts.size(),TacticalLines.DEPTH_AREA,this.width);
168        
169        
170        //for(j=0;j<pts.size();j++)
171        for(j=0;j<n;j++)
172        {
173            pt=pts.get(j);
174            if(j==0)
175                gp.moveTo(pt.x, pt.y);
176            else
177                gp.lineTo(pt.x, pt.y);
178        }
179        return gp;
180    }
181
182    public float getLineWidth() {
183        return width;
184    }
185
186    /**
187     * Returns the end cap style.
188     *
189     * @return the end cap style of this <code>BasicStroke</code> as one of the
190     * static <code>int</code> values that define possible end cap
191     * styles.
192     */
193    public int getEndCap() {
194        return cap;
195    }
196
197    public int getLineJoin() {
198        return join;
199    }
200
201    /**
202     * Returns the limit of miter joins.
203     *
204     * @return the limit of miter joins of the <code>BasicStroke</code>.
205     */
206    public float getMiterLimit() {
207        return miterlimit;
208    }
209
210    /**
211     * Returns the array representing the lengths of the dash segments.
212     * Alternate entries in the array represent the user space lengths of the
213     * opaque and transparent segments of the dashes. As the pen moves along the
214     * outline of the <code>Shape</code> to be stroked, the user space distance
215     * that the pen travels is accumulated. The distance value is used to index
216     * into the dash array. The pen is opaque when its current cumulative
217     * distance maps to an even element of the dash array and transparent
218     * otherwise.
219     *
220     * @return the dash array.
221     */
222    public float[] getDashArray() {
223        if (dash == null) {
224            return null;
225        }
226        return (float[]) dash.clone();
227    }
228
229    public float getDashPhase() {
230        return dash_phase;
231    }
232
233    /**
234     * Returns the hashcode for this stroke.
235     *
236     * @return a hash code for this stroke.
237     */
238    public int hashCode() {
239        int hash = Float.floatToIntBits(width);
240        hash = hash * 31 + join;
241        hash = hash * 31 + cap;
242        hash = hash * 31 + Float.floatToIntBits(miterlimit);
243        if (dash != null) {
244            hash = hash * 31 + Float.floatToIntBits(dash_phase);
245            int n=dash.length;
246            //for (int i = 0; i < dash.length; i++) 
247            for (int i = 0; i < n; i++) 
248            {
249                hash = hash * 31 + Float.floatToIntBits(dash[i]);
250            }
251        }
252        return hash;
253    }
254    //the Shape interface methods
255    public PathIterator getPathIterator(AffineTransform at)
256    {
257        return null;
258    }
259    public boolean contains (int x, int y)
260    {
261        return false;
262    }
263    public boolean contains (int x, int y, int width, int height)
264    {
265        return false;
266    }
267    public boolean contains (Point2D pt)
268    {
269        return false;
270    }
271    public Rectangle2D getBounds2D()
272    {
273        return  null;
274    }
275    public Rectangle getBounds()
276    {
277        return  null;
278    }
279    public boolean intersects(double x, double y, double w, double h)
280    {
281        return false;
282    }
283    public boolean intersects(Rectangle2D rect)
284    {
285        return false;
286    }
287    
288    public static ArrayList<POINT2> GetInteriorPoints(POINT2[] pLinePoints, 
289            int vblCounter, 
290            int lineType, 
291            double dist) {
292        //var j:int=0;
293        int j = 0;
294        //var index:int=-1;
295        int index = -1;
296        //var pt0:POINT2,pt1:POINT2,pt2:POINT2;
297        POINT2 pt0 = null, pt1 = null, pt2 = null;
298        ///var m01:refobj=new refobj(),m12:refobj=new refobj(); //slopes for lines pt0-pt1 and pt1-pt2
299        ref<double[]> m01 = new ref(), m12 = new ref(), m1 = new ref(), m2 = new ref();
300        //var direction:int=-1;
301        int direction = -1;
302        //var array:Array=new Array();
303        //ArrayList<POINT2>array=new ArrayList();
304        //var intersectPt:POINT2=null;
305        POINT2 intersectPt = null;
306        //var m1:refobj=new refobj(),m2:refobj=new refobj();
307        //var intersectPoints:Array=new Array();
308        ArrayList<POINT2> intersectPoints = new ArrayList();
309        //var b01:Number,b12:Number;    //the y intercepts for the lines corresponding to m1,m2 
310        double b01 = 0, b12 = 0;
311        //var dist:Number=10;
312        //double dist = 10;
313        //the first set of interior points
314        //this assumes the area is closed
315        for (j = 0; j < vblCounter; j++) {
316
317            if (j == 0 || j == vblCounter - 1) {
318                //pt0=new POINT2(pLinePoints[vblCounter-2]);
319                //pt1=new POINT2(pLinePoints[0]);
320                //pt2=new POINT2(pLinePoints[1]);
321                pt0 = pLinePoints[vblCounter - 2];
322                pt1 = pLinePoints[0];
323                pt2 = pLinePoints[1];
324            } else {
325                //pt0=new POINT2(pLinePoints[j-1]);
326                //pt1=new POINT2(pLinePoints[j]);
327                //pt2=new POINT2(pLinePoints[j+1]);                                     
328                pt0 = pLinePoints[j - 1];
329                pt1 = pLinePoints[j];
330                pt2 = pLinePoints[j+1];
331            }
332
333            //the interiior points
334            //var pt00:POINT2,pt01:POINT2;
335            //var pt10:POINT2,pt11:POINT2;
336            POINT2 pt00 = null, pt01 = null;
337            POINT2 pt10 = null, pt11 = null;
338
339            index = j - 1;
340            if (index < 0) {
341                index = vblCounter - 1;
342            }
343
344            direction = arraysupport.GetInsideOutsideDouble2(pt0, pt1, pLinePoints, vblCounter, index, lineType);
345            //reverse the directions     since these are interior points
346            //pt00-pt01 will be the interior line inside line pt0-pt1
347            //pt00 is inside pt0, pt01 is inside pt1
348            switch (direction) {
349                case 0:
350                    //direction=1;
351                    pt00 = lineutility.ExtendDirectedLine(pt0, pt1, pt0, 1, dist);
352                    pt01 = lineutility.ExtendDirectedLine(pt0, pt1, pt1, 1, dist);
353                    break;
354                case 1:
355                    //direction=0;
356                    pt00 = lineutility.ExtendDirectedLine(pt0, pt1, pt0, 0, dist);
357                    pt01 = lineutility.ExtendDirectedLine(pt0, pt1, pt1, 0, dist);
358                    break;
359                case 2:
360                    //direction=3;
361                    pt00 = lineutility.ExtendDirectedLine(pt0, pt1, pt0, 3, dist);
362                    pt01 = lineutility.ExtendDirectedLine(pt0, pt1, pt1, 3, dist);
363                    break;
364                case 3:
365                    //direction=2;
366                    pt00 = lineutility.ExtendDirectedLine(pt0, pt1, pt0, 2, dist);
367                    pt01 = lineutility.ExtendDirectedLine(pt0, pt1, pt1, 2, dist);
368                    break;
369            }
370
371            //pt10-pt11 will be the interior line inside line pt1-pt2
372            //pt10 is inside pt1, pt11 is inside pt2
373            index = j;
374            if (j == vblCounter - 1) {
375                index = 0;
376            }
377            direction = arraysupport.GetInsideOutsideDouble2(pt1, pt2, pLinePoints, vblCounter, index, lineType);
378            //reverse the directions     since these are interior points
379            switch (direction) {
380                case 0:
381                    //direction=1;
382                    pt10 = lineutility.ExtendDirectedLine(pt1, pt2, pt1, 1, dist);
383                    pt11 = lineutility.ExtendDirectedLine(pt1, pt2, pt2, 1, dist);
384                    break;
385                case 1:
386                    //direction=0;
387                    pt10 = lineutility.ExtendDirectedLine(pt1, pt2, pt1, 0, dist);
388                    pt11 = lineutility.ExtendDirectedLine(pt1, pt2, pt2, 0, dist);
389                    break;
390                case 2:
391                    //direction=3;
392                    pt10 = lineutility.ExtendDirectedLine(pt1, pt2, pt1, 3, dist);
393                    pt11 = lineutility.ExtendDirectedLine(pt1, pt2, pt2, 3, dist);
394                    break;
395                case 3:
396                    //direction=2;
397                    pt10 = lineutility.ExtendDirectedLine(pt1, pt2, pt1, 2, dist);
398                    pt11 = lineutility.ExtendDirectedLine(pt1, pt2, pt2, 2, dist);
399                    break;
400            }   //end switch
401            //intersectPt=new POINT2(null);
402            //get the intersection of pt01-p00 and pt10-pt11
403            //so it it is the interior intersection of pt0-pt1 and pt1-pt2
404
405            //first handle the case of vertical lines.
406            if (pt0.x == pt1.x && pt1.x == pt2.x) {
407                intersectPt = new POINT2(pt01);
408                intersectPoints.add(intersectPt);
409                continue;
410            }
411            //it's the same situation if the slopes are identical,
412            //simply use pt01 or pt10 since they already uniquely define the intesection
413            lineutility.CalcTrueSlopeDouble2(pt00, pt01, m01);
414            lineutility.CalcTrueSlopeDouble2(pt10, pt11, m12);
415            if (m01.value[0] == m12.value[0]) {
416                intersectPt = new POINT2(pt01);
417                intersectPoints.add(intersectPt);
418                continue;
419            }
420            //now we are assuming a non-trivial intersection
421            //calculate the y-intercepts using y=mx+b (use b=y-mx)
422            b01 = pt01.y - m01.value[0] * pt01.x;
423            b12 = pt11.y - m12.value[0] * pt11.x;
424
425            intersectPt = lineutility.CalcTrueIntersectDouble2(m01.value[0], b01, m12.value[0], b12, 1, 1, 0, 0);
426            intersectPoints.add(intersectPt);
427        }//end for
428        return intersectPoints;
429    }
430}