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