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}