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}