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.SINGLEC:
128                case TacticalLines.DOUBLEC:
129                case TacticalLines.TRIPLE:
130                    lineType2 = lineType;
131                    break;
132                case TacticalLines.SPT:
133                    if (fromSegment == 0) {
134                        lineType2 = TacticalLines.CHANNEL_FLARED;
135                    } else {
136                        lineType2 = TacticalLines.CHANNEL;
137                    }
138                    break;
139                case TacticalLines.MAIN:
140                    if (fromSegment == 0) {
141                        lineType2 = TacticalLines.CHANNEL_FLARED;
142                    } else {
143                        lineType2 = TacticalLines.CHANNEL;
144                    }
145                    break;
146                case TacticalLines.CATK:
147                    lineType2 = TacticalLines.CHANNEL_DASHED;
148                    break;
149                case TacticalLines.CATKBYFIRE:
150                    lineType2 = TacticalLines.CHANNEL_DASHED;
151                    break;
152                default:
153                    lineType2 = TacticalLines.CHANNEL;
154                    break;
155            }
156            if (bolLastSegment == true) {
157                if (fromSegment != 0) {
158                    switch (lineType) {
159                        case TacticalLines.SPT:
160                            lineType2 = TacticalLines.SPT_STRAIGHT;
161                            break;
162                        case TacticalLines.MAIN:
163                            lineType2 = TacticalLines.MAIN_STRAIGHT;
164                            break;
165                        default:
166                            lineType2 = (int) lineType;
167                            break;
168                    }
169                } else {
170                    lineType2 = (int) lineType;
171                }
172            }
173
174            if (fromSegment < 0) {
175                return returnValue;
176            }
177            if (toSegment < 0) {
178                return returnValue;
179            }
180            if (toSegment < fromSegment) {
181                return returnValue;
182            }
183            int j;
184            int lineCount;
185            int numPoints;
186            int counter;
187            double[] goodUpperPixels;
188            double[] goodLowerPixels;
189            numPoints = toSegment - fromSegment + 2;
190            goodUpperPixels = new double[2 * numPoints];
191            goodLowerPixels = new double[2 * numPoints];
192
193            counter = 0;
194            for (j = fromSegment; j < toSegment + 2; j++) {
195                goodUpperPixels[counter] = pixels[2 * j];
196                goodUpperPixels[counter + 1] = pixels[2 * j + 1];
197                goodLowerPixels[counter] = pixels[2 * j];
198                goodLowerPixels[counter + 1] = pixels[2 * j + 1];
199                counter = counter + 2;
200            }
201
202            tg.set_LineType(lineType2);
203            lineCount = CELineArray.CGetLineCountDouble(tg, goodUpperPixels, numPoints, channelWidth);
204            channelPixels = new double[3 * lineCount];
205            POINT2 pt = null;
206            lineCount = Channels.GetChannel1Double(tg, goodUpperPixels, goodLowerPixels, channelPixels, numPoints, numPoints, channelWidth / 2, (int) distanceToChannelPoint, shapes);
207            tg.set_LineType(lineType);
208
209            //if shapes is null then it is not a CPOF client
210            if (shapes == null && channelPixels != null) {
211                //do not clear channelPoints first because the function gets successive calls
212                int n = channelPixels.length;
213                //for (j = 0; j < channelPixels.length / 3; j++) 
214                for (j = 0; j < n / 3; j++) {
215                    pt = new POINT2(channelPixels[3 * j], channelPixels[3 * j + 1], (int) channelPixels[3 * j + 2]);
216                    if (j == channelPixels.length / 3 - 1) {
217                        pt.style = 5;
218                    }
219                    channelPoints.add(pt);
220                }
221            }
222
223            if (lineCount > 0) {
224                //DrawChannelPixels2(lineCount, channelPixels, (int)lineType);
225                returnValue = channelPixels.length;
226            } else {
227                returnValue = 0;
228            }
229            //channelPixels[channelPixels.length - 1] = 5;
230            if (lineCount > 0) {
231                channelPixels[lineCount - 1] = 5;
232            }
233            //clean up
234            goodUpperPixels = null;
235            goodLowerPixels = null;
236        } catch (Exception exc) {
237            //clsUtility.WriteFile("error in clsChanneUtility.DrawGoodChannel2");
238            ErrorLogger.LogException(_className, "DrawGoodChannel2",
239                    new RendererException("Failed inside DrawGoodChannel2", exc));
240        }
241        return returnValue;
242    }
243
244    /**
245     * Draws the channel partitions to the shapes array
246     *
247     * @param pixels
248     * @param partitions
249     * @param channelWidth channel width in pixels
250     * @param shapes
251     * @param channelPoints
252     * @param distanceToChannelPoint distance in pixels from the tip to the back
253     * of the arrow
254     */
255    private static void DrawSegments(TGLight tg,
256            double[] pixels,
257            ArrayList<P1> partitions,
258            int channelWidth,
259            ArrayList<Shape2> shapes,
260            ArrayList<POINT2> channelPoints,
261            double distanceToChannelPoint) {
262        try {
263            int j = 0;
264            int n = 0;
265            int t = partitions.size();
266            //for (j = 0; j < partitions.size() - 1; j++) 
267            for (j = 0; j < t - 1; j++) {
268                n = DrawGoodChannel2(tg, partitions.get(j).start, partitions.get(j).end_Renamed, pixels, channelWidth, false, shapes, channelPoints, distanceToChannelPoint);
269
270            }
271            //draw the last partition using linetype
272            n = DrawGoodChannel2(tg, partitions.get(j).start, partitions.get(j).end_Renamed, pixels, channelWidth, true, shapes, channelPoints, distanceToChannelPoint);
273        } catch (Exception exc) {
274            //clsUtility.WriteFile("error in clsChanneUtility.DrawSegments");
275            ErrorLogger.LogException(_className, "DrawSegments",
276                    new RendererException("Failed inside DrawSegments", exc));
277        }
278    }
279
280    private static void DrawLCSingleLineSegments(TGLight tg,
281                                                 double[] pixels2,
282                                                 ArrayList<P1> singleLinePartitions,
283                                                 ArrayList<Shape2> shapes,
284                                                 Rectangle2D clipBounds,
285                                                 IPointConversion converter) {
286        try {
287            for (P1 flotPartition : singleLinePartitions) {
288                int vblSaveCounter = flotPartition.end_Renamed - flotPartition.start + 1;
289                ArrayList<POINT2> flotPixels = new ArrayList<>();
290                for (int i = 0; i < vblSaveCounter; i++)
291                    flotPixels.add(new POINT2(pixels2[2 * (i + flotPartition.start)], pixels2[2 * (i + flotPartition.start) + 1]));
292
293                String flotID = tg.get_SymbolId();
294                flotID = SymbolID.setAffiliation(flotID, SymbolID.StandardIdentity_Affiliation_Hostile_Faker);
295                flotID = SymbolID.setEntityCode(flotID, EntityCode.EntityCode_FLOT);
296                TGLight flotTG = new TGLight();
297                flotTG.set_LineType(TacticalLines.FLOT);
298                flotTG.set_Pixels(flotPixels);
299                flotTG.set_SymbolId(flotID);
300                flotTG.set_LineThickness(tg.get_LineThickness());
301
302                ArrayList<Shape2> flotShapes = clsRenderer2.GetLineArray(flotTG, converter, false, clipBounds);
303
304                if (flotShapes != null) {
305                    for (Shape2 shape : flotShapes)
306                        shape.setLineColor(Color.RED);
307                    shapes.addAll(flotShapes);
308                }
309            }
310        } catch (Exception exc) {
311            ErrorLogger.LogException(_className, "DrawLCFlotSegments",
312                    new RendererException("Failed inside DrawLCFlotSegments", exc));
313        }
314    }
315
316    /**
317     * Handle symbol too small for line of contact
318     * @param tg
319     * @param pixels
320     * @return 
321     */
322    private static ArrayList<POINT2> getLCPixels(TGLight tg,ArrayList<POINT2>pixels)
323    {
324        ArrayList<POINT2>pixels2=null;
325        try
326        {
327            if(tg.get_LineType()!=TacticalLines.LC)
328                return pixels;
329            POINT2[] pts=tg.Pixels.toArray(new POINT2[pixels.size()]);
330            POINT2 ul=new POINT2(),lr=new POINT2();
331            lineutility.CalcMBRPoints((POINT2[])pts, pts.length, ul, lr);
332            double flotDiameter = arraysupport.getScaledSize(21, tg.get_LineThickness(), tg.get_patternScale());
333            if(lr.x-ul.x>=flotDiameter)
334                return pixels;
335            else if (lr.y-ul.y>=flotDiameter)
336                return pixels;
337            //at this point the mbr is too small for a meaningful LC symbol
338            double x0=pts[0].x,y0=pts[0].y,x1=pts[1].x,y1=pts[1].y;
339            if (x0<=x1)            
340                x1=x0+flotDiameter;
341            else
342                x1=x0-flotDiameter;
343            y1=y0;
344            POINT2 pt0=new POINT2(x0,y0),pt1=new POINT2(x1,y1);
345            pixels2=new ArrayList();
346            pixels2.add(pt0);
347            pixels2.add(pt1);
348        }
349        catch (Exception exc) {
350            //clsUtility.WriteFile("error in clsChanneUtility.DrawSegments");
351               ErrorLogger.LogException(_className ,"getLCPixels",
352                    new RendererException("Failed inside getLCPixels", exc));
353        }    
354        return pixels2;
355    }
356
357    /**
358     * The main interface to clsChannelUtility calls DrawChannel2 after stuffing
359     * the points into an array of 2-tuples x,y
360     *
361     * @param pixels the client points
362     * @param linetype the line type
363     * @param tg the tactical graphic
364     * @param shapes
365     * @param channelPoints
366     */
367    public static void DrawChannel(ArrayList<POINT2> pixels,
368            int linetype,
369            TGLight tg,
370            ArrayList<Shape2> shapes,
371            ArrayList<POINT2> channelPoints,
372            Rectangle2D clipBounds,
373            IPointConversion converter) {
374        try {
375            pixels=getLCPixels(tg,pixels);
376            //we must do this because the rotary arrow tip now has to match the
377            //anchor point, i.e. the rotary feature can no longer stick out past the anchor point
378            //45 pixels shift here matches the 45 pixels shift for catkbyfire found in Channels.GetAXADDouble
379            lineutility.adjustCATKBYFIREControlPoint(linetype, pixels, 45);
380
381            int j = 0;
382            double[] pixels2 = new double[pixels.size() * 2];
383            int n = pixels.size();
384            //for (j = 0; j < pixels.size(); j++) 
385            for (j = 0; j < n; j++) {
386                pixels2[2 * j] = pixels.get(j).x;
387                pixels2[2 * j + 1] = pixels.get(j).y;
388            }
389            DrawChannel2(pixels2, linetype, tg, shapes, channelPoints, clipBounds, converter);
390        } catch (Exception exc) {
391            //clsUtility.WriteFile("error in clsChanneUtility.DrawSegments");
392            ErrorLogger.LogException(_className, "DrawChannel",
393                    new RendererException("Failed inside DrawChannel", exc));
394        }
395    }
396
397    /**
398     * utility for clsMETOC to handle double-backed segments
399     *
400     * @param tg the tactical graphic object
401     */
402    public static ArrayList<P1> GetPartitions2(TGLight tg) {
403        ArrayList partitions = null;
404        try {
405            double[] pixels = new double[tg.Pixels.size() * 2];
406            int n = tg.Pixels.size();
407            //for(int j=0;j<tg.Pixels.size();j++)
408            for (int j = 0; j < n; j++) {
409                pixels[2 * j] = tg.Pixels.get(j).x;
410                pixels[2 * j + 1] = tg.Pixels.get(j).y;
411            }
412
413            boolean[] segments = new boolean[pixels.length / 2 - 1];
414            if (segments.length == 0) {
415                return null;
416            }
417
418            double factor = arraysupport.getScaledSize(3, tg.get_LineThickness(), tg.get_patternScale());
419
420            clsUtility.GetSegments(pixels, segments, factor);
421            partitions = new ArrayList<P1>();
422            GetPartitions(segments, partitions);
423        } catch (Exception exc) {
424            ErrorLogger.LogException(_className, "GetPartitions2",
425                    new RendererException("Failed inside GetPartitions2", exc));
426        }
427        return partitions;
428    }
429
430    /**
431     * The main function for processing channel types. Gets segments, then gets
432     * partitions from the segments and then draws the partitions.
433     *
434     * @param pixels the client points as an array of 2-tuples x,y
435     * @param linetype the line type
436     * @param tg the tactical graphic object
437     * @param shapes
438     * @param channelPoints
439     */
440    private static void DrawChannel2(double[] pixels,
441            int linetype,
442            TGLight tg,
443            ArrayList<Shape2> shapes,
444            ArrayList<POINT2> channelPoints,
445            Rectangle2D clipBounds,
446            IPointConversion converter) {
447        try {
448            ref<double[]> distanceToChannelPoint = new ref();
449            int j = 0;
450            double[] pixels2 = null;
451            int channelWidth = 0;
452            ArrayList<P1> partitions = null;
453            int n = pixels.length;
454            int numPoints = 0;
455            //LC and others do not call clsUtility.ChannelWidth, but the
456            //value array still needs to be allocated or there is a
457            //null pointer exception in DrawGoodChannel2
458            distanceToChannelPoint.value = new double[1];
459            distanceToChannelPoint.value[0] = arraysupport.getScaledSize(20, tg.get_LineThickness(), tg.get_patternScale());
460
461            switch (linetype) {
462                case TacticalLines.MAIN:
463                case TacticalLines.CATK:
464                case TacticalLines.CATKBYFIRE:
465                case TacticalLines.AIRAOA:
466                case TacticalLines.AAAAA:
467                case TacticalLines.SPT:
468                    clsUtility.ReorderPixels(pixels);
469                    numPoints = pixels.length / 2;
470
471                    if (numPoints < 3) {
472                        return;
473                    }
474                    //moved this to be prior to stuffing pixels2
475                    channelWidth = clsUtility.ChannelWidth(pixels, distanceToChannelPoint) / 2;
476                    //ValidateChannelPixels2(ref pixels, ref channelWidth, ref distanceToChannelPoint);
477
478                    numPoints = pixels.length / 2;
479                    pixels2 = new double[pixels.length - 2];
480
481                    for (j = 0; j < numPoints; j++) {
482                        if (j < numPoints - 1) {
483                            pixels2[2 * j] = pixels[2 * j];
484                            pixels2[2 * j + 1] = pixels[2 * j + 1];
485                        }
486                    }
487                    break;
488                case TacticalLines.LC:
489                    channelWidth = (int) arraysupport.getScaledSize(40, tg.get_LineThickness(), tg.get_patternScale());// was 20;
490                    pixels2 = new double[pixels.length];
491                    n = pixels.length;
492                    //for (j = 0; j < pixels.length; j++) 
493                    for (j = 0; j < n; j++) {
494                        pixels2[j] = pixels[j];
495                    }
496                    break;
497                case TacticalLines.UNSP:
498                case TacticalLines.DFENCE:
499                case TacticalLines.SFENCE:
500                case TacticalLines.DOUBLEA:
501                case TacticalLines.LWFENCE:
502                case TacticalLines.HWFENCE:
503                case TacticalLines.SINGLEC:
504                case TacticalLines.DOUBLEC:
505                case TacticalLines.TRIPLE:
506                    tg.set_lineCap(BasicStroke.CAP_BUTT);
507                    channelWidth = (int) arraysupport.getScaledSize(30, tg.get_LineThickness(), tg.get_patternScale());
508                    if (Channels.getShiftLines()) {
509                        channelWidth = (int) arraysupport.getScaledSize(60, tg.get_LineThickness(), tg.get_patternScale());
510                    }
511                    pixels2 = new double[pixels.length];
512                    n = pixels.length;
513                    //for (j = 0; j < pixels.length; j++) 
514                    for (j = 0; j < n; j++) {
515                        pixels2[j] = pixels[j];
516                    }
517                    break;
518                default:
519
520                    break;
521            }
522
523            //we require new partitions because pixels are dirty
524            boolean[] segments = new boolean[pixels2.length / 2 - 1];
525            if (segments.length == 0) {
526                return;
527            }
528
529            // Line of contact looks bad with small channel corners extending out
530            if (linetype == TacticalLines.LC) {
531                partitions = new ArrayList<>();
532                ArrayList<P1> singleLinePartitions = new ArrayList<>();
533                clsUtility.GetLCPartitions(pixels2, arraysupport.getScaledSize(40, tg.get_LineThickness(), tg.get_patternScale()), partitions, singleLinePartitions);
534                DrawSegments(tg, pixels2, partitions, channelWidth, shapes, channelPoints, distanceToChannelPoint.value[0]);
535
536                if (singleLinePartitions.size() > 0) {
537                    // Render any small angles that only have side (not channel) as FLOT
538                    DrawLCSingleLineSegments(tg, pixels2, singleLinePartitions, shapes, clipBounds, converter);
539                }
540            } else {
541                double factor = 3;
542
543                clsUtility.GetSegments(pixels2, segments, factor);
544                partitions = new ArrayList<>();
545                GetPartitions(segments, partitions);
546
547                DrawSegments(tg, pixels2, partitions, channelWidth, shapes, channelPoints, distanceToChannelPoint.value[0]);
548            }
549        } catch (Exception exc) {
550            ErrorLogger.LogException(_className, "DrawChannel2",
551                    new RendererException("Failed inside DrawChannel2", exc));
552        }
553    }
554}