001/*
002 * To change this template, choose Tools | Templates
003 * and open the template in the editor.
004 */
005package armyc2.c5isr.JavaTacticalRenderer;
006
007import armyc2.c5isr.JavaLineArray.TacticalLines;
008import armyc2.c5isr.JavaLineArray.arraysupport;
009import armyc2.c5isr.JavaLineArray.lineutility;
010import armyc2.c5isr.JavaLineArray.POINT2;
011import armyc2.c5isr.JavaLineArray.ref;
012import java.util.ArrayList;
013import java.util.HashMap;
014
015import armyc2.c5isr.JavaLineArray.Shape2;
016import java.io.*;
017
018import armyc2.c5isr.RenderMultipoints.clsRenderer;
019import armyc2.c5isr.renderer.utilities.DrawRules;
020import armyc2.c5isr.renderer.utilities.IPointConversion;
021import armyc2.c5isr.renderer.utilities.MSInfo;
022import armyc2.c5isr.renderer.utilities.MSLookup;
023import armyc2.c5isr.renderer.utilities.RendererSettings;
024import armyc2.c5isr.renderer.utilities.RendererUtilities;
025import armyc2.c5isr.renderer.utilities.ErrorLogger;
026import armyc2.c5isr.renderer.utilities.RendererException;
027import armyc2.c5isr.renderer.utilities.Color;
028import armyc2.c5isr.graphics2d.BasicStroke;
029import armyc2.c5isr.graphics2d.BufferedImage;
030import armyc2.c5isr.graphics2d.Graphics2D;
031import armyc2.c5isr.graphics2d.Line2D;
032import armyc2.c5isr.graphics2d.Point2D;
033import armyc2.c5isr.graphics2d.Polygon;
034import armyc2.c5isr.graphics2d.Rectangle2D;
035import armyc2.c5isr.graphics2d.TexturePaint;
036import armyc2.c5isr.renderer.utilities.SymbolID;
037
038/**
039 * A general utility class for the tactical renderer
040*
041 */
042public final class clsUtility {
043    private static final String _className = "clsUtility";
044    protected static Point2D POINT2ToPoint2D(POINT2 pt2) {
045        if (pt2 == null) {
046            return null;
047        }
048
049        double x = pt2.x;
050        double y = pt2.y;
051        Point2D pt = new Point2D.Double(x, y);
052        return pt;
053    }
054    /**
055     * returns true if the line segments are all outside the bounds
056     * @param tg the tactical graphic
057     * @param clipBounds the pixels based clip bounds
058     * @return 
059     */
060    public static boolean linesOutsideClipBounds(TGLight tg,
061            Rectangle2D clipBounds)
062    {
063        try
064        {
065            boolean isAutoshape=isAutoshape(tg);
066            if(isAutoshape)
067                return false;
068
069            double xmin=clipBounds.getMinX();
070            double xmax=clipBounds.getMaxX();
071            double ymin=clipBounds.getMinY();
072            double ymax=clipBounds.getMaxY();
073            int j=0;
074            POINT2 pt0=null,pt1=null;
075            Line2D boundsEdge=null,ptsLine=null;
076            int n=tg.Pixels.size();
077            //for(j=0;j<tg.Pixels.size()-1;j++)
078            for(j=0;j<n-1;j++)
079            {
080                pt0=tg.Pixels.get(j);
081                pt1=tg.Pixels.get(j+1);
082                
083                //if either point is inside the bounds return false
084                if(clipBounds.contains(pt0.x, pt0.y))
085                    return false;
086                if(clipBounds.contains(pt1.x, pt1.y))
087                    return false;
088                
089                ptsLine=new Line2D.Double(pt0.x,pt0.y,pt1.x,pt1.y);
090                
091                //if the pt0-pt1 line intersects any clip bounds edge then return false
092                boundsEdge=new Line2D.Double(xmin,ymin,xmax,ymin);
093                if(ptsLine.intersectsLine(boundsEdge))
094                    return false;                
095                
096                boundsEdge=new Line2D.Double(xmax,ymin,xmax,ymax);
097                if(ptsLine.intersectsLine(boundsEdge))
098                    return false;                
099                
100                boundsEdge=new Line2D.Double(xmax,ymax,xmin,ymax);
101                if(ptsLine.intersectsLine(boundsEdge))
102                    return false;                
103                
104                boundsEdge=new Line2D.Double(xmin,ymax,xmin,ymin);
105                if(ptsLine.intersectsLine(boundsEdge))
106                    return false;                
107            }
108        }
109        catch (Exception exc) 
110        {
111            ErrorLogger.LogException(_className ,"linesOutsideClipBounds",
112                    new RendererException("Failed inside linesOutsideClipBounds", exc));
113        }    
114        return true;
115    }
116    /**
117     * Returns the minimum client points needed for the symbol
118     * @param lineType line type
119     * @return minimum number of clients required to render the line
120     * @deprecated use MSInfo.getMinPointCount()
121     */
122    public static int GetMinPoints(int lineType) {
123        int result = -1;
124        switch (lineType) {
125            case TacticalLines.RECTANGULAR:
126            case TacticalLines.CUED_ACQUISITION:
127            case TacticalLines.CIRCULAR:
128            case TacticalLines.BDZ:
129            case TacticalLines.FSA_CIRCULAR:
130            case TacticalLines.NOTACK:
131            case TacticalLines.FFA_CIRCULAR:
132            case TacticalLines.NFA_CIRCULAR:
133            case TacticalLines.RFA_CIRCULAR:
134            case TacticalLines.ACA_CIRCULAR:
135            case TacticalLines.PAA_CIRCULAR:
136            case TacticalLines.ATI_CIRCULAR:
137            case TacticalLines.CFFZ_CIRCULAR:
138            case TacticalLines.SENSOR_CIRCULAR:
139            case TacticalLines.CENSOR_CIRCULAR:
140            case TacticalLines.DA_CIRCULAR:
141            case TacticalLines.CFZ_CIRCULAR:
142            case TacticalLines.ZOR_CIRCULAR:
143            case TacticalLines.TBA_CIRCULAR:
144            case TacticalLines.TVAR_CIRCULAR:
145            case TacticalLines.KILLBOXBLUE_CIRCULAR:
146            case TacticalLines.KILLBOXPURPLE_CIRCULAR:
147            case TacticalLines.LAUNCH_AREA:
148            case TacticalLines.DEFENDED_AREA_CIRCULAR:
149            case TacticalLines.SHIP_AOI_CIRCULAR:
150            case TacticalLines.RANGE_FAN:
151            case TacticalLines.RANGE_FAN_SECTOR:
152            case TacticalLines.RADAR_SEARCH:
153                result = 1;
154                break;
155            case TacticalLines.PAA_RECTANGULAR:
156            case TacticalLines.RECTANGULAR_TARGET:
157            case TacticalLines.FSA_RECTANGULAR:
158            case TacticalLines.SHIP_AOI_RECTANGULAR:
159            case TacticalLines.DEFENDED_AREA_RECTANGULAR:
160            case TacticalLines.FFA_RECTANGULAR:
161            case TacticalLines.RFA_RECTANGULAR:
162            case TacticalLines.NFA_RECTANGULAR:
163            case TacticalLines.ACA_RECTANGULAR:
164            case TacticalLines.ATI_RECTANGULAR:
165            case TacticalLines.CFFZ_RECTANGULAR:
166            case TacticalLines.SENSOR_RECTANGULAR:
167            case TacticalLines.CENSOR_RECTANGULAR:
168            case TacticalLines.DA_RECTANGULAR:
169            case TacticalLines.CFZ_RECTANGULAR:
170            case TacticalLines.ZOR_RECTANGULAR:
171            case TacticalLines.TBA_RECTANGULAR:
172            case TacticalLines.TVAR_RECTANGULAR:
173            case TacticalLines.KILLBOXBLUE_RECTANGULAR:
174            case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
175                result = 2; //was 3
176                break;
177            case TacticalLines.SPTBYFIRE:
178            case TacticalLines.RIP:
179            case TacticalLines.GAP:
180            case TacticalLines.ASLTXING:
181            case TacticalLines.MSDZ:
182                result = 4;
183                break;
184            case TacticalLines.BYPASS:
185            case TacticalLines.BLOCK:
186            case TacticalLines.BREACH:
187            case TacticalLines.CANALIZE:
188            case TacticalLines.CLEAR:
189            case TacticalLines.CONTAIN:
190            case TacticalLines.DELAY:
191            case TacticalLines.DISRUPT:
192            case TacticalLines.PENETRATE:
193            case TacticalLines.RETIRE:
194            case TacticalLines.FPOL:
195            case TacticalLines.RPOL:
196            case TacticalLines.SCREEN:
197            case TacticalLines.COVER:
198            case TacticalLines.GUARD:
199            case TacticalLines.SEIZE:
200            case TacticalLines.WITHDRAW:
201            case TacticalLines.WDRAWUP:
202            //non task autoshapes
203            case TacticalLines.SARA:
204            case TacticalLines.PDF:
205            case TacticalLines.IL:
206            case TacticalLines.ATKBYFIRE:
207            case TacticalLines.AMBUSH:
208            case TacticalLines.RELEASE:
209            case TacticalLines.HOL:
210            case TacticalLines.BHL:
211            case TacticalLines.MNFLDBLK:
212            case TacticalLines.MNFLDDIS:
213            case TacticalLines.TURN:
214            case TacticalLines.PLANNED:
215            case TacticalLines.ESR1:
216            case TacticalLines.ESR2:
217            case TacticalLines.ROADBLK:
218            case TacticalLines.TRIP:
219            case TacticalLines.EASY:
220            case TacticalLines.BYDIF:
221            case TacticalLines.BYIMP:
222            case TacticalLines.FORDSITE:
223            case TacticalLines.FORDIF:
224            //METOCs
225            case TacticalLines.IFR:
226            case TacticalLines.MVFR:
227            case TacticalLines.TURBULENCE:
228            case TacticalLines.ICING:
229            case TacticalLines.NON_CONVECTIVE:
230            case TacticalLines.CONVECTIVE:
231            case TacticalLines.FROZEN:
232            case TacticalLines.THUNDERSTORMS:
233            case TacticalLines.FOG:
234            case TacticalLines.SAND:
235            case TacticalLines.FREEFORM:
236            case TacticalLines.DEPTH_AREA:
237            case TacticalLines.ISLAND:
238            case TacticalLines.BEACH:
239            case TacticalLines.WATER:
240            case TacticalLines.FISH_TRAPS:
241            case TacticalLines.SWEPT_AREA:
242            case TacticalLines.OIL_RIG_FIELD:
243            case TacticalLines.FOUL_GROUND:
244            case TacticalLines.KELP:
245            case TacticalLines.BEACH_SLOPE_MODERATE:
246            case TacticalLines.BEACH_SLOPE_STEEP:
247            case TacticalLines.ANCHORAGE_AREA:
248            case TacticalLines.TRAINING_AREA:
249            case TacticalLines.FORESHORE_AREA:
250            case TacticalLines.DRYDOCK:
251            case TacticalLines.LOADING_FACILITY_AREA:
252            case TacticalLines.PERCHES:
253            case TacticalLines.UNDERWATER_HAZARD:
254            case TacticalLines.DISCOLORED_WATER:
255            case TacticalLines.BEACH_SLOPE_FLAT:
256            case TacticalLines.BEACH_SLOPE_GENTLE:
257            case TacticalLines.MARITIME_AREA:
258            case TacticalLines.OPERATOR_DEFINED:
259            case TacticalLines.SUBMERGED_CRIB:
260            case TacticalLines.VDR_LEVEL_12:
261            case TacticalLines.VDR_LEVEL_23:
262            case TacticalLines.VDR_LEVEL_34:
263            case TacticalLines.VDR_LEVEL_45:
264            case TacticalLines.VDR_LEVEL_56:
265            case TacticalLines.VDR_LEVEL_67:
266            case TacticalLines.VDR_LEVEL_78:
267            case TacticalLines.VDR_LEVEL_89:
268            case TacticalLines.VDR_LEVEL_910:
269            case TacticalLines.SOLID_ROCK:
270            case TacticalLines.CLAY:
271            case TacticalLines.VERY_COARSE_SAND:
272            case TacticalLines.COARSE_SAND:
273            case TacticalLines.MEDIUM_SAND:
274            case TacticalLines.FINE_SAND:
275            case TacticalLines.VERY_FINE_SAND:
276            case TacticalLines.VERY_FINE_SILT:
277            case TacticalLines.FINE_SILT:
278            case TacticalLines.MEDIUM_SILT:
279            case TacticalLines.COARSE_SILT:
280            case TacticalLines.BOULDERS:
281            case TacticalLines.OYSTER_SHELLS:
282            case TacticalLines.PEBBLES:
283            case TacticalLines.SAND_AND_SHELLS:
284            case TacticalLines.BOTTOM_SEDIMENTS_LAND:
285            case TacticalLines.BOTTOM_SEDIMENTS_NO_DATA:
286            case TacticalLines.BOTTOM_ROUGHNESS_SMOOTH:
287            case TacticalLines.BOTTOM_ROUGHNESS_MODERATE:
288            case TacticalLines.BOTTOM_ROUGHNESS_ROUGH:
289            case TacticalLines.CLUTTER_LOW:
290            case TacticalLines.CLUTTER_MEDIUM:
291            case TacticalLines.CLUTTER_HIGH:
292            case TacticalLines.IMPACT_BURIAL_0:
293            case TacticalLines.IMPACT_BURIAL_10:
294            case TacticalLines.IMPACT_BURIAL_20:
295            case TacticalLines.IMPACT_BURIAL_75:
296            case TacticalLines.IMPACT_BURIAL_100:
297            case TacticalLines.BOTTOM_CATEGORY_A:
298            case TacticalLines.BOTTOM_CATEGORY_B:
299            case TacticalLines.BOTTOM_CATEGORY_C:
300            case TacticalLines.BOTTOM_TYPE_A1:
301            case TacticalLines.BOTTOM_TYPE_A2:
302            case TacticalLines.BOTTOM_TYPE_A3:
303            case TacticalLines.BOTTOM_TYPE_B1:
304            case TacticalLines.BOTTOM_TYPE_B2:
305            case TacticalLines.BOTTOM_TYPE_B3:
306            case TacticalLines.BOTTOM_TYPE_C1:
307            case TacticalLines.BOTTOM_TYPE_C2:
308            case TacticalLines.BOTTOM_TYPE_C3:
309                result = 3;
310                break;
311            case TacticalLines.MRR:
312            case TacticalLines.SL:
313            case TacticalLines.TC:
314            case TacticalLines.SC:
315            case TacticalLines.LLTR:
316            case TacticalLines.DIRATKAIR:
317            case TacticalLines.ABATIS:
318            case TacticalLines.CLUSTER:
319            case TacticalLines.MNFLDFIX:
320            case TacticalLines.FERRY:
321            case TacticalLines.MFLANE:
322            case TacticalLines.RAFT:
323            case TacticalLines.FOXHOLE:
324            case TacticalLines.LINTGT:
325            case TacticalLines.LINTGTS:
326            case TacticalLines.FPF:
327            case TacticalLines.CONVOY:
328            case TacticalLines.HCONVOY:
329                result = 2;
330                break;
331            default:
332                result = 2;
333                break;
334        }
335        if (isClosedPolygon(lineType)) {
336            result = 3;
337        }
338        //add code for change 1 areas
339        return result;
340    }
341    /**
342     * @param linetype line type
343     * @return true if the line is a closed area
344     */
345    public static boolean isClosedPolygon(int linetype) {
346        boolean result = false;
347        switch (linetype) {
348            case TacticalLines.AT:
349            case TacticalLines.DEPICT:
350            case TacticalLines.DZ:
351            case TacticalLines.MINED:
352            case TacticalLines.FENCED:
353            case TacticalLines.UXO:
354            case TacticalLines.ROZ:
355            case TacticalLines.AARROZ:
356            case TacticalLines.UAROZ:
357            case TacticalLines.WEZ:
358            case TacticalLines.FEZ:
359            case TacticalLines.JEZ:
360            case TacticalLines.FAADZ:
361            case TacticalLines.HIDACZ:
362            case TacticalLines.MEZ:
363            case TacticalLines.LOMEZ:
364            case TacticalLines.HIMEZ:
365            case TacticalLines.WFZ:
366            case TacticalLines.PNO:
367            case TacticalLines.BATTLE:
368            case TacticalLines.EA:
369            case TacticalLines.EZ:
370            case TacticalLines.LZ:
371            case TacticalLines.PZ:
372            case TacticalLines.GENERAL:
373            case TacticalLines.JTAA:
374            case TacticalLines.SAA:
375            case TacticalLines.SGAA:
376            case TacticalLines.ASSAULT:
377            case TacticalLines.ATKPOS:
378            case TacticalLines.OBJ:
379            case TacticalLines.AO:
380            case TacticalLines.AIRHEAD:
381            case TacticalLines.NAI:
382            case TacticalLines.TAI:
383            case TacticalLines.BASE_CAMP_REVD:
384            case TacticalLines.BASE_CAMP:
385            case TacticalLines.GUERILLA_BASE_REVD:
386            case TacticalLines.GUERILLA_BASE:
387            case TacticalLines.GENERIC_AREA:
388            case TacticalLines.OBSFAREA:
389            case TacticalLines.OBSAREA:
390            case TacticalLines.ZONE:
391            case TacticalLines.STRONG:
392            case TacticalLines.DRCL:
393            case TacticalLines.FSA:
394            case TacticalLines.ACA:
395            case TacticalLines.ASSY:
396            case TacticalLines.BSA:
397            case TacticalLines.NFA:
398            case TacticalLines.RFA:
399            case TacticalLines.FARP:
400            case TacticalLines.AIRFIELD:
401            case TacticalLines.LAA:
402            case TacticalLines.BOMB:
403            case TacticalLines.FFA:
404            case TacticalLines.SMOKE:
405            case TacticalLines.PAA:
406            case TacticalLines.ENCIRCLE:
407            case TacticalLines.DHA_REVD:
408            case TacticalLines.DHA:
409            case TacticalLines.EPW:
410            case TacticalLines.RHA:
411            case TacticalLines.DSA:
412            case TacticalLines.CSA:
413            case TacticalLines.RSA:
414            case TacticalLines.FORT_REVD:
415            case TacticalLines.FORT:
416            case TacticalLines.PEN:
417            case TacticalLines.BIO:
418            case TacticalLines.NUC:
419            case TacticalLines.RAD:
420            case TacticalLines.CHEM:
421            case TacticalLines.SERIES:
422            case TacticalLines.ATI:
423            case TacticalLines.TBA:
424            case TacticalLines.TVAR:
425            case TacticalLines.CFFZ:
426            case TacticalLines.CENSOR:
427            case TacticalLines.SENSOR:
428            case TacticalLines.ZOR:
429            case TacticalLines.DA:
430            case TacticalLines.CFZ:
431            case TacticalLines.KILLBOXBLUE:
432            case TacticalLines.KILLBOXPURPLE:
433            //METOCs
434            case TacticalLines.IFR:
435            case TacticalLines.MVFR:
436            case TacticalLines.TURBULENCE:
437            case TacticalLines.ICING:
438            case TacticalLines.NON_CONVECTIVE:
439            case TacticalLines.CONVECTIVE:
440            case TacticalLines.FROZEN:
441            case TacticalLines.THUNDERSTORMS:
442            case TacticalLines.FOG:
443            case TacticalLines.SAND:
444            case TacticalLines.FREEFORM:
445            case TacticalLines.DEPTH_AREA:
446            case TacticalLines.ISLAND:
447            case TacticalLines.BEACH:
448            case TacticalLines.WATER:
449            case TacticalLines.FISH_TRAPS:
450            case TacticalLines.SWEPT_AREA:
451            case TacticalLines.OIL_RIG_FIELD:
452            case TacticalLines.FOUL_GROUND:
453            case TacticalLines.KELP:
454            case TacticalLines.BEACH_SLOPE_MODERATE:
455            case TacticalLines.BEACH_SLOPE_STEEP:
456            case TacticalLines.ANCHORAGE_AREA:
457            case TacticalLines.TRAINING_AREA:
458            case TacticalLines.FORESHORE_AREA:
459            case TacticalLines.DRYDOCK:
460            case TacticalLines.LOADING_FACILITY_AREA:
461            case TacticalLines.PERCHES:
462            case TacticalLines.UNDERWATER_HAZARD:
463            case TacticalLines.DISCOLORED_WATER:
464            case TacticalLines.BEACH_SLOPE_FLAT:
465            case TacticalLines.BEACH_SLOPE_GENTLE:
466            case TacticalLines.MARITIME_AREA:
467            case TacticalLines.OPERATOR_DEFINED:
468            case TacticalLines.SUBMERGED_CRIB:
469            case TacticalLines.VDR_LEVEL_12:
470            case TacticalLines.VDR_LEVEL_23:
471            case TacticalLines.VDR_LEVEL_34:
472            case TacticalLines.VDR_LEVEL_45:
473            case TacticalLines.VDR_LEVEL_56:
474            case TacticalLines.VDR_LEVEL_67:
475            case TacticalLines.VDR_LEVEL_78:
476            case TacticalLines.VDR_LEVEL_89:
477            case TacticalLines.VDR_LEVEL_910:
478            case TacticalLines.SOLID_ROCK:
479            case TacticalLines.CLAY:
480            case TacticalLines.VERY_COARSE_SAND:
481            case TacticalLines.COARSE_SAND:
482            case TacticalLines.MEDIUM_SAND:
483            case TacticalLines.FINE_SAND:
484            case TacticalLines.VERY_FINE_SAND:
485            case TacticalLines.VERY_FINE_SILT:
486            case TacticalLines.FINE_SILT:
487            case TacticalLines.MEDIUM_SILT:
488            case TacticalLines.COARSE_SILT:
489            case TacticalLines.BOULDERS:
490            case TacticalLines.OYSTER_SHELLS:
491            case TacticalLines.PEBBLES:
492            case TacticalLines.SAND_AND_SHELLS:
493            case TacticalLines.BOTTOM_SEDIMENTS_LAND:
494            case TacticalLines.BOTTOM_SEDIMENTS_NO_DATA:
495            case TacticalLines.BOTTOM_ROUGHNESS_SMOOTH:
496            case TacticalLines.BOTTOM_ROUGHNESS_MODERATE:
497            case TacticalLines.BOTTOM_ROUGHNESS_ROUGH:
498            case TacticalLines.CLUTTER_LOW:
499            case TacticalLines.CLUTTER_MEDIUM:
500            case TacticalLines.CLUTTER_HIGH:
501            case TacticalLines.IMPACT_BURIAL_0:
502            case TacticalLines.IMPACT_BURIAL_10:
503            case TacticalLines.IMPACT_BURIAL_20:
504            case TacticalLines.IMPACT_BURIAL_75:
505            case TacticalLines.IMPACT_BURIAL_100:
506            case TacticalLines.BOTTOM_CATEGORY_A:
507            case TacticalLines.BOTTOM_CATEGORY_B:
508            case TacticalLines.BOTTOM_CATEGORY_C:
509            case TacticalLines.BOTTOM_TYPE_A1:
510            case TacticalLines.BOTTOM_TYPE_A2:
511            case TacticalLines.BOTTOM_TYPE_A3:
512            case TacticalLines.BOTTOM_TYPE_B1:
513            case TacticalLines.BOTTOM_TYPE_B2:
514            case TacticalLines.BOTTOM_TYPE_B3:
515            case TacticalLines.BOTTOM_TYPE_C1:
516            case TacticalLines.BOTTOM_TYPE_C2:
517            case TacticalLines.BOTTOM_TYPE_C3:
518            case TacticalLines.TGMF:
519                result = true;
520                break;
521            default:
522                break;
523        }
524        return result;
525    }
526
527    /**
528     * Closes the polygon for areas
529     * @param Pixels the client points
530     */
531    public static void ClosePolygon(ArrayList<POINT2> Pixels) {
532        try {
533            POINT2 pt0 = Pixels.get(0);
534            POINT2 pt1 = Pixels.get(Pixels.size() - 1);
535            if (pt0.x != pt1.x || pt0.y != pt1.y) {
536                Pixels.add(new POINT2(pt0.x, pt0.y));
537            }
538        } catch (Exception exc) {
539               ErrorLogger.LogException(_className ,"ClosePolygon",
540                    new RendererException("Failed inside ClosePolygon", exc));
541        }
542    }
543    /**
544     * for change 1 symbol the W/w1 modifiers run too close to the symbol outline
545     * so it shifts the line along the line away from the edge
546     * @param p1
547     * @param p2
548     * @param shift
549     */
550    protected static void shiftModifiersLeft(POINT2 p1, POINT2 p2, double shift)
551    {
552        try
553        {
554            POINT2 pt1=new POINT2(p1);
555            POINT2 pt2=new POINT2(p2);
556            double dist=lineutility.CalcDistanceDouble(pt1, pt2);
557            if(pt1.x<pt2.x || (pt1.x==pt2.x && pt1.y<pt2.y))
558            {
559                pt1=lineutility.ExtendAlongLineDouble(pt2, pt1, dist+shift);
560                pt2=lineutility.ExtendAlongLineDouble(pt1, pt2, dist-shift);
561            }
562            else
563            {
564                pt1=lineutility.ExtendAlongLineDouble(pt2, pt1, dist-shift);
565                pt2=lineutility.ExtendAlongLineDouble(pt1, pt2, dist+shift);
566            }
567            p1.x=pt1.x;
568            p1.y=pt1.y;
569            p2.x=pt2.x;
570            p2.y=pt2.y;
571        }
572        catch (Exception exc) {
573               ErrorLogger.LogException(_className ,"shiftModifiersLeft",
574                    new RendererException("Failed inside shiftModifiersLeft", exc));
575        }
576    }
577    /**
578     * Overrides shape properties for symbols based on Mil-Std-2525
579     * @param tg
580     * @param shape
581     */
582    protected static void ResolveModifierShape(TGLight tg, Shape2 shape) {
583        try {
584            //shape style was set by CELineArray and takes precedence
585            //whenever it is set
586            int shapeStyle = shape.get_Style();
587            int lineStyle = tg.get_LineStyle();
588            int lineType = tg.get_LineType();
589            boolean hasFill=LinesWithFill(lineType);
590            int bolMETOC=clsMETOC.IsWeather(tg.get_SymbolId());
591            if(bolMETOC>0)
592                return;
593            int fillStyle=0;
594            //for some of these the style must be dashed
595            switch (tg.get_LineType()) {
596                case TacticalLines.NFA:
597                case TacticalLines.NFA_CIRCULAR:
598                case TacticalLines.NFA_RECTANGULAR:
599                case TacticalLines.BIO:
600                case TacticalLines.NUC:
601                case TacticalLines.CHEM:
602                case TacticalLines.RAD:
603                case TacticalLines.WFZ:
604                //case TacticalLines.OBSAREA:
605                    fillStyle=3;
606                    if(tg.get_UseHatchFill())
607                        fillStyle=0;
608                    if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
609                        shape.set_Style(tg.get_LineStyle());
610                        shape.setLineColor(tg.get_LineColor());
611                        shape.set_Fillstyle(fillStyle /*GraphicProperties.FILL_TYPE_RIGHT_SLANTS*/);//was 3
612                        shape.setFillColor(tg.get_FillColor());
613                    }
614                    break;
615                case TacticalLines.OBSAREA:
616                    if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
617                        shape.set_Style(tg.get_LineStyle());
618                        shape.setLineColor(tg.get_LineColor());
619                        shape.set_Fillstyle(0 /*GraphicProperties.FILL_TYPE_RIGHT_SLANTS*/);
620                        shape.setFillColor(tg.get_FillColor());
621                    }
622                    break;
623                case TacticalLines.LAA:
624                    fillStyle=2;
625                    if(tg.get_UseHatchFill())
626                        fillStyle=0;
627                    if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
628                        shape.set_Style(tg.get_LineStyle());
629                        shape.setLineColor(tg.get_LineColor());
630                        shape.set_Fillstyle(fillStyle /*GraphicProperties.FILL_TYPE_LEFT_SLANTS*/);//was 2
631                        shape.setFillColor(tg.get_FillColor());
632                    }
633                    break;
634                case TacticalLines.DIRATKAIR:
635                case TacticalLines.ATDITCHC:
636                case TacticalLines.ATDITCHM:
637                case TacticalLines.SARA:
638                case TacticalLines.FOLSP:
639                case TacticalLines.FERRY:
640                case TacticalLines.MNFLDFIX:
641                case TacticalLines.TURN:
642                case TacticalLines.MNFLDDIS:
643                case TacticalLines.EASY:
644                case TacticalLines.BYDIF:
645                case TacticalLines.BYIMP:
646                    tg.set_lineCap(BasicStroke.CAP_BUTT);
647                    if (shape.getShapeType() == Shape2.SHAPE_TYPE_FILL) {
648                        shape.set_Fillstyle(1 /*GraphicProperties.FILL_TYPE_SOLID*/);
649                        shape.setFillColor(tg.get_LineColor());
650                    }
651                    if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
652                        shape.set_Style(tg.get_LineStyle());
653                        shape.setLineColor(tg.get_LineColor());
654                    }
655                    break;
656                case TacticalLines.CLUSTER:
657                case TacticalLines.CATK:
658                case TacticalLines.CATKBYFIRE:
659                case TacticalLines.PLD:
660                case TacticalLines.PLANNED:
661                case TacticalLines.CFL:
662                case TacticalLines.FORDSITE:
663                case TacticalLines.ACOUSTIC_AMB:
664                    //any shape for these symbols is dashed
665                    if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
666                        shape.set_Style(1 /*GraphicProperties.LINE_TYPE_DASHED*/);
667                        shape.setLineColor(tg.get_LineColor());
668                    }
669                    break;
670                case TacticalLines.PNO: //always dashed
671                    if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
672                        shape.set_Style(1 /*GraphicProperties.LINE_TYPE_DASHED*/);
673                        shape.setLineColor(tg.get_LineColor());
674                        shape.setFillColor(tg.get_FillColor());
675                        shape.set_Fillstyle(tg.get_FillStyle());
676                    }
677                    break;
678                case TacticalLines.FOLLA:
679                case TacticalLines.ESR1:
680                case TacticalLines.FORDIF:
681                    if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
682                        shape.setLineColor(tg.get_LineColor());
683                        if (shapeStyle != lineStyle) {
684                            if (shapeStyle != 1 /*GraphicProperties.LINE_TYPE_DASHED*/) {
685                                shape.set_Style(lineStyle);
686                            }
687                        }
688                    }
689                    break;
690                default:
691                    if (shape.getShapeType() == Shape2.SHAPE_TYPE_FILL) {
692                        shape.set_Fillstyle(tg.get_FillStyle());
693                        shape.setFillColor(tg.get_FillColor());
694                    }
695                    if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
696                        if (lineType != TacticalLines.LC) {
697                            shape.setLineColor(tg.get_LineColor());
698                        } else {
699                            SetLCColor(tg, shape);
700                        }
701                        shape.set_Style(lineStyle);
702                        if (hasFill || clsUtility.isClosedPolygon(lineType) || clsUtility.IsChange1Area(lineType))
703                        {
704                            switch(lineType)
705                            {
706                                case TacticalLines.RANGE_FAN:
707                                case TacticalLines.RANGE_FAN_SECTOR:
708                                case TacticalLines.RADAR_SEARCH:
709                                    shape.setFillColor(null);
710                                    break;
711                                default:
712                                    shape.set_Fillstyle(tg.get_FillStyle());
713                                    shape.setFillColor(tg.get_FillColor());
714                                    break;
715                            }
716                        }
717                    }
718                    break;
719            }
720
721        } catch (Exception exc) {
722               ErrorLogger.LogException(_className ,"ResolveModifierShape",
723                    new RendererException("Failed inside ResolveModifierShape", exc));
724        }
725    }
726    public static Color GetOpaqueColor(Color color)
727    {
728        int r=color.getRed();
729        int g=color.getGreen();
730        int b=color.getBlue();
731        return new Color(r,g,b);
732    }
733    /**
734     * These lines allow fill
735     * @param linetype
736     * @return
737     */
738    public static boolean LinesWithFill(int linetype)
739    {
740        boolean result=false;
741        try
742        {
743            switch(linetype)
744            {
745                case TacticalLines.PAA_RECTANGULAR:
746                case TacticalLines.RECTANGULAR_TARGET:
747                case TacticalLines.CFL:
748                case TacticalLines.DIRATKAIR:
749                case TacticalLines.BOUNDARY:
750                case TacticalLines.ISOLATE:
751                case TacticalLines.CORDONKNOCK:
752                case TacticalLines.CORDONSEARCH:
753                case TacticalLines.OCCUPY:
754                case TacticalLines.RETAIN:
755                case TacticalLines.SECURE:
756                case TacticalLines.FLOT:
757                case TacticalLines.LC:
758                case TacticalLines.PL:
759                case TacticalLines.FEBA:
760                case TacticalLines.LL:
761                case TacticalLines.EWL:
762//                case TacticalLines.AC:
763//                case TacticalLines.SAAFR:
764                case TacticalLines.DIRATKGND:
765                case TacticalLines.DIRATKSPT:
766                case TacticalLines.FCL:
767                case TacticalLines.HOLD:
768                case TacticalLines.BRDGHD:
769                case TacticalLines.HOLD_GE:
770                case TacticalLines.BRDGHD_GE:
771                case TacticalLines.LOA:
772                case TacticalLines.LOD:
773                case TacticalLines.LDLC:
774                case TacticalLines.RELEASE:
775                case TacticalLines.HOL:
776                case TacticalLines.BHL:
777                case TacticalLines.LINE:
778                case TacticalLines.ABATIS:
779                case TacticalLines.ATDITCH:
780                case TacticalLines.ATWALL:
781                case TacticalLines.SFENCE:
782                case TacticalLines.DFENCE:
783                case TacticalLines.UNSP:
784                case TacticalLines.PLD:
785                case TacticalLines.DOUBLEA:
786                case TacticalLines.LWFENCE:
787                case TacticalLines.HWFENCE:
788                case TacticalLines.SINGLEC:
789                case TacticalLines.DOUBLEC:
790                case TacticalLines.TRIPLE:
791                case TacticalLines.FORTL:
792                case TacticalLines.LINTGT:
793                case TacticalLines.LINTGTS:
794                case TacticalLines.FSCL:
795                case TacticalLines.BCL_REVD:
796                case TacticalLines.BCL:
797                case TacticalLines.ICL:
798                case TacticalLines.IFF_OFF:
799                case TacticalLines.IFF_ON:
800                case TacticalLines.GENERIC_LINE:
801                case TacticalLines.NFL:
802                case TacticalLines.MFP:
803                case TacticalLines.RFL:
804                case TacticalLines.CONVOY:
805                case TacticalLines.HCONVOY:
806                case TacticalLines.MSR:
807                case TacticalLines.MSR_ONEWAY:
808                case TacticalLines.MSR_TWOWAY:
809                case TacticalLines.MSR_ALT:
810                case TacticalLines.ASR:
811                case TacticalLines.ASR_ONEWAY:
812                case TacticalLines.ASR_TWOWAY:
813                case TacticalLines.ASR_ALT:
814                case TacticalLines.ROUTE:
815                case TacticalLines.ROUTE_ONEWAY:
816                case TacticalLines.ROUTE_ALT:
817                    result = true;
818                    break;
819                default:
820                    result = false;
821                    break;
822            }
823        }
824        catch(Exception exc)
825        {
826               ErrorLogger.LogException(_className ,"LinesWithFill",
827                    new RendererException("Failed inside LinesWithFill", exc));
828        }
829        return result;
830    }
831    /**
832     * @deprecated
833     * if the line color and fill color are the same or very close then we want to
834     * tweak the fill color a bit to make the line appear distinct from the fill.
835     * @param tg
836     */
837    public static void tweakFillColor(TGLight tg)
838    {
839        try
840        {
841            if(isSameColor(tg.get_LineColor(),tg.get_FillColor())==false)
842                return;
843
844            Color fillColor=tg.get_FillColor();
845            int r=fillColor.getRed(),g=fillColor.getGreen(),b=fillColor.getBlue();
846            int alpha=fillColor.getAlpha();
847
848            r*=0.9;
849            g*=0.9;
850            b*=0.9;
851            alpha*=0.8;
852
853            fillColor=new Color(r,g,b,alpha);
854            tg.set_FillColor(fillColor);
855        }
856        catch(Exception exc)
857        {
858               ErrorLogger.LogException(_className ,"tweakFillColor",
859                    new RendererException("Failed inside tweakFillColor", exc));
860        }
861    }
862    /**
863     * @deprecated
864     * Test to see if two colors are similar
865     * @param c1
866     * @param c2
867     * @return true is same (or similar) color
868     */
869    public static Boolean isSameColor(Color c1, Color c2)
870    {
871        try
872        {
873            if(c1==null || c2==null)
874                return true;
875
876            int r1=c1.getRed(),r2=c2.getRed(),g1=c1.getGreen(),g2=c2.getGreen(),
877                    b1=c1.getBlue(),b2=c2.getBlue();
878
879            if(Math.abs(r1-r2)<5)
880                if(Math.abs(g1-g2)<5)
881                    if(Math.abs(b1-b2)<5)
882                        return true;
883        }
884        catch(Exception exc)
885        {
886               ErrorLogger.LogException(_className ,"isSameColor",
887                    new RendererException("Failed inside isSameColor", exc));
888        }
889        return false;
890    }
891    /**
892     * Customer requested routine for setting the stroke dash pattern
893     * Scales dash length with line width and DPI
894     * @param width
895     * @param style
896     * @param cap
897     * @param join
898     * @return
899     */
900    public static BasicStroke getLineStroke(int width, int style, int cap, int join) {
901        // Some segments are of length 0.1 because the Java2D renderer adds line caps of
902        // width/2 size to both ends of the segment when "round" is one of BasicStroke.CAP_ROUND
903        // or BasicStroke.CAP_SQUARE. This value is small enough not to affect the
904        // stipple bit pattern calculation for the 3d map and still look good on the
905        // 2d map.
906
907        // NOTE: The dash arrays below do not supportBasisStroke.CAP_BUTT line capping,
908        // although it would be relatively simple to change them such that they would.
909        BasicStroke stroke=null;
910        try {
911            final float dashLength = 2 * width;
912            final float dotLength = 1f;
913            final float dotSpace = 2 * width;
914            switch (style) {
915                case 0://GraphicProperties.LINE_TYPE_SOLID:
916                    stroke = new BasicStroke(width, cap, join);
917                    break;
918                case 1://GraphicProperties.LINE_TYPE_DASHED:
919                    float[] dash = {dashLength, dashLength};
920                    stroke = new BasicStroke(width, cap, join, 4f, dash, 0f);
921                    break;
922                case 2://GraphicProperties.LINE_TYPE_DOTTED:
923                    float[] dot = {dotLength, dotSpace};
924                    stroke = new BasicStroke(width, cap, join, 4f, dot, 0f);
925                    break;
926                case 3://GraphicProperties.LINE_TYPE_DASHDOT:
927                    float[] dashdot = {2 * dashLength, dotSpace, dotLength, dotSpace};
928                    stroke = new BasicStroke(width, cap, join, 4f, dashdot,0f );
929                    break;
930                case 4://GraphicProperties.LINE_TYPE_DASHDOTDOT:
931                    float[] dashdotdot = {dashLength, dotSpace, dotLength, dotSpace, dotLength, dotSpace};
932                    stroke = new BasicStroke(width, cap, join, 4f,dashdotdot, 0f );
933                    break;
934                default:
935                    stroke = new BasicStroke(width, cap, join);
936                    break;
937            }
938        } catch(Exception exc) {
939               ErrorLogger.LogException(_className ,"getLineStroke",
940                    new RendererException("Failed inside getLineStroke", exc));
941        }
942        return stroke;
943    }
944    /**
945     * Sets shape properties based on other properties which were set by JavaLineArray
946     * @param tg tactical graphic
947     * @param shapes the ShapeInfo array
948     * @param bi BufferedImage to use for setting shape TexturePaint
949     */
950    public static void SetShapeProperties(TGLight tg, ArrayList<Shape2> shapes,
951            BufferedImage bi) {
952        try
953        {
954            if (shapes == null)
955            {
956                return;
957            }
958            
959            int j = 0;
960            Shape2 shape = null;
961            BasicStroke stroke = null;
962            float[] dash = null;
963            int lineThickness = tg.get_LineThickness();
964            int shapeType = -1;
965            int lineType = tg.get_LineType();
966            boolean hasFill=LinesWithFill(lineType);
967            boolean isChange1Area = clsUtility.IsChange1Area(lineType);
968            boolean isClosedPolygon = clsUtility.isClosedPolygon(lineType);
969            //int n=shapes.size();
970            //remove air corridors fill shapes if fill is null
971            if(tg.get_FillColor()==null)
972            {
973                switch(tg.get_LineType())
974                {
975                    case TacticalLines.AC:
976                    case TacticalLines.SAAFR:
977                    case TacticalLines.MRR:
978                    case TacticalLines.SL:
979                    case TacticalLines.TC:
980                    case TacticalLines.SC:
981                    case TacticalLines.LLTR:
982                        shape=shapes.get(shapes.size()-1);
983                        shapes.clear();
984                        shapes.add(shape);
985                        break;
986                    case TacticalLines.CATK:
987                    case TacticalLines.AIRAOA:
988                    case TacticalLines.AAAAA:
989                    case TacticalLines.SPT:
990                    case TacticalLines.MAIN:
991                    case TacticalLines.CATKBYFIRE:      //80
992                        ArrayList<Shape2> tempShapes=new ArrayList();
993                        for(j=0;j<shapes.size();j++)
994                        {
995                            shape=shapes.get(j);
996                            if(shape.getShapeType() != Shape2.SHAPE_TYPE_FILL)
997                                tempShapes.add(shape);
998                        }
999                        shapes=tempShapes;
1000                        break;
1001                    default:
1002                        break;
1003                }
1004            }
1005            for (j = 0; j < shapes.size(); j++) 
1006            {
1007                shape = shapes.get(j);
1008                if (shape == null || shape.getShape() == null) {
1009                    continue;
1010                }
1011
1012                if (shape.getShapeType() == Shape2.SHAPE_TYPE_FILL) 
1013                {
1014                    switch(tg.get_LineType())
1015                    {
1016                        case TacticalLines.DEPTH_AREA:
1017                            break;
1018                        default:
1019                            shape.setFillColor(tg.get_FillColor());
1020                            break;
1021                    }
1022                }
1023
1024                //if(lineType != TacticalLines.LEADING_LINE)
1025                ResolveModifierShape(tg, shape);
1026                if(lineType==TacticalLines.AIRFIELD)
1027                    if(j==1)
1028                        shape.setFillColor(null);
1029
1030                shapeType = shape.getShapeType();
1031
1032                Rectangle2D.Double rect = null;
1033                Graphics2D grid = null;
1034                TexturePaint tp = tg.get_TexturePaint();
1035
1036                if(lineThickness==0)
1037                    lineThickness=1;
1038                //set the shape with the default properties
1039                //the switch statement below will override specific properties as needed
1040                stroke = getLineStroke(lineThickness,shape.get_Style(),tg.get_lineCap(),BasicStroke.JOIN_ROUND);
1041                if(shape.getShapeType()==Shape2.SHAPE_TYPE_FILL)
1042                {
1043                    stroke = new BasicStroke(lineThickness, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
1044                    //shape.setStroke(new BasicStroke(0));
1045                }
1046                shape.setStroke(stroke);
1047            } // end loop over shapes
1048           if (tg.get_LineType() == TacticalLines.DIRATKAIR) {
1049               // Make arrowhead and bowtie shapes solid even if tg.get_LineStyle() isn't
1050               for (int i = 2; i < shapes.size(); i++) {
1051                   Shape2 arrowHeadShape = shapes.get(i);
1052                   arrowHeadShape.set_Style(0);
1053                   stroke = getLineStroke(lineThickness, 0, tg.get_lineCap(), BasicStroke.JOIN_ROUND);
1054                   arrowHeadShape.setStroke(stroke);
1055               }
1056           } else if (tg.get_LineType() == TacticalLines.DIRATKGND || tg.get_LineType() == TacticalLines.DIRATKSPT) {
1057                // Make arrowhead shape solid even if tg.get_LineStyle() isn't
1058                Shape2 arrowHeadShape = shapes.get(1);
1059                arrowHeadShape.set_Style(0);
1060                stroke = getLineStroke(lineThickness,0,tg.get_lineCap(),BasicStroke.JOIN_ROUND);
1061                arrowHeadShape.setStroke(stroke);
1062           } else if (tg.get_LineType() == TacticalLines.PDF) {
1063               Shape2 rectShape = shapes.get(1);
1064               rectShape.set_Style(0);
1065               stroke = getLineStroke(lineThickness, 0, tg.get_lineCap(), BasicStroke.JOIN_ROUND);
1066               rectShape.setStroke(stroke);
1067               rectShape.setFillColor(rectShape.getLineColor());
1068           }
1069        }
1070        catch (Exception exc) {
1071               ErrorLogger.LogException(_className ,"SetShapeProperties",
1072                    new RendererException("Failed inside SetShapeProperties", exc));
1073        }
1074    }
1075    /**
1076     * Returns a boolean indicating whether the line type is a change 1 area
1077     * @param lineType the line type
1078     * @return true if change 1 area
1079     */
1080    public static boolean IsChange1Area(int lineType) {
1081        try {
1082            switch (lineType) {
1083                case TacticalLines.LAUNCH_AREA:
1084                case TacticalLines.DEFENDED_AREA_CIRCULAR:
1085                case TacticalLines.SHIP_AOI_CIRCULAR:
1086                case TacticalLines.RECTANGULAR:
1087                case TacticalLines.CUED_ACQUISITION:
1088                case TacticalLines.CIRCULAR:
1089                case TacticalLines.BDZ:
1090                case TacticalLines.FSA_CIRCULAR:
1091                case TacticalLines.NOTACK:
1092                case TacticalLines.FFA_CIRCULAR:
1093                case TacticalLines.NFA_CIRCULAR:
1094                case TacticalLines.RFA_CIRCULAR:
1095                case TacticalLines.ACA_CIRCULAR:
1096                case TacticalLines.PAA_CIRCULAR:
1097                case TacticalLines.ATI_CIRCULAR:
1098                case TacticalLines.CFFZ_CIRCULAR:
1099                case TacticalLines.SENSOR_CIRCULAR:
1100                case TacticalLines.CENSOR_CIRCULAR:
1101                case TacticalLines.DA_CIRCULAR:
1102                case TacticalLines.CFZ_CIRCULAR:
1103                case TacticalLines.ZOR_CIRCULAR:
1104                case TacticalLines.TBA_CIRCULAR:
1105                case TacticalLines.TVAR_CIRCULAR:
1106                case TacticalLines.KILLBOXBLUE_CIRCULAR:
1107                case TacticalLines.KILLBOXPURPLE_CIRCULAR:
1108                case TacticalLines.RANGE_FAN:
1109                case TacticalLines.RANGE_FAN_FILL:
1110                case TacticalLines.RANGE_FAN_SECTOR:
1111                case TacticalLines.RADAR_SEARCH:
1112                case TacticalLines.PAA_RECTANGULAR:
1113                case TacticalLines.RECTANGULAR_TARGET:
1114                case TacticalLines.FSA_RECTANGULAR:
1115                case TacticalLines.SHIP_AOI_RECTANGULAR:
1116                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
1117                case TacticalLines.FFA_RECTANGULAR:
1118                case TacticalLines.RFA_RECTANGULAR:
1119                case TacticalLines.NFA_RECTANGULAR:
1120                case TacticalLines.ACA_RECTANGULAR:
1121                case TacticalLines.ATI_RECTANGULAR:
1122                case TacticalLines.CFFZ_RECTANGULAR:
1123                case TacticalLines.SENSOR_RECTANGULAR:
1124                case TacticalLines.CENSOR_RECTANGULAR:
1125                case TacticalLines.DA_RECTANGULAR:
1126                case TacticalLines.CFZ_RECTANGULAR:
1127                case TacticalLines.ZOR_RECTANGULAR:
1128                case TacticalLines.TBA_RECTANGULAR:
1129                case TacticalLines.TVAR_RECTANGULAR:
1130                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
1131                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
1132                    return true;
1133                default:
1134                    return false;
1135            }
1136        } catch (Exception exc) {
1137            //clsUtility.WriteFile("Error in clsUtility.IsChange1Area");
1138               ErrorLogger.LogException(_className ,"IsChange1Area",
1139                    new RendererException("Failed inside IsChange1Area", exc));
1140        }
1141        return false;
1142    }
1143
1144    public static void WriteFile(String str) {
1145        try {
1146            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("Test.txt"));
1147            bufferedWriter.write(str);
1148            //bufferedWriter.newLine();
1149            //bufferedWriter.write(pointType);
1150            bufferedWriter.close();
1151            bufferedWriter = null;
1152        } 
1153        catch (Exception exc) {
1154               ErrorLogger.LogException(_className ,"WriteFile",
1155                    new RendererException("Failed inside WriteFile", exc));
1156        }
1157    }
1158
1159    /**
1160     * Calculates point where two lines intersect.
1161     * First line defined by pt1, m1.
1162     * Second line defined by pt2, m2.
1163     * result will be written to ptIntersect.
1164     * @param pt1 first line point
1165     * @param m1 slope of first line
1166     * @param pt2 second line point
1167     * @param m2 slope of second line
1168     * @param ptIntersect OUT - intersection point
1169     */
1170    protected static void CalcIntersectPt(POINT2 pt1,
1171            double m1,
1172            POINT2 pt2,
1173            double m2,
1174            POINT2 ptIntersect) {
1175        try {
1176            if (m1 == m2) {
1177                return;
1178            }
1179
1180            double x1 = pt1.x;
1181            double y1 = pt1.y;
1182            double x2 = pt2.x;
1183            double y2 = pt2.y;
1184            //formula for the intersection of two lines
1185            double dx2 = (double) ((y1 - y2 + m1 * x2 - m1 * x1) / (m2 - m1));
1186            double x3 = x2 + dx2;
1187            double y3 = (double) (y2 + m2 * dx2);
1188
1189            ptIntersect.x = x3;
1190            ptIntersect.y = y3;
1191        } catch (Exception exc) {
1192            //clsUtility.WriteFile("Error in clsUtility.CalcIntersectPt");
1193            ErrorLogger.LogException(_className, "CalcIntersectPt",
1194                    new RendererException("Failed inside CalcIntersectPt", exc));
1195        }
1196    }
1197
1198    /**
1199     * Calculates the channel width in pixels for channel types
1200     * @param pixels the client points as 2-tuples x,y in pixels
1201     * @param distanceToChannelPOINT2 OUT - the calculated distance in pixels from the tip of the
1202     * arrowhead to the back of the arrowhead.
1203     * @return the channel width in pixels
1204     */
1205    protected static int ChannelWidth(double[] pixels,
1206            ref<double[]> distanceToChannelPOINT2) {
1207        int width = 0;
1208        try {
1209            int numPOINT2s = pixels.length / 2;
1210            if (numPOINT2s < 3) {
1211                return 0;
1212            }
1213
1214            POINT2 channelWidthPOINT2 = new POINT2(0, 0);
1215            POINT2 lastSegmentPt1 = new POINT2(0, 0);
1216            POINT2 lastSegmentPt2 = new POINT2(0, 0);
1217
1218            lastSegmentPt1.x = (double) pixels[2 * numPOINT2s - 6];
1219            lastSegmentPt1.y = (double) pixels[2 * numPOINT2s - 5];
1220            lastSegmentPt2.x = (double) pixels[2 * numPOINT2s - 4];
1221            lastSegmentPt2.y = (double) pixels[2 * numPOINT2s - 3];
1222            channelWidthPOINT2.x = (double) pixels[2 * numPOINT2s - 2];
1223            channelWidthPOINT2.y = (double) pixels[2 * numPOINT2s - 1];
1224
1225            ref<double[]> m = new ref();
1226            double m1 = 0;
1227            //m1.value=new double[1];
1228            double distance = 0;
1229            POINT2 ptIntersect = new POINT2(0, 0);
1230            //boolean bolVertical = TrueSlope(lastSegmentPt1, lastSegmentPt2, ref m);
1231            boolean bolVertical = lineutility.CalcTrueSlopeDouble2(lastSegmentPt1, lastSegmentPt2, m);
1232            if (bolVertical == true && m.value[0] != 0) {
1233                m1 = -1 / m.value[0];
1234                CalcIntersectPt(channelWidthPOINT2, m1, lastSegmentPt2, m.value[0], ptIntersect);
1235                distance = lineutility.CalcDistanceDouble(channelWidthPOINT2, ptIntersect);
1236            }
1237            if (bolVertical == true && m.value[0] == 0) //horizontal segment
1238            {
1239                distance = Math.abs(channelWidthPOINT2.y - lastSegmentPt1.y);
1240            }
1241            if (bolVertical == false) //vertical segment
1242            {
1243                distance = Math.abs(channelWidthPOINT2.x - lastSegmentPt1.x);
1244                distanceToChannelPOINT2.value = new double[1];
1245                distanceToChannelPOINT2.value[0] = distance;
1246                return (int) distance * 4;
1247            }
1248
1249            width = (int) distance * 8;
1250            if (width < 2) {
1251                width = 2;
1252            }
1253
1254            double hypotenuse = lineutility.CalcDistanceDouble(lastSegmentPt2, channelWidthPOINT2);
1255            distanceToChannelPOINT2.value = new double[1];
1256            distanceToChannelPOINT2.value[0] = Math.sqrt(hypotenuse * hypotenuse - distance * distance);
1257
1258        } catch (Exception exc) {
1259            //clsUtility.WriteFile("Error in clsUtility.ChannelWidth");
1260            ErrorLogger.LogException(_className, "ChannelWidth",
1261                    new RendererException("Failed inside ChannelWidth", exc));
1262        }
1263        return width;
1264    }
1265
1266    private static boolean InYOrder(POINT2 pt0,
1267            POINT2 pt1,
1268            POINT2 pt2) {
1269        try {
1270            if (pt0.y <= pt1.y && pt1.y <= pt2.y) {
1271                return true;
1272            }
1273
1274            if (pt2.y <= pt1.y && pt1.y <= pt0.y) {
1275                return true;
1276            }
1277
1278        } catch (Exception exc) {
1279            //clsUtility.WriteFile("Error in clsUtility.InYOrder");
1280            ErrorLogger.LogException(_className, "InYOrder",
1281                    new RendererException("Failed inside InYOrder", exc));
1282        }
1283        return false;
1284    }
1285    /// <summary>
1286    /// tests if POINT2s have successively increasing or decreasing x values.
1287    /// </summary>
1288    /// <param name="pt0"></param>
1289    /// <param name="pt1"></param>
1290    /// <param name="pt2"></param>
1291    /// <returns>true if POINT2s are in X order</returns>
1292
1293    private static boolean InXOrder(POINT2 pt0,
1294            POINT2 pt1,
1295            POINT2 pt2) {
1296        try {
1297            if (pt0.x <= pt1.x && pt1.x <= pt2.x) {
1298                return true;
1299            }
1300
1301            if (pt2.x <= pt1.x && pt1.x <= pt0.x) {
1302                return true;
1303            }
1304
1305        } catch (Exception exc) {
1306            //clsUtility.WriteFile("Error in clsUtility.InXOrder");
1307            ErrorLogger.LogException(_className, "InXOrder",
1308                    new RendererException("Failed inside InXOrder", exc));
1309        }
1310        return false;
1311    }
1312
1313    /**
1314     * For each sector calculates left azimuth, right azimuth, min radius, max radius
1315     * and stuff H2 with the string delimited result. The function is public, called by JavaRendererServer
1316     * @param tg tactical graphic
1317     */
1318    public static void GetSectorRadiiFromPoints(TGLight tg) {
1319        try {
1320            if(tg.get_LineType()==TacticalLines.RANGE_FAN_FILL)
1321                return;
1322            POINT2 ptCenter = tg.LatLongs.get(0);
1323            POINT2 ptLeftMin = new POINT2(), ptRightMax = new POINT2();
1324            int k = 0;
1325            String strLeft = "", strRight = "", strMin = "", strMax = "", temp = "";
1326            double nLeft = 0, nRight = 0, nMin = 0, nMax = 0;
1327            //if tg.PointCollection has more than one point
1328            //we use the points to calculate left,right,min,max
1329            //and then stuff tg.H2 with the comma delimited string
1330            double dist = 0;
1331            ref<double[]> a12 = new ref(), a21 = new ref();
1332            int numSectors = 0;
1333            if (tg.LatLongs.size() > 2) {
1334                numSectors = (tg.LatLongs.size() - 2) / 2;
1335                for (k = 0; k < numSectors; k++) {
1336                    //get the sector points
1337                    ptLeftMin = tg.LatLongs.get(2 * k + 2);
1338                    ptRightMax = tg.LatLongs.get(2 * k + 3);
1339
1340                    dist = mdlGeodesic.geodesic_distance(ptCenter, ptLeftMin, a12, a21);
1341                    nLeft = a12.value[0];
1342                    strLeft = Double.toString(nLeft);
1343
1344                    nMin = dist;
1345                    strMin = Double.toString(nMin);
1346
1347                    dist = mdlGeodesic.geodesic_distance(ptCenter, ptRightMax, a12, a21);
1348                    nRight = a12.value[0];
1349                    strRight = Double.toString(nRight);
1350
1351                    nMax = dist;
1352                    strMax = Double.toString(nMax);
1353
1354                    if (k == 0) {
1355                        temp = strLeft + "," + strRight + "," + strMin + "," + strMax;
1356                    } else {
1357                        temp += "," + strLeft + "," + strRight + "," + strMin + "," + strMax;
1358                    }
1359                }
1360                if (!temp.equals("")) {
1361                    tg.set_LRMM(temp);
1362                }
1363            }
1364        } catch (Exception exc) {
1365            //clsUtility.WriteFile("Error in clsUtility.GetSectorRadiiFromPoints");
1366            ErrorLogger.LogException(_className, "GetSectorRadiiFromPoints",
1367                    new RendererException("Failed inside GetSectorRadiiFromPoints", exc));
1368        }
1369    }
1370
1371    /**
1372     * Reverses the pixels except for the last point. This is used for
1373     * the axis of advance type routes. The pixels are 2-tuples x,y
1374     *
1375     * @param pixels OUT - Array of client points
1376     */
1377    protected static void ReorderPixels(double[] pixels) {
1378        try {
1379            double[] tempPixels;
1380            //reverse the pixels
1381            int j;
1382            double x;
1383            double y;
1384            int counter;
1385            int numPoints;
1386            counter = 0;
1387            numPoints = pixels.length / 2;
1388            tempPixels = new double[pixels.length];
1389            for (j = 0; j < numPoints - 1; j++) {
1390                x = pixels[pixels.length - 2 * j - 4];
1391                y = pixels[pixels.length - 2 * j - 3];
1392                tempPixels[counter] = x;
1393                tempPixels[counter + 1] = y;
1394                counter += 2;
1395            }
1396            //put the last pixel point into the last temppixels point
1397            int intPixelSize = pixels.length;
1398            tempPixels[counter] = pixels[intPixelSize - 2];
1399            tempPixels[counter + 1] = pixels[intPixelSize - 1];
1400            //stuff the pixels
1401            int n=pixels.length;
1402            //for (j = 0; j < pixels.length; j++) 
1403            for (j = 0; j < n; j++) 
1404            {
1405                pixels[j] = tempPixels[j];
1406            }
1407            //tempPixels = null;
1408        } catch (Exception exc) {
1409            ErrorLogger.LogException(_className, "ReorderPixels",
1410                    new RendererException("Failed inside ReorderPixels", exc));
1411        }
1412    }
1413    /**
1414     * do not allow vertical segments for these, move the point x value by 1 pixel
1415     * @param tg tactical graphic
1416     */
1417    public static void FilterVerticalSegments(TGLight tg)
1418    {
1419        try
1420        {
1421            switch(tg.get_LineType())
1422            {
1423                case TacticalLines.MAIN:
1424                case TacticalLines.CATK:
1425                case TacticalLines.CATKBYFIRE:
1426                case TacticalLines.AIRAOA:
1427                case TacticalLines.AAAAA:
1428                case TacticalLines.SPT:
1429                case TacticalLines.LC:
1430                case TacticalLines.UNSP:
1431                case TacticalLines.DFENCE:
1432                case TacticalLines.SFENCE:
1433                case TacticalLines.DOUBLEA:
1434                case TacticalLines.LWFENCE:
1435                case TacticalLines.HWFENCE:
1436                case TacticalLines.SINGLEC:
1437                case TacticalLines.DOUBLEC:
1438                case TacticalLines.TRIPLE:
1439                case TacticalLines.MSR_ONEWAY:
1440                case TacticalLines.MSR_TWOWAY:
1441                case TacticalLines.MSR_ALT:
1442                case TacticalLines.ASR_ONEWAY:
1443                case TacticalLines.ASR_TWOWAY:
1444                case TacticalLines.ASR_ALT:
1445                case TacticalLines.ROUTE_ONEWAY:
1446                case TacticalLines.ROUTE_ALT:
1447                case TacticalLines.ATWALL:
1448                    break;
1449                default:
1450                    return;
1451            }
1452            POINT2 ptCurrent=null;
1453            POINT2 ptLast=null;
1454            int n=tg.Pixels.size();
1455            //for(int j=1;j<tg.Pixels.size();j++)
1456            for(int j=1;j<n;j++)
1457            {
1458                ptLast=new POINT2(tg.Pixels.get(j-1));
1459                ptCurrent=new POINT2(tg.Pixels.get(j));
1460                //if(Math.round(ptCurrent.x)==Math.round(ptLast.x))
1461                if(Math.abs(ptCurrent.x-ptLast.x)<1)
1462                {
1463                    if (ptCurrent.x>=ptLast.x)
1464                        ptCurrent.x += 1;
1465                    else
1466                        ptCurrent.x -= 1;
1467                    tg.Pixels.set(j, ptCurrent);
1468                }
1469            }
1470        }
1471        catch(Exception exc)
1472        {
1473            ErrorLogger.LogException("clsUtility", "FilterVerticalSegments",
1474                    new RendererException("Failed inside FilterVerticalSegments", exc));
1475
1476        }
1477    }
1478    /**
1479     * Client utility to calculate the channel points for channel types.
1480     * This code was ported from CJMTK.
1481     * @param arrLocation the client points
1482     * @return the channel point
1483     */
1484    public static POINT2 ComputeLastPoint(ArrayList<POINT2> arrLocation) {
1485        POINT2 locD = new POINT2(0, 0);
1486        try {
1487            POINT2 locA = arrLocation.get(1);
1488            //Get the first point (b) in pixels.
1489            //var locB:Point=new Point(arrLocation[0].x,arrLocation[0].y);
1490            POINT2 locB = arrLocation.get(0);
1491
1492            //Compute the distance in pixels from (a) to (b).
1493            double dblDx = locB.x - locA.x;
1494            double dblDy = locB.y - locA.y;
1495
1496            //Compute the dblAngle in radians from (a) to (b).
1497            double dblTheta = Math.atan2(-dblDy, dblDx);
1498
1499            //Compute a reasonable intermediate point along the line from (a) to (b).
1500            POINT2 locC = new POINT2(0, 0);
1501            locC.x = (int) (locA.x + 0.85 * dblDx);
1502            locC.y = (int) (locA.y + 0.85 * dblDy);
1503            //Put the last point on the left side of the line from (a) to (b).
1504            double dblAngle = dblTheta + Math.PI / 2.0;
1505            if (dblAngle > Math.PI) {
1506                dblAngle = dblAngle - 2.0 * Math.PI;
1507            }
1508            if (dblAngle < -Math.PI) {
1509                dblAngle = dblAngle + 2.0 * Math.PI;
1510            }
1511
1512            //Set the magnitude of the dblWidth in pixels.  Make sure it is at least 15 pixels.
1513            double dblWidth = 30;//was 15
1514
1515            //Compute the last point in pixels.
1516            locD.x = (locC.x + dblWidth * Math.cos(dblAngle));
1517            locD.y = (locC.y - dblWidth * Math.sin(dblAngle));
1518        } catch (Exception exc) {
1519            //clsUtility.WriteFile("Error in clsUtility.ComputeLatPoint");
1520            ErrorLogger.LogException(_className, "ComputeLastPoint",
1521                    new RendererException("Failed inside ComputeLastPoint", exc));
1522        }
1523        return locD;
1524    }
1525
1526    /**
1527     * Called by clsChannelUtility. The segments are used for managing double-backed segments
1528     * for channel types. If the new point is double-backed then the segment at that index will be false.
1529     *
1530     * @param pixels the client points as 2-tuples x,y in pixels
1531     * @param segments OUT - the segments
1532     * @param factor a steepness factor for calculating whether the segment is double-backed
1533     */
1534    protected static void GetSegments(double[] pixels,
1535            boolean[] segments,
1536            double factor) {
1537        try
1538        {
1539            int j = 0;
1540            ref<double[]> m1 = new ref();
1541            ref<double[]> m2 = new ref();
1542            long numPoints = 0;
1543            boolean bolVertical1 = false;
1544            boolean bolVertical2 = false;
1545
1546            POINT2 pt0F = new POINT2(0, 0);
1547            POINT2 pt1F = new POINT2(0, 0);
1548            POINT2 pt2F = new POINT2(0, 0);
1549
1550            segments[0] = true;
1551            
1552            numPoints = pixels.length / 2;
1553            for (j = 0; j < numPoints - 2; j++)
1554            {
1555                pt0F.x = (double) pixels[2 * j];
1556                pt0F.y = (double) pixels[2 * j + 1];
1557
1558                pt1F.x = (double) pixels[2 * j + 2];
1559                pt1F.y = (double) pixels[2 * j + 3];
1560
1561                pt2F.x = (double) pixels[2 * j + 4];
1562                pt2F.y = (double) pixels[2 * j + 5];
1563
1564                bolVertical1 = lineutility.CalcTrueSlopeDoubleForRoutes(pt0F, pt1F, m1);
1565                bolVertical2 = lineutility.CalcTrueSlopeDoubleForRoutes(pt1F, pt2F, m2);
1566
1567                segments[j + 1] = true;
1568                if (bolVertical1 == true && bolVertical2 == true)
1569                {
1570                    if (Math.abs(Math.atan(m1.value[0]) - Math.atan(m2.value[0])) < 1 / factor && InXOrder(pt0F, pt1F, pt2F) == false) //was 0.1
1571                    {
1572                        segments[j + 1] = false;
1573                    }
1574                }
1575
1576                if ((bolVertical1 == false || Math.abs(m1.value[0]) > factor) && (bolVertical2 == false || Math.abs(m2.value[0]) > factor) && InYOrder(pt0F, pt1F, pt2F) == false) //was 10
1577                {
1578                    segments[j + 1] = false;
1579                }
1580            }   //end for
1581            //int n=segments.length;
1582        }
1583        catch (Exception exc)
1584        {
1585            //System.out.println(e.getMessage());
1586            //clsUtility.WriteFile("Error in clsUtility.GetSegments");
1587            ErrorLogger.LogException(_className, "GetSegments",
1588                    new RendererException("Failed inside GetSegments", exc));
1589        }
1590    }
1591
1592    protected static void GetLCPartitions(double[] pixels,
1593                                          double LCChannelWith,
1594                                          ArrayList<P1> partitions,
1595                                          ArrayList<P1> singleLinePartitions) {
1596        try
1597        {
1598            int numPoints = pixels.length / 2;
1599            POINT2 pt0F = new POINT2(0, 0);
1600            POINT2 pt1F = new POINT2(0, 0);
1601            POINT2 pt2F = new POINT2(0, 0);
1602
1603            P1 nextP = new P1();
1604            nextP.start = 0;
1605
1606            //used for debugging
1607            double[] angles = new double[numPoints - 1];
1608
1609            for (int i = 0; i < numPoints - 2; i++) {
1610                pt0F.x = (double) pixels[2 * i];
1611                pt0F.y = (double) pixels[2 * i + 1];
1612
1613                pt1F.x = (double) pixels[2 * i + 2];
1614                pt1F.y = (double) pixels[2 * i + 3];
1615
1616                pt2F.x = (double) pixels[2 * i + 4];
1617                pt2F.y = (double) pixels[2 * i + 5];
1618
1619                double angle1 = Math.atan2(pt1F.y - pt0F.y, pt1F.x - pt0F.x);
1620                double angle2 = Math.atan2(pt1F.y - pt2F.y, pt1F.x - pt2F.x);
1621                double angle = angle1-angle2;// * 180/Math.PI;
1622                double degrees = angle * 180/Math.PI;
1623                if (angle < 0) {
1624                    degrees = 360 + degrees;
1625                }
1626
1627                if (degrees > 270) {
1628                    boolean angleTooSmall = false;
1629
1630                    if (lineutility.CalcDistanceDouble(pt0F, pt1F) < lineutility.CalcDistanceDouble(pt1F, pt2F)) {
1631                        POINT2 newPt = lineutility.ExtendAlongLineDouble2(pt1F, pt2F, lineutility.CalcDistanceDouble(pt1F, pt0F));
1632                        if (lineutility.CalcDistanceDouble(pt0F, newPt) < LCChannelWith)
1633                            angleTooSmall = true;
1634                    } else {
1635                        POINT2 newPt = lineutility.ExtendAlongLineDouble2(pt1F, pt0F, lineutility.CalcDistanceDouble(pt1F, pt2F));
1636                        if (lineutility.CalcDistanceDouble(pt2F, newPt) < LCChannelWith)
1637                            angleTooSmall = true;
1638                    }
1639                    if (angleTooSmall) {
1640                        // Angle is too small to fit channel, make it a single line partition
1641                        nextP.end_Renamed = i - 1;
1642                        partitions.add(nextP);
1643                        nextP = new P1();
1644                        nextP.start = i;
1645                        nextP.end_Renamed=i + 2;
1646                        singleLinePartitions.add(nextP);
1647                        i++;
1648                        nextP = new P1();
1649                        nextP.start = i + 1;
1650                    }
1651                } else if(degrees < 90) {
1652                    // new Partition
1653                    nextP.end_Renamed = i;
1654                    partitions.add(nextP);
1655                    nextP = new P1();
1656                    nextP.start = i + 1;
1657                }
1658                angles[i] = degrees;
1659            } //end for
1660            nextP.end_Renamed = numPoints - 2;
1661            partitions.add(nextP);
1662        } catch (Exception exc) {
1663            ErrorLogger.LogException(_className, "GetLCPartitions",
1664                    new RendererException("Failed inside GetLCPartitions", exc));
1665        }
1666    }
1667
1668    /**
1669     * Sets the color for the current shape depending on the affiliation
1670     * @param tg
1671     * @param shape
1672     */
1673    protected static void SetLCColor(TGLight tg, Shape2 shape) {
1674        try {
1675            if (tg.isHostile()) {
1676                if (shape.getLineColor() == Color.RED) {
1677                    shape.setLineColor(tg.get_LineColor());
1678                } else {
1679                    shape.setLineColor(Color.RED);
1680                }
1681            } else {
1682                if (shape.getLineColor() != Color.RED) {
1683                    shape.setLineColor(tg.get_LineColor());
1684                } else {
1685                    shape.setLineColor(Color.RED);
1686                }
1687            }
1688
1689        } catch (Exception exc) {
1690            //WriteFile("Error in clsUtility.SetLCColor");
1691            ErrorLogger.LogException(_className, "SetLCColor",
1692                    new RendererException("Failed inside SetLCColor", exc));
1693        }
1694    }
1695    /**
1696     * USAS requires a left-right orientation for ENY, which negates the upper-lower
1697     * orientation we used for Mil-Std-2525 ENY compliance. Therefore we must reverse
1698     * the client points for two of the quadrants
1699     * @param tg tactical graphic
1700     */
1701    public static void ReverseUSASLCPointsByQuadrant(TGLight tg)
1702    {
1703        try
1704        {
1705            if(tg.Pixels.size()<2)
1706                return;
1707            int quadrant=lineutility.GetQuadrantDouble(tg.Pixels.get(0), tg.Pixels.get(1));
1708            switch(tg.get_LineType())
1709            {
1710                case TacticalLines.LC:
1711                    if(tg.isHostile())
1712                    {
1713                        switch(quadrant)
1714                        {
1715                            case 2:
1716                            case 3:
1717                                break;
1718                            case 1://reverse the points for these two quadrants
1719                            case 4:
1720                                int n=tg.Pixels.size();
1721                                ArrayList<POINT2> pts2=(ArrayList<POINT2>)tg.Pixels.clone();
1722                                        //for(int j=0;j<tg.Pixels.size();j++)
1723                                        for(int j=0;j<n;j++)
1724                                            tg.Pixels.set(j, pts2.get(n-j-1));
1725                                break;
1726                        }//end switch quadrant
1727                    }//end if
1728                    else
1729                    {
1730                        switch(quadrant)
1731                        {
1732                            case 1:
1733                            case 4:
1734                                break;
1735                            case 2://reverse the points for these two quadrants
1736                            case 3:
1737                                int n=tg.Pixels.size();
1738                                ArrayList<POINT2> pts2=(ArrayList<POINT2>)tg.Pixels.clone();
1739                                        //for(int j=0;j<tg.Pixels.size();j++)
1740                                        for(int j=0;j<n;j++)
1741                                            tg.Pixels.set(j, pts2.get(n-j-1));
1742                                break;
1743                        }//end switch quadrant
1744                    }//end else
1745                    break;
1746                default:
1747                    break;
1748            }//end switch linetype
1749        }
1750        catch (Exception exc) {
1751            //WriteFile("Error in clsUtility.SetLCColor");
1752            ErrorLogger.LogException(_className, "ReverseUSASLCPointsByQuadrant",
1753                    new RendererException("Failed inside ReverseUSASLCPointsByQuadrant", exc));
1754        }
1755    }//end ReverseUSASLCPointsByQuadrant
1756
1757    /**
1758     * use str if tg is null
1759     * @param symbolId Mil=Standard-2525 symbol id
1760     * @return line type
1761     */
1762    public static int GetLinetypeFromString(String symbolId)
1763    {
1764        try
1765        {
1766            if (symbolId.length() < 16){
1767                return -1;
1768            }
1769            int symbolSet = SymbolID.getSymbolSet(symbolId);
1770            int entityCode = SymbolID.getEntityCode(symbolId);
1771            int version = SymbolID.getVersion(symbolId);
1772            if (symbolSet == 25) {
1773                return clsRenderer.getCMLineType(version, entityCode);
1774            } else if (symbolSet == 45 || symbolSet == 46) {
1775                return clsMETOC.getWeatherLinetype(version, entityCode);
1776            }
1777        }
1778        catch (Exception exc) {
1779            ErrorLogger.LogException(_className ,"GetLinetypeFromString",
1780                    new RendererException("Failed inside GetLinetypeFromString", exc));
1781        }
1782        return -1;
1783    }
1784
1785    /**
1786     * An auto-shape is a symbol with a fixed number of anchor points
1787     *
1788     * @param tg tactical graphic
1789     * @return true if auto-shape
1790     */
1791    public static boolean isAutoshape(TGLight tg) {
1792        try {
1793            MSInfo msInfo = MSLookup.getInstance().getMSLInfo(tg.get_SymbolId());
1794            if (msInfo == null || IsChange1Area(tg.get_LineType())) {
1795                return false;
1796            }
1797            switch (tg.get_LineType()) {
1798                case TacticalLines.DIRATKAIR:
1799                case TacticalLines.DIRATKGND:
1800                case TacticalLines.DIRATKSPT:
1801                    // Direction of attack symbols only have two points but can handle more
1802                    return false;
1803                default:
1804                    break;
1805            }
1806            switch (msInfo.getDrawRule()) {
1807                case DrawRules.LINE26: // Two ways to draw but fixed points
1808                case DrawRules.LINE27: // Two ways to draw but fixed points
1809                case DrawRules.AREA26: // Need same number of points in first half and second half to make two shapes
1810                case DrawRules.CORRIDOR1: // Each point represents an Air Control Point or Communications Checkpoint
1811                    return true;
1812                default:
1813                    return msInfo.getMaxPointCount() == msInfo.getMinPointCount();
1814            }
1815        } catch (Exception exc) {
1816            ErrorLogger.LogException(_className, "isAutoshape",
1817                    new RendererException("Failed inside isAutoshape", exc));
1818        }
1819        return false;
1820    }
1821    /**
1822     * Client will send the segment colors within a modifier.
1823     * Format is 0:FFBBBB,4:FFAAAA,...
1824     * For the time being will assume the modifier being used is the H modifier
1825     * @param tg
1826     * @return 
1827     */
1828    public static HashMap<Integer,Color> getMSRSegmentColors(TGLight tg)
1829    {
1830        HashMap<Integer,Color> hMap=null;
1831        try
1832        {
1833            int linetype=tg.get_LineType();
1834            switch(linetype)
1835            {
1836                case TacticalLines.MSR:
1837                case TacticalLines.ASR:
1838                case TacticalLines.ROUTE:
1839                case TacticalLines.BOUNDARY:
1840                    if(tg.get_H()==null || tg.get_H().isEmpty())
1841                        return null;
1842                    hMap=new HashMap<Integer,Color>();
1843                    break;
1844                default:
1845                    return null;
1846            }
1847            String[]colorStrs=tg.get_H().split(",");
1848            int j=0,numSegs=colorStrs.length;
1849            String segPlusColor="";
1850            String[]seg=null;     
1851            Color color=null;
1852            int index=-1;
1853            for(j=0;j<numSegs;j++)
1854            {
1855                segPlusColor=colorStrs[j];
1856                if(!segPlusColor.contains(":"))
1857                    continue;
1858                seg=segPlusColor.split(":");
1859                color= RendererUtilities.getColorFromHexString(seg[1]);
1860                index=Integer.parseInt(seg[0]);
1861                hMap.put(index, color);
1862            }
1863        }
1864        catch (Exception exc)
1865        {
1866            ErrorLogger.LogException(_className ,"getMSRSegmentColors",
1867                    new RendererException("Failed inside getMSRSegmentColors", exc));
1868        }
1869        return hMap;
1870    }
1871    public static HashMap<Integer,String> getMSRSegmentColorStrings(TGLight tg)
1872    {
1873        HashMap<Integer,String> hMap=null;
1874        try
1875        {
1876            int linetype = tg.get_LineType();
1877            switch (linetype) {
1878                case TacticalLines.MSR:
1879                case TacticalLines.ASR:
1880                case TacticalLines.ROUTE:
1881                case TacticalLines.BOUNDARY:
1882                    if (tg.get_H() == null || tg.get_H().isEmpty())
1883                        return null;
1884                    hMap = new HashMap<>();
1885                    break;
1886                default:
1887                    return null;
1888            }
1889            String[] colorStrs = tg.get_H().split(",");
1890            int j = 0;
1891            int numSegs = colorStrs.length;
1892            String segPlusColor = "";
1893            String[] seg = null;
1894            //Color color = null;
1895            int index = -1;
1896            for (j = 0; j < numSegs; j++) {
1897                segPlusColor = colorStrs[j];
1898                if (!segPlusColor.contains(":"))
1899                    continue;
1900                seg = segPlusColor.split(":");
1901                //color = armyc2.c5isr.renderer.utilities.SymbolUtilitiesD.getColorFromHexString(seg[1]);
1902                index = Integer.parseInt(seg[0]);
1903                //hMap.put(new Integer(index), color);
1904                hMap.put(new Integer(index), seg[1]);
1905            }            
1906        }
1907        catch (Exception exc)
1908        {
1909            ErrorLogger.LogException(_className ,"getMSRSegmentColorStrings",
1910                    new RendererException("Failed inside getMSRSegmentColorStrings", exc));
1911        }
1912        return hMap;
1913    }
1914    /**
1915     * tg.H must be revised for clipped MSR, ASR and Boundary
1916     * This function is called after the pixels were clipped
1917     * @param originalPixels the tactical graphic pixels before clipping
1918     * @param tg
1919     * @deprecated
1920     */
1921    public static void reviseHModifier(ArrayList<POINT2>originalPixels, 
1922            TGLight tg)
1923    {
1924        try
1925        {
1926            //only revise tg.H if it is not null or empty
1927            //and the linetype is bounday, MSR, or ASR
1928            if(tg.get_H()==null || tg.get_H().isEmpty())
1929                return;
1930            int linetype=tg.get_LineType();
1931            switch(linetype)
1932            {
1933                case TacticalLines.ASR:
1934                case TacticalLines.MSR:
1935                case TacticalLines.ROUTE:
1936                case TacticalLines.BOUNDARY:
1937                    break;
1938                default:
1939                    return;
1940            }
1941            int j=0,k=0;
1942            //Line2D line=new Line2D.Double();
1943            
1944            //get the first common point between the original points and tg.Pixels
1945            //if it is n then n segments will have been dropped at the front end of
1946            //the clipped array (from the original pixels) so then we would want to
1947            //set the start index to n for the loop through the original points
1948            int n=-1; 
1949            boolean foundPt=false;
1950            int t=originalPixels.size();
1951            int u=tg.Pixels.size();
1952            //for(j=0;j<originalPixels.size();j++)
1953            for(j=0;j<t;j++)
1954            {
1955                //for(k=0;k<tg.Pixels.size();k++)
1956                for(k=0;k<u;k++)
1957                {
1958                    if(originalPixels.get(j).x==tg.Pixels.get(k).x && originalPixels.get(j).y==tg.Pixels.get(k).y)
1959                    {
1960                        n=j;
1961                        foundPt=true;
1962                        break;
1963                    }
1964                }
1965                if(foundPt)
1966                    break;
1967            }
1968            HashMap<Integer,Color> hmap=getMSRSegmentColors(tg);
1969            //use a 2nd hashmap to store the revised segment numbers, and exisitng Colors
1970            HashMap<Integer,Color> hmap2=new HashMap<Integer,Color>();
1971            POINT2 segPt0=null,segPt1=null; //the original segments
1972            POINT2 pt0=null,pt1=null;   //the clipped segments
1973            Color color=null;
1974            if(n<1)
1975                n=1;
1976            for(Integer key : hmap.keySet()) //keys can begin at 0
1977            {
1978                if(key<n-1)
1979                    continue;
1980                if(key+1>originalPixels.size()-1)
1981                    break;
1982                color=hmap.get(key);
1983                segPt0=originalPixels.get(key);
1984                segPt1=originalPixels.get(key+1);
1985                u=tg.Pixels.size();
1986                //for(j=0;j<tg.Pixels.size()-1;j++)
1987                for(j=0;j<u-1;j++)
1988                {
1989                    pt0=tg.Pixels.get(j);//clipped pixels
1990                    pt1=tg.Pixels.get(j+1);
1991                    if(segPt0.x==pt0.x && segPt0.y==pt0.y)
1992                    {
1993                        hmap2.put(j, color);
1994                        break;
1995                    }
1996                    else if(segPt1.x==pt1.x && segPt1.y==pt1.y)
1997                    {
1998                        hmap2.put(j, color);
1999                        break;
2000                    }
2001                    else
2002                    {
2003                        if(pt0.x==segPt1.x && pt0.y==segPt1.y)
2004                            continue;
2005                        if(pt1.x==segPt0.x && pt1.y==segPt0.y)
2006                            continue;
2007                        else    
2008                        {       
2009                            //if the original segment straddles or clips the clipping area
2010                            //then the original segment will contain the clipped segment
2011                            double dist0=lineutility.CalcDistanceToLineDouble(segPt0, segPt1, pt0);
2012                            double dist1=lineutility.CalcDistanceToLineDouble(segPt0, segPt1, pt1);
2013                            Line2D lineOrigPts=new Line2D.Double(segPt0.x,segPt0.y, segPt1.x,segPt1.y);
2014                            Rectangle2D rectOrigPts=lineOrigPts.getBounds2D();
2015                            Line2D lineClipPts=new Line2D.Double(pt0.x,pt0.y, pt1.x, pt1.y);
2016                            Rectangle2D rectClipPts=lineClipPts.getBounds2D();
2017                            //test if the lines coincide and the clipped segment is within the original segment
2018                            if(dist0<1 && dist1<1 && rectOrigPts.contains(rectClipPts))
2019                            {
2020                                hmap2.put(j, color);                                
2021                            }
2022                        }
2023                    }
2024                }
2025            }        
2026            if(hmap2.isEmpty())
2027            {
2028                tg.set_H("");
2029                return;
2030            }
2031           
2032            String h="",temp="";
2033            for(Integer key : hmap2.keySet()) 
2034            {
2035                color=hmap2.get(key);
2036                temp=Integer.toHexString(color.toARGB());
2037                h+=key.toString()+":"+temp+",";
2038            }
2039            h=h.substring(0, h.length()-1);
2040            tg.set_H(h);
2041        }
2042        catch (Exception exc) {
2043            ErrorLogger.LogException(_className, "reviseHModifer",
2044                    new RendererException("Failed inside reviseHModifier", exc));
2045        }
2046    }
2047
2048    /**
2049     * Adds extra points to LC if there are angles too small to fit the channel
2050     * @param tg
2051     * @param converter
2052     */
2053    public static void SegmentLCPoints(TGLight tg, IPointConversion converter) {
2054        try {
2055            if (tg.get_LineType() != TacticalLines.LC && tg.get_LineType() != TacticalLines.LC_HOSTILE)
2056                return;
2057
2058            ArrayList<POINT2> points = tg.get_Pixels();
2059
2060            double LCChannelWith = arraysupport.getScaledSize(40, tg.get_LineThickness());
2061
2062            for (int i = 0; i < points.size() - 2; i++) {
2063                POINT2 ptA = new POINT2(points.get(i).x, points.get(i).y);
2064                POINT2 ptB = new POINT2(points.get(i+1).x, points.get(i+1).y);
2065                POINT2 ptC = new POINT2(points.get(i+2).x, points.get(i+2).y);
2066
2067                double angle1 = Math.atan2(ptB.y - ptA.y, ptB.x - ptA.x);
2068                double angle2 = Math.atan2(ptB.y - ptC.y, ptB.x - ptC.x);
2069                double angle = angle1 - angle2;
2070                double degrees = angle * 180/Math.PI;
2071
2072                if(angle < 0) {
2073                    degrees = 360 + degrees;
2074                }
2075
2076                if (degrees > 270) {
2077                    // For acute angles where red is the outer line
2078                    // Determine shorter segment (BA or BC)
2079                    // On longer segment calculate potential new point (newPt) that is length of smaller segment from B
2080                    // If distance between smaller segment end point (A or C) and newPt is smaller than the channel width add newPt to points
2081                    // In GetLCPartitions() the black line won't be included between the smaller line and newPt since there isn't enough space to fit the channel
2082                    if (lineutility.CalcDistanceDouble(ptB, ptA) < lineutility.CalcDistanceDouble(ptB, ptC)) {
2083                        // BA is smaller segment
2084                        POINT2 newPt = lineutility.ExtendAlongLineDouble2(ptB, ptC, lineutility.CalcDistanceDouble(ptB, ptA));
2085                        if (lineutility.CalcDistanceDouble(ptA, newPt) < LCChannelWith) {
2086                            points.add(i + 2, new POINT2(newPt.x, newPt.y));
2087                            i++;
2088                        }
2089                    } else {
2090                        // BC is smaller segment
2091                        POINT2 newPt = lineutility.ExtendAlongLineDouble2(ptB, ptA, lineutility.CalcDistanceDouble(ptB, ptC));
2092                        if (lineutility.CalcDistanceDouble(ptC, newPt) < LCChannelWith) {
2093                            points.add(i + 1, new POINT2(newPt.x, newPt.y));
2094                            i++;
2095                        }
2096                    }
2097                }
2098            }
2099            tg.Pixels = points;
2100            tg.LatLongs = armyc2.c5isr.RenderMultipoints.clsUtility.PixelsToLatLong(points, converter);
2101        } catch (Exception exc) {
2102            ErrorLogger.LogException(_className, "segmentLCPoints",
2103                    new RendererException("Failed inside segmentLCPoints", exc));
2104        }
2105    }
2106    /**
2107     * Interpolate pixels for lines with points too close together.
2108     * Drops successive points until the next point is at least 10 pixels from the preceding point
2109     * @param tg 
2110     */
2111    public static void InterpolatePixels(TGLight tg)
2112    {
2113        try
2114        {
2115            if(tg.get_UseLineInterpolation()==false)
2116                return;
2117            
2118            int linetype=tg.get_LineType();
2119            double glyphSize=10;
2120            switch(linetype)
2121            {
2122                case TacticalLines.ATDITCH:
2123                case TacticalLines.ATDITCHC:
2124                    glyphSize=25;
2125                    break;
2126                case TacticalLines.ATDITCHM:
2127                    glyphSize=50;
2128                    break;
2129                case TacticalLines.FLOT:
2130                case TacticalLines.LC:
2131                case TacticalLines.FORT_REVD:
2132                case TacticalLines.FORT:
2133                case TacticalLines.FORTL:
2134                case TacticalLines.ENCIRCLE:
2135                case TacticalLines.ZONE:
2136                case TacticalLines.OBSFAREA:
2137                case TacticalLines.OBSAREA:
2138                case TacticalLines.DOUBLEA:
2139                case TacticalLines.LWFENCE:
2140                case TacticalLines.HWFENCE:
2141                case TacticalLines.SINGLEC:
2142                case TacticalLines.DOUBLEC:
2143                case TacticalLines.TRIPLE:
2144                case TacticalLines.STRONG:
2145                    glyphSize= arraysupport.getScaledSize(30, tg.get_LineThickness());
2146                    break;
2147                case TacticalLines.UNSP:
2148                case TacticalLines.LINE:
2149                case TacticalLines.ATWALL:
2150                case TacticalLines.SFENCE:
2151                    glyphSize=arraysupport.getScaledSize(40, tg.get_LineThickness());
2152                    break;
2153                case TacticalLines.DFENCE:
2154                    glyphSize=arraysupport.getScaledSize(50, tg.get_LineThickness());
2155                    break;
2156                default:
2157                    return;
2158            }
2159            HashMap<Integer,POINT2> hmapPixels=new HashMap<Integer,POINT2>();
2160            HashMap<Integer,POINT2> hmapGeo=new HashMap<Integer,POINT2>();
2161            int j=0,currentIndex=0;
2162            double dist=0,dist2=0;
2163            double direction1=0,direction2=0,delta=0;
2164            POINT2 pt0=null,pt1=null,pt2=null;
2165            int n=tg.Pixels.size();
2166            //for(j=0;j<tg.Pixels.size();j++)
2167            for(j=0;j<n;j++)
2168            {
2169                if(j==0)
2170                {
2171                    hmapPixels.put(j, tg.Pixels.get(j));
2172                    hmapGeo.put(j, tg.LatLongs.get(j));
2173                    currentIndex=0;
2174                }
2175                else if(j==tg.Pixels.size()-1)
2176                {
2177                    hmapPixels.put(j, tg.Pixels.get(j));
2178                    hmapGeo.put(j, tg.LatLongs.get(j));                    
2179                }
2180                else
2181                {
2182                    dist=lineutility.CalcDistanceDouble(tg.Pixels.get(currentIndex), tg.Pixels.get(j));
2183                    dist2=lineutility.CalcDistanceDouble(tg.Pixels.get(j), tg.Pixels.get(j+1));
2184                    
2185                    //change of direction test 2-28-13
2186                    pt0=tg.Pixels.get(currentIndex);
2187                    pt1=tg.Pixels.get(j);
2188                    pt2=tg.Pixels.get(j+1);
2189                    direction1=(180/Math.PI)*Math.atan((pt0.y-pt1.y)/(pt0.x-pt1.x));
2190                    direction2=(180/Math.PI)*Math.atan((pt1.y-pt2.y)/(pt1.x-pt2.x));
2191                    delta=Math.abs(direction1-direction2);
2192                    if(dist>glyphSize || dist2>glyphSize || delta>20)
2193                    {
2194                        hmapPixels.put(j, tg.Pixels.get(j));
2195                        hmapGeo.put(j, tg.LatLongs.get(j));
2196                        currentIndex=j;
2197                    }
2198                }
2199            }
2200            ArrayList<POINT2>pixels=new ArrayList();
2201            ArrayList<POINT2>geo=new ArrayList();
2202            n=tg.Pixels.size();
2203            //for(j=0;j<tg.Pixels.size();j++)
2204            for(j=0;j<n;j++)
2205            {
2206                if(hmapPixels.containsKey(j))
2207                    pixels.add((POINT2)hmapPixels.get(j));
2208                if(hmapGeo.containsKey(j))
2209                    geo.add((POINT2)hmapGeo.get(j));
2210            }
2211            switch(linetype)
2212            {
2213                case TacticalLines.FORT_REVD:
2214                case TacticalLines.FORT:
2215                case TacticalLines.ENCIRCLE:
2216                case TacticalLines.ZONE:
2217                case TacticalLines.OBSFAREA:
2218                case TacticalLines.OBSAREA:
2219                case TacticalLines.STRONG:
2220                    if(pixels.size()==2)
2221                    {
2222                        n=tg.Pixels.size();
2223                        //for(j=0;j<tg.Pixels.size();j++)
2224                        for(j=0;j<n;j++)
2225                        {
2226                            if(hmapPixels.containsKey(j)==false && hmapGeo.containsKey(j)==false)
2227                            {
2228                                pixels.add(j,tg.Pixels.get(j));
2229                                geo.add(j,tg.LatLongs.get(j));
2230                                break;
2231                            }
2232                        }                        
2233                    }
2234                    break;
2235                default:
2236                    break;
2237            }            
2238            tg.Pixels=pixels;
2239            tg.LatLongs=geo;
2240        }
2241        catch (Exception exc) {
2242            ErrorLogger.LogException(_className, "InterpolatePixels",
2243                    new RendererException("Failed inside InterpolatePixels", exc));
2244        }
2245    }
2246    /**
2247     * construct a line segment outside the polygon corresponding to some index
2248     * @param tg
2249     * @param index
2250     * @param dist
2251     * @return 
2252     */
2253    protected static Line2D getExtendedLine(TGLight tg,
2254            int index,
2255            double dist)
2256    {
2257        Line2D line=null;
2258        try
2259        {
2260            Polygon polygon=new Polygon();
2261            int j=0;
2262            int n=tg.Pixels.size();
2263            //for(j=0;j<tg.Pixels.size();j++)
2264            for(j=0;j<n;j++)
2265            {
2266                polygon.addPoint((int)tg.Pixels.get(j).x, (int)tg.Pixels.get(j).y);
2267            }
2268            POINT2 pt0=null; 
2269            POINT2 pt1=null; 
2270            if(tg.Pixels.size()>3)
2271            {
2272                pt0=tg.Pixels.get(index);
2273                pt1=tg.Pixels.get(index+1);
2274            }
2275            else
2276            {
2277                pt0=tg.Pixels.get(1);
2278                pt1=tg.Pixels.get(2);                
2279            }
2280            
2281            POINT2 ptExtend=null;
2282            int extend=-1;
2283            POINT2 midPt=lineutility.MidPointDouble(pt0, pt1,0);
2284            double slope=Math.abs(pt1.y-pt0.y)/(pt1.x-pt0.x);
2285            if(slope<=1)
2286            {
2287                ptExtend=lineutility.ExtendDirectedLine(pt0, pt1, midPt, lineutility.extend_above, 2);
2288                if(polygon.contains(ptExtend.x,ptExtend.y))
2289                    extend=lineutility.extend_below;
2290                else
2291                    extend=lineutility.extend_above;
2292            }
2293            else
2294            {
2295                ptExtend=lineutility.ExtendDirectedLine(pt0, pt1, midPt, lineutility.extend_left, 2);
2296                if(polygon.contains(ptExtend.x,ptExtend.y))
2297                    extend=lineutility.extend_right;
2298                else
2299                    extend=lineutility.extend_left;
2300                
2301            }
2302            POINT2 pt3=null;
2303            POINT2 pt4=null;
2304            pt3=lineutility.ExtendDirectedLine(pt0, pt1, pt0, extend, dist);
2305            pt4=lineutility.ExtendDirectedLine(pt0, pt1, pt1, extend, dist);
2306            line=new Line2D.Double(pt3.x, pt3.y, pt4.x, pt4.y);
2307        }
2308        catch (Exception exc) {            
2309            ErrorLogger.LogException(_className, "getExtendedLine",
2310                    new RendererException("Failed inside getExtendedLine", exc));
2311        }
2312        return line;
2313    }
2314
2315}//end clsUtility