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}