Logo Search packages:      
Sourcecode: whitedune version File versions  Download package

ChannelView.cpp

/*
 * ChannelView.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include <stdlib.h>

#include "stdafx.h"

#include "ChannelView.h"
#include "swt.h"
#include "Scene.h"
#include "Node.h"
#include "Interpolator.h"
#include "Path.h"

#define GRID_MIN  15        // minimum distance between grid lines
#define BORDER_WIDTH    20        // border at right and bottom edges
#define X_RULER_WIDTH   20        // width of horizontal ruler
#define Y_RULER_WIDTH   40        // width of vertical ruler
#define TICK_SIZE 3         // size of "ticks" in ruler

#define TO_WORLD_X(x)       (((x) - _rect.left) / (float) _rect.Width())
#define TO_WORLD_Y(y)       ((_rect.bottom - (y)) / (float) _rect.Height())
#define TO_VALUE(y)         ((TO_WORLD_Y(y) / _yScale) + _yMin)
#define TO_SCREEN_X(x)      (_rect.left + (int) ((x) * _rect.Width() + 0.5f))
#define TO_SCREEN_Y(y)      (_rect.bottom - (int) ((y) * _rect.Height() + 0.5f))

#define AUTOSCROLL_MARGIN   10
#define AUTOSCROLL_AMOUNT   20

static int      currentX, currentY;

// maximum size for drawing to bitmap
#define MAX_BACK_WIDTH 500
#define MAX_BACK_HEIGHT 500

#define MoveTo(dc, x, y)        { currentX = x; currentY = y; }
#define LineTo(dc, x, y)        { swDrawLine(dc, currentX, currentY, x, y); \
                                  currentX = x; currentY = y; }
#ifdef BLINKTIMER
static int
timerCallback(void *data)
{
    return ((ChannelView *) data)->OnBlinkTimer();
}
#endif

static int
autoScrollCallback(void *data)
{
    return ((ChannelView *) data)->OnAutoScrollTimer();
}

ChannelView::ChannelView(Scene *scene, SWND wnd)
  : SceneView(scene, wnd)
{
    int           width, height;
    swGetSize(_wnd, &width, &height);

    _state = NORMAL;
    _interpolator = NULL;
    _selectedChannel = -1;
    _selectedKey = -1;

    _yMin = _yMax = 0.0f;
    _yScale = _xScale = 1.0f;
    _anchor = 0;
    _selMin = _selMax = 0;

#ifdef BLINKTIMER
    _timer = swSetTimer(_wnd, 500, timerCallback, this);
#endif
    _autoScrollTimer = NULL;
    _cursorOn = true;
    _autoScrolling = false;

    _rect.left = Y_RULER_WIDTH;
    _rect.top = X_RULER_WIDTH;
}

ChannelView::~ChannelView()
{
#ifdef BLINKTIMER
    if (_timer) swKillTimer(_timer);
#endif
    if (_autoScrollTimer) swKillTimer(_autoScrollTimer);
}

void ChannelView::OnSize(int width, int height)
{
    swInvalidateWindow(_wnd);
}

void ChannelView::OnDraw(int x, int y, int width, int height)
{
    SDC dc = swCreateDC(_wnd);
//    swSetClipRect(dc, x, y, width, height);
    swSetFGColor(dc, swGetWindowColor(_wnd, SW_COLOR_WINDOW_BG));
    swFillRect(dc, x, y, width, height);

    int           w, h;
    swGetSize(_wnd, &w, &h);

    _rect.right = w - BORDER_WIDTH;
    _rect.bottom = h - BORDER_WIDTH;

    if (_interpolator && _rect.Width() > 0 && _rect.Height() > 0) {
      // draw selection
      DrawSelection(dc);
      DrawRulers(dc, 0.0f, 1.0f, _yMin, _yMax);
      DrawKeys(dc);
    }
    swDestroyDC(dc);
}

void
ChannelView::AutoScale()
{
    if (!_interpolator) return;

    int         numChannels = _interpolator->getNumChannels();
    int         numKeys = _interpolator->getNumKeys();

    _yMin = 0.0f;
    _yMax = 1.0f;

    for (int chan = 0; chan < numChannels; chan++) {
      for (int k = 0; k < numKeys; k++) {
          float       value = _interpolator->getKeyValue(chan, k);
          if (value < _yMin) _yMin = value;
          if (value > _yMax) _yMax = value;
      }
    }

    _yScale = 1.0f / (_yMax - _yMin);
}

// reduce drawing/use of MFVec?f Interpolators
// only draw/use channels with different keyValues

bool                
ChannelView::isDrawableChannel(int chan)
{
    if (_interpolator->getNumChannels() > 4)
        if (_multipleValuesInChannel[chan])
            return true;
        else
            return false;
    return true;
}

void
ChannelView::findDrawableChannels(void)
{
    int     numChannels = _interpolator->getNumChannels();
    int     numKeys = _interpolator->getNumKeys();

    _multipleValuesInChannel.resize(numChannels);
    int maxNumChannels = TheApp->GetMaxKeysInChannelView();
    int channelCount = 0;

    for (int chan = 0; chan < numChannels; chan++) {
        _multipleValuesInChannel[chan] = false;
        if (maxNumChannels > 0)
            if (channelCount > maxNumChannels)
                continue; 
        float firstKey;
        if (numKeys > 0)
            firstKey = _interpolator->getKeyValue(chan, 0);
        else 
            continue;
        for (int j = 1; j < numKeys; j++)
            if (_interpolator->getKeyValue(chan, j) != firstKey) {
                _multipleValuesInChannel[chan] = true;
                channelCount++;
                continue;
            }
    }
    // if no channel is changed, use first channel
    if ((numChannels > 0) && (channelCount == 0))
        _multipleValuesInChannel[0] = true;
}

void
ChannelView::DrawKeys(SDC dc)
{
    int     numChannels = _interpolator->getNumChannels();
    int     numKeys = _interpolator->getNumKeys();

    for (int chan = 0; chan < numChannels; chan++) {
        if (!isDrawableChannel(chan))
            continue;

      int         y = _rect.bottom;
      float       key;

        if ((numChannels == 4) && (chan == 3))
          swSetFGColor(dc, 0x00FF00FF); // only for SFRotation
        else
            swSetFGColor(dc, 0x000000FF << ((chan % 3) * 8));
      MoveTo(dc, _rect.left, y);
      for (int k = 0; k < numKeys; k++) {
          key = _interpolator->getKey(k);
          float       value = _interpolator->getKeyValue(chan, k);
          int               x = TO_SCREEN_X(key);

          // normalize value into range 0..1
          float       sv = (value - _yMin) * _yScale;

          y = TO_SCREEN_Y(sv);
          if (k == 0) MoveTo(dc, _rect.left, y);
          LineTo(dc, x, y);
          swFillRect(dc, x - 2, y - 2, 5, 5);
      }
      LineTo(dc, _rect.right, y);
    }
}

static float getStep(float ratio)
{
    float   step = 1.0f;

    while (ratio > GRID_MIN) {
      ratio *= 0.1f;
      step *= 0.1f;
    }

    while (ratio < GRID_MIN) {
      ratio *= 10.0f;
      step *= 10.0f;
    }

    return step;
}

void
ChannelView::DrawRulers(SDC dc, float xMin, float xMax, float yMin, float yMax)
{
    float   xRatio = (float) _rect.Width() / (xMax - xMin);
    float   yRatio = (float) _rect.Height() / (yMax - yMin);

    // find nearest power of 10 

    float   xStep = getStep(xRatio);
    float   yStep = getStep(yRatio);

    int           grey = swGetWindowColor(_wnd, SW_COLOR_BSHADOW);
    char    buf[32];
    int           theight = swGetFontHeight(swGetDefaultFont());

    swSetFGColor(dc, grey);
    swFillRect(dc, 0, 0, _rect.left, _rect.bottom +BORDER_WIDTH);
    swFillRect(dc, _rect.left, 0, _rect.right+BORDER_WIDTH, _rect.top);
    swDrawLine(dc, _rect.left, _rect.top-1, _rect.right, _rect.top-1);
    for (float x = xStep * (float) floor(xMin / xStep);
             x < xMax + 0.001;
             x += xStep) {
      int scrx = (int) (_rect.left + x * xRatio + 0.5f);
      swSetFGColor(dc, 0);
      swDrawLine(dc, scrx, _rect.top, scrx, _rect.top - TICK_SIZE);
      sprintf(buf, "%g", x);
      int twidth = swGetStringWidth(swGetDefaultFont(), buf);
      swDrawText(dc, scrx - twidth / 2, _rect.top - TICK_SIZE,
                buf);
    }
    MoveTo(dc, _rect.left-1, _rect.top);
    LineTo(dc, _rect.left-1, _rect.bottom);
    for (float y = yStep * (float) floor(yMin / yStep);
               y < yMax + 0.001;
             y += yStep) {
      int   scry = (int) (_rect.bottom - (y - yMin) * yRatio + 0.5f);
      swSetFGColor(dc, 0);
      swDrawLine(dc, _rect.left, scry, _rect.left - TICK_SIZE, scry);
      sprintf(buf, "%g", y);
      int twidth = swGetStringWidth(swGetDefaultFont(), buf);
      swDrawText(dc, _rect.left - TICK_SIZE - twidth, scry + theight / 2,
                buf);
    }
}

void
ChannelView::DrawSelection(SDC dc)
{
    if (_selMin != _selMax || _cursorOn) {
      swSetFGColor(dc, 0);
      swFillRect(dc, _rect.left + _selMin, _rect.top,
               _selMax - _selMin+1, _rect.Height());
    }
}

void ChannelView::OnLButtonDown(int px, int py, int modifiers) 
{
    const Interpolator   *node = _interpolator;

    if (node == NULL) return;

    int         numChannels = node->getNumChannels();
    int         numKeys = node->getNumKeys();

    for (int i = 0; i < numKeys; i++) {
      float     key = _interpolator->getKey(i);
      int       x = TO_SCREEN_X(key);
      if (px >= x - 3 && px <= x + 3) {
          for (int chan = numChannels - 1; chan >= 0; chan--) {
                if (!isDrawableChannel(chan))
                    continue;

            float     value = node->getKeyValue(chan, i);

            // normalize value into range 0..1
            float     sv = (value - _yMin) * _yScale;
            int             y = TO_SCREEN_Y(sv);

            if (py >= y - 3 && py <= y + 3) {
                _selectedChannel = chan;
                _selectedKey = i;
                _state = DRAGGING;
                swSetCapture(_wnd);
                _interpolator->backupKey(_selectedKey);
                return;
            }
          }
      }
    }

    // no keys picked; check for line picking
    for (int chan = numChannels - 1 ; chan >= 0; chan--) {
        if (!isDrawableChannel(chan))
            continue;
            int         x1 = _rect.left, y1 = TO_SCREEN_Y(0.0f);
      int         k;

      for (k = 0; k < numKeys; k++) {
          float       key = node->getKey(k);
          float       value = node->getKeyValue(chan, k);
          int               x2 = TO_SCREEN_X(key);

          // normalize value into range 0..1
          float       sv = (value - _yMin) * _yScale;

          int               y2 = TO_SCREEN_Y(sv);

          if (k == 0) y1 = y2;

          if (PointNearLine(px, py, x1, y1, x2, y2)) {
            AddKey(chan, k, px);
            return;
          }
          x1 = x2;  y1 = y2;
      }

      // check last line segment
      if (PointNearLine(px, py, x1, y1, _rect.right, y1)) {
          AddKey(chan, k, px);
          return;
      }
    }

    // no keys picked; do a drag-select

    _anchor = _lastX = (px - _rect.left);
    SetSelection(_anchor);
    _interpolator->sendInterpolatedValue(0.0, _selMin / (float) (_rect.Width() - 1));
    _state = SELECTING;
    _scene->setViewOfLastSelection(this);
    swSetCapture(_wnd);
}

void ChannelView::AddKey(int chan, int key, int x)
{
    // picked a line; create a new key at that point
    float   newKey = TO_WORLD_X(x);
    float      *values = new float[_interpolator->getNumChannels()];
    _interpolator->interpolate(newKey, values);
    _interpolator->insertKey(key, newKey, values);
    delete [] values;
    _selectedChannel = chan;
    _selectedKey = key;
    _state = DRAGGING;
    swSetCapture(_wnd);
}

void ChannelView::OnLButtonUp(int x, int y, int modifiers) 
{
    if (_autoScrolling) {   // stop scrolling
      swKillTimer(_autoScrollTimer);
      _autoScrollTimer = NULL;
      _autoScrolling = false;
      AutoScale();
      swInvalidateWindow(_wnd);
    }
    if (_state == DRAGGING || _state == SELECTING) {
      swReleaseCapture(_wnd);
      _state = NORMAL;  
    }
}

void ChannelView::OnMouseMove(int x, int y, int modifiers) 
{
    if (_state == DRAGGING || _state == SELECTING) {
      CheckAutoScroll(x, y);
    }
    DoMouseMove(x, y);
}

void ChannelView::DoMouseMove(int px, int py)
{
    if (_state == DRAGGING) {
      float key = TO_WORLD_X(px);
      float value = TO_VALUE(py);
      if (_selectedKey > 0) {
          float prevKey = _interpolator->getKey(_selectedKey - 1);
          if (key < prevKey) key = prevKey;
      }
      if (_selectedKey < _interpolator->getNumKeys() - 1) {
          float nextKey = _interpolator->getKey(_selectedKey + 1);
          if (key > nextKey) key = nextKey;
      }
      _interpolator->setKey(_selectedKey, key);
      _interpolator->setKeyValue(_selectedChannel, _selectedKey, value);
      _interpolator->sendInterpolatedValue(0.0, _interpolator->getFraction());
    } else if (_state == SELECTING) {
      int       x = px - _rect.left;
      x = CLAMP(x, 0, _rect.Width() -1);
      int min = MIN(_lastX, x);
      int max = MAX(_lastX, x);
      swInvalidateRect(_wnd, _rect.left + min, _rect.top,
                         max - min + 1, _rect.Height());
      if (x < _anchor) {
          _selMin = x;
          _selMax = _anchor;
      } else {
          _selMin = _anchor;
          _selMax = x;
      }
      _lastX = x;
      _interpolator->sendInterpolatedValue(0.0, x / (float) (_rect.Width() - 1));
    }
}

void
ChannelView::InvalidateSelection()
{
    swInvalidateRect(_wnd, _rect.left + _selMin, _rect.top,
                     _selMax - _selMin + 1, _rect.Height());
}

#ifdef BLINKTIMER
int ChannelView::OnBlinkTimer() 
{
    if (_selMin == _selMax && !_scene->isRunning()) {
      _cursorOn = !_cursorOn;
      InvalidateSelection();
    }
    return TRUE;
}
#endif

int ChannelView::OnAutoScrollTimer()
{
    AutoScale();
    DoMouseMove(_autoScrollPX, _autoScrollPY);
    return TRUE;
}

void ChannelView::OnUpdate(SceneView* sender, int type, Hint *hint) 
{
    const Path       *sel = _scene->getSelection();
    Node           *node = sel ? sel->getNode() : NULL;
    Interpolator   *interp = dynamic_cast_Interpolator(node);
    FieldUpdate      *fieldUpdate;
    NodeUpdate       *nodeUpdate;

    switch (type) {
      case UPDATE_ALL:
      swInvalidateWindow(_wnd);
      break;
      case UPDATE_SELECTION:
      if (interp != NULL && _interpolator != interp) {
          _interpolator = interp;
          AutoScale();
            findDrawableChannels();
//            _scene->setViewOfLastSelection(NULL);
          swInvalidateWindow(_wnd);
      } 
      break;
      case UPDATE_FIELD:
      fieldUpdate = (FieldUpdate *) hint;
      if (fieldUpdate->node == _interpolator) {
          AutoScale();
            findDrawableChannels();
          swInvalidateWindow(_wnd);
      }
      break;
      case UPDATE_ADD_NODE:
      break;
      case UPDATE_REMOVE_NODE:
      nodeUpdate = (NodeUpdate *) hint;
      if (nodeUpdate->node == _interpolator) {
          _interpolator = NULL;
          swInvalidateWindow(_wnd);
      }
      break;
      case UPDATE_ADD_ROUTE:
      case UPDATE_DELETE_ROUTE:
      case UPDATE_MODE:
      break;
      case UPDATE_TIME:
        if (_interpolator) {
          SetSelection((int) (_interpolator->getFraction() 
                        * (_rect.Width() - 1)));
      }
      break;
    }
}

bool ChannelView::PointNearLine(int x, int y, int x1, int y1, int x2, int y2) const
{
    if (x1 != x2) {
      float     slope = (y2 - y1) / (float) (x2 - x1);
      float     v = y1 + slope * (x - x1);

      if (x >= x1 && x <= x2 && y >= v - 3.0f && y <= v + 3.0f) {
          return true;
      }
    }
    return false;
}

void ChannelView::OnFastForward() 
{
    if (_interpolator) {
      _interpolator->sendInterpolatedValue(0.0, 1.0f);
      SetSelection(_rect.Width()-1);
    }
}

void ChannelView::OnRewind() 
{
    if (_interpolator) {
      _interpolator->sendInterpolatedValue(0.0, 0.0f);
      SetSelection(0);
    }
}

void ChannelView::SetSelection(int pos)
{
    InvalidateSelection();
    _selMin = _selMax = pos;
    _cursorOn = true;
    InvalidateSelection();
}

void ChannelView::OnEditDelete() 
{
    if (_interpolator) {
      int       min = _interpolator->findKeyInclusive(_selMin / (float) (_rect.Width() - 1));
      int       max = _interpolator->findKey(_selMax / (float) (_rect.Width() - 1));
      _interpolator->deleteKeys(min, max);
    }
}

#if 0
void ChannelView::OnUpdateEditDelete(CCmdUI* pCmdUI) 
{
    if (_interpolator) {
      int       min = _interpolator->findKeyInclusive(_selMin / (float) (_rect.Width() - 1));
      int       max = _interpolator->findKey(_selMax / (float) (_rect.Width() - 1));

      pCmdUI->Enable(min < max);
    } else {
      pCmdUI->Enable(FALSE);
    }
}
#endif

void ChannelView::CheckAutoScroll(int px, int py)
{
    int         width, height;
    bool    autoScroll = true;

    swGetSize(_wnd, &width, &height);
    _autoScrollPX = px;
    _autoScrollPY = py;

    if (px < AUTOSCROLL_MARGIN) {
        autoScroll = true;
    } else if (px > width - AUTOSCROLL_MARGIN) {
        autoScroll = true;
    } else if (py < AUTOSCROLL_MARGIN) {
        autoScroll = true;
    } else if (py > height - AUTOSCROLL_MARGIN) {
        autoScroll = true;
    } else {
        autoScroll = false;
    }

    if (autoScroll && !_autoScrolling) {   // start scrolling
      _autoScrollTimer = swSetTimer(_wnd, 100, autoScrollCallback, this);
      _autoScrolling = true;
    }
    if (!autoScroll && _autoScrolling) {   // stop scrolling
      swKillTimer(_autoScrollTimer);
        _autoScrollTimer = NULL;
      _autoScrolling = false;
    }
}

void ChannelView::DeleteLastSelection(void)
{
    OnEditDelete();
}

Generated by  Doxygen 1.6.0   Back to index