Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "tvectorgl.h"
Toshihiro Shimizu 890ddd
#include "tgl.h"
Toshihiro Shimizu 890ddd
#include "tpalette.h"
Toshihiro Shimizu 890ddd
#include "tproperty.h"
Toshihiro Shimizu 890ddd
#include "tthreadmessage.h"
Toshihiro Shimizu 890ddd
#include "tvectorimage.h"
Toshihiro Shimizu 890ddd
#include "drawutil.h"
Toshihiro Shimizu 890ddd
#include "tcurveutil.h"
Toshihiro Shimizu 890ddd
#include "tstroke.h"
Toshihiro Shimizu 890ddd
#include "tstrokeutil.h"
Toshihiro Shimizu 890ddd
#include "tvectorrenderdata.h"
Toshihiro Shimizu 890ddd
#include "tstrokedeformations.h"
Toshihiro Shimizu 890ddd
#include "tmathutil.h"
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
// Toonz includes
Toshihiro Shimizu 890ddd
#include "toonz/tobjecthandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/txshlevelhandle.h"
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
// TnzTools includes
Toshihiro Shimizu 890ddd
#include "tools/tool.h"
Toshihiro Shimizu 890ddd
#include "tools/toolutils.h"
Toshihiro Shimizu 890ddd
#include "tools/cursors.h"
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
// Qt includes
Shinya Kitaoka 120a6e
#include <qcoreapplication>  // For Qt translation support</qcoreapplication>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
using namespace ToolUtils;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
//    PumpTool declaration
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class PumpTool final : public TTool {
Shinya Kitaoka 120a6e
  Q_DECLARE_TR_FUNCTIONS(PumpTool)
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int m_strokeStyleId, m_strokeIndex;  //!< Edited stroke indices
Shinya Kitaoka 120a6e
  TStroke *m_inStroke, *m_outStroke;   //!< Input/Output strokes
Shinya Kitaoka 120a6e
  std::vector<tstroke *=""></tstroke>
Shinya Kitaoka 120a6e
      m_splitStrokes;  //!< Merging these, m_inStroke is reformed
Shinya Kitaoka 120a6e
  int m_stroke1Idx,
Shinya Kitaoka 120a6e
      m_stroke2Idx;  //!< Indices of deformed strokes among split ones
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TUndo *m_undo;  //!< Undo to be added upon non-trivial button up
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double m_actionW;               //!< The action center stroke parameter
Shinya Kitaoka 120a6e
  double m_actionS1, m_actionS2;  //!< Action center length in m_stroke
Shinya Kitaoka 120a6e
  double m_actionRadius;          //!< Tool action radius in curve length
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  std::vector<double></double>
Shinya Kitaoka 120a6e
      m_splitPars;  //!< Split parameters for action localization
Shinya Kitaoka 120a6e
  std::vector<double> m_cpLenDiff1,</double>
Shinya Kitaoka 120a6e
      m_cpLenDiff2;  //!< Distorted CPs' length distances from action center
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  bool m_active;         //!< Whether a stroke is currently being edited
Shinya Kitaoka 120a6e
  bool m_enabled;        //!< Tells whether the image allows editing
Shinya Kitaoka 120a6e
  bool m_cursorEnabled;  //!< Whether the 'pump preview cursor' can be seen
Shinya Kitaoka 120a6e
  bool m_draw;           //!< Should be removed...?
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD m_oldPoint, m_downPoint;  //!< Mouse positions upon editing
Shinya Kitaoka 120a6e
  TThickPoint m_cursor;             //!< Pump preview cursor data
Shinya Kitaoka 120a6e
  int m_cursorId;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double m_errorTol;  //!< Allowed approximation error during edit
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TDoubleProperty m_toolSize;
Shinya Kitaoka 120a6e
  TIntProperty m_accuracy;
Shinya Kitaoka 120a6e
  TPropertyGroup m_prop;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  PumpTool()
Shinya Kitaoka 120a6e
      : TTool("T_Pump")
Shinya Kitaoka 120a6e
      , m_active(false)
Shinya Kitaoka 120a6e
      , m_actionW(0)
Shinya Kitaoka 120a6e
      , m_strokeIndex((std::numeric_limits<uint>::max)())</uint>
Shinya Kitaoka 120a6e
      , m_inStroke(0)
Shinya Kitaoka 120a6e
      , m_outStroke(0)
Shinya Kitaoka 120a6e
      , m_stroke1Idx(-1)
Shinya Kitaoka 120a6e
      , m_stroke2Idx(-1)
Shinya Kitaoka 120a6e
      , m_cursorEnabled(false)
Shinya Kitaoka 120a6e
      , m_cursorId(ToolCursor::PumpCursor)
Shinya Kitaoka 120a6e
      , m_actionRadius(1)
Shinya Kitaoka 120a6e
      , m_draw(false)
Shinya Kitaoka 120a6e
      , m_undo(0)
Shinya Kitaoka 120a6e
      , m_toolSize("Size:", 1, 100, 20)
Shinya Kitaoka 120a6e
      , m_accuracy("Accuracy:", 0, 100, 40)
Shinya Kitaoka 120a6e
      , m_enabled(false) {
Shinya Kitaoka 120a6e
    bind(TTool::VectorImage);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_splitPars.resize(2);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_prop.bind(m_toolSize);
Shinya Kitaoka 120a6e
    m_prop.bind(m_accuracy);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  ToolType getToolType() const override { return TTool::LevelWriteTool; }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  TPropertyGroup *getProperties(int targetType) override { return &m_prop; }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void updateTranslation() override {
Shinya Kitaoka 120a6e
    m_toolSize.setQStringName(tr("Size:"));
Shinya Kitaoka 120a6e
    m_accuracy.setQStringName(tr("Accuracy:"));
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void onEnter() override;
Shinya Kitaoka 473e70
  void onLeave() override;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void draw() override;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override;
Shinya Kitaoka 473e70
  void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override;
Shinya Kitaoka 473e70
  void leftButtonUp(const TPointD &pos, const TMouseEvent &e) override;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
Shinya Kitaoka 120a6e
  bool moveCursor(const TPointD &pos);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  int getCursorId() const override { return m_cursorId; }
Shinya Kitaoka 120a6e
  void invalidateCursorArea();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void onDeactivate() override;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
private:
Shinya Kitaoka 120a6e
  double actionRadius(double strokeLength);
Shinya Kitaoka 120a6e
  void splitStroke(TStroke *s);
Shinya Kitaoka 120a6e
  TStroke *mergeStrokes(const std::vector<tstroke *=""> &strokes);</tstroke>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
} PumpToolInstance;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
//    PumpTool implementation
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PumpTool::onEnter() {
Shinya Kitaoka 120a6e
  m_draw = true;
Shinya Kitaoka 120a6e
  if (TTool::getApplication()->getCurrentObject()->isSpline() ||
Shinya Kitaoka 120a6e
      !(TVectorImageP)getImage(false)) {
Shinya Kitaoka 120a6e
    m_enabled  = false;
Shinya Kitaoka 120a6e
    m_cursorId = ToolCursor::CURSOR_NO;
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    m_enabled  = true;
Shinya Kitaoka 120a6e
    m_cursorId = ToolCursor::PumpCursor;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PumpTool::draw() {
Shinya Kitaoka 120a6e
  if (!m_draw || !m_enabled) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TVectorImageP vi = TImageP(getImage(false));
Shinya Kitaoka 120a6e
  if (!vi) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  QMutexLocker lock(vi->getMutex());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPalette *palette = vi->getPalette();
Shinya Kitaoka 120a6e
  assert(palette);
Shinya Kitaoka 120a6e
  if (m_active) {
Shinya Kitaoka 120a6e
    // Editing with the tool
Shinya Kitaoka 120a6e
    assert(m_outStroke);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TRectD bboxD(m_outStroke->getBBox());
Shinya Kitaoka 120a6e
    TRect bbox(tfloor(bboxD.x0), tfloor(bboxD.y0), tceil(bboxD.x1) - 1,
Shinya Kitaoka 120a6e
               tceil(bboxD.y1) - 1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    tglDraw(TVectorRenderData(TAffine(), bbox, palette, 0, true), m_outStroke);
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    // Hovering
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    double w, dist;
Shinya Kitaoka 120a6e
    UINT index;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (m_cursorEnabled) {
Shinya Kitaoka 120a6e
      // Draw cursor
Shinya Kitaoka 120a6e
      glColor3d(1.0, 0.0, 1.0);
Shinya Kitaoka 120a6e
      if (m_cursor.thick > 0) tglDrawCircle(m_cursor, m_cursor.thick);
Shinya Kitaoka 120a6e
      tglDrawCircle(m_cursor, m_cursor.thick + 4 * getPixelSize());
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (vi->getNearestStroke(m_cursor, w, index, dist, true)) {
Shinya Kitaoka 120a6e
      TStroke *stroke  = vi->getStroke(index);
Shinya Kitaoka 120a6e
      double totalLen  = stroke->getLength();
Shinya Kitaoka 120a6e
      double actionLen = actionRadius(totalLen);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      tglColor(TPixel32::Red);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      if (totalLen < actionLen ||
Shinya Kitaoka 120a6e
          (stroke->isSelfLoop() && totalLen < actionLen + actionLen))
Shinya Kitaoka 120a6e
        drawStrokeCenterline(*stroke, getPixelSize());
Shinya Kitaoka 120a6e
      else {
Shinya Kitaoka 120a6e
        int i, chunckIndex1, chunckIndex2;
Shinya Kitaoka 120a6e
        double t, t1, t2, w1, w2;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        double len = stroke->getLength(w);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        double len1 = len - actionLen;
Shinya Kitaoka 120a6e
        if (len1 < 0)
Shinya Kitaoka 120a6e
          if (stroke->isSelfLoop())
Shinya Kitaoka 120a6e
            len1 += totalLen;
Shinya Kitaoka 120a6e
          else
Shinya Kitaoka 120a6e
            len1 = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        double len2 = len + actionLen;
Shinya Kitaoka 120a6e
        if (len2 > totalLen)
Shinya Kitaoka 120a6e
          if (stroke->isSelfLoop())
Shinya Kitaoka 120a6e
            len2 -= totalLen;
Shinya Kitaoka 120a6e
          else
Shinya Kitaoka 120a6e
            len2 = totalLen;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        w1 = stroke->getParameterAtLength(len1);
Shinya Kitaoka 120a6e
        w2 = stroke->getParameterAtLength(len2);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        int chunkCount = stroke->getChunkCount();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        stroke->getChunkAndT(w1, chunckIndex1, t1);
Shinya Kitaoka 120a6e
        stroke->getChunkAndT(w2, chunckIndex2, t2);
Shinya Kitaoka 120a6e
        double step;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        const TThickQuadratic *q = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        glBegin(GL_LINE_STRIP);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        q    = stroke->getChunk(chunckIndex1);
Shinya Kitaoka 120a6e
        step = computeStep(*q, getPixelSize());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        if (chunckIndex1 == chunckIndex2 && t1 < t2) {
Shinya Kitaoka 120a6e
          for (t = t1; t < t2; t += step) tglVertex(q->getPoint(t));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
          tglVertex(stroke->getPoint(w2));
Shinya Kitaoka 120a6e
          glEnd();
Shinya Kitaoka 120a6e
          return;
Shinya Kitaoka 120a6e
        }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        for (t = t1; t < 1; t += step) tglVertex(q->getPoint(t));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        for (i = chunckIndex1 + 1; i != chunckIndex2; i++) {
Shinya Kitaoka 120a6e
          if (i == chunkCount) i = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
          if (i == chunckIndex2) break;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
          q    = stroke->getChunk(i);
Shinya Kitaoka 120a6e
          step = computeStep(*q, getPixelSize());
Shinya Kitaoka 120a6e
          for (t = 0; t < 1; t += step) tglVertex(q->getPoint(t));
Shinya Kitaoka 120a6e
        }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        q    = stroke->getChunk(chunckIndex2);
Shinya Kitaoka 120a6e
        step = computeStep(*q, getPixelSize());
Shinya Kitaoka 120a6e
        for (t = 0; t < t2; t += step) tglVertex(q->getPoint(t));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        tglVertex(stroke->getPoint(w2));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        glEnd();
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PumpTool::leftButtonDown(const TPointD &pos, const TMouseEvent &) {
Shinya Kitaoka 120a6e
  if (m_active || !m_enabled) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  assert(m_undo == 0);
Shinya Kitaoka 120a6e
  m_active = false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TVectorImageP vi(getImage(true));
Shinya Kitaoka 120a6e
  if (!vi) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  QMutexLocker lock(vi->getMutex());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // set current point and init parameters
Shinya Kitaoka 120a6e
  m_oldPoint  = pos;
Shinya Kitaoka 120a6e
  m_downPoint = pos;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_inStroke = m_outStroke = 0;
Shinya Kitaoka 120a6e
  m_stroke1Idx = m_stroke2Idx = -1;
Shinya Kitaoka 120a6e
  m_splitPars[0] = m_splitPars[1] = -2;
Shinya Kitaoka 120a6e
  m_actionW                       = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_errorTol = (1.0 - 0.01 * m_accuracy.getValue()) * getPixelSize();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double dist2 = 0.0;
Shinya Kitaoka 120a6e
  int cpCount;
Shinya Kitaoka 120a6e
  int i;
Shinya Kitaoka 120a6e
  UINT index;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (vi->getNearestStroke(pos, m_actionW, index, dist2)) {
Shinya Kitaoka 120a6e
    // A stroke near the pressed point was found - modify it
Shinya Kitaoka 120a6e
    m_active      = true;
Shinya Kitaoka 120a6e
    m_strokeIndex = index;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_inStroke  = vi->getStroke(m_strokeIndex);
Shinya Kitaoka 120a6e
    m_outStroke = new TStroke(*m_inStroke);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    double totalLength = m_inStroke->getLength();
Shinya Kitaoka 120a6e
    TXshSimpleLevel *sl =
Shinya Kitaoka 120a6e
        TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
Shinya Kitaoka 120a6e
    assert(sl);
Shinya Kitaoka 120a6e
    TFrameId id = getCurrentFid();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Allocate the modification undo - will be assigned to the undo manager on
Shinya Kitaoka 120a6e
    // mouse release
Shinya Kitaoka 120a6e
    m_undo = new UndoModifyStrokeAndPaint(sl, id, m_strokeIndex);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Set the stroke's style to 'none'. This is needed to make the original
Shinya Kitaoka 120a6e
    // stroke transparent,
Shinya Kitaoka 120a6e
    // while the deformed one is shown at its place.
Shinya Kitaoka 120a6e
    m_strokeStyleId = m_inStroke->getStyle();
Shinya Kitaoka 120a6e
    m_inStroke->setStyle(0);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (totalLength <= 0.0) {
Shinya Kitaoka 120a6e
      // Single point case
Shinya Kitaoka 120a6e
      cpCount = m_inStroke->getControlPointCount();
Shinya Kitaoka 120a6e
      m_cpLenDiff1.resize(cpCount);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      for (i = 0; i < cpCount; i++) m_cpLenDiff1[i] = 0.0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_splitStrokes.resize(1);
Shinya Kitaoka 120a6e
      m_splitStrokes[0] = new TStroke(*m_inStroke);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_stroke1Idx = 0;
Shinya Kitaoka 120a6e
    } else
Shinya Kitaoka 120a6e
      // Common strokes - split the stroke according to deformation requirements
Shinya Kitaoka 120a6e
      splitStroke(m_inStroke);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PumpTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
Shinya Kitaoka 120a6e
  if (!m_active || !m_enabled) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TVectorImageP vi(getImage(true));
Shinya Kitaoka 120a6e
  if (!vi || !m_outStroke) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  QMutexLocker lock(vi->getMutex());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Revert current deformation, recovering the one from button press
Shinya Kitaoka 120a6e
  delete m_outStroke;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Retrieve cursor's vertical displacement
Shinya Kitaoka 120a6e
  TPointD delta = TPointD(0, (pos - m_downPoint).y);
Shinya Kitaoka 120a6e
  int deltaSign = tsign(delta.y);
Shinya Kitaoka 120a6e
  if (deltaSign == 0) {
Shinya Kitaoka 120a6e
    // Use a copy of the original stroke
Shinya Kitaoka 120a6e
    m_outStroke = new TStroke(*m_inStroke);
Shinya Kitaoka 120a6e
    m_outStroke->setStyle(m_strokeStyleId);
Shinya Kitaoka 120a6e
    invalidate();
Shinya Kitaoka 120a6e
    return;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Build deformation upon the original stroke pieces
Shinya Kitaoka 120a6e
  TStroke *stroke1 = 0, *stroke2 = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  stroke1 = new TStroke(*m_splitStrokes[m_stroke1Idx]);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Deform stroke1
Shinya Kitaoka 120a6e
  TStrokeThicknessDeformation deformer(stroke1, delta, m_actionS1,
Shinya Kitaoka 120a6e
                                       m_actionRadius, deltaSign);
Shinya Kitaoka 120a6e
  modifyThickness(*stroke1, deformer, m_cpLenDiff1, deltaSign < 0);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (m_stroke2Idx >= 0) {
Shinya Kitaoka 120a6e
    // Deform stroke2
Shinya Kitaoka 120a6e
    stroke2 = new TStroke(*m_splitStrokes[m_stroke2Idx]);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TStrokeThicknessDeformation deformer2(stroke2, delta, m_actionS2,
Shinya Kitaoka 120a6e
                                          m_actionRadius, deltaSign);
Shinya Kitaoka 120a6e
    modifyThickness(*stroke2, deformer2, m_cpLenDiff2, deltaSign < 0);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Apply deformation
Shinya Kitaoka 120a6e
  std::vector<tstroke *=""> splitStrokesCopy(m_splitStrokes);</tstroke>
Shinya Kitaoka 120a6e
  splitStrokesCopy[m_stroke1Idx]              = stroke1;
Shinya Kitaoka 120a6e
  if (stroke2) splitStrokesCopy[m_stroke2Idx] = stroke2;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_outStroke = mergeStrokes(splitStrokesCopy);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  delete stroke1;
Shinya Kitaoka 120a6e
  delete stroke2;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PumpTool::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
Shinya Kitaoka 120a6e
  TVectorImageP vi;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (!m_active || !m_enabled) goto cleanup;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  vi = TVectorImageP(getImage(true));
Shinya Kitaoka 120a6e
  if (!vi) goto cleanup;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    m_active = false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    QMutexLocker lock(vi->getMutex());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Reset cursor data
Shinya Kitaoka 120a6e
    double t;
Shinya Kitaoka 120a6e
    UINT index;
Shinya Kitaoka 120a6e
    double dist2;
Shinya Kitaoka 120a6e
    if (vi->getNearestStroke(pos, t, index, dist2)) {
Shinya Kitaoka 120a6e
      TStroke *nearestStroke      = vi->getStroke(index);
Shinya Kitaoka 120a6e
      if (nearestStroke) m_cursor = nearestStroke->getThickPoint(t);
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (m_outStroke &&
Shinya Kitaoka 120a6e
        !areAlmostEqual(m_downPoint, pos, PickRadius * getPixelSize())) {
Shinya Kitaoka 120a6e
      // Accept action
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      // Clone input stroke - it is someway needed by the stroke change
Shinya Kitaoka 120a6e
      // notifier... I wonder why...
Shinya Kitaoka 120a6e
      TStroke *oldStroke = new TStroke(*m_inStroke);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_outStroke->swap(*m_inStroke);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_inStroke->invalidate();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      delete m_outStroke;
Shinya Kitaoka 120a6e
      m_outStroke = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      assert(m_undo);
Shinya Kitaoka 120a6e
      TUndoManager::manager()->add(m_undo);
Shinya Kitaoka 120a6e
      m_undo = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      vi->notifyChangedStrokes(m_strokeIndex, oldStroke);
Shinya Kitaoka 120a6e
      notifyImageChanged();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      delete oldStroke;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
cleanup:
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (m_inStroke)
Shinya Kitaoka 120a6e
    m_inStroke->setStyle(
Shinya Kitaoka 120a6e
        m_strokeStyleId);  // Make the image stroke visible again
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_strokeIndex = m_strokeStyleId = -1;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  clearPointerContainer(m_splitStrokes);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  delete m_outStroke;
Shinya Kitaoka 120a6e
  m_inStroke = m_outStroke = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  delete m_undo;
Shinya Kitaoka 120a6e
  m_undo = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PumpTool::invalidateCursorArea() {
Shinya Kitaoka 120a6e
  double r = m_cursor.thick + 6;
Shinya Kitaoka 120a6e
  TPointD d(r, r);
Shinya Kitaoka 120a6e
  invalidate(TRectD(m_cursor - d, m_cursor + d));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PumpTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
Shinya Kitaoka 120a6e
  if (m_active || !m_enabled) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Cursor preview updates on 3-pixel steps
Shinya Kitaoka 120a6e
  if (tdistance2(pos, m_oldPoint) < 9.0 * sq(getPixelSize())) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (!m_draw) m_draw = true;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_oldPoint = pos;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (moveCursor(pos)) {
Shinya Kitaoka 120a6e
    m_cursorEnabled = true;
Shinya Kitaoka 120a6e
    invalidate();
Shinya Kitaoka 120a6e
  } else
Shinya Kitaoka 120a6e
    m_cursorEnabled = false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool PumpTool::moveCursor(const TPointD &pos) {
Shinya Kitaoka 120a6e
  TVectorImageP vi(getImage(false));
Shinya Kitaoka 120a6e
  if (vi) {
Shinya Kitaoka 120a6e
    double t;
Shinya Kitaoka 120a6e
    UINT index;
Shinya Kitaoka 120a6e
    double dist2;
Shinya Kitaoka 120a6e
    if (vi->getNearestStroke(pos, t, index, dist2)) {
Shinya Kitaoka 120a6e
      TStroke *stroke = vi->getStroke(index);
Shinya Kitaoka 120a6e
      if (stroke) {
Shinya Kitaoka 120a6e
        m_cursor = stroke->getThickPoint(t);
Shinya Kitaoka 120a6e
        return true;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  return false;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PumpTool::onDeactivate() {
Shinya Kitaoka 120a6e
  m_draw = false;
Shinya Kitaoka 120a6e
  if (m_active) {
Shinya Kitaoka 120a6e
    m_active = false;
Shinya Kitaoka 120a6e
    TVectorImageP vi(getImage(true));
Shinya Kitaoka 120a6e
    assert(!!vi && m_outStroke);
Shinya Kitaoka 120a6e
    if (!vi || !m_outStroke) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    clearPointerContainer(m_splitStrokes);
Shinya Kitaoka 120a6e
    if (m_splitPars[0] == -1) {
Shinya Kitaoka 120a6e
      delete m_outStroke;
Shinya Kitaoka 120a6e
      m_outStroke = 0;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // restore previous style
Shinya Kitaoka 120a6e
    assert(m_strokeIndex >= 0);
Shinya Kitaoka 120a6e
    if (m_strokeIndex >= 0) {
Shinya Kitaoka 120a6e
      TStroke *stroke = vi->getStroke(m_strokeIndex);
Shinya Kitaoka 120a6e
      stroke->setStyle(m_strokeStyleId);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    assert(m_undo);
Shinya Kitaoka 120a6e
    delete m_undo;
Shinya Kitaoka 120a6e
    m_undo = 0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    invalidate();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    m_strokeIndex = -1;
Shinya Kitaoka 120a6e
    m_outStroke   = 0;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PumpTool::onLeave() {
Shinya Kitaoka 120a6e
  if (!m_active) m_draw = false;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
//    PumpTool privates
Toshihiro Shimizu 890ddd
//*****************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
double PumpTool::actionRadius(double strokeLength) {
Jeremy Bullock 0aebf8
  double toolSize         = m_toolSize.getValue();
Shinya Kitaoka 120a6e
  double toolPercent      = toolSize * 0.01;
Shinya Kitaoka 120a6e
  double interpolationVal = pow(toolPercent, 5);
Shinya Kitaoka 120a6e
  double indipendentValue = 7.0 * toolSize;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double actionRadius = (indipendentValue) * (1.0 - interpolationVal) +
Shinya Kitaoka 120a6e
                        (strokeLength * toolPercent) * interpolationVal;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return std::max(actionRadius, indipendentValue);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Toshihiro Shimizu 890ddd
  Edited strokes are split near the corresponding editing position, in order
Toshihiro Shimizu 890ddd
  to localize stroke manipulation.
Toshihiro Shimizu 890ddd
  Only the localized part of the stroke will receive CP increase and thickness
Toshihiro Shimizu 890ddd
  tuning needed for the tool action.
Toshihiro Shimizu 890ddd
*/
Shinya Kitaoka 120a6e
void PumpTool::splitStroke(TStroke *s) {
Shinya Kitaoka 120a6e
  assert(m_splitStrokes.empty());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TStroke *stroke1 = 0, *stroke2 = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Build the action radius
Shinya Kitaoka 120a6e
  double totalLength = s->getLength();
Shinya Kitaoka 120a6e
  m_actionRadius     = actionRadius(totalLength);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Get the length at selected point and build the split (length) positions
Shinya Kitaoka 120a6e
  m_actionS1      = s->getLength(m_actionW);
Shinya Kitaoka 120a6e
  double startLen = m_actionS1 - m_actionRadius;
Shinya Kitaoka 120a6e
  double endLen   = m_actionS1 + m_actionRadius;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Now, perform splitting
Shinya Kitaoka 120a6e
  int i, cpCount;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if ((startLen <= 0 && endLen >= totalLength) ||
Shinya Kitaoka 120a6e
      (s->isSelfLoop() && totalLength < (m_actionRadius + m_actionRadius))) {
Shinya Kitaoka 120a6e
    // The whole stroke is included in the action - no split
Shinya Kitaoka 120a6e
    m_splitStrokes.resize(1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_splitPars[0] = -1;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_splitStrokes[0] = new TStroke(*s);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_stroke1Idx = 0;
Shinya Kitaoka 120a6e
    stroke1      = m_splitStrokes[m_stroke1Idx];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TStrokeThicknessDeformation deformer(s, m_actionS1, m_actionRadius);
Shinya Kitaoka 120a6e
    increaseControlPoints(*stroke1, deformer, getPixelSize());
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    if (!s->isSelfLoop() || (startLen >= 0.0 && endLen <= totalLength)) {
Shinya Kitaoka 120a6e
      // Regular split positions, in the [0.0, totalLength] range.
Shinya Kitaoka 120a6e
      // Split points at extremities are dealt.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_splitPars[0] = s->getParameterAtLength(
Shinya Kitaoka 120a6e
          std::max(startLen, 0.0));  // Crop in the open case
Shinya Kitaoka 120a6e
      m_splitPars[1] = s->getParameterAtLength(std::min(endLen, totalLength));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      if (m_splitPars[0] ==
Shinya Kitaoka 120a6e
          0.0)  // the "&& m_splitPars[0] == totalLength" was dealt outside
Shinya Kitaoka 120a6e
      {
Shinya Kitaoka 120a6e
        m_splitStrokes.resize(2);
Shinya Kitaoka 120a6e
        m_splitStrokes[0] = new TStroke;
Shinya Kitaoka 120a6e
        m_splitStrokes[1] = new TStroke;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        s->split(m_splitPars[1], *(m_splitStrokes[0]), *(m_splitStrokes[1]));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        m_stroke1Idx = 0;
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        if (m_splitPars[1] == 1.0) {
Shinya Kitaoka 120a6e
          m_splitStrokes.resize(2);
Shinya Kitaoka 120a6e
          m_splitStrokes[0] = new TStroke;
Shinya Kitaoka 120a6e
          m_splitStrokes[1] = new TStroke;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
          s->split(m_splitPars[0], *(m_splitStrokes[0]), *(m_splitStrokes[1]));
Shinya Kitaoka 120a6e
        } else
Shinya Kitaoka 120a6e
          ::splitStroke(*s, m_splitPars, m_splitStrokes);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        m_stroke1Idx = 1;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        // Update the edit point to refer to the central stroke piece
Shinya Kitaoka 120a6e
        m_actionS1 -= m_splitStrokes[0]->getLength();
Shinya Kitaoka 120a6e
      }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      stroke1 = m_splitStrokes[m_stroke1Idx];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      // Apply deformation to the middle piece
Shinya Kitaoka 120a6e
      TStrokeThicknessDeformation deformer(stroke1, m_actionS1, m_actionRadius);
Shinya Kitaoka 120a6e
      increaseControlPoints(*stroke1, deformer, getPixelSize());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_actionS2 = 0;
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      // Circular 'overflow' case - (exactly) one split point is outside the
Shinya Kitaoka 120a6e
      // regular scope.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      // Since the action diameter is < totalLength, these cases are mutually
Shinya Kitaoka 120a6e
      // exclusive.
Shinya Kitaoka 120a6e
      if (startLen < 0)
Shinya Kitaoka 120a6e
        startLen += totalLength;
Shinya Kitaoka 120a6e
      else {
Shinya Kitaoka 120a6e
        endLen -= totalLength;
Shinya Kitaoka 120a6e
        m_actionS1 -= totalLength;
Shinya Kitaoka 120a6e
      }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      // The deformation must be applied in two distinct strokes, since its
Shinya Kitaoka 120a6e
      // action interval crosses the junction point
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_splitPars[0] = s->getParameterAtLength(endLen);
Shinya Kitaoka 120a6e
      m_splitPars[1] = s->getParameterAtLength(startLen);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      ::splitStroke(*s, m_splitPars, m_splitStrokes);
Shinya Kitaoka 120a6e
      assert(m_splitStrokes.size() >= 3);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_stroke1Idx = 0;
Shinya Kitaoka 120a6e
      m_stroke2Idx = 2;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      stroke1 = m_splitStrokes[m_stroke1Idx];
Shinya Kitaoka 120a6e
      stroke2 = m_splitStrokes[m_stroke2Idx];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      m_actionS2 = m_actionS1 + stroke2->getLength();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      TStrokeThicknessDeformation deformer(stroke1, m_actionS1, m_actionRadius);
Shinya Kitaoka 120a6e
      increaseControlPoints(*stroke1, deformer, getPixelSize());
Shinya Kitaoka 120a6e
      TStrokeThicknessDeformation deformer2(stroke2, m_actionS2,
Shinya Kitaoka 120a6e
                                            m_actionRadius);
Shinya Kitaoka 120a6e
      increaseControlPoints(*stroke2, deformer2, getPixelSize());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      cpCount = stroke2->getControlPointCount();
Shinya Kitaoka 120a6e
      m_cpLenDiff2.resize(cpCount);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      for (i            = 0; i < cpCount; ++i)
Shinya Kitaoka 120a6e
        m_cpLenDiff2[i] = stroke2->getLengthAtControlPoint(i) - m_actionS2;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  cpCount = stroke1->getControlPointCount();
Shinya Kitaoka 120a6e
  m_cpLenDiff1.resize(cpCount);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double diff;
Shinya Kitaoka 120a6e
  for (i = 0; i < cpCount; i++) {
Shinya Kitaoka 120a6e
    diff            = stroke1->getLengthAtControlPoint(i) - m_actionS1;
Shinya Kitaoka 120a6e
    m_cpLenDiff1[i] = (s->isSelfLoop() && stroke2 && totalLength - diff < diff)
Shinya Kitaoka 120a6e
                          ? totalLength - diff
Shinya Kitaoka 120a6e
                          : diff;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//----------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Toshihiro Shimizu 890ddd
  A split stroke must be reassembled before it is output.
Toshihiro Shimizu 890ddd
  In particular, it must be ensured that the merge does not add additional CPS
Toshihiro Shimizu 890ddd
  at split points, leaving the output seamless.
Toshihiro Shimizu 890ddd
*/
Shinya Kitaoka 120a6e
TStroke *PumpTool::mergeStrokes(const std::vector<tstroke *=""> &strokes) {</tstroke>
Shinya Kitaoka 120a6e
  assert(strokes.size() > 0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TStroke *mergedStroke;
Shinya Kitaoka 120a6e
  if (strokes.size() > 1) {
Shinya Kitaoka 120a6e
    if (m_errorTol > 0.0) {
Shinya Kitaoka 120a6e
      strokes[m_stroke1Idx]->reduceControlPoints(m_errorTol);
Shinya Kitaoka 120a6e
      if (m_stroke2Idx >= 0)
Shinya Kitaoka 120a6e
        strokes[m_stroke2Idx]->reduceControlPoints(m_errorTol);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Merge split strokes
Shinya Kitaoka 120a6e
    mergedStroke = merge(strokes);
Shinya Kitaoka 120a6e
    // mergedStroke->reduceControlPoints(0.4*getPixelSize());    //Originally on
Shinya Kitaoka 120a6e
    // the whole result...
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (m_inStroke->isSelfLoop()) {
Shinya Kitaoka 120a6e
      int cpCount = mergedStroke->getControlPointCount();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      TThickPoint p1   = mergedStroke->getControlPoint(0);
Shinya Kitaoka 120a6e
      TThickPoint p2   = mergedStroke->getControlPoint(cpCount - 1);
Shinya Kitaoka 120a6e
      TThickPoint midP = 0.5 * (p1 + p2);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      mergedStroke->setControlPoint(0, midP);
Shinya Kitaoka 120a6e
      mergedStroke->setControlPoint(cpCount - 1, midP);
Shinya Kitaoka 120a6e
      mergedStroke->setSelfLoop(true);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    mergedStroke->outlineOptions() = strokes[0]->outlineOptions();
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    mergedStroke = new TStroke(*strokes[0]);
Shinya Kitaoka 120a6e
    if (m_errorTol > 0.0) mergedStroke->reduceControlPoints(m_errorTol);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  mergedStroke->setStyle(m_strokeStyleId);
Shinya Kitaoka 120a6e
  mergedStroke->invalidate();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  return mergedStroke;
Toshihiro Shimizu 890ddd
}