001/*
002 * To change this template, choose Tools | Templates
003 * and open the template in the editor.
004 */
005package armyc2.c5isr.JavaTacticalRenderer;
006
007import java.util.ArrayList;
008
009import armyc2.c5isr.JavaLineArray.arraysupport;
010import armyc2.c5isr.JavaLineArray.ref;
011import armyc2.c5isr.JavaLineArray.POINT2;
012import armyc2.c5isr.JavaLineArray.TacticalLines;
013import armyc2.c5isr.JavaLineArray.CELineArray;
014import armyc2.c5isr.JavaLineArray.Shape2;
015import armyc2.c5isr.RenderMultipoints.clsRenderer2;
016import armyc2.c5isr.graphics2d.BasicStroke;
017import armyc2.c5isr.graphics2d.Rectangle2D;
018import armyc2.c5isr.renderer.utilities.Color;
019import armyc2.c5isr.renderer.utilities.EntityCode;
020import armyc2.c5isr.renderer.utilities.ErrorLogger;
021import armyc2.c5isr.renderer.utilities.IPointConversion;
022import armyc2.c5isr.renderer.utilities.RendererException;
023import armyc2.c5isr.JavaLineArray.Channels;
024import armyc2.c5isr.JavaLineArray.lineutility;
025import armyc2.c5isr.renderer.utilities.SymbolID;
026
027/**
028 * A class to process channel types.
029 *
030*
031 */
032public final class clsChannelUtility {
033
034    private static final String _className = "clsChannelUtility";
035
036    /**
037     * Gets partitions from the client points based on the segments generated by
038     * GetSegments. Partitions are used handle double-backed segments. Each
039     * partition is a continuous sequence of points for a channel.
040     *
041     * @param segments
042     * @param partitions OUT the partitions
043     * @return
044     */
045    private static int GetPartitions(boolean[] segments,
046            ArrayList<P1> partitions) {
047        try {
048            int j = 0;
049            boolean nextSegment = false;
050            //worst case is every segment is a separate partition
051            //caller must deallocate partitions
052            P1 p1 = new P1();
053            //first segment will always be true,
054            //there are no bad one-segment channels
055            if (segments[0] == false) {
056                return 0;
057            }
058
059            if (partitions != null) {
060                partitions.clear();
061            } else {
062                return 0;
063            }
064
065            p1.start = 0;
066            //only add the partitions after p1.end has been set
067            int n = segments.length;
068            //for (j = 0; j < segments.length - 1; j++) 
069            for (j = 0; j < n - 1; j++) {
070                nextSegment = segments[j + 1];
071                if (nextSegment == false) {
072                    //the end of the current partition is the last good segment
073                    p1.end_Renamed = j;
074                    partitions.add(p1);
075                    //beginning of the next partition
076                    p1 = new P1();
077                    p1.start = j + 1;
078                }
079            }
080            p1.end_Renamed = j;
081            partitions.add(p1);
082        } catch (Exception exc) {
083            //System.out.println(e.getMessage());
084            //clsUtility.WriteFile("error in clsChanneUtility.GetPartitions");
085            ErrorLogger.LogException(_className, "GetPartitions",
086                    new RendererException("Failed inside GetPartitions", exc));
087        }
088        return partitions.size();
089    }
090
091    /**
092     * Draws a partition to the shapes array and stores the calculated channel
093     * points
094     *
095     * @param fromSegment
096     * @param toSegment
097     * @param pixels
098     * @param channelWidth
099     * @param bolLastSegment
100     * @param shapes
101     * @param channelPoints
102     * @param distanceToChannelPoint
103     * @return
104     */
105    private static int DrawGoodChannel2(TGLight tg,
106            int fromSegment,
107            int toSegment,
108            double[] pixels,
109            int channelWidth,
110            boolean bolLastSegment,
111            ArrayList<Shape2> shapes,
112            ArrayList<POINT2> channelPoints,
113            double distanceToChannelPoint) {
114        int returnValue = 0;    // Had to initialize to something
115        try {
116            int lineType = tg.get_LineType();
117            int lineType2;
118            double[] channelPixels = null;
119            switch (lineType) {
120                case TacticalLines.LC:
121                case TacticalLines.UNSP:
122                case TacticalLines.DFENCE:
123                case TacticalLines.SFENCE:
124                case TacticalLines.DOUBLEA:
125                case TacticalLines.LWFENCE:
126                case TacticalLines.HWFENCE:
127                case TacticalLines.BBS_LINE:
128                case TacticalLines.SINGLEC:
129                case TacticalLines.DOUBLEC:
130                case TacticalLines.TRIPLE:
131                    lineType2 = lineType;
132                    break;
133                case TacticalLines.SPT:
134                case TacticalLines.FRONTAL_ATTACK:
135                case TacticalLines.TURNING_MOVEMENT:
136                case TacticalLines.MOVEMENT_TO_CONTACT:
137                    if (fromSegment == 0) {
138                        lineType2 = TacticalLines.CHANNEL_FLARED;
139                    } else {
140                        lineType2 = TacticalLines.CHANNEL;
141                    }
142                    break;
143                case TacticalLines.MAIN:
144                    if (fromSegment == 0) {
145                        lineType2 = TacticalLines.CHANNEL_FLARED;
146                    } else {
147                        lineType2 = TacticalLines.CHANNEL;
148                    }
149                    break;
150                case TacticalLines.CATK:
151                    lineType2 = TacticalLines.CHANNEL_DASHED;
152                    break;
153                case TacticalLines.CATKBYFIRE:
154                    lineType2 = TacticalLines.CHANNEL_DASHED;
155                    break;
156                default:
157                    lineType2 = TacticalLines.CHANNEL;
158                    break;
159            }
160            if (bolLastSegment == true) {
161                if (fromSegment != 0) {
162                    switch (lineType) {
163                        case TacticalLines.SPT:
164                        case TacticalLines.FRONTAL_ATTACK:
165                        case TacticalLines.TURNING_MOVEMENT:
166                        case TacticalLines.MOVEMENT_TO_CONTACT:
167                            lineType2 = TacticalLines.SPT_STRAIGHT;
168                            break;
169                        case TacticalLines.MAIN:
170                            lineType2 = TacticalLines.MAIN_STRAIGHT;
171                            break;
172                        default:
173                            lineType2 = (int) lineType;
174                            break;
175                    }
176                } else {
177                    lineType2 = (int) lineType;
178                }
179            }
180
181            if (fromSegment < 0) {
182                return returnValue;
183            }
184            if (toSegment < 0) {
185                return returnValue;
186            }
187            if (toSegment < fromSegment) {
188                return returnValue;
189            }
190            int j;
191            int lineCount;
192            int numPoints;
193            int counter;
194            double[] goodUpperPixels;
195            double[] goodLowerPixels;
196            numPoints = toSegment - fromSegment + 2;
197            goodUpperPixels = new double[2 * numPoints];
198            goodLowerPixels = new double[2 * numPoints];
199
200            counter = 0;
201            for (j = fromSegment; j < toSegment + 2; j++) {
202                goodUpperPixels[counter] = pixels[2 * j];
203                goodUpperPixels[counter + 1] = pixels[2 * j + 1];
204                goodLowerPixels[counter] = pixels[2 * j];
205                goodLowerPixels[counter + 1] = pixels[2 * j + 1];
206                counter = counter + 2;
207            }
208
209            tg.set_LineType(lineType2);
210            lineCount = CELineArray.CGetLineCountDouble(tg, goodUpperPixels, numPoints, channelWidth);
211            channelPixels = new double[3 * lineCount];
212            POINT2 pt = null;
213            lineCount = Channels.GetChannel1Double(tg, goodUpperPixels, goodLowerPixels, channelPixels, numPoints, numPoints, channelWidth / 2, (int) distanceToChannelPoint, shapes);
214            tg.set_LineType(lineType);
215
216            //if shapes is null then it is not a CPOF client
217            if (shapes == null && channelPixels != null) {
218                //do not clear channelPoints first because the function gets successive calls
219                int n = channelPixels.length;
220                //for (j = 0; j < channelPixels.length / 3; j++) 
221                for (j = 0; j < n / 3; j++) {
222                    pt = new POINT2(channelPixels[3 * j], channelPixels[3 * j + 1], (int) channelPixels[3 * j + 2]);
223                    if (j == channelPixels.length / 3 - 1) {
224                        pt.style = 5;
225                    }
226                    channelPoints.add(pt);
227                }
228            }
229
230            if (lineCount > 0) {
231                //DrawChannelPixels2(lineCount, channelPixels, (int)lineType);
232                returnValue = channelPixels.length;
233            } else {
234                returnValue = 0;
235            }
236            //channelPixels[channelPixels.length - 1] = 5;
237            if (lineCount > 0) {
238                channelPixels[lineCount - 1] = 5;
239            }
240            //clean up
241            goodUpperPixels = null;
242            goodLowerPixels = null;
243        } catch (Exception exc) {
244            //clsUtility.WriteFile("error in clsChanneUtility.DrawGoodChannel2");
245            ErrorLogger.LogException(_className, "DrawGoodChannel2",
246                    new RendererException("Failed inside DrawGoodChannel2", exc));
247        }
248        return returnValue;
249    }
250
251    /**
252     * Draws the channel partitions to the shapes array
253     *
254     * @param pixels
255     * @param partitions
256     * @param channelWidth channel width in pixels
257     * @param shapes
258     * @param channelPoints
259     * @param distanceToChannelPoint distance in pixels from the tip to the back
260     * of the arrow
261     */
262    private static void DrawSegments(TGLight tg,
263            double[] pixels,
264            ArrayList<P1> partitions,
265            int channelWidth,
266            ArrayList<Shape2> shapes,
267            ArrayList<POINT2> channelPoints,
268            double distanceToChannelPoint) {
269        try {
270            int j = 0;
271            int n = 0;
272            int t = partitions.size();
273            //for (j = 0; j < partitions.size() - 1; j++) 
274            for (j = 0; j < t - 1; j++) {
275                n = DrawGoodChannel2(tg, partitions.get(j).start, partitions.get(j).end_Renamed, pixels, channelWidth, false, shapes, channelPoints, distanceToChannelPoint);
276
277            }
278            //draw the last partition using linetype
279            n = DrawGoodChannel2(tg, partitions.get(j).start, partitions.get(j).end_Renamed, pixels, channelWidth, true, shapes, channelPoints, distanceToChannelPoint);
280        } catch (Exception exc) {
281            //clsUtility.WriteFile("error in clsChanneUtility.DrawSegments");
282            ErrorLogger.LogException(_className, "DrawSegments",
283                    new RendererException("Failed inside DrawSegments", exc));
284        }
285    }
286
287    private static void DrawLCSingleLineSegments(TGLight tg,
288                                                 double[] pixels2,
289                                                 ArrayList<P1> singleLinePartitions,
290                                                 ArrayList<Shape2> shapes,
291                                                 Rectangle2D clipBounds,
292                                                 IPointConversion converter) {
293        try {
294            for (P1 flotPartition : singleLinePartitions) {
295                int vblSaveCounter = flotPartition.end_Renamed - flotPartition.start + 1;
296                ArrayList<POINT2> flotPixels = new ArrayList<>();
297                for (int i = 0; i < vblSaveCounter; i++)
298                    flotPixels.add(new POINT2(pixels2[2 * (i + flotPartition.start)], pixels2[2 * (i + flotPartition.start) + 1]));
299
300                String flotID = tg.get_SymbolId();
301                flotID = SymbolID.setAffiliation(flotID, SymbolID.StandardIdentity_Affiliation_Hostile_Faker);
302                flotID = SymbolID.setEntityCode(flotID, EntityCode.EntityCode_FLOT);
303                TGLight flotTG = new TGLight();
304                flotTG.set_LineType(TacticalLines.FLOT);
305                flotTG.set_Pixels(flotPixels);
306                flotTG.set_SymbolId(flotID);
307                flotTG.set_LineThickness(tg.get_LineThickness());
308
309                ArrayList<Shape2> flotShapes = clsRenderer2.GetLineArray(flotTG, converter, false, clipBounds);
310
311                if (flotShapes != null) {
312                    for (Shape2 shape : flotShapes)
313                        shape.setLineColor(Color.RED);
314                    shapes.addAll(flotShapes);
315                }
316            }
317        } catch (Exception exc) {
318            ErrorLogger.LogException(_className, "DrawLCFlotSegments",
319                    new RendererException("Failed inside DrawLCFlotSegments", exc));
320        }
321    }
322
323    /**
324     * Handle symbol too small for line of contact
325     * @param tg
326     * @param pixels
327     * @return 
328     */
329    private static ArrayList<POINT2> getLCPixels(TGLight tg,ArrayList<POINT2>pixels)
330    {
331        ArrayList<POINT2>pixels2=null;
332        try
333        {
334            if(tg.get_LineType()!=TacticalLines.LC)
335                return pixels;
336            POINT2[] pts=tg.Pixels.toArray(new POINT2[pixels.size()]);
337            POINT2 ul=new POINT2(),lr=new POINT2();
338            lineutility.CalcMBRPoints((POINT2[])pts, pts.length, ul, lr);
339            double flotDiameter = arraysupport.getScaledSize(21, tg.get_LineThickness(), tg.get_patternScale());
340            if(lr.x-ul.x>=flotDiameter)
341                return pixels;
342            else if (lr.y-ul.y>=flotDiameter)
343                return pixels;
344            //at this point the mbr is too small for a meaningful LC symbol
345            double x0=pts[0].x,y0=pts[0].y,x1=pts[1].x,y1=pts[1].y;
346            if (x0<=x1)            
347                x1=x0+flotDiameter;
348            else
349                x1=x0-flotDiameter;
350            y1=y0;
351            POINT2 pt0=new POINT2(x0,y0),pt1=new POINT2(x1,y1);
352            pixels2=new ArrayList();
353            pixels2.add(pt0);
354            pixels2.add(pt1);
355        }
356        catch (Exception exc) {
357            //clsUtility.WriteFile("error in clsChanneUtility.DrawSegments");
358               ErrorLogger.LogException(_className ,"getLCPixels",
359                    new RendererException("Failed inside getLCPixels", exc));
360        }    
361        return pixels2;
362    }
363
364    /**
365     * The main interface to clsChannelUtility calls DrawChannel2 after stuffing
366     * the points into an array of 2-tuples x,y
367     *
368     * @param pixels the client points
369     * @param linetype the line type
370     * @param tg the tactical graphic
371     * @param shapes
372     * @param channelPoints
373     */
374    public static void DrawChannel(ArrayList<POINT2> pixels,
375            int linetype,
376            TGLight tg,
377            ArrayList<Shape2> shapes,
378            ArrayList<POINT2> channelPoints,
379            Rectangle2D clipBounds,
380            IPointConversion converter) {
381        try {
382            pixels=getLCPixels(tg,pixels);
383            //we must do this because the rotary arrow tip now has to match the
384            //anchor point, i.e. the rotary feature can no longer stick out past the anchor point
385            //45 pixels shift here matches the 45 pixels shift for catkbyfire found in Channels.GetAXADDouble
386            lineutility.adjustCATKBYFIREControlPoint(linetype, pixels, 45);
387
388            int j = 0;
389            double[] pixels2 = new double[pixels.size() * 2];
390            int n = pixels.size();
391            //for (j = 0; j < pixels.size(); j++) 
392            for (j = 0; j < n; j++) {
393                pixels2[2 * j] = pixels.get(j).x;
394                pixels2[2 * j + 1] = pixels.get(j).y;
395            }
396            DrawChannel2(pixels2, linetype, tg, shapes, channelPoints, clipBounds, converter);
397        } catch (Exception exc) {
398            //clsUtility.WriteFile("error in clsChanneUtility.DrawSegments");
399            ErrorLogger.LogException(_className, "DrawChannel",
400                    new RendererException("Failed inside DrawChannel", exc));
401        }
402    }
403
404    /**
405     * utility for clsMETOC to handle double-backed segments
406     *
407     * @param tg the tactical graphic object
408     */
409    public static ArrayList<P1> GetPartitions2(TGLight tg) {
410        ArrayList partitions = null;
411        try {
412            double[] pixels = new double[tg.Pixels.size() * 2];
413            int n = tg.Pixels.size();
414            //for(int j=0;j<tg.Pixels.size();j++)
415            for (int j = 0; j < n; j++) {
416                pixels[2 * j] = tg.Pixels.get(j).x;
417                pixels[2 * j + 1] = tg.Pixels.get(j).y;
418            }
419
420            boolean[] segments = new boolean[pixels.length / 2 - 1];
421            if (segments.length == 0) {
422                return null;
423            }
424
425            double factor = arraysupport.getScaledSize(3, tg.get_LineThickness(), tg.get_patternScale());
426
427            clsUtility.GetSegments(pixels, segments, factor);
428            partitions = new ArrayList<P1>();
429            GetPartitions(segments, partitions);
430        } catch (Exception exc) {
431            ErrorLogger.LogException(_className, "GetPartitions2",
432                    new RendererException("Failed inside GetPartitions2", exc));
433        }
434        return partitions;
435    }
436
437    /**
438     * The main function for processing channel types. Gets segments, then gets
439     * partitions from the segments and then draws the partitions.
440     *
441     * @param pixels the client points as an array of 2-tuples x,y
442     * @param linetype the line type
443     * @param tg the tactical graphic object
444     * @param shapes
445     * @param channelPoints
446     */
447    private static void DrawChannel2(double[] pixels,
448            int linetype,
449            TGLight tg,
450            ArrayList<Shape2> shapes,
451            ArrayList<POINT2> channelPoints,
452            Rectangle2D clipBounds,
453            IPointConversion converter) {
454        try {
455            ref<double[]> distanceToChannelPoint = new ref();
456            int j = 0;
457            double[] pixels2 = null;
458            int channelWidth = 0;
459            ArrayList<P1> partitions = null;
460            int n = pixels.length;
461            int numPoints = 0;
462            //LC and others do not call clsUtility.ChannelWidth, but the
463            //value array still needs to be allocated or there is a
464            //null pointer exception in DrawGoodChannel2
465            distanceToChannelPoint.value = new double[1];
466            distanceToChannelPoint.value[0] = arraysupport.getScaledSize(20, tg.get_LineThickness(), tg.get_patternScale());
467
468            switch (linetype) {
469                case TacticalLines.MAIN:
470                case TacticalLines.CATK:
471                case TacticalLines.CATKBYFIRE:
472                case TacticalLines.AIRAOA:
473                case TacticalLines.AAAAA:
474                case TacticalLines.SPT:
475                case TacticalLines.FRONTAL_ATTACK:
476                case TacticalLines.TURNING_MOVEMENT:
477                case TacticalLines.MOVEMENT_TO_CONTACT:
478                    clsUtility.ReorderPixels(pixels);
479                    numPoints = pixels.length / 2;
480
481                    if (numPoints < 3) {
482                        return;
483                    }
484                    //moved this to be prior to stuffing pixels2
485                    channelWidth = clsUtility.ChannelWidth(pixels, distanceToChannelPoint) / 2;
486                    //ValidateChannelPixels2(ref pixels, ref channelWidth, ref distanceToChannelPoint);
487
488                    numPoints = pixels.length / 2;
489                    pixels2 = new double[pixels.length - 2];
490
491                    for (j = 0; j < numPoints; j++) {
492                        if (j < numPoints - 1) {
493                            pixels2[2 * j] = pixels[2 * j];
494                            pixels2[2 * j + 1] = pixels[2 * j + 1];
495                        }
496                    }
497                    break;
498                case TacticalLines.LC:
499                    channelWidth = (int) arraysupport.getScaledSize(40, tg.get_LineThickness(), tg.get_patternScale());// was 20;
500                    pixels2 = new double[pixels.length];
501                    n = pixels.length;
502                    //for (j = 0; j < pixels.length; j++) 
503                    for (j = 0; j < n; j++) {
504                        pixels2[j] = pixels[j];
505                    }
506                    break;
507                case TacticalLines.UNSP:
508                case TacticalLines.DFENCE:
509                case TacticalLines.SFENCE:
510                case TacticalLines.DOUBLEA:
511                case TacticalLines.LWFENCE:
512                case TacticalLines.HWFENCE:
513                case TacticalLines.SINGLEC:
514                case TacticalLines.DOUBLEC:
515                case TacticalLines.TRIPLE:
516                    tg.set_lineCap(BasicStroke.CAP_BUTT);
517                    channelWidth = (int) arraysupport.getScaledSize(30, tg.get_LineThickness(), tg.get_patternScale());
518                    if (Channels.getShiftLines()) {
519                        channelWidth = (int) arraysupport.getScaledSize(60, tg.get_LineThickness(), tg.get_patternScale());
520                    }
521                    pixels2 = new double[pixels.length];
522                    n = pixels.length;
523                    //for (j = 0; j < pixels.length; j++) 
524                    for (j = 0; j < n; j++) {
525                        pixels2[j] = pixels[j];
526                    }
527                    break;
528                case TacticalLines.BBS_LINE:
529                    channelWidth = 8 * tg.Pixels.get(0).style;  //was 20 1-10-13
530                    pixels2 = new double[pixels.length];
531                    n = pixels.length;
532                    //for (j = 0; j < pixels.length; j++)
533                    for (j = 0; j < n; j++) {
534                        pixels2[j] = pixels[j];
535                    }
536                    break;
537                default:
538
539                    break;
540            }
541
542            //we require new partitions because pixels are dirty
543            boolean[] segments = new boolean[pixels2.length / 2 - 1];
544            if (segments.length == 0) {
545                return;
546            }
547
548            // Line of contact looks bad with small channel corners extending out
549            if (linetype == TacticalLines.LC) {
550                partitions = new ArrayList<>();
551                ArrayList<P1> singleLinePartitions = new ArrayList<>();
552                clsUtility.GetLCPartitions(pixels2, arraysupport.getScaledSize(40, tg.get_LineThickness(), tg.get_patternScale()), partitions, singleLinePartitions);
553                DrawSegments(tg, pixels2, partitions, channelWidth, shapes, channelPoints, distanceToChannelPoint.value[0]);
554
555                if (singleLinePartitions.size() > 0) {
556                    // Render any small angles that only have side (not channel) as FLOT
557                    DrawLCSingleLineSegments(tg, pixels2, singleLinePartitions, shapes, clipBounds, converter);
558                }
559            } else {
560                double factor = 3;
561
562                clsUtility.GetSegments(pixels2, segments, factor);
563                partitions = new ArrayList<>();
564                GetPartitions(segments, partitions);
565
566                DrawSegments(tg, pixels2, partitions, channelWidth, shapes, channelPoints, distanceToChannelPoint.value[0]);
567            }
568        } catch (Exception exc) {
569            ErrorLogger.LogException(_className, "DrawChannel2",
570                    new RendererException("Failed inside DrawChannel2", exc));
571        }
572    }
573}