001/*
002 *   Constants  -- Global variables and methods used for reading an IGES file.
003 *
004 *   Copyright (C) 2010-2025, Joseph A. Huwaldt. All rights reserved.
005 *   
006 *   This library is free software; you can redistribute it and/or
007 *   modify it under the terms of the GNU Lesser General Public
008 *   License as published by the Free Software Foundation; either
009 *   version 2.1 of the License, or (at your option) any later version.
010 *   
011 *   This library is distributed in the hope that it will be useful,
012 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *   Lesser General Public License for more details.
015 *
016 *   You should have received a copy of the GNU Lesser General Public License
017 *   along with this program; if not, write to the Free Software
018 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
019 *   Or visit:  http://www.gnu.org/licenses/lgpl.html
020 *
021 *   Based on, but heavily modified from, IGESView ( http://ts.nist.gov/Standards/IGES/igesTools.cfm )
022 */
023package geomss.geom.reader.iges;
024
025import java.awt.Color;
026import java.io.IOException;
027import java.io.PrintWriter;
028import java.io.RandomAccessFile;
029import java.util.Locale;
030import java.util.ResourceBundle;
031import javax.measure.quantity.Length;
032import javax.measure.unit.SI;
033import javax.measure.unit.Unit;
034
035/**
036 * The Constants class is a holding area for any "global" variables that are needed for
037 * multiple classes. Also included are several utility methods that may be used any place
038 * that needs them. It is hoped that there will be no hard-coded values anywhere.
039 *
040 * <p> Modified by: Joseph A. Huwaldt </p>
041 *
042 * @author JDN, Version 1.0
043 * @version February 22, 2025
044 */
045public class Constants {
046
047    /**
048     * Sets debug mode.
049     */
050    public static final boolean DEBUG = false;
051
052    /**
053     * The String resources used by this package.
054     */
055    public static final ResourceBundle RESOURCES
056            = ResourceBundle.getBundle("geomss.geom.reader.iges.IGESResources", java.util.Locale.getDefault());
057
058    /**
059     * The IGES ASCII file format must always be written using the US locale.
060     */
061    public static final Locale US = Locale.US;
062
063    /**
064     * Column in IGES file with section identifier ('S', 'G', etc)
065     */
066    public static final int SECTID_COL = 72;
067
068    /**
069     * Column in IGES file Parameter Data delimiting free-form data with DENum of the
070     * entity. According to the IGES specification, this must be a space (' ').
071     */
072    public static final int PDID_COL = 64;
073
074    /**
075     * String type identifier. Used when reading in parameters from file.
076     */
077    public static final int TYPE_STRING = 0;
078
079    /**
080     * Integer type identifier. Used when reading in parameters from file.
081     */
082    public static final int TYPE_INT = 1;
083
084    /**
085     * Real type identifier. Used when reading in parameters from file.
086     */
087    public static final int TYPE_FLOAT = 2;
088
089    /**
090     * Character type identifier. Used when reading in parameters from file.
091     */
092    public static final int TYPE_CHAR = 3;
093
094    /**
095     * Globalized terminate character. Done this way because Part.getGlobal().getTerm()
096     * was too long to type over and over.
097     */
098    public static char Term;
099
100    /**
101     * Globalized delimiter character. Done this way because Part.getGlobal().getDelim()
102     * was too long to type over and over.
103     */
104    public static char Delim;
105
106    /**
107     * Globalized grain value. Done this way because Part.getGlobal().getGrain() was too
108     * long to type over and over.
109     */
110    public static double Grain;
111
112    /**
113     * Globalized length units for the file. Done this way because
114     * Part.getGlobal().getUnit() was too long to type over and over.
115     */
116    public static Unit<Length> unit = SI.METER;
117
118    /**
119     * X-Axis
120     */
121    public static final int X_AXIS = 0;
122
123    /**
124     * Y-Axis
125     */
126    public static final int Y_AXIS = 1;
127
128    /**
129     * Z-Axis
130     */
131    public static final int Z_AXIS = 2;
132
133    /**
134     * Standard IGES Colors (Index 0, "undefined" is set to black).
135     */
136    public static final Color IGESColor[] = new Color[9];
137
138    /**
139     * Initialize IGES color array. Sets up array with standard IGES colors. Index 0,
140     * "undefined", will be displayed as black.
141     */
142    static {
143        IGESColor[0] = Color.BLACK;
144        IGESColor[1] = Color.BLACK;
145        IGESColor[2] = Color.RED;
146        IGESColor[3] = Color.GREEN;
147        IGESColor[4] = Color.BLUE;
148        IGESColor[5] = Color.YELLOW;
149        IGESColor[6] = Color.MAGENTA;
150        IGESColor[7] = Color.CYAN;
151        IGESColor[8] = Color.WHITE;
152    }
153
154    /**
155     * Set up globals from GlobalSection
156     *
157     * @param g Global Section object
158     */
159    public static void initGlobals(GlobalSection g) {
160        Term = g.getTerm();
161        Delim = g.getDelim();
162        Grain = g.getGrain();
163        unit = g.getUnit();
164    }
165
166    /**
167     * Reads in a line of text, 80 characters, ignoring any EOL chars
168     *
169     * @param in input file
170     * @return the single text line of input
171     * @exception IOException if end of file is reached, or other generic file I/O error.
172     */
173    public static String myReadLine(RandomAccessFile in) throws IOException {
174        char[] line = new char[80];
175        int i;
176
177        do {
178            for (i = 0; i < 80; i++) {
179                char ch = (char)in.readByte();
180
181                if ((ch == '\r') || (ch == '\n'))
182                    break;
183
184                line[i] = ch;
185            }
186        } while (i == 0);
187
188        return new String(line);
189    }
190
191    /**
192     * Converts the string to an integer. This method can handle "integers" that are
193     * technically real, such as 1.000D+001. A zero length string results in the value
194     * 0 being returned.
195     *
196     * @param s the string to be parsed. Assumed to be trimmed of whitespace.
197     * @return resulting integer
198     */
199    public static int toInt(String s) {
200        if (s.length() == 0)
201            return 0;
202
203        if ((s.indexOf('E') < 0) && (s.indexOf('e') < 0) && (s.indexOf('D') < 0) && (s.indexOf('d') < 0)
204                && s.indexOf('.') < 0) {
205            return Integer.parseInt(s);
206
207        } else {
208            s = s.replace('d', 'e');
209            s = s.replace('D', 'e');
210            return (Double.valueOf(s)).intValue();
211        }
212    }
213
214    /**
215     * Converts the string to an double. This method can handle IGES floats of single
216     * ("1.00e+010") or double ("1.00d+010") precision.  A zero length string results
217     * in the value 0.0 being returned.
218     *
219     * @param s the string to be parsed. Assumed to be trimmed of whitespace.
220     * @return resulting double value
221     */
222    public static double toDouble(String s) {
223        if (s.length() == 0)
224            return 0.0;
225
226        s = s.replace('d', 'e');
227        s = s.replace('D', 'e');
228
229        return Double.valueOf(s);
230    }
231
232    /**
233     * 2D distance. Distance between two points in a plane.
234     *
235     * @param x1 abscissa of first point
236     * @param y1 ordinate of first point
237     * @param x2 abscissa of 2nd point
238     * @param y2 ordinate of second point
239     * @return 2D distance between the points
240     */
241    public static double dist(double x1, double y1, double x2, double y2) {
242        double dx = x2 - x1;
243        double dy = y2 - y1;
244        return Math.sqrt(dx * dx + dy * dy);
245    }
246
247    /**
248     * 3D distance. Distance between two points in space.
249     *
250     * @param x1 X-coordinate of
251     * @param y1 Y-coordinate of
252     * @param z1 Z-coordinate of first point
253     * @param x2 X-coordinate of second point
254     * @param y2 Y-coordinate of second point
255     * @param z2 Z-coordinate of second point
256     * @return 3D distance between the points
257     */
258    public static double dist(double x1, double y1, double z1, double x2, double y2, double z2) {
259        double dx = x2 - x1;
260        double dy = y2 - y1;
261        double dz = z2 - z1;
262        return Math.sqrt(dx * dx + dy * dy + dz * dz);
263    }
264
265    /**
266     * Add the specified pad character to the left of the input String until it is the
267     * specified length.
268     *
269     * @param string The string to be made the specified length.
270     * @param pad    The character to insert into the beginning of the string to reach the
271     *               specified length.
272     * @param length The desired length of the padded string.
273     * @return The input string padded with the specified pad character on the left until
274     *         the length matches the specified length.
275     */
276    public static String padLeft(String string, char pad, int length) {
277        StringBuilder buffer = new StringBuilder(string);
278        while (buffer.length() < length) {
279            buffer.insert(0, pad);
280        }
281        return buffer.toString();
282    }
283
284    /**
285     * Add the specified pad character to the right of the input String until it is the
286     * specified length.
287     *
288     * @param string The string to be made the specified length.
289     * @param pad    The character to append onto the string to reach the specified
290     *               length.
291     * @param length The desired length of the padded string.
292     * @return The input string padded with the specified pad character on the right until
293     *         the length matches the specified length.
294     */
295    public static String padRight(String string, char pad, int length) {
296        if (string.length() < length) {
297            StringBuilder buffer = new StringBuilder(string);
298            for (int i = buffer.length(); i < length; ++i) {
299                buffer.append(pad);
300            }
301            string = buffer.toString();
302        }
303        return string;
304    }
305
306    /**
307     * Create a sequence number using the specified integer and padding it with zeros on
308     * the left until it is 7 characters long.
309     *
310     * @param number The number to turn into a sequence number.
311     * @return A properly formatted sequence number made from the input integer.
312     */
313    public static String makeSequenceNumber(int number) {
314        return padLeft(String.valueOf(number), '0', 7);
315    }
316
317    /**
318     * Write out a single section of an IGES file. The section is provided as a single
319     * String that contains no line-breaks or control characters. The output will be
320     * limited to 72 characters plus a section code and sequence index.
321     *
322     * @param writer      The PrintWriter to write the section to.
323     * @param startIndex  The starting sequence index for this section.
324     * @param deNumber    The 7 character DE number code (made with "makeSequenceNumber")
325     *                    for this part or "".
326     * @param sectionCode The code used to identify this section's type ('S', 'G', 'P',
327     *                    etc").
328     * @param data        A String containing the data for the section.
329     * @return The index for the next sequence following this section.
330     * @throws java.io.IOException if the section could not be written.
331     */
332    public static int writeSection(PrintWriter writer, int startIndex, String deNumber,
333            char sectionCode, StringBuilder data) throws IOException {
334        if (DEBUG)
335            System.out.println("length = " + data.length() + ", data = " + data);
336
337        int deNumberLength = deNumber.length();
338        int strLen = 71 - deNumberLength;
339        boolean notDone;
340        do {
341            notDone = data.length() > strLen;
342            if (notDone) {
343                String tmp = data.substring(0, strLen);
344                int idx = tmp.lastIndexOf(',') + 1;
345                tmp = data.substring(0, idx);
346                writer.print(padRight(tmp, ' ', strLen));
347                data = data.delete(0, idx);
348
349            } else {
350                writer.print(padRight(data.toString(), ' ', strLen));
351            }
352
353            writer.print(' ');
354            writer.print(deNumber);
355            writer.print(sectionCode);
356            writer.println(makeSequenceNumber(startIndex++));
357
358        } while (notDone);
359
360        return startIndex;
361    }
362
363}