001package votorola.s.gwt.scene.vote; // Copyright 2011, 2013, 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.dom.client.Style;
004import org.vectomatic.dom.svg.*;
005import votorola.a.count.gwt.*;
006import votorola.g.lang.*;
007
008import static org.vectomatic.dom.svg.OMSVGLength.SVG_LENGTHTYPE_PX;
009import static votorola.s.gwt.scene.vote.VoterCircle.VOTER_CIRCLE_RADIUS;
010
011
012/** A view of a voter's count node.
013  */
014final class VoterNodeV extends NodeV
015{
016
017
018    /** Constructs a VoterNodeV.
019      *
020      *     @param parent the parent circle to be {@linkplain
021      *       #setParent(votorola.g.web.gwt.svg.SVGNest) set} by the caller.  It must
022      *       never be changed.
023      *
024      *     @see #dartSector()
025      */
026    VoterNodeV( final VotespaceV vV, final int dartSector, final Circle<?> parent )
027    {
028        super( vV, dartSector );
029     // initParent = parent;
030
031      // LAYOUT  rightward of candidate
032      // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
033        final float pxPerEm = vV.pxPerEm();
034
035      // Mnemonic.
036      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
037        {
038            final OMSVGTextElement text = vV.document().createSVGTextElement();
039            localView().appendChild( text );
040            text.addClassNameBaseVal( "mnemonic" );
041            vV.append( text.getY(), 0.35f ); // down, center in nodular standoff
042
043            text.appendChild( mnemonicTextNode() );
044        }
045
046      // Voteflow radial back (left) to candidate
047      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
048        final float radialOuterMargin = parent.nodularStandOff() - 0.4f; // outer somewhat tighter
049        {
050            final OMSVGLineElement line = vV.document().createSVGLineElement();
051            svgView().appendChild( line );
052            line.addClassNameBaseVal( "voteflow" );
053            line.getX1().getBaseVal().setValue( -radialOuterMargin * pxPerEm );
054            final float x2 = -VOTER_CIRCLE_RADIUS + parent.inCircle().nodularStandOff();
055            line.getX2().getBaseVal().setValue( x2 * pxPerEm );
056        }
057
058      // Carry cast count.
059      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
060        final float outflowMargin = radialOuterMargin + 1.1f;
061        outflowVSur = newCarryCastV( vV, -outflowMargin );
062        outflowVSur.addClassNameBaseVal( "sur" );
063
064        outflowVSub = newCarryCastV( vV, outflowMargin );
065        outflowVSub.addClassNameBaseVal( "sub" );
066        outflowVSub.setAttribute( "transform", "rotate(180)" );
067
068      // TRANSFORM  angularly and radially
069      // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
070        svgView().setAttribute( "transform", "rotate(" + angleToNode( dartSector )
071          + ") translate(" + VOTER_CIRCLE_RADIUS * pxPerEm + ")" );
072    }
073
074
075
076   // ------------------------------------------------------------------------------------
077
078
079    /** Calculates the angle of the voter node at the specified dart sector.
080      *
081      *     @see votorola.a.count.CountNode#dartSector()
082      */
083    static float angleToNode( final int dartSector )
084    {
085        return (dartSector - 10.5f) * INTER_VOTER_ANGLE; // aligns 10 and 11 to angle zero
086    }
087
088
089
090    /** The angle between voter nodes within the circle.
091      */
092    static final float INTER_VOTER_ANGLE = 11.6f;
093
094
095
096   // - C o u n t - N o d e - V ----------------------------------------------------------
097
098
099    @Override void repaintRegister( final CountingMethodJS.SwitchMnemonic mCM,
100      final String accountName )
101    {
102        final CountNodeJS node = model();
103        if( node == null ) return;
104
105        final long outflowVolume;
106        if( mCM == CountingMethodJS.SwitchMnemonic.q )
107        {
108            final SacRegisterJS_q r = node.register( mCM.fullName(), accountName );
109            if( r == null ) outflowVolume = 0; // no such superaccount
110            else outflowVolume = r.castVolume() + r.carryVolume();
111        }
112        else if( mCM == CountingMethodJS.SwitchMnemonic.v )
113        {
114            final SacRegisterJS_v r = node.register( mCM.fullName(), accountName );
115            if( r == null ) outflowVolume = 0; // no such superaccount
116            else outflowVolume = r.castVolume() + r.carryVolume();
117        }
118        else throw new IllegalArgumentException();
119
120        final OMSVGMatrix currentTransforms = svgView().getCTM();
121        final FlowVolumeV outflowVOn;
122        final FlowVolumeV outflowVOff;
123        if( currentTransforms.getA() > 0 ) // on right side, -90 < angle <= 90
124        {
125            outflowVOn = outflowVSur;
126            outflowVOff = outflowVSub;
127        }
128        else // on left side, 90 < angle <= 270
129        {
130            outflowVOn = outflowVSub;
131            outflowVOff = outflowVSur;
132        }
133        outflowVOff.getStyle().setDisplay( Style.Display.NONE );
134        if( outflowVolume > 0 ) // superaccount exists and outflow is non-zero
135        {
136            outflowVOn.set( outflowVolume );
137            outflowVOn.getStyle().clearDisplay();
138        }
139        else outflowVOn.getStyle().setDisplay( Style.Display.NONE );
140    }
141
142
143
144    @Override boolean setModel( final CountNodeJS node )
145    {
146        if( !super.setModel( node )) return false;
147
148        if( node != null )
149        {
150            localView().getElement().setPropertyString( "votepath",
151              DartScoping.DIGIT_ENCODER.charAt(node.dartSector())
152              + parent().candidateV().votepath() );
153            final SacRegisterJS_v r = node.voteRegister();
154            final long outflowVolume = r.castVolume() + r.carryVolume();
155            setMosquito( outflowVolume < parent().mosquitoBar() );
156        }
157        return true;
158    }
159
160
161   // - S  V  G - P a r a - N e s t ------------------------------------------------------
162
163
164 //     private final Circle initParent;
165 //
166 //
167 // public final @Override @Warning("init call") void setParent( final Circle circle )
168 // {
169 //     assert initParent.equals( circle ): "parent not changed after construction";
170 //     super.setParent( circle );
171 // }
172 //
173 /// Stack overflow (!) between here and super method in devmode, and apparently super
174 /// method not called at all when compiled (GWT 2.1.1).  Do without this guard for now.
175
176
177
178//// P r i v a t e ///////////////////////////////////////////////////////////////////////
179
180
181    private final FlowVolumeV outflowVSur;
182
183    private final FlowVolumeV outflowVSub;
184
185
186
187    private FlowVolumeV newCarryCastV( final VotespaceV vV, final float x )
188    {
189        final FlowVolumeV outflowV = new FlowVolumeV( vV );
190        svgView().appendChild( outflowV );
191        outflowV.addClassNameBaseVal( "radial" );
192        vV.append( outflowV.getX(), x );
193        vV.append( outflowV.getY(), -0.3f ); // up from radial line
194        return outflowV;
195    }
196
197
198
199}