001/* 002 * VECCGeomReader -- A class that can read and write a VECC MK5 formatted geometry file. 003 * 004 * Copyright (C) 2009-2016, Joseph A. Huwaldt 005 * All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public License 018 * along with this program; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 020 * Or visit: http://www.gnu.org/licenses/lgpl.html 021 */ 022package geomss.geom.reader; 023 024import geomss.geom.*; 025import static geomss.geom.reader.AbstractGeomReader.RESOURCES; 026import java.io.*; 027import java.nio.charset.StandardCharsets; 028import java.text.MessageFormat; 029import java.util.Locale; 030import static java.util.Objects.isNull; 031import static java.util.Objects.nonNull; 032import static java.util.Objects.requireNonNull; 033import javax.measure.quantity.Length; 034import javax.measure.unit.NonSI; 035import javax.measure.unit.SI; 036import javax.measure.unit.Unit; 037import javolution.context.StackContext; 038import javolution.text.Text; 039import javolution.text.TypeFormat; 040import javolution.util.FastTable; 041 042/** 043 * A {@link GeomReader} for reading and writing vehicle geometry from/to a VECC (Viscous 044 * Effects on Complex Configurations) MK5 formatted geometry file. This class can also 045 * read the related VECC GEO file. 046 * 047 * <p> Modified by: Joseph A. Huwaldt </p> 048 * 049 * @author Joseph A. Huwaldt, Date: April 10, 2009 050 * @version September 9, 2016 051 */ 052public class VECCGeomReader extends AbstractGeomReader { 053 054 // Debug output flag. 055 private static final boolean DEBUG = false; 056 057 // A brief description of the data read by this reaader. 058 private static final String DESCRIPTION = RESOURCES.getString("veccDescription"); 059 060 // The preferred file extension for files of this reader's type. 061 public static final String EXTENSION = "mk5"; 062 063 // Units strings in Text format. 064 private static final Text IN_TEXT = Text.intern("in"); 065 private static final Text FT_TEXT = Text.intern("ft"); 066 private static final Text CM_TEXT = Text.intern("cm"); 067 private static final Text MM_TEXT = Text.intern("mm"); 068 private static final Text ME_TEXT = Text.intern("me"); 069 070 private boolean _isUnitAware = true; 071 072 /** 073 * Returns a string representation of the object. This will return a brief description 074 * of the format read by this reader. 075 * 076 * @return A brief description of the format read by this reader. 077 */ 078 @Override 079 public String toString() { 080 return DESCRIPTION; 081 } 082 083 /** 084 * Returns the preferred file extension (not including the ".") for files of this 085 * GeomReader's type. 086 * 087 * @return The preferred file extension for files of this readers type. 088 */ 089 @Override 090 public String getExtension() { 091 return EXTENSION; 092 } 093 094 /** 095 * Method that determines if this reader can read paneled geometry from the specified 096 * input file. 097 * 098 * @param inputFile The input file containing the geometry to be read in. 099 * @return GeomReader.NO if the file format is not recognized by this reader. 100 * GeomReader.YES if the file has the extension ".geo" or ".mk5". 101 * GeomReader.MAYBE if the file has the extension ".lib". 102 * @throws java.io.IOException If there is a problem reading from the specified 103 * file. 104 */ 105 @Override 106 public int canReadData(File inputFile) throws IOException { 107 108 int response = NO; 109 String name = inputFile.getName(); 110 name = name.toLowerCase().trim(); 111 if (name.endsWith(".geo")) { 112 response = YES; 113 _isUnitAware = false; 114 } else if (name.endsWith(".mk5")) { 115 response = YES; 116 _isUnitAware = true; 117 } else if (name.endsWith(".lib")) { 118 response = MAYBE; 119 _isUnitAware = false; 120 } 121 122 return response; 123 } 124 125 /** 126 * Returns true. This class can write PointVehicle data to a VECC Geometry file. 127 * 128 * @return this method always returns true. 129 */ 130 @Override 131 public boolean canWriteData() { 132 return true; 133 } 134 135 /** 136 * Reads in a VECC MK5 or GEO geometry file from the specified input file and returns 137 * a {@link PointVehicle} object that contains the geometry from the file. 138 * 139 * @param inputFile The input file containing the geometry to be read in. May not be 140 * null. 141 * @return A {@link PointVehicle} object containing the geometry read in from the 142 * file. If the file has no geometry in it, then this list will have no 143 * components in it (will have a size() of zero). 144 * @throws IOException If there is a problem reading the specified file. 145 */ 146 @Override 147 public PointVehicle read(File inputFile) throws IOException { 148 requireNonNull(inputFile); 149 _warnings.clear(); 150 151 // Create an empty vehicle with the provided name as the vehicle name. 152 PointVehicle vehicle = PointVehicle.newInstance(inputFile.getName()); 153 154 // VECC/GEO files are required to be in ASCII with U.S. style number formatting. 155 // Get the default locale and save it. Then change the default locale to US. 156 Locale defLocale = Locale.getDefault(); 157 Locale.setDefault(Locale.US); 158 159 // Create a reader to access the ASCII file. 160 try (LineNumberReader reader = new LineNumberReader(new FileReader(inputFile))) { 161 reader.mark(1024); 162 163 // Is this a MK5 file or a plain GEO file? 164 String aLine = readLine(reader); 165 if (aLine.contains("new mk IV")) 166 // We have a mk5 file. 167 read_habp(reader, vehicle); 168 169 else { 170 // We just have a plain GEO file, so read in the panels into a single component. 171 172 // Read in the panels (and any sections that the panels may contain). 173 reader.reset(); 174 PointComponent panelList = PointComponent.newInstance(); 175 read_panels(reader, panelList); 176 177 if (panelList.size() > 0) { 178 // Create the component that will go in the vehicle. 179 PointComponent component = PointComponent.newInstance(); 180 181 // Loop over all the panels in the panel list. 182 for (PointArray panel : panelList) { 183 // Add the panel to the component. 184 component.add(panel); 185 186 // Add any sections temporary stored in the panel's user data. 187 extractSections(panel, component); 188 } 189 190 // Add the component to the vehicle. 191 vehicle.add(component); 192 } 193 } 194 195 } finally { 196 // Restore the default locale. 197 Locale.setDefault(defLocale); 198 } 199 200 return vehicle; 201 } 202 203 /** 204 * Writes out a VECC MK5 geometry file for the geometry contained in the supplied 205 * {@link PointVehicle} object. 206 * 207 * @param outputFile The output File to which the geometry is to be written. May not 208 * be null. 209 * @param geometry The {@link PointVehicle} object to be written out. May not be 210 * null. 211 * @throws IOException If there is a problem writing to the specified file. 212 */ 213 @Override 214 public void write(File outputFile, GeometryList geometry) throws IOException { 215 requireNonNull(outputFile); 216 _warnings.clear(); 217 218 // Convert the input generic geometry list to a PointVehicle. 219 PointVehicle vehicle = geomList2PointVehicle(requireNonNull(geometry)); 220 if (!vehicle.containsGeometry()) { 221 _warnings.add(RESOURCES.getString("noGeometryWarning")); 222 return; 223 } 224 225 // Determine the unit code for the input geometry (and user's locale). 226 int unitCode = getUnitCode(vehicle); 227 228 // VECC/GEO files are required to be in ASCII with U.S. style number formatting. 229 // Get the default locale and save it. Then change the default locale to US. 230 Locale defLocale = Locale.getDefault(); 231 Locale.setDefault(Locale.US); 232 233 // Get a reference to the output stream writer. 234 StackContext.enter(); 235 try (PrintWriter writer = new PrintWriter(outputFile, StandardCharsets.US_ASCII.name())) { 236 237 // Write the executive control card. 238 writer.println("10 new mk IV 0 4"); 239 240 // Write the system control card. 241 writer.println("10000000000000000000"); 242 243 // Write the geometry control card. 244 writer.print(" 800000"); 245 writer.print(padSpaces(String.valueOf(vehicle.size()), 2)); 246 String name = vehicle.getName(); 247 if (isNull(name) || name.equals("")) 248 name = RESOURCES.getString("veccDefaultVehName"); 249 writer.print(padSpaces(name, 60)); 250 writer.println(unitCode); 251 252 // Combine all the arrays in all the components in this vehicle into one list of panels 253 // for writing out. 254 PointComponent panelList = PointComponent.newInstance(); 255 for (PointComponent comp : vehicle) { 256 if (comp.containsGeometry()) 257 panelList.addAll(comp); 258 } 259 260 // Write out the geometry. 261 writeGeometry(writer, panelList); 262 263 // Write the component names. 264 writeComponentNames(writer, vehicle, panelList); 265 266 // Write out the other misc. stuff. 267 writer.println(" 0 other run(s) follow"); 268 writer.println(" 0 quadstream inputs"); 269 writer.println(" 1 trim inputs"); 270 writer.println(" 0"); 271 writer.println(" 0"); 272 writer.println(" 1 Cg locations follow"); 273 writer.println(" -100.0000 0.0000 0.0000"); 274 275 } finally { 276 StackContext.exit(); 277 278 // Restore the default locale. 279 Locale.setDefault(defLocale); 280 } 281 282 } 283 284 /** 285 * Returns true if this reader is unit aware and false if it is not. VECC MK5 files 286 * are unit aware and this method returns true by default. However, the related GEO 287 * files do not encode the units that are being used (are not unit aware). You must 288 * call <code>setFileUnits</code> to set the units being used before reading from a 289 * file of GEO format. If "canReadData" is called with either a GEO or MK5 file before 290 * calling this method, then this method will return the correct value. 291 * 292 * @return true if this reader is unit aware. 293 * @see #setFileUnits(javax.measure.unit.Unit) 294 * @see #canReadData(java.io.File) 295 */ 296 @Override 297 public boolean isUnitAware() { 298 return _isUnitAware; 299 } 300 301 /** 302 * Read in geometry stored in a MK5 file (which has some header info and a lot of 303 * other stuff we are ignoring. 304 */ 305 private void read_habp(LineNumberReader in, PointVehicle vehicle) throws IOException { 306 try { 307 // Read system control card 308 String aLine = readLine(in); 309 Text text = Text.valueOf(aLine).trim(); 310 311 // Read in the geometry from the mk5 file. 312 for (int i = 0; i < 20; ++i) { 313 int ipg = TypeFormat.parseInt(text.subtext(i, i + 1)); 314 if (ipg == 1) { 315 read_habp_geom(in, vehicle); 316 break; 317 } 318 } 319 if (vehicle.size() == 0) 320 return; 321 322 // Extract the temporary list of panels that have been read in. 323 PointComponent panelList = vehicle.get(0); 324 325 // Find the list of components in the mk5 file. 326 aLine = readLine(in); 327 while (!aLine.startsWith("component names")) { 328 aLine = in.readLine(); 329 } 330 331 // Parse out the number of components. 332 int nc = TypeFormat.parseInt(aLine.substring(15, 19).trim()); 333 if (DEBUG) 334 System.out.println("Number of Components: nc = " + nc + ", numPanels = " + panelList.size()); 335 336 if (nc > 0) 337 vehicle.remove(0); // Remove the temporary list from the vehicle. 338 339 // Loop over each of the components. 340 for (int i = 0; i < nc; ++i) { 341 // Read in the next line. 342 aLine = readLine(in); 343 text = Text.valueOf(aLine); 344 345 // Parse out the number of panels. 346 int np = TypeFormat.parseInt(text.subtext(0, 2).trim()); 347 348 // Parse out the name of the component. 349 Text title = text.subtext(2).trim(); 350 351 // Create a new component. 352 PointComponent comp = PointComponent.newInstance(title.toString()); 353 354 if (DEBUG) 355 System.out.println("Component #" + (i + 1) + ": title = " + title + ", np = " + np); 356 357 // Add each panel from the panelList as needed. 358 aLine = readLine(in); 359 text = Text.valueOf(aLine); 360 for (int j = 0; j < np; ++j) { 361 int pos = j * 2; 362 Text numText = text.subtext(pos, pos + 2).trim(); 363 int ipanel = TypeFormat.parseInt(numText); 364 365 // Get the panel indicated. 366 PointArray panel = panelList.get(ipanel - 1); 367 368 // Add the panel to the component. 369 comp.add(panel); 370 371 if (DEBUG) 372 System.out.println(" ipanel = " + ipanel + ", name = " + panel.getName()); 373 374 // Does the panel have any sections? If so, store them 375 // in the component as panels. 376 extractSections(panel, comp); 377 } 378 379 // Add the component to the vehicle. 380 if (comp.size() > 0) 381 vehicle.add(comp); 382 } 383 384 } catch (NumberFormatException e) { 385 e.printStackTrace(); 386 throw new IOException(MessageFormat.format( 387 RESOURCES.getString("parseErrMsg"), DESCRIPTION, in.getLineNumber())); 388 } 389 } 390 391 /** 392 * Extracts any "sections" stored in the user data of the provided panel and adds them 393 * to the specified component as panels. 394 */ 395 private void extractSections(PointArray panel, PointComponent component) { 396 // Does the panel have any sections? 397 FastTable<PointArray> sectionList = (FastTable<PointArray>)panel.getUserData("Sections"); 398 if (nonNull(sectionList)) { 399 // Add all the sections to the component as panels. 400 for (PointArray section : sectionList) { 401 if (DEBUG) { 402 System.out.println(" section name = " + section.getName()); 403 } 404 component.add(section); 405 } 406 407 // Remove the sections from the user data of the main panel. 408 panel.removeUserData("Sections"); 409 FastTable.recycle(sectionList); 410 } 411 } 412 413 /** 414 * Read in geometry stored in a MK5 file (which has some header info and a lot of 415 * other stuff we are ignoring. 416 */ 417 private void read_habp_geom(LineNumberReader in, PointVehicle vehicle) 418 throws IOException, NumberFormatException { 419 // Read geometry control card 420 String aLine = readLine(in); 421 Text text = Text.valueOf(aLine); 422 423 // Parse out the configuration name. 424 //Text name = text.subtext(8, 8+61); 425 //vehicle.setName(name); 426 427 // Parse out the units. 428 int unitCode = TypeFormat.parseInt(text.subtext(9 + 60).trim()); 429 switch (unitCode) { 430 case 0: 431 setFileUnits(NonSI.INCH); 432 break; 433 case 1: 434 setFileUnits(NonSI.FOOT); 435 break; 436 case 2: 437 setFileUnits(SI.MILLIMETER); 438 break; 439 case 3: 440 setFileUnits(SI.CENTIMETER); 441 break; 442 case 4: 443 setFileUnits(SI.METER); 444 break; 445 } 446 447 // Now read in the panels into a temporary component which is a list of all the panels. 448 PointComponent component = PointComponent.newInstance(); 449 read_panels(in, component); 450 451 // Store the temporary component in the vehicle. 452 if (component.size() > 0) 453 vehicle.add(component); 454 } 455 456 /** 457 * Read in the panels stored in a GEO file (which may or may not have a header 458 * identifying the panel name, etc). 459 * 460 * @param in The reader used to read text from the file. 461 * @param component The component to add this panel to. 462 */ 463 private void read_panels(LineNumberReader in, PointComponent component) throws IOException { 464 int last = 0; 465 466 // Loop over all the panels 467 while (last == 0) { 468 int isymm = 0; 469 int itype = 3; 470 Text name = Text.EMPTY; 471 472 // Read in the 1st line. 473 in.mark(1024); 474 String aLine = readLine(in); 475 Text text = Text.valueOf(aLine); 476 477 // See if the first record is a panel header by attempting to 478 // parse grid points from it. 479 try { 480 TypeFormat.parseDouble(text.subtext(0, 10).trim()); 481 TypeFormat.parseDouble(text.subtext(10, 20).trim()); 482 TypeFormat.parseDouble(text.subtext(20, 30).trim()); 483 TypeFormat.parseInt(text.subtext(30, 31)); 484 in.reset(); 485 // If no error, there are not any header lines 486 487 } catch (NumberFormatException e) { 488 // Parse the panel header using defaults for any errors. 489 490 try { 491 // Read in the last flag. If it is != 0, this is the last panel. 492 last = TypeFormat.parseInt(text.subtext(4, 5)); 493 494 // Read in the symmetry flag. 495 isymm = TypeFormat.parseInt(text.subtext(16, 17)); 496 497 // Read in the panel type. 498 itype = TypeFormat.parseInt(text.subtext(26, 27)); 499 500 // Read in the name. 501 name = text.subtext(29).trim(); 502 } catch (NumberFormatException err) { /* ignore */ } 503 504 if (name.equals(Text.EMPTY)) 505 name = text.subtext(0, 5); 506 507 // Read in the 2nd part of the header. 508 aLine = readLine(in); 509 parse_units(Text.valueOf(aLine)); 510 } 511 512 if (DEBUG) { 513 System.out.println("name = " + name + ", last = " + last + ", isymm = " 514 + isymm + ", itype = " + itype); 515 System.out.println("units = " + getFileUnits()); 516 } 517 518 // Create a new PointArray object for this panel. 519 PointArray panel = PointArray.newInstance(name.toString()); 520 panel.putUserData("Symmetry", (isymm == 0 ? 1 : 0)); 521 panel.putUserData("HABPType", itype); 522 523 // Read in all the sections. 524 read_sections(in, panel); 525 526 // Add the panel to the component. 527 component.add(panel); 528 529 } // Next panel. 530 } 531 532 /** 533 * Parses the units from the 2nd line of the GEO header (if there are any). 534 */ 535 private void parse_units(Text aLine) { 536 Text text = aLine.subtext(2, 4).toLowerCase(); 537 538 if (text.equals(IN_TEXT)) 539 setFileUnits(NonSI.INCH); 540 else if (text.equals(FT_TEXT)) 541 setFileUnits(NonSI.FOOT); 542 else if (text.equals(CM_TEXT)) 543 setFileUnits(SI.CENTIMETER); 544 else if (text.equals(MM_TEXT)) 545 setFileUnits(SI.MILLIMETER); 546 else if (text.equals(ME_TEXT)) 547 setFileUnits(SI.METER); 548 } 549 550 /** 551 * Read in all the single sections for a single panel from a GEO file. 552 * 553 * @param in The reader used to read text from the file. 554 * @param panel The panel to add this section to. 555 * 556 */ 557 private void read_sections(LineNumberReader in, PointArray panel) throws IOException { 558 PointArray mainPanel = panel; 559 int sectionCount = 1; 560 561 // Create a new string. 562 PointString section = PointString.newInstance(); 563 564 try { 565 boolean first = true; 566 boolean moreSections = true; 567 readLoop: 568 while (moreSections) { 569 // Read in a line. 570 String aLine = readLine(in); 571 Text text = Text.valueOf(aLine); 572 573 // Try to read two points from the record. 574 for (int jj = 0; jj < 2; ++jj) { 575 int icol = jj * 31; 576 Text pointText = text.subtext(icol, icol + 31); 577 if (pointText.trim().equals(Text.EMPTY)) 578 continue readLoop; 579 580 // Parse out the coordinate values. 581 double xValue = TypeFormat.parseDouble(pointText.subtext(0, 10).trim()); 582 double yValue = TypeFormat.parseDouble(pointText.subtext(10, 20).trim()); 583 double zValue = TypeFormat.parseDouble(pointText.subtext(20, 30).trim()); 584 int iflag = TypeFormat.parseInt(pointText.subtext(30, 31)); 585 586 // Create a point. 587 Point point = Point.valueOf(xValue, yValue, zValue, getFileUnits()); 588 589 if (iflag == 1 || (!first && iflag == 2)) { 590 // We are done with this section, start a new one. 591 panel.add(section); 592 section = PointString.newInstance(); 593 } 594 595 if (!first && iflag == 2) { 596 // Create a new panel, based on the last panel, for the new sections. 597 // Store them in the main panel's user data for now. 598 PointArray newPanel = PointArray.newInstance( 599 mainPanel.getName() + Integer.toString(sectionCount++)); 600 Object ud = mainPanel.getUserData("Symmetry"); 601 if (nonNull(ud)) 602 newPanel.putUserData("Symmetry", ud); 603 ud = mainPanel.getUserData("HABPType"); 604 if (nonNull(ud)) 605 newPanel.putUserData("HABPType", ud); 606 FastTable<PointArray> sectionList 607 = (FastTable<PointArray>)mainPanel.getUserData("Sections"); 608 if (isNull(sectionList)) { 609 sectionList = FastTable.newInstance(); 610 mainPanel.putUserData("Sections", sectionList); 611 } 612 sectionList.add(newPanel); 613 panel = newPanel; 614 } 615 616 // Add this point to the section. 617 section.add(point); 618 619 if (iflag == 3) { 620 // If this is the end of the panel we are finished. 621 panel.add(section); 622 moreSections = false; 623 } 624 625 first = false; 626 } 627 628 } 629 } catch (NumberFormatException e) { 630 e.printStackTrace(); 631 throw new IOException(MessageFormat.format( 632 RESOURCES.getString("parseErrMsg"), DESCRIPTION, in.getLineNumber())); 633 } 634 } 635 636 /** 637 * Add spaces to the right of a string of text until that text equals the specified 638 * length. 639 * 640 */ 641 private String padSpaces(String input, int length) { 642 int inputLength = input.length(); 643 if (inputLength < length) { 644 StringBuilder buf = new StringBuilder(input); 645 for (int i = inputLength; i < length; ++i) { 646 buf.append(" "); 647 } 648 input = buf.toString(); 649 } 650 return input; 651 } 652 653 /** 654 * Returns the unit code for the input geometry. This method gets the unit for the 655 * minimum bounds of the geometry, then makes ure it is a unit that the VECC format 656 * recognizes (and changes it to one that it does recognize if necessary). Then the 657 * unit is stored using "setFileUnits". All input geometry must be converted to this 658 * unit for storage in the VECC file. 659 * 660 */ 661 private int getUnitCode(PointVehicle geometry) { 662 663 GeomPoint point = geometry.getBoundsMin(); 664 Unit<Length> unit = point.getUnit(); 665 666 int unitCode; 667 if (unit.equals(NonSI.INCH)) 668 unitCode = 0; 669 else if (unit.equals(NonSI.FOOT)) 670 unitCode = 1; 671 else if (unit.equals(SI.MILLIMETER)) 672 unitCode = 2; 673 else if (unit.equals(SI.CENTIMETER)) 674 unitCode = 3; 675 else if (unit.equals(SI.METER)) 676 unitCode = 4; 677 else { 678 // Convert to a locale specific option. 679 if (java.util.Locale.getDefault().equals(java.util.Locale.US)) { 680 unit = NonSI.INCH; 681 unitCode = 0; 682 683 } else { 684 unit = SI.METER; 685 unitCode = 4; 686 } 687 } 688 689 setFileUnits(unit); 690 691 return unitCode; 692 } 693 694 /** 695 * Writes out the HABP geometry to the file. 696 * 697 */ 698 private void writeGeometry(PrintWriter out, PointComponent panelList) throws IOException { 699 700 // Loop over all the panels. 701 int size = panelList.size(); 702 for (int i = 0; i < size; ++i) { 703 PointArray panel = panelList.get(i); 704 if (!panel.containsGeometry()) 705 continue; // Skip any empty panels. 706 707 // Write out this panel. 708 write_panel(out, panel, (i == size - 1), i + 1); 709 } 710 711 } 712 713 /** 714 * Writes out a single panel. 715 */ 716 private void write_panel(PrintWriter out, PointArray<? extends GeomPoint> panel, boolean last, int count) throws IOException { 717 718 // Write the panel header 719 720 // Write out the name. 721 String fullName = panel.getName(); 722 if (isNull(fullName)) { 723 // No name given, so use a panel count instead (must start with a letter though). 724 fullName = "P" + String.valueOf(count); 725 if (fullName.length() > 4) 726 fullName = "P" + fullName.substring(fullName.length() - 3); 727 } 728 String name = fullName; 729 if (name.length() > 4) 730 name = name.substring(0, 4); 731 else 732 name = padSpaces(name, 4); 733 out.print(name); 734 735 // Indicate if this is the last panel or not. 736 out.print((last ? 1 : 0)); 737 out.print("01 "); 738 739 // Write out the symmetry flag (0 == symmetric, 1 = non-symmetric). 740 Integer isymm = (Integer)panel.getUserData("Symmetry"); 741 if (isNull(isymm)) 742 out.print("0"); 743 else 744 out.print((isymm == 0 ? 1 : 0)); 745 out.print("0 "); 746 747 // Write out the panel type. 748 Integer itype = (Integer)panel.getUserData("HABPType"); 749 if (isNull(itype)) 750 out.print("3"); 751 else 752 out.print(itype.intValue()); 753 out.print(" "); 754 755 // Write out the name again (but longer this time). 756 if (fullName.length() > 16) 757 fullName = fullName.substring(0, 16); 758 else 759 fullName = padSpaces(fullName, 16); 760 out.println(fullName); 761 762 out.println(" 1 000"); 763 764 // Write out the array of data. 765 int icol = 1; 766 int iflag = 2; // flag indicating we are at the start of a panel. 767 int nCuts = panel.size() - 1; 768 int nPoints = panel.get(0).size() - 1; 769 770 // Loop over all the cuts (strings) in this panel. 771 for (int l = 0; l <= nCuts; ++l) { 772 PointString<?> cut = panel.get(l); 773 774 // Loop over all the points in this cut/string. 775 for (int m = 0; m <= nPoints; ++m) { 776 if (m == nPoints && l == nCuts) 777 iflag = 3; // flag indicating we are at the end of the panel. 778 779 // Get the point (in the right units). 780 GeomPoint point = cut.get(m).to(getFileUnits()); 781 double x = point.getValue(0); 782 double y = point.getValue(1); 783 double z = point.getValue(2); 784 785 // Write it out. 786 out.printf("%10.4f", x); 787 out.printf("%10.4f", y); 788 out.printf("%10.4f", z); 789 790 // Write the iflag. 791 out.print(iflag); 792 793 // Deal with the columns. 794 if (icol != 1) { 795 out.println(" 3"); 796 icol = 0; 797 } 798 ++icol; 799 800 // Change the iflag to indicate we are in the middle of a cut. 801 iflag = 0; 802 } 803 // Change the iflag to indicate we are starting a new cut. 804 iflag = 1; 805 } 806 807 // If there were an odd number of points, output the last line ending. 808 if (icol != 1) 809 out.println(" 3"); 810 811 } 812 813 /** 814 * Write out the names of all the components. 815 */ 816 private void writeComponentNames(PrintWriter out, PointVehicle geometry, PointComponent panelList) throws IOException { 817 818 // Collect only non-empty components. 819 PointVehicle geomV = PointVehicle.newInstance(geometry.getName()); 820 for (PointComponent comp : geometry) { 821 if (comp.containsGeometry()) 822 geomV.add(comp); 823 } 824 825 // Output header. 826 out.printf("component names %3d", geomV.size()); 827 out.println(); 828 829 for (PointComponent comp : geomV) { 830 831 // Get a 16 character title. 832 String title = comp.getName(); 833 if (isNull(title)) 834 title = String.valueOf(comp.getID()); 835 if (title.length() > 16) 836 title = title.substring(0, 16); 837 else 838 title = padSpaces(title, 16); 839 840 // Write out the number of panels in this component. 841 int size = comp.size(); 842 out.printf("%2d", size); 843 844 // Write out the name of the panel. 845 out.println(title); 846 847 // Write out the indices to each panel in this component. 848 for (PointArray array : comp) { 849 if (array.containsGeometry()) { 850 int index = panelList.indexOf(array); 851 out.printf("%2d", index + 1); 852 } 853 } 854 out.println(); 855 } 856 857 } 858}