Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "shifttracetool.h"
Toshihiro Shimizu 890ddd
#include "toonz/onionskinmask.h"
Toshihiro Shimizu 890ddd
#include "toonz/tonionskinmaskhandle.h"
Toshihiro Shimizu 890ddd
#include "tools/cursors.h"
Toshihiro Shimizu 890ddd
#include "timage.h"
Toshihiro Shimizu 890ddd
#include "trasterimage.h"
Toshihiro Shimizu 890ddd
#include "ttoonzimage.h"
Toshihiro Shimizu 890ddd
#include "tvectorimage.h"
Toshihiro Shimizu 890ddd
#include "toonz/txsheet.h"
Toshihiro Shimizu 890ddd
#include "toonz/txshcell.h"
Toshihiro Shimizu 890ddd
#include "toonz/txsheethandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/tframehandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/tcolumnhandle.h"
shun-iwasawa 2b24e8
#include "toonz/txshlevelhandle.h"
shun-iwasawa 1d5937
#include "tools/toolhandle.h"
shun-iwasawa 2b24e8
#include "toonz/txshsimplelevel.h"
Toshihiro Shimizu 890ddd
#include "toonz/dpiscale.h"
shun-iwasawa 2b24e8
#include "toonz/stage.h"
Toshihiro Shimizu 890ddd
#include "tpixel.h"
Toshihiro Shimizu 890ddd
#include "toonzqt/menubarcommand.h"
Toshihiro Shimizu 890ddd
shun-iwasawa 2b24e8
#include "toonz/preferences.h"
shun-iwasawa 2b24e8
#include "toonzqt/gutil.h"
shun-iwasawa 2b24e8
Toshihiro Shimizu 890ddd
#include "tgl.h"
Toshihiro Shimizu 890ddd
#include <math.h></math.h>
shun-iwasawa 2b24e8
#include <qkeyevent></qkeyevent>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=============================================================================
Toshihiro Shimizu 890ddd
Campbell Barton ccd505
static bool circumCenter(TPointD &out, const TPointD &a, const TPointD &b,
Campbell Barton ccd505
                         const TPointD &c) {
Shinya Kitaoka 120a6e
  double d = 2 * (a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y));
