Пример #1
0
        private void HandleTimelineWidgetTouch(Touch touch)
        {
            /*
             * The timeline interaction code and statemachine is a little bit complex and has several conditional checks for each widget that constraint the timeline
             * Each timeline has three widgets, LEFT_WIDGET, MID_WIDGET, RIGHT_WIDGET.
             * LEFT and RIGHT _WIDGET control the left and right zoom and operate symmetrically - the equations are nearly the same and their state machine is identical.

             * The constraints ensure that the L_RANGE should not be greater than 0, and the R_RANGE should not be less than 1.
             * This constraint ensures that the timeline range is never out of bounds, inverted (e.g. R_RANGE less than L_RANGE), etc.
             * The timeline works on a single touch interaction. Whenever the user slides their finger along the timeline, something should happen.
             * Either it should zoom or it should slide.
             *
             * Here is how the LEFT_WIDGET operates (from here on out called L_W).
             *     In the constraint condition, when L_RANGE = 0:
             * (1) - if the user drags L_W to the left, the timeline should "zoom in" by moving L_RANGE to the left.
             * (2) - if the user drags L_W to the right, it should zoom the other way, moving R_RANGE to the right.
             *     In the arbitrary state (L_RANGE < 0):
             * (3) - if the user drags L_W either left or right, timeline should zoom between L_RANGE and 1 (not between L_RANGE & R_RANGE)
             *       in other words, if year 2000 is at the very rightmost edge of the timeline (at position 1), year 2000 should stay at that position.
             *       This means that R_RANGE has to be moved along with L_RANGE. The trivial situation is if R_RANGE = 1, then it stays where it is.
             *       Bug if 1 < R_RANGE, then it has to be moved to keep 2000 at position 1, following this equation that can be derived without much difficulty:
             *       Let L & R be the old L_RANGE and R_RANGE values. _L & _R are the new L_RANGE and R_RANGE values. In this case, we are solving for _R, all others are known.
             *       Let x = (1 - L) / (R - L) be the proportion of the timeline that is between L and 1 (the year at 1 should be clamped)
             *       When L_RANGE moves, this proportion should stay the same, so that x = (1 - _L) / (_R - _L) giving:
             *       _R = _L + (R - L) * (1 - _L) / (1 - L);
             * (4) - there is a special condition when L_RANGE starts < 0 and reaches its constraint L_RANGE = 0.
             *       In this case, the timeline should start zooming by moving R_RANGE to the right. There is a minor bug related to (touch.X - touch.OriginX)
             *       that causes R_RANGE to suddenly jump when this condition is reached. If we add catalogTimelineRange[L_OFFSET] to the R_RANGE, then it behaves smoothly.
             *
             * The behavior of RIGHT_WIDGET is identical to L_W. The equation in (3) is slightly different but the derivation is identical.
             * Condition (4) is also changed. We must add (catalogTimelineRange[R_OFFSET] - 1) to ensure continuity of L_RANGE.
             *
             * MIDDLE_WIDGET has simple behavior because it changes L_RANGE and R_RANGE by the same values causing the timeline to slide.
             * If L_RANGE or R_RANGE reach their constraint, they are simply clamped to either 0 or 1 but the other value continues to move, creating the situation of zooming.
            */

            float L, R; // old L & R range values
            float _L, _R; // new L & R range values

            // Catalog widget
            // copy the current RANGE values, these are now the "old" values
            L = catalogTimelineRange[L_RANGE];
            R = catalogTimelineRange[R_RANGE];
            // Only LEFT_WIDGET behavior is documented, RIGHT_WIDGET has symmetrical behavior.
            if (touch.OriginObject == catalogWidgets[LEFT_WIDGET])
            {
                // compute new L range
                _L = catalogTimelineRange[L_OFFSET] + (touch.X - touch.OriginX) / size.X;
                if (0 < _L)
                {
                    // L_RANGE can't be greater than 0. this handles state (2) and (4)
                    _L = 0;
                    _R = catalogTimelineRange[R_OFFSET] + (touch.X - touch.OriginX) / size.X + catalogTimelineRange[L_OFFSET];
                    if (_R < 1)
                    {
                        // sanity check, make SURE that R_RANGE is not less than 1.
                        _R = 1;
                    }
                }
                else
                {
                    // state (3)
                    _R = _L + (R - L) * (1 - _L) / (1 - L);
                }
                // copy the new RANGE values to the array
                catalogTimelineRange[L_RANGE] = _L;
                catalogTimelineRange[R_RANGE] = _R;
                UpdateTopCurves();
            }
            else if (touch.OriginObject == catalogWidgets[RIGHT_WIDGET])
            {
                _R = catalogTimelineRange[R_OFFSET] + (touch.X - touch.OriginX) / size.X;
                if (_R < 1)
                {
                    _R = 1;
                    _L = catalogTimelineRange[L_OFFSET] + (touch.X - touch.OriginX) / size.X + (catalogTimelineRange[R_OFFSET] - 1);
                    if (0 < _L)
                    {
                        _L = 0;
                    }
                }
                else
                {
                    _L = _R - _R * (R - L) / R;
                }
                catalogTimelineRange[L_RANGE] = _L;
                catalogTimelineRange[R_RANGE] = _R;
                UpdateTopCurves();
            }
            else if (touch.OriginObject == catalogWidgets[MID_WIDGET])
            {
                // move L_RANGE and R_RANGE by the same amount
                _L = catalogTimelineRange[L_OFFSET] + (touch.X - touch.OriginX) / size.X;
                _R = catalogTimelineRange[R_OFFSET] + (touch.X - touch.OriginX) / size.X;
                // check the constraint on each
                if (0 < _L)
                {
                    _L = 0;
                }
                if (_R < 1)
                {
                    _R = 1;
                }
                catalogTimelineRange[L_RANGE] = _L;
                catalogTimelineRange[R_RANGE] = _R;
                UpdateTopCurves();
            }
            // update tick scale
            catalogTickSkipScale = ComputeTickScale(catalogTicks.Count / (catalogTimelineRange[R_RANGE] - catalogTimelineRange[L_RANGE]), 1, 40, 80, 200);
            //Console.WriteLine("C_Tick: " + catalogTickSkipScale + "\tC_R: " + catalogTimelineRange[R_RANGE] + "\tC_L: " + catalogTimelineRange[L_RANGE]);

            // use widgets
            L = useTimelineRange[L_RANGE];
            R = useTimelineRange[R_RANGE];
            if (touch.OriginObject == useWidgets[LEFT_WIDGET])
            {
                _L = useTimelineRange[L_OFFSET] + (touch.X - touch.OriginX) / size.X;
                if (0 < _L)
                {
                    _L = 0;
                    _R = useTimelineRange[R_OFFSET] + (touch.X - touch.OriginX) / size.X + useTimelineRange[L_OFFSET];
                    if (_R < 1)
                    {
                        _R = 1;
                    }
                }
                else
                {
                    _R = _L + (R - L) * (1 - _L) / (1 - L);
                }
                useTimelineRange[L_RANGE] = _L;
                useTimelineRange[R_RANGE] = _R;
                UpdateTopCurves();
                UpdateBottomCurves();
            }
            else if (touch.OriginObject == useWidgets[RIGHT_WIDGET])
            {
                _R = useTimelineRange[R_OFFSET] + (touch.X - touch.OriginX) / size.X;
                if (_R < 1)
                {
                    _R = 1;
                    _L = useTimelineRange[L_OFFSET] + (touch.X - touch.OriginX) / size.X + (useTimelineRange[R_OFFSET] - 1);
                    if (0 < _L)
                    {
                        _L = 0;
                    }
                }
                else
                {
                    _L = _R - _R * (R - L) / R;
                }
                useTimelineRange[L_RANGE] = _L;
                useTimelineRange[R_RANGE] = _R;
                UpdateTopCurves();
                UpdateBottomCurves();
            }
            else if (touch.OriginObject == useWidgets[MID_WIDGET])
            {
                _L = useTimelineRange[L_OFFSET] + (touch.X - touch.OriginX) / size.X;
                _R = useTimelineRange[R_OFFSET] + (touch.X - touch.OriginX) / size.X;
                if (0 < _L)
                {
                    _L = 0;
                }
                if (_R < 1)
                {
                    _R = 1;
                }
                useTimelineRange[L_RANGE] = _L;
                useTimelineRange[R_RANGE] = _R;
                UpdateTopCurves();
                UpdateBottomCurves();
            }
            useTickSkipScale = ComputeTickScale(useTicks.Count / (useTimelineRange[R_RANGE] - useTimelineRange[L_RANGE]), 5, 40, 80, 200);
            //Console.WriteLine("Use_T: " + useTickSkipScale + "\tUse_R: " + useTimelineRange[R_RANGE] + "\tUse_L: " + useTimelineRange[L_RANGE]);

            // manufacture widgets
            L = manufactureTimelineRange[L_RANGE];
            R = manufactureTimelineRange[R_RANGE];
            if (touch.OriginObject == manufactureWidgets[LEFT_WIDGET])
            {
                _L = manufactureTimelineRange[L_OFFSET] + (touch.X - touch.OriginX) / size.X;
                if (0 < _L)
                {
                    _L = 0;
                    _R = manufactureTimelineRange[R_OFFSET] + (touch.X - touch.OriginX) / size.X + manufactureTimelineRange[L_OFFSET];
                    if (_R < 1)
                    {
                        _R = 1;
                    }
                }
                else
                {
                    _R = _L + (R - L) * (1 - _L) / (1 - L);
                }
                manufactureTimelineRange[L_RANGE] = _L;
                manufactureTimelineRange[R_RANGE] = _R;
                UpdateBottomCurves();
            }
            else if (touch.OriginObject == manufactureWidgets[RIGHT_WIDGET])
            {
                _R = manufactureTimelineRange[R_OFFSET] + (touch.X - touch.OriginX) / size.X;
                if (_R < 1)
                {
                    _R = 1;
                    _L = manufactureTimelineRange[L_OFFSET] + (touch.X - touch.OriginX) / size.X + (manufactureTimelineRange[R_OFFSET] - 1);
                    if (0 < _L)
                    {
                        _L = 0;
                    }
                }
                else
                {
                    _L = _R - _R * (R - L) / R;
                }
                manufactureTimelineRange[L_RANGE] = _L;
                manufactureTimelineRange[R_RANGE] = _R;
                UpdateBottomCurves();
            }
            else if (touch.OriginObject == manufactureWidgets[MID_WIDGET])
            {
                _L = manufactureTimelineRange[L_OFFSET] + (touch.X - touch.OriginX) / size.X;
                _R = manufactureTimelineRange[R_OFFSET] + (touch.X - touch.OriginX) / size.X;
                if (0 < _L)
                {
                    _L = 0;
                }
                if (_R < 1)
                {
                    _R = 1;
                }
                manufactureTimelineRange[L_RANGE] = _L;
                manufactureTimelineRange[R_RANGE] = _R;
                UpdateBottomCurves();
            }
            manufactureTickSkipScale = ComputeTickScale(manufactureTicks.Count / (manufactureTimelineRange[R_RANGE] - manufactureTimelineRange[L_RANGE]), 5, 40, 80, 200);
        }
Пример #2
0
 void touchTarget_TouchDown(object sender, TouchEventArgs e)
 {
     lock (touchPoints)
     {
         if (touchPoints.Count == 0)
         {
             Touch touch = new Touch(e);
             touchPoints.Add(e.Id, touch);
         }
         /* extra safety checks
         if (touchPoints.ContainsKey(t.Id) == false)
         {
             touchPoints.Add(e.TouchPoint.Id, e.TouchPoint);
         }
         //*/
     }
 }