View Javadoc
1   /*
2    * Copyright 2012 Daniel Kurka
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    * 
7    * http://www.apache.org/licenses/LICENSE-2.0
8    * 
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package com.googlecode.mgwt.dom.client.recognizer.pinch;
15  
16  import com.google.gwt.core.client.GWT;
17  import com.google.gwt.event.shared.HasHandlers;
18  import com.googlecode.mgwt.dom.client.event.touch.Touch;
19  import com.googlecode.mgwt.dom.client.event.touch.TouchCancelEvent;
20  import com.googlecode.mgwt.dom.client.event.touch.TouchEndEvent;
21  import com.googlecode.mgwt.dom.client.event.touch.TouchHandler;
22  import com.googlecode.mgwt.dom.client.event.touch.TouchMoveEvent;
23  import com.googlecode.mgwt.dom.client.event.touch.TouchStartEvent;
24  import com.googlecode.mgwt.dom.client.event.touch.TouchUtil;
25  import com.googlecode.mgwt.dom.client.recognizer.EventPropagator;
26  
27  /**
28   * A PinchRecognizer tracks two finger on a screen that perform a zooming / pinching action
29   * 
30   * @author Daniel Kurka
31   * 
32   */
33  public class PinchRecognizer implements TouchHandler {
34  
35    private static EventPropagator DEFAULT_EVENT_PROPAGATOR;
36  
37    private final HasHandlers source;
38  
39    private EventPropagator eventPropagator;
40  
41    private enum State {
42      READY, INVALID, ONE_FINGER, TWO_FINGER;
43    }
44  
45    private State state;
46  
47    private Touch touchStart1;
48    private Touch touchStart2;
49  
50    private int touchCount;
51    private double distance;
52  
53    private final OffsetProvider offsetProvider;
54  
55    /**
56     * Construct a {@link PinchRecognizer}
57     * 
58     * @param source the source to fire events on
59     * @param offsetProvider the offset provider
60     */
61    public PinchRecognizer(HasHandlers source, OffsetProvider offsetProvider) {
62  
63      if (source == null) {
64        throw new IllegalArgumentException("source can not be null");
65      }
66      if (offsetProvider == null) {
67        throw new IllegalArgumentException("offsetProvider can not be null");
68      }
69  
70      this.source = source;
71      this.offsetProvider = offsetProvider;
72      state = State.READY;
73  
74    }
75  
76    /*
77     * (non-Javadoc)
78     * 
79     * @see
80     * com.googlecode.mgwt.dom.client.event.touch.TouchStartHandler#onTouchStart(com.googlecode.mgwt
81     * .dom.client.event.touch.TouchStartEvent)
82     */
83    @Override
84    public void onTouchStart(TouchStartEvent event) {
85      touchCount++;
86      switch (state) {
87        case READY:
88          touchStart1 = TouchUtil.cloneTouch(event.getTouches().get(0));
89          state = State.ONE_FINGER;
90          break;
91        case ONE_FINGER:
92          touchStart2 = TouchUtil.cloneTouch(event.getTouches().get(1));
93          distance = (int) Math.sqrt(Math.pow(touchStart1.getPageX() - touchStart2.getPageX(), 2) + Math.pow(touchStart1.getPageY() - touchStart1.getPageY(), 2));
94          state = State.TWO_FINGER;
95          break;
96  
97        default:
98          state = State.INVALID;
99          break;
100     }
101 
102   }
103 
104   /*
105    * (non-Javadoc)
106    * 
107    * @see
108    * com.googlecode.mgwt.dom.client.event.touch.TouchMoveHandler#onTouchMove(com.googlecode.mgwt
109    * .dom.client.event.touch.TouchMoveEvent)
110    */
111   @Override
112   public void onTouchMove(TouchMoveEvent event) {
113     switch (state) {
114       case TWO_FINGER:
115 
116         Touch touch1 = event.getTouches().get(0);
117         Touch touch2 = event.getTouches().get(1);
118 
119         int left = offsetProvider.getLeft();
120         int top = offsetProvider.getTop();
121 
122         int x1 = touch1.getPageX() - left;
123         int y1 = touch1.getPageY() - top;
124         int x2 = touch2.getPageX() - left;
125         int y2 = touch2.getPageY() - top;
126 
127         double newDistance = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
128         int x = (x1 + x2) / 2;
129         int y = (y1 + y2) / 2;
130 
131         getEventPropagator().fireEvent(source, new PinchEvent(x, y, distance / newDistance));
132         distance = newDistance;
133 
134         break;
135 
136       default:
137         state = State.INVALID;
138         break;
139     }
140 
141   }
142 
143   /*
144    * (non-Javadoc)
145    * 
146    * @see
147    * com.googlecode.mgwt.dom.client.event.touch.TouchEndHandler#onTouchEnd(com.googlecode.mgwt.dom
148    * .client.event.touch.TouchEndEvent)
149    */
150   @Override
151   public void onTouchEnd(TouchEndEvent event) {
152     touchCount--;
153     if (touchCount <= 0) {
154       reset();
155     } else {
156       if (state == State.TWO_FINGER) {
157         state = State.ONE_FINGER;
158       } else {
159         if (touchCount == 2) {
160           state = State.TWO_FINGER;
161         }
162       }
163     }
164 
165   }
166 
167   /*
168    * (non-Javadoc)
169    * 
170    * @see
171    * com.googlecode.mgwt.dom.client.event.touch.TouchCancelHandler#onTouchCanceled(com.googlecode
172    * .mgwt.dom.client.event.touch.TouchCancelEvent)
173    */
174   @Override
175   public void onTouchCanceled(TouchCancelEvent event) {
176     touchCount--;
177     if (touchCount <= 0) {
178       reset();
179     } else {
180       if (state == State.TWO_FINGER) {
181         state = State.ONE_FINGER;
182       } else {
183         if (touchCount == 2) {
184           state = State.TWO_FINGER;
185         }
186       }
187     }
188   }
189 
190   protected EventPropagator getEventPropagator() {
191     if (eventPropagator == null) {
192       if (DEFAULT_EVENT_PROPAGATOR == null) {
193         DEFAULT_EVENT_PROPAGATOR = GWT.create(EventPropagator.class);
194       }
195       eventPropagator = DEFAULT_EVENT_PROPAGATOR;
196     }
197     return eventPropagator;
198   }
199 
200   protected void setEventPropagator(EventPropagator eventPropagator) {
201     this.eventPropagator = eventPropagator;
202 
203   }
204 
205   private void reset() {
206     touchCount = 0;
207     state = State.READY;
208 
209   }
210 
211 }