package 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.
import com.google.gwt.event.logical.shared.*;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.google.gwt.user.client.*;
import votorola.s.gwt.scene.*;
import votorola.g.hold.*;
import votorola.g.lang.*;
import votorola.g.web.gwt.*;
/** A scoping model based on two orthogonal axes (x,y). Each axis may be set to its own
* independent range of values, such that both ranges together specify a square sub-area.
* The scoping state is exposed in the {@linkplain Scenes#sScopingSwitch() 's'
* switch}, as follows:
*
*
*
* Switch |
* Controlled state |
* Default |
*
* s |
*
* The scoping state. 4 continguous numbers all padded to the same digit
* length, together representing the ranges of both axes (x,y). Each number is
* translated to a decimal by adding a leading decimal. An exception is made for
* numbers beginning with the letter N, which are always translated as 1.0. For
* example "s=00NN1030" is taken as x in the range 0.00 to 1.00 and y 0.10 to
* 0.30. |
*
* "0N0N" which specifies the full range on both axes. |
*
*
*
*
* @see Scoping
*/
public final class DiaxialScoping implements Scoping
{
// cf. TriaxialScoping
/** Constructs a DiaxialScoping.
*
* @param spool for release of internal holds. When unwound, this instance will
* release its internal holds and become disabled.
*/
public DiaxialScoping( final Spool spool )
{
spool.add( new Hold()
{
final HandlerRegistration hR = Scenes.i().sScopingSwitch().addHandler(
new ValueChangeHandler()
{
public void onValueChange( final ValueChangeEvent e )
{
rescope( e.getValue() );
GWTX.i().bus().fireEventFromSource( new ScopeChangeEvent(), DiaxialScoping.this );
}
});
public void release() { hR.removeHandler(); }
});
rescope( Scenes.i().sScopingSwitch().get() ); // init state
}
// ------------------------------------------------------------------------------------
/** The x-axis minimum value, inclusive.
*
* @return a number between 0.0 and 1.0 inclusive.
*/
public float xMin() { return xMin; }
private float xMin = 0f;
/** The x-axis maximum value, inclusive.
*
* @return a number between 0.0 and 1.0 inclusive.
*/
public float xMax() { return xMax; }
private float xMax = 1f;
/** The y-axis minimum value, inclusive.
*
* @return a number between 0.0 and 1.0 inclusive.
*/
public float yMin() { return yMin; }
private float yMin = 0f;
/** The y-axis maximum value, inclusive.
*
* @return a number between 0.0 and 1.0 inclusive.
*/
public float yMax() { return yMax; }
private float yMax = 1f;
// - O b j e c t ----------------------------------------------------------------------
public @Override String toString()
{
return "diaxial scope = x(" + xMin + ", " + xMax + ") y(" + yMin + ", " + yMax + ")";
}
// - S c o p i n g --------------------------------------------------------------------
public HandlerRegistration addHandler( final ScopeChangeHandler handler )
{
return GWTX.i().bus().addHandlerToSource( ScopeChangeEvent.TYPE, /*source*/DiaxialScoping.this,
handler );
}
//// P r i v a t e ///////////////////////////////////////////////////////////////////////
static final String assumedNumericPrefix = "0.";
static float floatFrom( final String s, int offset, final int digitLength,
final StringBuilder buf )
{
char ch = s.charAt( offset );
if( ch == 'N' ) return 1f;
final int offsetBound = offset + digitLength;
buf.setLength( assumedNumericPrefix.length() );
for( ;; )
{
buf.append( ch );
if( ++offset >= offsetBound ) break;
ch = s.charAt( offset );
}
return Float.parseFloat( buf.toString() );
}
private void rescopeToDefault()
{
xMin = 0f; xMax = 1f;
yMin = 0f; yMax = 1f;
}
private @Warning("init call") void rescope( final String s )
{
if( s == null )
{
rescopeToDefault();
return;
}
final int sLength = s.length();
if( sLength % 4 > 0 )
{
Window.alert( "Length of switch s not a multiple of 4: " + s );
rescopeToDefault();
return;
}
final int digitLength = sLength / 4;
final StringBuilder buf = new StringBuilder(
/*initial capacity*/assumedNumericPrefix.length() + digitLength );
buf.append( assumedNumericPrefix );
int digitOffset = 0;
try
{
xMin = floatFrom( s, digitOffset, digitLength, buf ); digitOffset += digitLength;
xMax = floatFrom( s, digitOffset, digitLength, buf ); digitOffset += digitLength;
yMin = floatFrom( s, digitOffset, digitLength, buf ); digitOffset += digitLength;
yMax = floatFrom( s, digitOffset, digitLength, buf );
}
catch( NumberFormatException x )
{
Window.alert( "Unable to parse switch s=" + s + ": " + x.toString() );
rescopeToDefault();
}
}
}