Shinya Kitaoka 120a6e
  if (fabs(d) < 0.0001) {
Shinya Kitaoka 120a6e
    out = TPointD();
Shinya Kitaoka 120a6e
    return false;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  out.x = ((a.y * a.y + a.x * a.x) * (b.y - c.y) +
Shinya Kitaoka 120a6e
           (b.y * b.y + b.x * b.x) * (c.y - a.y) +
Shinya Kitaoka 120a6e
           (c.y * c.y + c.x * c.x) * (a.y - b.y)) /
Shinya Kitaoka 120a6e
          d;
Shinya Kitaoka 120a6e
  out.y = ((a.y * a.y + a.x * a.x) * (c.x - b.x) +
Shinya Kitaoka 120a6e
           (b.y * b.y + b.x * b.x) * (a.x - c.x) +
Shinya Kitaoka 120a6e
           (c.y * c.y + c.x * c.x) * (b.x - a.x)) /
Shinya Kitaoka 120a6e
          d;
Shinya Kitaoka 120a6e
  return true;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=============================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
ShiftTraceTool::ShiftTraceTool()
Shinya Kitaoka 120a6e
    : TTool("T_ShiftTrace")
Shinya Kitaoka 120a6e
    , m_ghostIndex(0)
Shinya Kitaoka 120a6e
    , m_curveStatus(NoCurve)
Shinya Kitaoka 120a6e
    , m_gadget(NoGadget)
Shinya Kitaoka 120a6e
    , m_highlightedGadget(NoGadget) {
Shinya Kitaoka 120a6e
  bind(TTool::AllTargets);  // Deals with tool deactivation internally
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ShiftTraceTool::clearData() {
Shinya Kitaoka 120a6e
  m_ghostIndex        = 0;
Shinya Kitaoka 120a6e
  m_curveStatus       = NoCurve;
Shinya Kitaoka 120a6e
  m_gadget            = NoGadget;
Shinya Kitaoka 120a6e
  m_highlightedGadget = NoGadget;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_box = TRectD();
Shinya Kitaoka 120a6e
  for (int i = 0; i < 2; i++) {
Shinya Kitaoka 120a6e
    m_row[i]    = -1;
Shinya Kitaoka 120a6e
    m_aff[i]    = TAffine();
Shinya Kitaoka 120a6e
    m_center[i] = TPointD();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ShiftTraceTool::updateBox() {
shun-iwasawa 2b24e8
  if (m_ghostIndex < 0 || 2 <= m_ghostIndex || m_row[m_ghostIndex] < 0) return;
shun-iwasawa 2b24e8
shun-iwasawa 2b24e8
  TImageP img;
shun-iwasawa 2b24e8
shun-iwasawa 1d5937
  TApplication *app = TTool::getApplication();
shun-iwasawa 2b24e8
  if (app->getCurrentFrame()->isEditingScene()) {
shun-iwasawa 2b24e8
    int col      = app->getCurrentColumn()->getColumnIndex();
Shinya Kitaoka 120a6e
    int row      = m_row[m_ghostIndex];
shun-iwasawa 2b24e8
    TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TXshCell cell       = xsh->getCell(row, col);
Shinya Kitaoka 120a6e
    TXshSimpleLevel *sl = cell.getSimpleLevel();
Shinya Kitaoka 120a6e
    if (sl) {
shun-iwasawa 2b24e8
      m_dpiAff = getDpiAffine(sl, cell.m_frameId);
shun-iwasawa 2b24e8
      img      = cell.getImage(false);
shun-iwasawa 2b24e8
    }
shun-iwasawa 2b24e8
  }
shun-iwasawa 2b24e8
  // on editing level
shun-iwasawa 2b24e8
  else {
shun-iwasawa 2b24e8
    TXshLevel *level = app->getCurrentLevel()->getLevel();
shun-iwasawa 2b24e8
    if (!level) return;
shun-iwasawa 2b24e8
    TXshSimpleLevel *sl = level->getSimpleLevel();
shun-iwasawa 2b24e8
    if (!sl) return;
shun-iwasawa 2b24e8
shun-iwasawa 2b24e8
    const TFrameId &ghostFid = sl->index2fid(m_row[m_ghostIndex]);
shun-iwasawa 2b24e8
    m_dpiAff                 = getDpiAffine(sl, ghostFid);
shun-iwasawa 2b24e8
    img                      = sl->getFrame(ghostFid, false);
shun-iwasawa 2b24e8
  }
shun-iwasawa 2b24e8
shun-iwasawa 2b24e8
  if (img) {
shun-iwasawa 2b24e8
    if (TRasterImageP ri = img) {
shun-iwasawa 2b24e8
      TRasterP ras = ri->getRaster();
shun-iwasawa 2b24e8
      m_box        = (convert(ras->getBounds()) - ras->getCenterD()) *
shun-iwasawa 2b24e8
              ri->getSubsampling();
shun-iwasawa 2b24e8
    } else if (TToonzImageP ti = img) {
shun-iwasawa 2b24e8
      TRasterP ras = ti->getRaster();
shun-iwasawa 2b24e8
      m_box        = (convert(ras->getBounds()) - ras->getCenterD()) *
shun-iwasawa 2b24e8
              ti->getSubsampling();
shun-iwasawa 2b24e8
    } else if (TVectorImageP vi = img) {
shun-iwasawa 2b24e8
      m_box = vi->getBBox();
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ShiftTraceTool::updateData() {
shun-iwasawa 2b24e8
  m_box = TRectD();
Shinya Kitaoka 120a6e
  for (int i = 0; i < 2; i++) m_row[i] = -1;
shun-iwasawa 80c2e7
  m_dpiAff          = TAffine();
shun-iwasawa 80c2e7
  TApplication *app = TTool::getApplication();
Shinya Kitaoka 120a6e
shun-iwasawa 1d5937
  OnionSkinMask osm  = app->getCurrentOnionSkin()->getOnionSkinMask();
shun-iwasawa 2b24e8
  int previousOffset = osm.getShiftTraceGhostFrameOffset(0);
shun-iwasawa 2b24e8
  int forwardOffset  = osm.getShiftTraceGhostFrameOffset(1);
Shinya Kitaoka 120a6e
  // we must find the prev (m_row[0]) and next (m_row[1]) reference images
Shinya Kitaoka 120a6e
  // (either might not exist)
Shinya Kitaoka 120a6e
  // see also stage.cpp, StageBuilder::addCellWithOnionSkin
shun-iwasawa 2b24e8
  if (app->getCurrentFrame()->isEditingScene()) {
shun-iwasawa 2b24e8
    TXsheet *xsh  = app->getCurrentXsheet()->getXsheet();
shun-iwasawa 2b24e8
    int row       = app->getCurrentFrame()->getFrame();
shun-iwasawa 2b24e8
    int col       = app->getCurrentColumn()->getColumnIndex();
shun-iwasawa 2b24e8
    TXshCell cell = xsh->getCell(row, col);
shun-iwasawa 2b24e8
    int r;
shun-iwasawa 2b24e8
    r = row + previousOffset;
shun-iwasawa 2b24e8
    if (r >= 0 && xsh->getCell(r, col) != cell &&
shun-iwasawa 2b24e8
        (cell.getSimpleLevel() == 0 ||
shun-iwasawa 2b24e8
         xsh->getCell(r, col).getSimpleLevel() == cell.getSimpleLevel())) {
shun-iwasawa 2b24e8
      m_row[0] = r;
shun-iwasawa 2b24e8
    }
Shinya Kitaoka 120a6e
shun-iwasawa 2b24e8
    r = row + forwardOffset;
shun-iwasawa 2b24e8
    if (r >= 0 && xsh->getCell(r, col) != cell &&
shun-iwasawa 2b24e8
        (cell.getSimpleLevel() == 0 ||
shun-iwasawa 2b24e8
         xsh->getCell(r, col).getSimpleLevel() == cell.getSimpleLevel())) {
shun-iwasawa 2b24e8
      m_row[1] = r;
shun-iwasawa 2b24e8
    }
shun-iwasawa 2b24e8
  }
shun-iwasawa 2b24e8
  // on editing level
shun-iwasawa 2b24e8
  else {
shun-iwasawa 2b24e8
    TXshLevel *level = app->getCurrentLevel()->getLevel();
shun-iwasawa 2b24e8
    if (level) {
shun-iwasawa 2b24e8
      TXshSimpleLevel *sl = level->getSimpleLevel();
shun-iwasawa 2b24e8
      if (sl) {
shun-iwasawa 2b24e8
        TFrameId fid = app->getCurrentFrame()->getFid();
shun-iwasawa 2b24e8
        int row      = sl->guessIndex(fid);
shun-iwasawa 2b24e8
        m_row[0]     = row + previousOffset;
shun-iwasawa 2b24e8
        m_row[1]     = row + forwardOffset;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  updateBox();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//
Toshihiro Shimizu 890ddd
// Compute m_aff[0] and m_aff[1] according to the current curve
Toshihiro Shimizu 890ddd
//
Shinya Kitaoka 120a6e
void ShiftTraceTool::updateCurveAffs() {
Shinya Kitaoka 120a6e
  if (m_curveStatus != ThreePointsCurve) {
Shinya Kitaoka 120a6e
    m_aff[0] = m_aff[1] = TAffine();
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    double phi0 = 0, phi1 = 0;
Shinya Kitaoka 120a6e
    TPointD center;
Shinya Kitaoka 120a6e
    if (circumCenter(center, m_p0, m_p1, m_p2)) {
Shinya Kitaoka 120a6e
      TPointD v0 = normalize(m_p0 - center);
Shinya Kitaoka 120a6e
      TPointD v1 = normalize(m_p1 - center);
Shinya Kitaoka 120a6e
      TPointD v2 = normalize(m_p2 - center);
Shinya Kitaoka 120a6e
      TPointD u0(-v0.y, v0.x);
Shinya Kitaoka 120a6e
      TPointD u1(-v1.y, v1.x);
Shinya Kitaoka 120a6e
      phi0 = atan2((v2 * u0), (v2 * v0)) * 180.0 / 3.1415;
Shinya Kitaoka 120a6e
      phi1 = atan2((v2 * u1), (v2 * v1)) * 180.0 / 3.1415;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    m_aff[0] = TTranslation(m_p2 - m_p0) * TRotation(m_p0, phi0);
Shinya Kitaoka 120a6e
    m_aff[1] = TTranslation(m_p2 - m_p1) * TRotation(m_p1, phi1);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ShiftTraceTool::updateGhost() {
Shinya Kitaoka 120a6e
  OnionSkinMask osm =
shun-iwasawa 1d5937
      TTool::getApplication()->getCurrentOnionSkin()->getOnionSkinMask();
Shinya Kitaoka 120a6e
  osm.setShiftTraceGhostAff(0, m_aff[0]);
Shinya Kitaoka 120a6e
  osm.setShiftTraceGhostAff(1, m_aff[1]);
Shinya Kitaoka 120a6e
  osm.setShiftTraceGhostCenter(0, m_center[0]);
Shinya Kitaoka 120a6e
  osm.setShiftTraceGhostCenter(1, m_center[1]);
shun-iwasawa 1d5937
  TTool::getApplication()->getCurrentOnionSkin()->setOnionSkinMask(osm);
shun-iwasawa 1d5937
}
shun-iwasawa 1d5937
shun-iwasawa 1d5937
void ShiftTraceTool::reset() {
shun-iwasawa 1d5937
  int ghostIndex = m_ghostIndex;
shun-iwasawa 1d5937
  onActivate();
shun-iwasawa 1d5937
  invalidate();
shun-iwasawa 1d5937
  m_ghostIndex = ghostIndex;
shun-iwasawa 1d5937
shun-iwasawa 1d5937
  TTool::getApplication()
shun-iwasawa 1d5937
      ->getCurrentTool()
shun-iwasawa 1d5937
      ->notifyToolChanged();  // Refreshes toolbar values
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TAffine ShiftTraceTool::getGhostAff() {
Shinya Kitaoka 120a6e
  if (0 <= m_ghostIndex && m_ghostIndex < 2)
Shinya Kitaoka 120a6e
    return m_aff[m_ghostIndex] * m_dpiAff;
Shinya Kitaoka 120a6e
  else
Shinya Kitaoka 120a6e
    return TAffine();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ShiftTraceTool::drawDot(const TPointD ¢er, double r,
Shinya Kitaoka 120a6e
                             const TPixel32 &color) {
Shinya Kitaoka 120a6e
  tglColor(color);
Shinya Kitaoka 120a6e
  tglDrawDisk(center, r);
Shinya Kitaoka 120a6e
  glColor3d(0.2, 0.2, 0.2);
Shinya Kitaoka 120a6e
  tglDrawCircle(center, r);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
shun-iwasawa 2b24e8
void ShiftTraceTool::drawControlRect() {  // TODO
Shinya Kitaoka 120a6e
  if (m_ghostIndex < 0 || m_ghostIndex > 1) return;
Shinya Kitaoka 120a6e
  int row = m_row[m_ghostIndex];
Shinya Kitaoka 120a6e
  if (row < 0) return;
shun-iwasawa 2b24e8
shun-iwasawa 2b24e8
  TRectD box = m_box;
shun-iwasawa 2b24e8
  if (box.isEmpty()) return;
Shinya Kitaoka 120a6e
  glPushMatrix();
Shinya Kitaoka 120a6e
  tglMultMatrix(getGhostAff());
shun-iwasawa 2b24e8
Shinya Kitaoka 120a6e
  TPixel32 color;
shun-iwasawa 2b24e8
shun-iwasawa 2b24e8
  // draw onion-colored rectangle to indicate which ghost is grabbed
shun-iwasawa 2b24e8
  {
shun-iwasawa 2b24e8
    TPixel32 frontOniColor, backOniColor;
shun-iwasawa 2b24e8
    bool inksOnly;
shun-iwasawa 2b24e8
    Preferences::instance()->getOnionData(frontOniColor, backOniColor,
shun-iwasawa 2b24e8
                                          inksOnly);
shun-iwasawa 2b24e8
    color       = (m_ghostIndex == 0) ? backOniColor : frontOniColor;
shun-iwasawa 2b24e8
    double unit = sqrt(tglGetPixelSize2());
shun-iwasawa f2e168
    unit *= getDevicePixelRatio(m_viewer->viewerWidget());
shun-iwasawa 2b24e8
    TRectD coloredBox = box.enlarge(3.0 * unit);
shun-iwasawa 2b24e8
    tglColor(color);
shun-iwasawa 2b24e8
    glBegin(GL_LINE_STRIP);
shun-iwasawa 2b24e8
    glVertex2d(coloredBox.x0, coloredBox.y0);
shun-iwasawa 2b24e8
    glVertex2d(coloredBox.x1, coloredBox.y0);
shun-iwasawa 2b24e8
    glVertex2d(coloredBox.x1, coloredBox.y1);
shun-iwasawa 2b24e8
    glVertex2d(coloredBox.x0, coloredBox.y1);
shun-iwasawa 2b24e8
    glVertex2d(coloredBox.x0, coloredBox.y0);
shun-iwasawa 2b24e8
    glEnd();
shun-iwasawa 2b24e8
  }
shun-iwasawa 2b24e8
shun-iwasawa 2b24e8
  color = m_highlightedGadget == TranslateGadget
shun-iwasawa 2b24e8
              ? TPixel32(200, 100, 100)
shun-iwasawa 2b24e8
              : m_highlightedGadget == RotateGadget ? TPixel32(100, 200, 100)
shun-iwasawa 2b24e8
                                                    : TPixel32(120, 120, 120);
Shinya Kitaoka 120a6e
  tglColor(color);
Shinya Kitaoka 120a6e
  glBegin(GL_LINE_STRIP);
Shinya Kitaoka 120a6e
  glVertex2d(box.x0, box.y0);
Shinya Kitaoka 120a6e
  glVertex2d(box.x1, box.y0);
Shinya Kitaoka 120a6e
  glVertex2d(box.x1, box.y1);
Shinya Kitaoka 120a6e
  glVertex2d(box.x0, box.y1);
Shinya Kitaoka 120a6e
  glVertex2d(box.x0, box.y0);
Shinya Kitaoka 120a6e
  glEnd();
shun-iwasawa 2b24e8
  color = m_highlightedGadget == ScaleGadget ? TPixel32(200, 100, 100)
shun-iwasawa 2b24e8
                                             : TPixel32::White;
Shinya Kitaoka 120a6e
  double r = 4 * sqrt(tglGetPixelSize2());
Shinya Kitaoka 120a6e
  drawDot(box.getP00(), r, color);
Shinya Kitaoka 120a6e
  drawDot(box.getP01(), r, color);
Shinya Kitaoka 120a6e
  drawDot(box.getP10(), r, color);
Shinya Kitaoka 120a6e
  drawDot(box.getP11(), r, color);
Shinya Kitaoka 120a6e
  if (m_curveStatus == NoCurve) {
shun-iwasawa 2b24e8
    color = m_highlightedGadget == MoveCenterGadget ? TPixel32(200, 100, 100)
shun-iwasawa 2b24e8
                                                    : TPixel32::White;
Shinya Kitaoka 120a6e
    TPointD c = m_center[m_ghostIndex];
Shinya Kitaoka 120a6e
    drawDot(c, r, color);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  glPopMatrix();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ShiftTraceTool::drawCurve() {
Shinya Kitaoka 120a6e
  if (m_curveStatus == NoCurve) return;
Shinya Kitaoka 120a6e
  double r = 4 * sqrt(tglGetPixelSize2());
Shinya Kitaoka 120a6e
  double u = getPixelSize();
Shinya Kitaoka 120a6e
  if (m_curveStatus == TwoPointsCurve) {
shun-iwasawa 2b24e8
    TPixel32 color = m_highlightedGadget == CurveP0Gadget
shun-iwasawa 2b24e8
                         ? TPixel32(200, 100, 100)
shun-iwasawa 2b24e8
                         : TPixel32::White;
Shinya Kitaoka 120a6e
    drawDot(m_p0, r, color);
Shinya Kitaoka 120a6e
    glColor3d(0.2, 0.2, 0.2);
Shinya Kitaoka 120a6e
    tglDrawSegment(m_p0, m_p1);
Shinya Kitaoka 120a6e
    drawDot(m_p1, r, TPixel32::Red);
Shinya Kitaoka 120a6e
  } else if (m_curveStatus == ThreePointsCurve) {
shun-iwasawa 2b24e8
    TPixel32 color = m_highlightedGadget == CurveP0Gadget
shun-iwasawa 2b24e8
                         ? TPixel32(200, 100, 100)
shun-iwasawa 2b24e8
                         : TPixel32::White;
Shinya Kitaoka 120a6e
    drawDot(m_p0, r, color);
shun-iwasawa 2b24e8
    color = m_highlightedGadget == CurveP1Gadget ? TPixel32(200, 100, 100)
shun-iwasawa 2b24e8
                                                 : TPixel32::White;
Shinya Kitaoka 120a6e
    drawDot(m_p1, r, color);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    glColor3d(0.2, 0.2, 0.2);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TPointD center;
Shinya Kitaoka 120a6e
    if (circumCenter(center, m_p0, m_p1, m_p2)) {
Shinya Kitaoka 120a6e
      double radius = norm(center - m_p1);
Shinya Kitaoka 120a6e
      glBegin(GL_LINE_STRIP);
Shinya Kitaoka 120a6e
      int n = 100;
Shinya Kitaoka 120a6e
      for (int i = 0; i < n; i++) {
Shinya Kitaoka 120a6e
        double t  = (double)i / n;
Shinya Kitaoka 120a6e
        TPointD p = (1 - t) * m_p0 + t * m_p2;
Shinya Kitaoka 120a6e
        p         = center + radius * normalize(p - center);
Shinya Kitaoka 120a6e
        tglVertex(p);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
      for (int i = 0; i < n; i++) {
Shinya Kitaoka 120a6e
        double t  = (double)i / n;
Shinya Kitaoka 120a6e
        TPointD p = (1 - t) * m_p2 + t * m_p1;
Shinya Kitaoka 120a6e
        p         = center + radius * normalize(p - center);
Shinya Kitaoka 120a6e
        tglVertex(p);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
      glEnd();
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      tglDrawSegment(m_p0, m_p1);
Shinya Kitaoka 120a6e
    }
shun-iwasawa 2b24e8
    color = m_highlightedGadget == CurvePmGadget ? TPixel32(200, 100, 100)
shun-iwasawa 2b24e8
                                                 : TPixel32::White;
Shinya Kitaoka 120a6e
    drawDot(m_p2, r, color);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
shun-iwasawa 1d5937
void ShiftTraceTool::onActivate() {
shun-iwasawa 1d5937
  m_ghostIndex  = 0;
shun-iwasawa 1d5937
  m_curveStatus = NoCurve;
shun-iwasawa 1d5937
  clearData();
shun-iwasawa 1d5937
  OnionSkinMask osm =
shun-iwasawa 1d5937
      TTool::getApplication()->getCurrentOnionSkin()->getOnionSkinMask();
shun-iwasawa 1d5937
  m_aff[0]    = osm.getShiftTraceGhostAff(0);
shun-iwasawa 1d5937
  m_aff[1]    = osm.getShiftTraceGhostAff(1);
shun-iwasawa 1d5937
  m_center[0] = osm.getShiftTraceGhostCenter(0);
shun-iwasawa 1d5937
  m_center[1] = osm.getShiftTraceGhostCenter(1);
shun-iwasawa 1d5937
}
shun-iwasawa 1d5937
shun-iwasawa 1d5937
void ShiftTraceTool::onDeactivate() {
shun-iwasawa 1e3399
  // Deactivating Shift and Trace mode resets the pseudo tool with keeping the
shun-iwasawa 1e3399
  // Edit Shift checkbox unchanged
shun-iwasawa 1e3399
  QAction *shiftTrace = CommandManager::instance()->getAction("MI_ShiftTrace");
shun-iwasawa 1e3399
  if (!shiftTrace->isChecked()) return;
shun-iwasawa 1d5937
  QAction *action = CommandManager::instance()->getAction("MI_EditShift");
shun-iwasawa 1d5937
  action->setChecked(false);
shun-iwasawa 1d5937
}
shun-iwasawa 1d5937
Shinya Kitaoka 120a6e
ShiftTraceTool::GadgetId ShiftTraceTool::getGadget(const TPointD &p) {
Shinya Kitaoka 120a6e
  std::vector<std::pair<tpointd, gadgetid="">> gadgets;</std::pair<tpointd,>
Shinya Kitaoka 120a6e
  gadgets.push_back(std::make_pair(m_p0, CurveP0Gadget));
Shinya Kitaoka 120a6e
  gadgets.push_back(std::make_pair(m_p1, CurveP1Gadget));
Shinya Kitaoka 120a6e
  gadgets.push_back(std::make_pair(m_p2, CurvePmGadget));
shun-iwasawa 2b24e8
  TAffine aff      = getGhostAff();
shun-iwasawa 2b24e8
  double pixelSize = getPixelSize();
shun-iwasawa 2b24e8
  double d         = 15 * pixelSize;  // offset for rotation handle
Shinya Kitaoka 120a6e
  if (0 <= m_ghostIndex && m_ghostIndex < 2) {
shun-iwasawa 2b24e8
    gadgets.push_back(std::make_pair(aff * m_box.getP00(), ScaleGadget));
shun-iwasawa 2b24e8
    gadgets.push_back(std::make_pair(aff * m_box.getP01(), ScaleGadget));
shun-iwasawa 2b24e8
    gadgets.push_back(std::make_pair(aff * m_box.getP10(), ScaleGadget));
shun-iwasawa 2b24e8
    gadgets.push_back(std::make_pair(aff * m_box.getP11(), ScaleGadget));
Shinya Kitaoka 120a6e
    gadgets.push_back(
Shinya Kitaoka 120a6e
        std::make_pair(aff * m_center[m_ghostIndex], MoveCenterGadget));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  int k           = -1;
shun-iwasawa 2b24e8
  double minDist2 = pow(10 * pixelSize, 2);
Shinya Kitaoka 120a6e
  for (int i = 0; i < (int)gadgets.size(); i++) {
Shinya Kitaoka 120a6e
    double d2 = norm2(gadgets[i].first - p);
Shinya Kitaoka 120a6e
    if (d2 < minDist2) {
Shinya Kitaoka 120a6e
      minDist2 = d2;
Shinya Kitaoka 120a6e
      k        = i;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  if (k >= 0) return gadgets[k].second;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // rect-point
Shinya Kitaoka 120a6e
  if (0 <= m_ghostIndex && m_ghostIndex < 2) {
shun-iwasawa 2b24e8
    TPointD q  = aff.inv() * p;
Shinya Kitaoka 120a6e
    double big = 1.0e6;
Shinya Kitaoka 120a6e
    double d = big, x = 0, y = 0;
Shinya Kitaoka 120a6e
    if (m_box.x0 < q.x && q.x < m_box.x1) {
Shinya Kitaoka 120a6e
      x         = q.x;
Shinya Kitaoka 120a6e
      double d0 = fabs(m_box.y0 - q.y);
Shinya Kitaoka 120a6e
      double d1 = fabs(m_box.y1 - q.y);
Shinya Kitaoka 120a6e
      if (d0 < d1) {
Shinya Kitaoka 120a6e
        d = d0;
Shinya Kitaoka 120a6e
        y = m_box.y0;
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        d = d1;
Shinya Kitaoka 120a6e
        y = m_box.y1;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    if (m_box.y0 < q.y && q.y < m_box.y1) {
shun-iwasawa 2b24e8
      double d0 = fabs(m_box.x0 - q.x);
shun-iwasawa 2b24e8
      double d1 = fabs(m_box.x1 - q.x);
Shinya Kitaoka 120a6e
      if (d0 < d) {
Shinya Kitaoka 120a6e
        d = d0;
Shinya Kitaoka 120a6e
        y = q.y;
Shinya Kitaoka 120a6e
        x = m_box.x0;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
      if (d1 < d) {
Shinya Kitaoka 120a6e
        d = d1;
Shinya Kitaoka 120a6e
        y = q.y;
Shinya Kitaoka 120a6e
        x = m_box.x1;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    if (d < big) {
Shinya Kitaoka 120a6e
      TPointD pp = aff * TPointD(x, y);
Shinya Kitaoka 120a6e
      double d   = norm(p - pp);
Shinya Kitaoka 120a6e
      if (d < 10 * getPixelSize()) {
shun-iwasawa 2b24e8
        if (m_box.contains(q))
shun-iwasawa 2b24e8
          return TranslateGadget;
shun-iwasawa 2b24e8
        else
shun-iwasawa 2b24e8
          return RotateGadget;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
shun-iwasawa 2b24e8
    if (m_box.contains(q))
shun-iwasawa 2b24e8
      return NoGadget_InBox;
shun-iwasawa 2b24e8
    else
shun-iwasawa 2b24e8
      return NoGadget;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  return NoGadget;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ShiftTraceTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
Shinya Kitaoka 120a6e
  GadgetId highlightedGadget = getGadget(pos);
Shinya Kitaoka 120a6e
  if (highlightedGadget != m_highlightedGadget) {
Shinya Kitaoka 120a6e
    m_highlightedGadget = highlightedGadget;
Shinya Kitaoka 120a6e
    invalidate();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ShiftTraceTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
Shinya Kitaoka 120a6e
  m_gadget = m_highlightedGadget;
Shinya Kitaoka 120a6e
  m_oldPos = m_startPos = pos;
Shinya Kitaoka 120a6e
shun-iwasawa 1d5937
  bool notify = false;
shun-iwasawa 1d5937
shun-iwasawa 80c2e7
  if (!e.isCtrlPressed() &&
shun-iwasawa 80c2e7
      (m_gadget == NoGadget || m_gadget == NoGadget_InBox)) {
shun-iwasawa 80c2e7
    if (m_gadget == NoGadget_InBox) {
shun-iwasawa 80c2e7
      m_gadget = TranslateGadget;
shun-iwasawa 80c2e7
    } else {
shun-iwasawa 80c2e7
      m_gadget = RotateGadget;
shun-iwasawa 2b24e8
    }
shun-iwasawa 80c2e7
shun-iwasawa 2d0135
    int row = getViewer()->posToRow(e.m_pos, 5.0, false, true);
Shinya Kitaoka 120a6e
    if (row >= 0) {
shun-iwasawa 1d5937
      int index         = -1;
shun-iwasawa 1d5937
      TApplication *app = TTool::getApplication();
shun-iwasawa 2b24e8
      if (app->getCurrentFrame()->isEditingScene()) {
shun-iwasawa 2b24e8
        int currentRow = getFrame();
shun-iwasawa 1d5937
        if (m_row[0] >= 0 && row < currentRow)
shun-iwasawa 2b24e8
          index = 0;
shun-iwasawa 2b24e8
        else if (m_row[1] >= 0 && row > currentRow)
shun-iwasawa 2b24e8
          index = 1;
shun-iwasawa 2b24e8
      } else {
shun-iwasawa 2b24e8
        if (m_row[0] == row)
shun-iwasawa 2b24e8
          index = 0;
shun-iwasawa 2b24e8
        else if (m_row[1] == row)
shun-iwasawa 2b24e8
          index = 1;
shun-iwasawa 2b24e8
      }
shun-iwasawa 2b24e8
Shinya Kitaoka 120a6e
      if (index >= 0) {
Shinya Kitaoka 120a6e
        m_ghostIndex = index;
Shinya Kitaoka 120a6e
        updateBox();
shun-iwasawa 2b24e8
        m_gadget            = TranslateGadget;
shun-iwasawa 2b24e8
        m_highlightedGadget = TranslateGadget;
shun-iwasawa 1d5937
        notify              = true;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
shun-iwasawa 80c2e7
  } else if (e.isCtrlPressed()) {
shun-iwasawa 80c2e7
    m_gadget = NoGadget_InBox;
Shinya Kitaoka 120a6e
  }
shun-iwasawa 2b24e8
shun-iwasawa 2b24e8
  m_oldAff = m_aff[m_ghostIndex];
Shinya Kitaoka 120a6e
  invalidate();
shun-iwasawa 1d5937
shun-iwasawa 1d5937
  if (notify) {
shun-iwasawa 1d5937
    TTool::getApplication()
shun-iwasawa 1d5937
        ->getCurrentTool()
shun-iwasawa 1d5937
        ->notifyToolChanged();  // Refreshes toolbar values
shun-iwasawa 1d5937
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
shun-iwasawa 2b24e8
void ShiftTraceTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
shun-iwasawa 2b24e8
  if (m_gadget == NoGadget || m_gadget == NoGadget_InBox) {
Shinya Kitaoka 120a6e
    if (norm(pos - m_oldPos) > 10 * getPixelSize()) {
Shinya Kitaoka 120a6e
      m_curveStatus = TwoPointsCurve;
Shinya Kitaoka 120a6e
      m_p0          = m_oldPos;
Shinya Kitaoka 120a6e
      m_gadget      = CurveP1Gadget;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (isCurveGadget(m_gadget)) {
Shinya Kitaoka 120a6e
    if (m_gadget == CurveP0Gadget)
Shinya Kitaoka 120a6e
      m_p0 = pos;
Shinya Kitaoka 120a6e
    else if (m_gadget == CurveP1Gadget)
Shinya Kitaoka 120a6e
      m_p1 = pos;
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      m_p2 = pos;
Shinya Kitaoka 120a6e
    updateCurveAffs();
Shinya Kitaoka 120a6e
  } else if (m_gadget == RotateGadget) {
Shinya Kitaoka 120a6e
    TAffine aff = getGhostAff();
Shinya Kitaoka 120a6e
    TPointD c   = aff * m_center[m_ghostIndex];
Shinya Kitaoka 120a6e
    TPointD a   = m_oldPos - c;
Shinya Kitaoka 120a6e
    TPointD b   = pos - c;
Shinya Kitaoka 120a6e
    m_oldPos    = pos;
Shinya Kitaoka 120a6e
    TPointD u   = normalize(a);
Shinya Kitaoka 120a6e
    double phi =
Shinya Kitaoka 120a6e
        atan2(-u.y * b.x + u.x * b.y, u.x * b.x + u.y * b.y) * 180.0 / 3.14153;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TPointD imgC = aff * m_center[m_ghostIndex];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    m_aff[m_ghostIndex] = TRotation(imgC, phi) * m_aff[m_ghostIndex];
Shinya Kitaoka 120a6e
  } else if (m_gadget == MoveCenterGadget) {
Shinya Kitaoka 120a6e
    TAffine aff   = getGhostAff().inv();
Shinya Kitaoka 120a6e
    TPointD delta = aff * pos - aff * m_oldPos;
Shinya Kitaoka 120a6e
    m_oldPos      = pos;
Shinya Kitaoka 120a6e
    m_center[m_ghostIndex] += delta;
Shinya Kitaoka 120a6e
  } else if (m_gadget == TranslateGadget) {
Shinya Kitaoka 120a6e
    TPointD delta       = pos - m_oldPos;
Shinya Kitaoka 120a6e
    m_oldPos            = pos;
Shinya Kitaoka 120a6e
    m_aff[m_ghostIndex] = TTranslation(delta) * m_aff[m_ghostIndex];
shun-iwasawa 2b24e8
  } else if (m_gadget == ScaleGadget) {
shun-iwasawa 92418a
    TAffine aff  = getGhostAff();
shun-iwasawa 92418a
    TPointD c    = m_center[m_ghostIndex];
shun-iwasawa 92418a
    TPointD a    = aff.inv() * m_oldPos - c;
shun-iwasawa 92418a
    TPointD b    = aff.inv() * pos - c;
shun-iwasawa 92418a
    TPointD imgC = aff * m_center[m_ghostIndex];
shun-iwasawa 92418a
shun-iwasawa 2b24e8
    if (e.isShiftPressed())
shun-iwasawa 92418a
      m_aff[m_ghostIndex] = TScale(imgC, b.x / a.x, b.y / a.y) * m_oldAff;
shun-iwasawa 2b24e8
    else {
shun-iwasawa 2b24e8
      double scale        = std::max(b.x / a.x, b.y / a.y);
shun-iwasawa 92418a
      m_aff[m_ghostIndex] = TScale(imgC, scale) * m_oldAff;
shun-iwasawa 2b24e8
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  updateGhost();
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ShiftTraceTool::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
Shinya Kitaoka 120a6e
  if (CurveP0Gadget <= m_gadget && m_gadget <= CurvePmGadget) {
Shinya Kitaoka 120a6e
    if (m_curveStatus == TwoPointsCurve) {
Shinya Kitaoka 120a6e
      m_p2          = (m_p0 + m_p1) * 0.5;
Shinya Kitaoka 120a6e
      m_curveStatus = ThreePointsCurve;
Shinya Kitaoka 120a6e
      updateCurveAffs();
Shinya Kitaoka 120a6e
      updateGhost();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      m_center[0] = (m_aff[0] * m_dpiAff).inv() * m_p2;
Shinya Kitaoka 120a6e
      m_center[1] = (m_aff[1] * m_dpiAff).inv() * m_p2;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  m_gadget = NoGadget;
Shinya Kitaoka 120a6e
  invalidate();
shun-iwasawa 1d5937
shun-iwasawa 1d5937
  TTool::getApplication()
shun-iwasawa 1d5937
      ->getCurrentTool()
shun-iwasawa 1d5937
      ->notifyToolChanged();  // Refreshes toolbar values
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ShiftTraceTool::draw() {
Shinya Kitaoka 120a6e
  updateData();
Shinya Kitaoka 120a6e
  drawControlRect();
Shinya Kitaoka 120a6e
  drawCurve();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
int ShiftTraceTool::getCursorId() const {
shun-iwasawa 2b24e8
  if (m_highlightedGadget == RotateGadget || m_highlightedGadget == NoGadget)
Shinya Kitaoka 120a6e
    return ToolCursor::RotateCursor;
shun-iwasawa 2b24e8
  else if (m_highlightedGadget == ScaleGadget)
shun-iwasawa 2b24e8
    return ToolCursor::ScaleCursor;
Shinya Kitaoka 120a6e
  else if (isCurveGadget(m_highlightedGadget))
Shinya Kitaoka 120a6e
    return ToolCursor::PinchCursor;
shun-iwasawa 2b24e8
  else  // Curve Points, TranslateGadget, NoGadget_InBox
Shinya Kitaoka 120a6e
    return ToolCursor::MoveCursor;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
shun-iwasawa 2b24e8
bool ShiftTraceTool::isEventAcceptable(QEvent *e) {
shun-iwasawa 2b24e8
  // F1, F2 and F3 keys are used for flipping
shun-iwasawa 2b24e8
  QKeyEvent *keyEvent = static_cast<qkeyevent *="">(e);</qkeyevent>
shun-iwasawa 2b24e8
  int key             = keyEvent->key();
shun-iwasawa 2b24e8
  return (Qt::Key_F1 <= key && key <= Qt::Key_F3);
shun-iwasawa 2b24e8
}
shun-iwasawa 2b24e8
shun-iwasawa 1d5937
void ShiftTraceTool::onLeave() {
shun-iwasawa 1d5937
  OnionSkinMask osm =
shun-iwasawa 1d5937
      TTool::getApplication()->getCurrentOnionSkin()->getOnionSkinMask();
shun-iwasawa 1d5937
  osm.clearGhostFlipKey();
shun-iwasawa 1d5937
  TTool::getApplication()->getCurrentOnionSkin()->setOnionSkinMask(osm);
shun-iwasawa 1d5937
}
shun-iwasawa 1d5937
shun-iwasawa 1d5937
void ShiftTraceTool::setCurrentGhostIndex(int index) {
shun-iwasawa 1d5937
  m_ghostIndex = index;
shun-iwasawa 1d5937
  updateBox();
shun-iwasawa 1d5937
  invalidate();
shun-iwasawa 1d5937
}
shun-iwasawa 1d5937
Toshihiro Shimizu 890ddd
ShiftTraceTool shiftTraceTool;