001package votorola.s.gwt.scene.axial; // Copyright 2010-2011, Michael Allan.  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE.
002
003import com.google.gwt.event.logical.shared.*;
004import com.google.web.bindery.event.shared.HandlerRegistration;
005import com.google.gwt.user.client.*;
006import votorola.s.gwt.scene.*;
007import votorola.g.hold.*;
008import votorola.g.lang.*;
009import votorola.g.web.gwt.*;
010
011
012/** A scoping model based on three orthogonal axes (x,y,z).  Each axis may be set to its
013  * own independent range of values, such that all three ranges together specify a cubic
014  * sub-volume.  The scoping state is exposed in the 's' scoping switch, as follows:
015  *
016  * <table class='definition' style='margin-left:1em'>
017  *     <tr>
018  *         <th class='key'>Switch</th>
019  *         <th>Controlled state</th>
020  *         <th>Default</th>
021  *         </tr>
022  *     <tr><td class='key'>s</td>
023  *
024  *         <td>The scope control.  6 continguous numbers all padded to the same digit
025  *         length, together representing the ranges of all three axes (x,y,z).  Each
026  *         number is translated to a decimal by adding a leading decimal.  An exception
027  *         is made for numbers beginning with the letter N, which are always translated
028  *         as 1.0.  For example "s=00NN103055NN" is taken as x in the range 0.00 to 1.00,
029  *         y 0.10 to 0.30, and z 0.55 to 1.0.</td>
030  *
031  *         <td>"0N0N0N" which specifies the full range on all three axes.</td>
032  *
033  *         </tr>
034  *     </table>
035  *
036  *     @see Scoping
037  */
038public final class TriaxialScoping implements Scoping
039{
040
041    // cf. DiaxialScoping
042
043
044    /** Constructs a TriaxialScoping.
045      *
046      *     @param spool for release of internal holds.  When unwound, this instance will
047      *         release its internal holds and become disabled.
048      */
049    public TriaxialScoping( final Spool spool )
050    {
051        spool.add( new Hold()
052        {
053            final HandlerRegistration hR = Scenes.i().sScopingSwitch().addHandler(
054              new ValueChangeHandler<String>()
055            {
056                public void onValueChange( final ValueChangeEvent<String> e )
057                {
058                    rescope( e.getValue() );
059                    GWTX.i().bus().fireEventFromSource( new ScopeChangeEvent(), TriaxialScoping.this );
060                }
061            });
062            public void release() { hR.removeHandler(); }
063        });
064        rescope( Scenes.i().sScopingSwitch().get() ); // init state
065    }
066
067
068
069   // ------------------------------------------------------------------------------------
070
071
072    /** The x-axis minimum value, inclusive.
073      *
074      *     @return a number between 0.0 and 1.0 inclusive.
075      */
076    public float xMin() { return xMin; }
077
078
079        private float xMin = 0f;
080
081
082    /** The x-axis maximum value, inclusive.
083      *
084      *     @return a number between 0.0 and 1.0 inclusive.
085      */
086    public float xMax() { return xMax; }
087
088
089        private float xMax = 1f;
090
091
092
093    /** The y-axis minimum value, inclusive.
094      *
095      *     @return a number between 0.0 and 1.0 inclusive.
096      */
097    public float yMin() { return yMin; }
098
099
100        private float yMin = 0f;
101
102
103    /** The y-axis maximum value, inclusive.
104      *
105      *     @return a number between 0.0 and 1.0 inclusive.
106      */
107    public float yMax() { return yMax; }
108
109
110        private float yMax = 1f;
111
112
113
114    /** The z-axis minimum value, inclusive.
115      *
116      *     @return a number between 0.0 and 1.0 inclusive.
117      */
118    public float zMin() { return zMin; }
119
120
121        private float zMin = 0f;
122
123
124    /** The z-axis maximum value, inclusive.
125      *
126      *     @return a number between 0.0 and 1.0 inclusive.
127      */
128    public float zMax() { return zMax; }
129
130
131        private float zMax = 1f;
132
133
134
135   // - O b j e c t ----------------------------------------------------------------------
136
137
138    public @Override String toString()
139    {
140        return "triaxial scope = x(" + xMin + ", " + xMax + ") y(" + yMin + ", " + yMax
141          + ") z(" + zMin + ", " + zMax + ")";
142    }
143
144
145
146   // - S c o p i n g --------------------------------------------------------------------
147
148
149    public HandlerRegistration addHandler( final ScopeChangeHandler handler )
150    {
151        return GWTX.i().bus().addHandlerToSource( ScopeChangeEvent.TYPE,
152          /*source*/TriaxialScoping.this, handler );
153    }
154
155
156
157//// P r i v a t e ///////////////////////////////////////////////////////////////////////
158
159
160    private void rescopeToDefault()
161    {
162        xMin = 0f; xMax = 1f;
163        yMin = 0f; yMax = 1f;
164        zMin = 0f; zMax = 1f;
165    }
166
167
168
169    private @Warning("init call") void rescope( final String s )
170    {
171        if( s == null )
172        {
173            rescopeToDefault();
174            return;
175        }
176
177        final int sLength = s.length();
178        if( sLength % 6 > 0 )
179        {
180            Window.alert( "Length of switch s not a multiple of 6: " + s );
181            rescopeToDefault();
182            return;
183        }
184
185        final int digitLength = sLength / 6;
186        final StringBuilder buf = new StringBuilder(
187          /*initial capacity*/DiaxialScoping.assumedNumericPrefix.length() + digitLength );
188        buf.append( DiaxialScoping.assumedNumericPrefix );
189        int digitOffset = 0;
190        try
191        {
192            xMin = DiaxialScoping.floatFrom( s, digitOffset, digitLength, buf ); digitOffset += digitLength;
193            xMax = DiaxialScoping.floatFrom( s, digitOffset, digitLength, buf ); digitOffset += digitLength;
194            yMin = DiaxialScoping.floatFrom( s, digitOffset, digitLength, buf ); digitOffset += digitLength;
195            yMax = DiaxialScoping.floatFrom( s, digitOffset, digitLength, buf ); digitOffset += digitLength;
196            zMin = DiaxialScoping.floatFrom( s, digitOffset, digitLength, buf ); digitOffset += digitLength;
197            zMax = DiaxialScoping.floatFrom( s, digitOffset, digitLength, buf );
198        }
199        catch( NumberFormatException x )
200        {
201            Window.alert( "Unable to parse switch s=" + s + ": " + x.toString() );
202            rescopeToDefault();
203        }
204    }
205
206
207}