1 /**
  2  * Copyright (c) 2008-2010 Ricardo Quesada
  3  * Copyright (c) 2011-2012 cocos2d-x.org
  4  * Copyright (c) 2013-2014 Chukong Technologies Inc.
  5  *
  6  * http://www.cocos2d-x.org
  7  *
  8  * Copyright 2012 Yannick Loriot. All rights reserved.
  9  * http://yannickloriot.com
 10  * 
 11  * Permission is hereby granted, free of charge, to any person obtaining a copy
 12  * of this software and associated documentation files (the "Software"), to deal
 13  * in the Software without restriction, including without limitation the rights
 14  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 15  * copies of the Software, and to permit persons to whom the Software is
 16  * furnished to do so, subject to the following conditions:
 17  * 
 18  * The above copyright notice and this permission notice shall be included in
 19  * all copies or substantial portions of the Software.
 20  * 
 21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 27  * THE SOFTWARE.
 28  *
 29  */
 30 
 31 /**
 32  * CCControlPotentiometer: Potentiometer control for Cocos2D.
 33  * @class
 34  * @extends cc.Control
 35  *
 36  * @property {Number}           value           - The current value of the potentionmeter
 37  * @property {Number}           minValue        - The minimum value of the potentionmeter
 38  * @property {Number}           maxValue        - The maximum value of the potentionmeter
 39  * @property {cc.ProgressTimer} progressTimer   - The progress timer of the potentionmeter
 40  * @property {cc.Sprite}        thumbSprite     - The thumb sprite of the potentionmeter
 41  * @property {cc.Point}         prevLocation    - The previous location of the potentionmeter
 42  */
 43 cc.ControlPotentiometer = cc.Control.extend(/** @lends cc.ControlPotentiometer# */{
 44     _thumbSprite:null,
 45     _progressTimer:null,
 46     _previousLocation:null,
 47     /** Contains the receiver’s current value. */
 48     _value:0,
 49     /** Contains the minimum value of the receiver.
 50      * The default value of this property is 0.0. */
 51     _minimumValue:0,
 52     /** Contains the maximum value of the receiver.
 53      * The default value of this property is 1.0. */
 54     _maximumValue:1,
 55     _className:"ControlPotentiometer",
 56 
 57     ctor:function (backgroundFile, progressFile, thumbFile) {
 58         cc.Control.prototype.ctor.call(this);
 59         if (thumbFile != undefined) {
 60             // Prepare track for potentiometer
 61             var backgroundSprite = new cc.Sprite(backgroundFile);
 62 
 63             // Prepare thumb for potentiometer
 64             var thumbSprite = new cc.Sprite(thumbFile);
 65 
 66             // Prepare progress for potentiometer
 67             var progressTimer = new cc.ProgressTimer(new cc.Sprite(progressFile));
 68             this.initWithTrackSprite_ProgressTimer_ThumbSprite(backgroundSprite, progressTimer, thumbSprite);
 69         }
 70     },
 71 
 72     /**
 73      *
 74      * @param {cc.Sprite} trackSprite
 75      * @param {cc.ProgressTimer}  progressTimer
 76      * @param {cc.Sprite}  thumbSprite
 77      * @return {Boolean}
 78      */
 79     initWithTrackSprite_ProgressTimer_ThumbSprite:function (trackSprite, progressTimer, thumbSprite) {
 80         if (this.init()) {
 81             this.setProgressTimer(progressTimer);
 82             this.setThumbSprite(thumbSprite);
 83             this._thumbSprite.setPosition(progressTimer.getPosition());
 84 
 85             this.addChild(thumbSprite, 2);
 86             this.addChild(progressTimer, 1);
 87             this.addChild(trackSprite);
 88 
 89             this.setContentSize(trackSprite.getContentSize());
 90 
 91             // Init default values
 92             this._minimumValue = 0.0;
 93             this._maximumValue = 1.0;
 94             this.setValue(this._minimumValue);
 95             return true;
 96         }
 97         return false;
 98     },
 99 
100     setEnabled:function (enabled) {
101         this.setEnabled(enabled);
102         if (this._thumbSprite !== null) {
103             this._thumbSprite.setOpacity((enabled) ? 255 : 128);
104         }
105     },
106 
107     setValue:function (value) {
108         // set new value with sentinel
109         if (value < this._minimumValue) {
110             value = this._minimumValue;
111         }
112 
113         if (value > this._maximumValue) {
114             value = this._maximumValue;
115         }
116 
117         this._value = value;
118 
119         // Update thumb and progress position for new value
120         var percent = (value - this._minimumValue) / (this._maximumValue - this._minimumValue);
121         this._progressTimer.setPercentage(percent * 100.0);
122         this._thumbSprite.setRotation(percent * 360.0);
123 
124         this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED);
125     },
126 
127     getValue:function () {
128         return this._value;
129     },
130 
131     setMinimumValue:function (minimumValue) {
132         this._minimumValue = minimumValue;
133 
134         if (this._minimumValue >= this._maximumValue) {
135             this._maximumValue = this._minimumValue + 1.0;
136         }
137 
138         this.setValue(this._maximumValue);
139     },
140 
141     getMinimumValue:function () {
142         return this._minimumValue;
143     },
144 
145     setMaximumValue:function (maximumValue) {
146         this._maximumValue = maximumValue;
147 
148         if (this._maximumValue <= this._minimumValue) {
149             this._minimumValue = this._maximumValue - 1.0;
150         }
151 
152         this.setValue(this._minimumValue);
153     },
154 
155     getMaximumValue:function () {
156         return this._maximumValue;
157     },
158 
159     isTouchInside:function (touch) {
160         var touchLocation = this.getTouchLocation(touch);
161 
162         var distance = this.distanceBetweenPointAndPoint(this._progressTimer.getPosition(), touchLocation);
163 
164         return distance < Math.min(this.getContentSize().width / 2, this.getContentSize().height / 2);
165     },
166 
167     onTouchBegan:function (touch, event) {
168         if (!this.isTouchInside(touch) || !this.isEnabled() || !this.isVisible()) {
169             return false;
170         }
171 
172         this._previousLocation = this.getTouchLocation(touch);
173 
174         this.potentiometerBegan(this._previousLocation);
175 
176         return true;
177     },
178 
179     onTouchMoved:function (touch, event) {
180         var location = this.getTouchLocation(touch);
181 
182         this.potentiometerMoved(location);
183     },
184 
185     onTouchEnded:function (touch, event) {
186         this.potentiometerEnded(cc.p(0, 0));
187     },
188 
189     /**
190      * the distance between the point1 and point2
191      * @param {cc.Point} point1
192      * @param {cc.Point}  point2
193      * @return {Number}
194      */
195     distanceBetweenPointAndPoint:function (point1, point2) {
196         var dx = point1.x - point2.x;
197         var dy = point1.y - point2.y;
198         return Math.sqrt(dx * dx + dy * dy);
199     },
200 
201     /**
202      * the angle in degree between line1 and line2.
203      * @param {cc.Point}  beginLineA
204      * @param {cc.Point}  endLineA
205      * @param {cc.Point}  beginLineB
206      * @param {cc.Point}  endLineB
207      * @return {Number}
208      */
209     angleInDegreesBetweenLineFromPoint_toPoint_toLineFromPoint_toPoint:function (beginLineA, endLineA, beginLineB, endLineB) {
210         var a = endLineA.x - beginLineA.x;
211         var b = endLineA.y - beginLineA.y;
212         var c = endLineB.x - beginLineB.x;
213         var d = endLineB.y - beginLineB.y;
214 
215         var atanA = Math.atan2(a, b);
216         var atanB = Math.atan2(c, d);
217 
218         // convert radiants to degrees
219         return (atanA - atanB) * 180 / Math.PI;
220     },
221 
222     potentiometerBegan:function (location) {
223         this.setSelected(true);
224         this.getThumbSprite().setColor(cc.color.GRAY);
225     },
226 
227     potentiometerMoved:function (location) {
228         var angle = this.angleInDegreesBetweenLineFromPoint_toPoint_toLineFromPoint_toPoint(this._progressTimer.getPosition(), location, this._progressTimer.getPosition(), this._previousLocation);
229 
230         // fix value, if the 12 o'clock position is between location and previousLocation
231         if (angle > 180) {
232             angle -= 360;
233         }
234         else if (angle < -180) {
235             angle += 360;
236         }
237 
238         this.setValue(this._value + angle / 360.0 * (this._maximumValue - this._minimumValue));
239 
240         this._previousLocation = location;
241     },
242 
243     potentiometerEnded:function (location) {
244         this.getThumbSprite().setColor(cc.color.WHITE);
245         this.setSelected(false);
246     },
247     setThumbSprite:function (sprite) {
248         this._thumbSprite = sprite;
249     },
250     getThumbSprite:function () {
251         return this._thumbSprite;
252     },
253     setProgressTimer:function (sprite) {
254         this._progressTimer = sprite;
255     },
256     getProgressTimer:function () {
257         return this._progressTimer;
258     },
259     setPreviousLocation:function (point) {
260         this._previousLocation = point;
261     },
262     getPreviousLocation:function () {
263         return this._previousLocation;
264     }
265 });
266 
267 var _p = cc.ControlPotentiometer.prototype;
268 
269 // Extended properties
270 /** @expose */
271 _p.value;
272 cc.defineGetterSetter(_p, "value", _p.getValue, _p.setValue);
273 /** @expose */
274 _p.minValue;
275 cc.defineGetterSetter(_p, "minValue", _p.getMinimumValue, _p.setMinimumValue);
276 /** @expose */
277 _p.maxValue;
278 cc.defineGetterSetter(_p, "maxValue", _p.getMaximumValue, _p.setMaximumValue);
279 /** @expose */
280 _p.progressTimer;
281 cc.defineGetterSetter(_p, "progressTimer", _p.getProgressTimer, _p.setProgressTimer);
282 /** @expose */
283 _p.thumbSprite;
284 cc.defineGetterSetter(_p, "thumbSprite", _p.getThumbSprite, _p.setThumbSprite);
285 /** @expose */
286 _p.prevLocation;
287 cc.defineGetterSetter(_p, "prevLocation", _p.getPreviousLocation, _p.setPreviousLocation);
288 
289 _p = null;
290 
291 /**
292  * @deprecated
293  * @param backgroundFile
294  * @param progressFile
295  * @param thumbFile
296  * @returns {ControlPotentiometer}
297  */
298 cc.ControlPotentiometer.create = function (backgroundFile, progressFile, thumbFile) {
299     return new cc.ControlPotentiometer(backgroundFile, progressFile, thumbFile);
300 };