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}