001package jmri.jmrit.display.layoutEditor;
002
003/**
004 * Enum denoting the different behaviors of different
005 * types of "HitPoints".
006 * <p>
007 * Extracted from static in constants in LayoutEditor
008 *
009 * @author Dave Duchamp Copyright: (c) 2004-2007
010 * @author George Warner Copyright: (c) 2017-2019
011 * @author Bob Jacobsen Copyright: (c) 2020
012 */
013
014public enum HitPointType {
015                    //  Historical numerical values, used in old files, now ordinals with tests
016    NONE,           //     0
017    POS_POINT,      //     1
018    TURNOUT_A,      //     2    throat for RH, LH, and WYE turnouts
019    TURNOUT_B,      //     3    continuing route for RH and LH turnouts
020    TURNOUT_C,      //     4    diverging route for RH and LH turnouts
021    TURNOUT_D,      //     5    4th route for crossovers
022    LEVEL_XING_A,   //     6
023    LEVEL_XING_B,   //     7
024    LEVEL_XING_C,   //     8
025    LEVEL_XING_D,   //     9
026    TRACK,          //     10
027    TURNOUT_CENTER,   //     11    non-connection points should be last
028    LEVEL_XING_CENTER,  //     12
029    TURNTABLE_CENTER,   //     13
030    LAYOUT_POS_LABEL,   //     14
031    LAYOUT_POS_JCOMP,   //     15
032    MULTI_SENSOR,   //     16
033    MARKER,         //     17
034    TRACK_CIRCLE_CENTRE,   //     18
035    UNUSED_19,      //     19
036    SLIP_CENTER,    //     20   TODO: should be deprecated (use SLIP_LEFT & SLIP_RIGHT instead)
037    SLIP_A,         //     21
038    SLIP_B,         //     22
039    SLIP_C,         //     23
040    SLIP_D,         //     24
041    SLIP_LEFT,      //     25
042    SLIP_RIGHT,     //     26
043    UNUSED_27,      //     27
044    UNUSED_28,                //     28
045    UNUSED_29,                //     29
046    BEZIER_CONTROL_POINT_0,   //     30    offset for TrackSegment Bezier control points , minimum)
047    BEZIER_CONTROL_POINT_1,   //     31     \
048    BEZIER_CONTROL_POINT_2,   //     32      \
049    BEZIER_CONTROL_POINT_3,   //     33       \
050    BEZIER_CONTROL_POINT_4,   //     34        } -- DON'T USE THESE; PLACEHOLDERS ONLY
051    BEZIER_CONTROL_POINT_5,   //     35       /
052    BEZIER_CONTROL_POINT_6,   //     36      /
053    BEZIER_CONTROL_POINT_7,   //     37     /
054    BEZIER_CONTROL_POINT_8,   //     38    offset for TrackSegment Bezier control points , maximum)
055    SHAPE_CENTER,    //     39
056    SHAPE_POINT_0,   //     40    offset for Shape points, minimum)
057    SHAPE_POINT_1,   //     41     \
058    SHAPE_POINT_2,   //     42      \
059    SHAPE_POINT_3,   //     43       \
060    SHAPE_POINT_4,   //     44        \ __ DON'T USE THESE; PLACEHOLDERS ONLY
061    SHAPE_POINT_5,   //     45        /
062    SHAPE_POINT_6,   //     46       /
063    SHAPE_POINT_7,   //     47      /
064    SHAPE_POINT_8,   //     48     /
065    SHAPE_POINT_9,   //     49    offset for Shape points ,   //     maximum)
066    TURNTABLE_RAY_0,   //     50    offset for turntable connection points ,   //     minimum)
067    TURNTABLE_RAY_1,   //     51    \
068    TURNTABLE_RAY_2,   //     52     \
069    TURNTABLE_RAY_3,   //     53      \
070    TURNTABLE_RAY_4,   //     54       \
071    TURNTABLE_RAY_5,   //     55        \
072    TURNTABLE_RAY_6,   //     56         \
073    TURNTABLE_RAY_7,   //     57          |
074    TURNTABLE_RAY_8,   //     58          |
075    TURNTABLE_RAY_9,   //     59          |
076    TURNTABLE_RAY_10,   //     60         |
077    TURNTABLE_RAY_11,   //     61         |
078    TURNTABLE_RAY_12,   //     62         |
079    TURNTABLE_RAY_13,   //     63         |
080    TURNTABLE_RAY_14,   //     64         |
081    TURNTABLE_RAY_15,   //     65         |
082    TURNTABLE_RAY_16,   //     66         |
083    TURNTABLE_RAY_17,   //     67         |
084    TURNTABLE_RAY_18,   //     68         |
085    TURNTABLE_RAY_19,   //     69         |
086    TURNTABLE_RAY_20,   //     70         |
087    TURNTABLE_RAY_21,   //     71         |
088    TURNTABLE_RAY_22,   //     72         |
089    TURNTABLE_RAY_23,   //     73         |
090    TURNTABLE_RAY_24,   //     74         |
091    TURNTABLE_RAY_25,   //     75         |
092    TURNTABLE_RAY_26,   //     76         |
093    TURNTABLE_RAY_27,   //     77         |
094    TURNTABLE_RAY_28,   //     78         |
095    TURNTABLE_RAY_29,   //     79         |
096    TURNTABLE_RAY_30,   //     80         |
097    TURNTABLE_RAY_31,   //     81         |
098    TURNTABLE_RAY_32,   //     82         |
099    TURNTABLE_RAY_33,   //     83         |
100    TURNTABLE_RAY_34,   //     84         |
101    TURNTABLE_RAY_35,   //     85         |
102    TURNTABLE_RAY_36,   //     86         |
103    TURNTABLE_RAY_37,   //     87         |
104    TURNTABLE_RAY_38,   //     88         |
105    TURNTABLE_RAY_39,   //     89         |
106    TURNTABLE_RAY_40,   //     90         |
107    TURNTABLE_RAY_41,   //     91         |
108    TURNTABLE_RAY_42,   //     92         |
109    TURNTABLE_RAY_43,   //     93         |
110    TURNTABLE_RAY_44,   //     94         |
111    TURNTABLE_RAY_45,   //     95         | -- DON'T USE THESE; PLACEHOLDERS ONLY
112    TURNTABLE_RAY_46,   //     96         |
113    TURNTABLE_RAY_47,   //     97         |
114    TURNTABLE_RAY_48,   //     98         |
115    TURNTABLE_RAY_49,   //     99         |
116    TURNTABLE_RAY_50,   //     100         |
117    TURNTABLE_RAY_51,   //     101         |
118    TURNTABLE_RAY_52,   //     102         |
119    TURNTABLE_RAY_53,   //     103         |
120    TURNTABLE_RAY_54,   //     104         |
121    TURNTABLE_RAY_55,   //     105         |
122    TURNTABLE_RAY_56,   //     106         |
123    TURNTABLE_RAY_57,   //     107         |
124    TURNTABLE_RAY_58,   //     108        /
125    TURNTABLE_RAY_59,   //     109       /
126    TURNTABLE_RAY_60,   //     110      /
127    TURNTABLE_RAY_61,   //     111     /
128    TURNTABLE_RAY_62,   //     112    /
129    TURNTABLE_RAY_63;   //     113
130
131    /**
132     * @param hitType the hit point type
133     * @return true if this is for a connection to a LayoutTrack
134     */
135    protected static boolean isConnectionHitType(HitPointType hitType) {
136        switch (hitType) {
137            case POS_POINT:
138            case TURNOUT_A:
139            case TURNOUT_B:
140            case TURNOUT_C:
141            case TURNOUT_D:
142            case LEVEL_XING_A:
143            case LEVEL_XING_B:
144            case LEVEL_XING_C:
145            case LEVEL_XING_D:
146            case TRACK:
147            case SLIP_A:
148            case SLIP_B:
149            case SLIP_C:
150            case SLIP_D:
151                return true; // these are all connection types
152            case NONE:
153            case TURNOUT_CENTER:
154            case LEVEL_XING_CENTER:
155            case TURNTABLE_CENTER:
156            case LAYOUT_POS_LABEL:
157            case LAYOUT_POS_JCOMP:
158            case MULTI_SENSOR:
159            case MARKER:
160            case TRACK_CIRCLE_CENTRE:
161            case SLIP_CENTER:
162            case SLIP_LEFT:
163            case SLIP_RIGHT:
164                return false; // these are not
165            default:
166                break;
167        }
168        if (isBezierHitType(hitType)) {
169            return false; // these are not
170        } else if (isTurntableRayHitType(hitType)) {
171            return true; // these are all connection types
172        }
173        return false; // This is unexpected
174    }
175
176    /**
177     * @param hitType the hit point type
178     * @return true if this hit type is for a layout control
179     */
180    protected static boolean isControlHitType(HitPointType hitType) {
181        switch (hitType) {
182            case TURNOUT_CENTER:
183            case SLIP_CENTER:
184            case SLIP_LEFT:
185            case SLIP_RIGHT:
186                return true; // these are all control types
187            case POS_POINT:
188            case TURNOUT_A:
189            case TURNOUT_B:
190            case TURNOUT_C:
191            case TURNOUT_D:
192            case LEVEL_XING_A:
193            case LEVEL_XING_B:
194            case LEVEL_XING_C:
195            case LEVEL_XING_D:
196            case TRACK:
197            case SLIP_A:
198            case SLIP_B:
199            case SLIP_C:
200            case SLIP_D:
201            case NONE:
202            case LEVEL_XING_CENTER:
203            case TURNTABLE_CENTER:
204            case LAYOUT_POS_LABEL:
205            case LAYOUT_POS_JCOMP:
206            case MULTI_SENSOR:
207            case MARKER:
208            case TRACK_CIRCLE_CENTRE:
209                return false; // these are not
210            default:
211                break;
212        }
213        if (isBezierHitType(hitType)) {
214            return false; // these are not control types
215        } else if (isTurntableRayHitType(hitType)) {
216            return true; // these are all control types
217        }
218        return false; // This is unexpected
219    }
220
221    protected static boolean isTurnoutHitType(HitPointType hitType) {
222        return (hitType.compareTo(HitPointType.TURNOUT_A) >= 0) && (hitType.compareTo(HitPointType.TURNOUT_D) <= 0);
223    }
224
225    protected static boolean isSlipHitType(HitPointType hitType) {
226        return (hitType.compareTo(HitPointType.SLIP_A) >= 0) && (hitType.compareTo(HitPointType.SLIP_RIGHT) <= 0);
227    }
228
229    protected static boolean isBezierHitType(HitPointType hitType) {
230        return (hitType.compareTo(HitPointType.BEZIER_CONTROL_POINT_0) >= 0) && (hitType.compareTo(HitPointType.BEZIER_CONTROL_POINT_8) <= 0);
231    }
232
233    protected static boolean isLevelXingHitType(HitPointType hitType) {
234        return (hitType.compareTo(HitPointType.LEVEL_XING_A) >= 0) && (hitType.compareTo(HitPointType.LEVEL_XING_D) <= 0);
235    }
236
237    protected static boolean isTurntableRayHitType(HitPointType hitType) {
238        return (hitType.compareTo(HitPointType.TURNTABLE_RAY_0) >= 0) && (hitType.compareTo(HitPointType.TURNTABLE_RAY_63) <= 0);
239    }
240
241    /**
242     * @param hitType the hit point type
243     * @return true if this is for a popup menu
244     */
245    protected static boolean isPopupHitType(HitPointType hitType) {
246        switch (hitType) {
247            case LEVEL_XING_CENTER:
248            case POS_POINT:
249            case SLIP_CENTER:
250            case SLIP_LEFT:
251            case SLIP_RIGHT:
252            case TRACK:
253            case TRACK_CIRCLE_CENTRE:
254            case TURNOUT_CENTER:
255            case TURNTABLE_CENTER:
256                return true;
257            case LAYOUT_POS_JCOMP:
258            case LAYOUT_POS_LABEL:
259            case LEVEL_XING_A:
260            case LEVEL_XING_B:
261            case LEVEL_XING_C:
262            case LEVEL_XING_D:
263            case MARKER:
264            case MULTI_SENSOR:
265            case NONE:
266            case SLIP_A:
267            case SLIP_B:
268            case SLIP_C:
269            case SLIP_D:
270            case TURNOUT_A:
271            case TURNOUT_B:
272            case TURNOUT_C:
273            case TURNOUT_D:
274                return false; // these are not
275            default:
276                break;
277        }
278        if (isBezierHitType(hitType)) {
279            return true; // these are all popup hit types
280        } else if (isTurntableRayHitType(hitType)) {
281            return true; // these are all popup hit types
282        }
283        return false;
284    }
285
286    // *****************************************************************
287    //    TURNTABLE_RAY support
288    // *****************************************************************
289    /**
290     * Find the 0-63 index with respect to TURNTABLE_RAY_0
291     * of a given enum entry.  Throws {@link IllegalArgumentException} if
292     * the given enum value isn't one of the TURNTABLE_RAY_n entries.
293     * <p>
294     * Ideally, this would be replaced by turntable code that works
295     * directly with the enum values as a step toward using objects
296     * to implement hit points.
297     * @return (Temporary) 0-63 index of the enum element
298     */
299    protected int turntableTrackIndex() {
300        int result = this.ordinal() - HitPointType.TURNTABLE_RAY_0.ordinal();
301        if (result < 0) {
302            throw new IllegalArgumentException(this.toString() + " is not a valid TURNTABLE_RAY");
303        }
304        if (result > 63) {
305            throw new IllegalArgumentException(this.toString() + " is not a valid TURNTABLE_RAY");
306        }
307        return result;
308    }
309
310    /**
311     * Return a specific TURNTABLE_RAY from its 0-63 index.
312     * Throws {@link IllegalArgumentException} if
313     * the given index value isn't valid for the TURNTABLE_RAY entries.
314     * <p>
315     * Ideally, this would be replaced by turntable code that works
316     * directly with the enum values as a step toward using objects
317     * to implement hit points.
318     * @param i (Temporary) 0-63 index of the enum element
319     * @return Requested enum element
320     */
321    protected static HitPointType turntableTrackIndexedValue(int i) {
322        if (i < 0 || i > 63) {
323            throw new IllegalArgumentException(i + " is not a valid TURNTABLE_RAY index");
324        }
325        return HitPointType.values()[(TURNTABLE_RAY_0.ordinal() + i)];
326    }
327
328    /**
329     * Return an array of the valid TURNTABLE_RAY enum values.
330     * Meant for interations over the set of rays.  Order is
331     * from 0 to 63.
332     * @return (Temporary) Array containing TURNTABLE_RAY_0 through TURNTABLE_RAY_63
333     */
334    protected static HitPointType[] turntableValues() {
335        return new HitPointType[]{TURNTABLE_RAY_0, TURNTABLE_RAY_1, TURNTABLE_RAY_2, TURNTABLE_RAY_3, TURNTABLE_RAY_4, TURNTABLE_RAY_5, TURNTABLE_RAY_6, TURNTABLE_RAY_7, TURNTABLE_RAY_8, TURNTABLE_RAY_9, TURNTABLE_RAY_10, TURNTABLE_RAY_11, TURNTABLE_RAY_12, TURNTABLE_RAY_13, TURNTABLE_RAY_14, TURNTABLE_RAY_15, TURNTABLE_RAY_16, TURNTABLE_RAY_17, TURNTABLE_RAY_18, TURNTABLE_RAY_19, TURNTABLE_RAY_20, TURNTABLE_RAY_21, TURNTABLE_RAY_22, TURNTABLE_RAY_23, TURNTABLE_RAY_24, TURNTABLE_RAY_25, TURNTABLE_RAY_26, TURNTABLE_RAY_27, TURNTABLE_RAY_28, TURNTABLE_RAY_29, TURNTABLE_RAY_30, TURNTABLE_RAY_31, TURNTABLE_RAY_32, TURNTABLE_RAY_33, TURNTABLE_RAY_34, TURNTABLE_RAY_35, TURNTABLE_RAY_36, TURNTABLE_RAY_37, TURNTABLE_RAY_38, TURNTABLE_RAY_39, TURNTABLE_RAY_40, TURNTABLE_RAY_41, TURNTABLE_RAY_42, TURNTABLE_RAY_43, TURNTABLE_RAY_44, TURNTABLE_RAY_45, TURNTABLE_RAY_46, TURNTABLE_RAY_47, TURNTABLE_RAY_48, TURNTABLE_RAY_49, TURNTABLE_RAY_50, TURNTABLE_RAY_51, TURNTABLE_RAY_52, TURNTABLE_RAY_53, TURNTABLE_RAY_54, TURNTABLE_RAY_55, TURNTABLE_RAY_56, TURNTABLE_RAY_57, TURNTABLE_RAY_58, TURNTABLE_RAY_59, TURNTABLE_RAY_60, TURNTABLE_RAY_61, TURNTABLE_RAY_62, TURNTABLE_RAY_63};
336    }
337
338    // *****************************************************************
339    //    SHAPE_POINT support
340    // *****************************************************************
341    /**
342     * Find the 0-9 index with respect to SHAPE_POINT_0
343     * of a given enum entry.  Throws {@link IllegalArgumentException} if
344     * the given enum value isn't one of the SHAPE_POINT_n entries.
345     * <p>
346     * Ideally, this would be replaced by shape code that works
347     * directly with the enum values as a step toward using objects
348     * to implement hit points.
349     * @return (Temporary) 0-9 index of the enum element
350     */
351    protected int shapePointIndex() {
352        int result = this.ordinal() - HitPointType.SHAPE_POINT_0.ordinal();
353        if (result < 0) {
354            throw new IllegalArgumentException(this.toString() + " is not a valid SHAPE_POINT");
355        }
356        if (result > 9) {
357            throw new IllegalArgumentException(this.toString() + " is not a valid SHAPE_POINT");
358        }
359        return result;
360    }
361
362    /**
363     * Return a specific SHAPE_POINT from its 0-9 index.
364     * Throws {@link IllegalArgumentException} if
365     * the given index value isn't valid for the SHAPE_POINT entries.
366     * <p>
367     * Ideally, this would be replaced by shape code that works
368     * directly with the enum values as a step toward using objects
369     * to implement hit points.
370     * @param i (Temporary) 0-9 index of the enum element
371     * @return Requested enum element
372     */
373    protected static HitPointType shapePointIndexedValue(int i) {
374        if (i < 0 || i > 9) {
375            throw new IllegalArgumentException(i + " is not a valid SHAPE_POINT index");
376        }
377        return HitPointType.values()[(SHAPE_POINT_0.ordinal() + i)];
378    }
379
380    /**
381     * Return an array of the valid SHAPE_POINT enum values.
382     * Meant for interations over the set of points.  Order is
383     * from 0 to 9.
384     * @return (Temporary) Array containing SHAPE_POINT_0 through SHAPE_POINT_9
385     */
386    protected static HitPointType[] shapePointValues() {
387        return new HitPointType[]{SHAPE_POINT_0, SHAPE_POINT_1, SHAPE_POINT_2, SHAPE_POINT_3, SHAPE_POINT_4, SHAPE_POINT_5, SHAPE_POINT_6, SHAPE_POINT_7, SHAPE_POINT_8, SHAPE_POINT_9};
388    }
389
390    protected static boolean isShapePointOffsetHitPointType(HitPointType t) {
391        return (t.compareTo(SHAPE_POINT_0) >= 0) && (t.compareTo(SHAPE_POINT_9) <= 0);
392    }
393    // limited use, remove?
394    public static final int NUM_SHAPE_POINTS = 10;
395
396    // *****************************************************************
397    //    BEZIER_CONTROL_POINT support
398    // *****************************************************************
399    /**
400     * Find the 0-8 index with respect to BEZIER_CONTROL_POINT_0
401     * of this enum entry. Throws {@link IllegalArgumentException} if
402     * the enum value isn't one of the BEZIER_CONTROL_POINT_n entries.
403     * <p>
404     * Ideally, this would be replaced by bezier code that works
405     * directly with the enum values as a step toward using objects
406     * to implement hit points.
407     * @return (Temporary) 0-8 index of this enum
408     */
409    protected int bezierPointIndex() {
410        int result = this.ordinal() - HitPointType.BEZIER_CONTROL_POINT_0.ordinal();
411        if (result < 0) {
412            throw new IllegalArgumentException(this.toString() + " is not a valid BEZIER_CONTROL_POINT");
413        }
414        if (result > 8) {
415            throw new IllegalArgumentException(this.toString() + " is not a valid BEZIER_CONTROL_POINT");
416        }
417        return result;
418    }
419
420    /**
421     * Return a specific BEZIER_CONTROL_POINT from its 0-8 index.
422     * Throws {@link IllegalArgumentException} if
423     * the given index value isn't valid for the SHAPE_POINT entries.
424     * <p>
425     * Ideally, this would be replaced by shape code that works
426     * directly with the enum values as a step toward using objects
427     * to implement hit points.
428     * @param i (Temporary) 0-8 index of the enum element
429     * @return Requested enum element
430     */
431    protected static HitPointType bezierPointIndexedValue(int i) {
432        if (i < 0 || i > 8) {
433            throw new IllegalArgumentException(i + " is not a valid BEZIER_CONTROL_POINT index");
434        }
435        return HitPointType.values()[(BEZIER_CONTROL_POINT_0.ordinal() + i)];
436    }
437    // limited use, remove?
438    public static final int NUM_BEZIER_CONTROL_POINTS = 9;
439
440}