/* * 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 #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(); }