| |
| |
| #include "tools/toolutils.h" |
| #include "tools/toolhandle.h" |
| #include "toonzqt/imageutils.h" |
| #include "trop.h" |
| #include "tools/tool.h" |
| #include "tstroke.h" |
| #include "timageinfo.h" |
| #include "timagecache.h" |
| #include "tgl.h" |
| |
| #include "toonz/txsheethandle.h" |
| #include "toonz/tframehandle.h" |
| #include "toonz/txshlevelhandle.h" |
| #include "toonz/tscenehandle.h" |
| #include "toonz/txshleveltypes.h" |
| #include "toonz/tcolumnhandle.h" |
| #include "toonz/tpalettehandle.h" |
| #include "toonz/txshlevel.h" |
| #include "toonz/txshcell.h" |
| #include "toonz/txshsimplelevel.h" |
| #include "toonz/imagemanager.h" |
| #include "toonz/ttileset.h" |
| #include "toonz/toonzimageutils.h" |
| #include "toonz/levelproperties.h" |
| #include "toonz/tstageobjectspline.h" |
| #include "toonz/tobjecthandle.h" |
| #include "toonz/tstageobject.h" |
| #include "toonz/trasterimageutils.h" |
| #include "toonz/levelset.h" |
| #include "toonz/toonzscene.h" |
| #include "toonz/preferences.h" |
| #include "toonz/palettecontroller.h" |
| #include "toonz/txshchildlevel.h" |
| |
| #include "toonzqt/tselectionhandle.h" |
| #include "toonzqt/icongenerator.h" |
| #include "toonzqt/selection.h" |
| #include "toonzqt/gutil.h" |
| |
| #include "tools/strokeselection.h" |
| |
| #include <QPainter> |
| #include <QPainterPath> |
| #include <QGLWidget> // for QGLWidget::convertToGLFormat |
| #include <QFont> |
| #include <QFontMetrics> |
| |
| |
| |
| |
| |
| namespace { |
| |
| |
| |
| void mapToVector(const std::map<int, VIStroke *> &theMap, |
| std::vector<int> &theVect) { |
| assert(theMap.size() == theVect.size()); |
| std::map<int, VIStroke *>::const_iterator it = theMap.begin(); |
| UINT i = 0; |
| for (; it != theMap.end(); ++it) { |
| theVect[i++] = it->first; |
| } |
| } |
| |
| |
| |
| void updateSaveBox(const TToonzImageP &ti) { |
| if (ti) { |
| assert(ti->getRaster()); |
| assert(ti->getSubsampling() == 1); |
| |
| |
| |
| const TRect &savebox = ti->getSavebox(); |
| { |
| TRect newSaveBox; |
| TRop::computeBBox(ti->getRaster(), newSaveBox); |
| |
| |
| |
| if (!Preferences::instance()->isMinimizeSaveboxAfterEditing()) |
| newSaveBox += |
| savebox; |
| |
| ti->setSavebox(newSaveBox); |
| } |
| } |
| } |
| |
| } |
| |
| |
| |
| |
| |
| void ToolUtils::updateSaveBox(const TXshSimpleLevelP &sl, const TFrameId &fid) { |
| |
| |
| sl->setDirtyFlag(true); |
| |
| TImageP img = sl->getFrame(fid, true); |
| |
| if (!img) return; |
| |
| ::updateSaveBox(img); |
| |
| TImageInfo *info = sl->getFrameInfo(fid, true); |
| ImageBuilder::setImageInfo(*info, img.getPointer()); |
| } |
| |
| |
| |
| void ToolUtils::updateSaveBox(const TXshSimpleLevelP &sl, const TFrameId &fid, |
| TImageP img) { |
| sl->setFrame(fid, img); |
| ToolUtils::updateSaveBox(sl, fid); |
| } |
| |
| |
| |
| void ToolUtils::updateSaveBox() { |
| TTool::Application *application = TTool::getApplication(); |
| if (!application) return; |
| |
| TXshLevel *xl = application->getCurrentLevel()->getLevel(); |
| if (!xl) return; |
| |
| TXshSimpleLevel *sl = xl->getSimpleLevel(); |
| if (!sl || sl->getType() != TZP_XSHLEVEL) return; |
| |
| TFrameId fid = getFrameId(); |
| ToolUtils::updateSaveBox(sl, fid); |
| } |
| |
| |
| |
| TFrameId ToolUtils::getFrameId() { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return TFrameId(); |
| |
| TFrameHandle *frameHandle = app->getCurrentFrame(); |
| if (frameHandle->isEditingScene()) { |
| TXsheet *xsh = app->getCurrentXsheet()->getXsheet(); |
| if (!xsh) return 0; |
| int row = frameHandle->getFrame(); |
| int col = app->getCurrentColumn()->getColumnIndex(); |
| if (col < 0) return 0; |
| TXshCell cell = xsh->getCell(row, col); |
| return cell.getFrameId(); |
| } else |
| return frameHandle->getFid(); |
| } |
| |
| |
| |
| void ToolUtils::drawRect(const TRectD &rect, const TPixel32 &color, |
| unsigned short stipple, bool doContrast) { |
| GLint src, dst; |
| bool isEnabled; |
| tglColor(color); |
| if (doContrast) { |
| if (color == TPixel32::Black) tglColor(TPixel32(90, 90, 90)); |
| isEnabled = glIsEnabled(GL_BLEND); |
| glGetIntegerv(GL_BLEND_SRC, &src); |
| glGetIntegerv(GL_BLEND_DST, &dst); |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); |
| } |
| |
| if (stipple != 0xffff) { |
| glLineStipple(1, stipple); |
| glEnable(GL_LINE_STIPPLE); |
| } |
| |
| glBegin(GL_LINE_STRIP); |
| tglVertex(rect.getP00()); |
| tglVertex(rect.getP01()); |
| tglVertex(rect.getP11()); |
| tglVertex(rect.getP10()); |
| tglVertex(rect.getP00()); |
| glEnd(); |
| glDisable(GL_LINE_STIPPLE); |
| if (doContrast) { |
| if (!isEnabled) glDisable(GL_BLEND); |
| glBlendFunc(src, dst); |
| } |
| } |
| |
| |
| |
| void ToolUtils::fillRect(const TRectD &rect, const TPixel32 &color) { |
| tglColor(color); |
| glBegin(GL_QUADS); |
| tglVertex(rect.getP00()); |
| tglVertex(rect.getP01()); |
| tglVertex(rect.getP11()); |
| tglVertex(rect.getP10()); |
| tglVertex(rect.getP00()); |
| glEnd(); |
| } |
| |
| |
| |
| void ToolUtils::drawPoint(const TPointD &q, double pixelSize) { |
| double size = pixelSize * 2.0; |
| glBegin(GL_QUADS); |
| glVertex2d(q.x - size, q.y - size); |
| glVertex2d(q.x - size, q.y + size); |
| glVertex2d(q.x + size, q.y + size); |
| glVertex2d(q.x + size, q.y - size); |
| glEnd(); |
| } |
| |
| |
| |
| void ToolUtils::drawCross(const TPointD &q, double pixelSize) { |
| double size = pixelSize; |
| glBegin(GL_LINES); |
| glVertex2d(q.x - size, q.y); |
| glVertex2d(q.x + size, q.y); |
| glEnd(); |
| glBegin(GL_LINES); |
| glVertex2d(q.x, q.y - size); |
| glVertex2d(q.x, q.y + size); |
| glEnd(); |
| } |
| |
| |
| |
| void ToolUtils::drawArrow(const TSegment &s, double pixelSize) { |
| TPointD v, vn; |
| |
| double length = s.getLength() * pixelSize; |
| |
| if (length == 0) return; |
| |
| v = normalize(s.getSpeed()); |
| vn = v; |
| |
| TPointD p1 = s.getP0() + v * length; |
| glBegin(GL_LINES); |
| tglVertex(s.getP0()); |
| tglVertex(p1); |
| glEnd(); |
| |
| v = v * length * 0.7; |
| |
| vn = vn * length * 0.2; |
| |
| TPointD p; |
| |
| glBegin(GL_TRIANGLES); |
| p = s.getP0() + v + rotate90(vn); |
| tglVertex(p); |
| tglVertex(p1); |
| p = s.getP0() + v + rotate270(vn); |
| tglVertex(p); |
| glEnd(); |
| } |
| |
| |
| |
| void ToolUtils::drawSquare(const TPointD &pos, double r, |
| const TPixel32 &color) { |
| TRectD rect(pos - TPointD(r, r), pos + TPointD(r, r)); |
| tglColor(color); |
| glBegin(GL_LINE_STRIP); |
| tglVertex(rect.getP00()); |
| tglVertex(rect.getP01()); |
| tglVertex(rect.getP11()); |
| tglVertex(rect.getP10()); |
| tglVertex(rect.getP00()); |
| glEnd(); |
| } |
| |
| |
| |
| void ToolUtils::drawRectWhitArrow(const TPointD &pos, double r) { |
| if (TTool::getApplication()->getCurrentObject()->isSpline()) return; |
| TRectD rect(pos - TPointD(14 * r, 2 * r), pos + TPointD(14 * r, 2 * r)); |
| tglColor(TPixel32::Black); |
| glBegin(GL_POLYGON); |
| tglVertex(rect.getP00()); |
| tglVertex(rect.getP10()); |
| tglVertex(rect.getP11()); |
| tglVertex(rect.getP01()); |
| glEnd(); |
| |
| double par = 5 * r; |
| |
| TPointD p01 = 0.5 * (rect.getP00() + rect.getP10()); |
| TPointD p02 = 0.5 * (rect.getP01() + rect.getP11()); |
| TPointD p11 = TPointD(p01.x, p01.y - par); |
| TPointD p12 = TPointD(p02.x, p02.y + par); |
| TPointD p; |
| |
| tglColor(TPixel32(130, 130, 130)); |
| |
| glBegin(GL_TRIANGLES); |
| p = p11 + rotate90(TPointD(0, par)); |
| tglVertex(p); |
| tglVertex(p01); |
| p = p11 + rotate270(TPointD(0, par)); |
| tglVertex(p); |
| glEnd(); |
| |
| glBegin(GL_TRIANGLES); |
| p = p12 + rotate90(TPointD(0, -par)); |
| tglVertex(p); |
| tglVertex(p02); |
| p = p12 + rotate270(TPointD(0, -par)); |
| tglVertex(p); |
| glEnd(); |
| } |
| |
| |
| |
| QRadialGradient ToolUtils::getBrushPad(int size, double hardness) { |
| hardness = tcrop(hardness, 0.0, 0.97); |
| double halfSize = size * 0.5; |
| double x = halfSize * hardness; |
| TQuadratic q(TPointD(x, 1.0), TPointD((halfSize + x) * 0.5, 0.0), |
| TPointD(halfSize, 0.0)); |
| QRadialGradient rd(QPointF(halfSize, halfSize), halfSize, |
| QPointF(halfSize, halfSize)); |
| rd.setColorAt(0, QColor(0, 0, 0)); |
| |
| double t; |
| double offset = halfSize - x; |
| assert(offset > 0); |
| for (t = 0; t <= 1; t += 1.0 / offset) { |
| TPointD p = q.getPoint(t); |
| int value = 255 * p.y; |
| rd.setColorAt(p.x / halfSize, QColor(0, 0, 0, value)); |
| } |
| return rd; |
| } |
| |
| |
| |
| QList<TRect> ToolUtils::splitRect(const TRect &first, const TRect &second) { |
| TRect intersection = first * second; |
| QList<TRect> rects; |
| if (intersection.isEmpty()) { |
| rects.append(first); |
| return rects; |
| } |
| |
| TRect rect; |
| if (first.x0 < intersection.x0) { |
| rect = TRect(first.getP00(), TPoint(intersection.x0 - 1, first.y1)); |
| rects.append(rect); |
| } |
| if (intersection.x1 < first.x1) { |
| rect = TRect(TPoint(intersection.x1 + 1, first.y0), first.getP11()); |
| rects.append(rect); |
| } |
| if (intersection.y1 < first.y1) { |
| rect = |
| TRect(intersection.x0, intersection.y1 + 1, intersection.x1, first.y1); |
| rects.append(rect); |
| } |
| if (first.y0 < intersection.y0) { |
| rect = |
| TRect(intersection.x0, first.y0, intersection.x1, intersection.y0 - 1); |
| rects.append(rect); |
| } |
| return rects; |
| } |
| |
| |
| |
| TRaster32P ToolUtils::convertStrokeToImage(TStroke *stroke, |
| const TRect &imageBounds, |
| TPoint &pos, bool pencilMode) { |
| int count = stroke->getControlPointCount(); |
| if (count == 0) return TRaster32P(); |
| TPointD imgCenter = TPointD((imageBounds.x0 + imageBounds.x1) * 0.5, |
| (imageBounds.y0 + imageBounds.y1) * 0.5); |
| |
| TStroke s(*stroke); |
| |
| |
| TThickPoint first = s.getControlPoint(0); |
| TThickPoint back = s.getControlPoint(count - 1); |
| if (first != back) { |
| TPointD mid = (first + back) * 0.5; |
| s.setControlPoint(count, mid); |
| s.setControlPoint(count + 1, first); |
| } |
| |
| |
| s.transform(TTranslation(imgCenter)); |
| TRectD bbox = s.getBBox(); |
| TRect rect(tfloor(bbox.x0), tfloor(bbox.y0), tfloor(bbox.x1), |
| tfloor(bbox.y1)); |
| pos = rect.getP00(); |
| pos = TPoint(pos.x > 0 ? pos.x : 0, pos.y > 0 ? pos.y : 0); |
| rect *= imageBounds; |
| if (rect.isEmpty()) return TRaster32P(); |
| |
| |
| QImage img(rect.getLx(), rect.getLy(), QImage::Format_ARGB32); |
| img.fill(Qt::transparent); |
| QColor color = Qt::black; |
| QPainter p(&img); |
| p.setPen(QPen(color, 1, Qt::SolidLine)); |
| p.setBrush(color); |
| p.setRenderHint(QPainter::Antialiasing, !pencilMode); |
| QPainterPath path = strokeToPainterPath(&s); |
| QRectF pathRect = path.boundingRect(); |
| p.translate(-toQPoint(pos)); |
| p.drawPath(path); |
| return rasterFromQImage(img, true, false); |
| } |
| |
| |
| |
| TStroke *ToolUtils::merge(const ArrayOfStroke &a) { |
| std::vector<TThickPoint> v; |
| |
| TStroke *ref = 0; |
| int controlPoints = 0; |
| |
| for (UINT i = 0; i < a.size(); ++i) { |
| ref = a[i]; |
| assert(ref); |
| if (!ref) continue; |
| |
| controlPoints = ref->getControlPointCount() - 1; |
| |
| for (int j = 0; j < controlPoints; ++j) |
| v.push_back(ref->getControlPoint(j)); |
| } |
| |
| if (controlPoints > 0) v.push_back(ref->getControlPoint(controlPoints)); |
| |
| TStroke *out = new TStroke(v); |
| |
| return out; |
| } |
| |
| |
| |
| |
| |
| |
| |
| ToolUtils::TToolUndo::TToolUndo(TXshSimpleLevel *level, const TFrameId &frameId, |
| bool createdFrame, bool createdLevel, |
| const TPaletteP &oldPalette) |
| : TUndo() |
| , m_level(level) |
| , m_frameId(frameId) |
| , m_oldPalette(oldPalette) |
| , m_col(-2) |
| , m_row(-1) |
| , m_isEditingLevel(false) |
| , m_createdFrame(createdFrame) |
| , m_createdLevel(createdLevel) |
| , m_renumberedLevel(TTool::m_isLevelRenumbererd) |
| , m_imageId("") { |
| TTool::Application *app = TTool::getApplication(); |
| m_isEditingLevel = app->getCurrentFrame()->isEditingLevel(); |
| if (!m_isEditingLevel) { |
| m_col = app->getCurrentColumn()->getColumnIndex(); |
| m_row = app->getCurrentFrame()->getFrameIndex(); |
| m_cellsData = TTool::m_cellsData; |
| } |
| if (m_renumberedLevel) { |
| m_oldFids = TTool::m_oldFids; |
| m_newFids = TTool::m_newFids; |
| } |
| if (createdFrame) { |
| m_imageId = "TToolUndo" + std::to_string(m_idCount++); |
| TImageCache::instance()->add(m_imageId, level->getFrame(frameId, false), |
| false); |
| } |
| } |
| |
| |
| |
| ToolUtils::TToolUndo::~TToolUndo() { |
| TImageCache::instance()->remove(m_imageId); |
| } |
| |
| |
| |
| void ToolUtils::TToolUndo::insertLevelAndFrameIfNeeded() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (m_renumberedLevel) { |
| TXsheet *xsh = app->getCurrentScene()->getScene()->getTopXsheet(); |
| std::vector<TXshChildLevel *> childLevels; |
| ToolUtils::doUpdateXSheet(m_level.getPointer(), m_oldFids, m_newFids, xsh, |
| childLevels); |
| m_level->renumber(m_newFids); |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| } |
| if (m_createdLevel) { |
| TLevelSet *levelSet = app->getCurrentScene()->getScene()->getLevelSet(); |
| if (levelSet) { |
| levelSet->insertLevel(m_level.getPointer()); |
| app->getCurrentScene()->notifyCastChange(); |
| } |
| } |
| if (m_createdFrame) { |
| TXsheet *xsh = app->getCurrentXsheet()->getXsheet(); |
| TImageP img = TImageCache::instance()->get(m_imageId, false); |
| m_level->setFrame(m_frameId, img); |
| if (!m_isEditingLevel) { |
| for (const TTool::CellOps &cellOps : m_cellsData) { |
| TXshCell cell; |
| if (cellOps.type == TTool::CellOps::BlankToExisting) |
| cell = xsh->getCell(cellOps.r0 - 1, m_col); |
| else |
| cell = TXshCell(m_level.getPointer(), m_frameId); |
| for (int r = cellOps.r0; r <= cellOps.r1; r++) |
| xsh->setCell(r, m_col, cell); |
| } |
| } |
| app->getCurrentLevel()->notifyLevelChange(); |
| } |
| } |
| |
| |
| |
| void ToolUtils::TToolUndo::removeLevelAndFrameIfNeeded() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (m_createdFrame) { |
| m_level->eraseFrame(m_frameId); |
| if (!m_isEditingLevel) { |
| TXsheet *xsh = app->getCurrentXsheet()->getXsheet(); |
| TXshCell cell; |
| for (const TTool::CellOps &cellOps : m_cellsData) { |
| if (cellOps.type == TTool::CellOps::ExistingToNew) |
| cell = xsh->getCell(cellOps.r0 - 1, m_col); |
| for (int r = cellOps.r0; r <= cellOps.r1; r++) |
| xsh->setCell(r, m_col, cell); |
| } |
| if (m_cellsData.size() < 1) { |
| xsh->setCell(m_row, m_col, cell); |
| } |
| } |
| if (m_createdLevel) { |
| |
| TLevelSet *levelSet = app->getCurrentScene()->getScene()->getLevelSet(); |
| if (levelSet) { |
| levelSet->removeLevel(m_level.getPointer()); |
| app->getCurrentScene()->notifyCastChange(); |
| } |
| } |
| app->getCurrentLevel()->notifyLevelChange(); |
| } |
| if (m_oldPalette.getPointer()) { |
| m_level->getPalette()->assign(m_oldPalette->clone()); |
| app->getPaletteController() |
| ->getCurrentLevelPalette() |
| ->notifyPaletteChanged(); |
| } |
| if (m_renumberedLevel) { |
| TXsheet *xsh = app->getCurrentScene()->getScene()->getTopXsheet(); |
| std::vector<TXshChildLevel *> childLevels; |
| ToolUtils::doUpdateXSheet(m_level.getPointer(), m_newFids, m_oldFids, xsh, |
| childLevels); |
| m_level->renumber(m_oldFids); |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| } |
| } |
| |
| |
| |
| void ToolUtils::TToolUndo::notifyImageChanged() const { |
| TTool::Application *app = TTool::getApplication(); |
| |
| TXshSimpleLevel *currentSl = 0; |
| TFrameId currentFrameId; |
| if (app->getCurrentFrame()->isEditingLevel()) { |
| TXshLevel *xl = app->getCurrentLevel()->getLevel(); |
| if (!xl) return; |
| currentSl = xl->getSimpleLevel(); |
| currentFrameId = app->getCurrentFrame()->getFid(); |
| } else { |
| int row = app->getCurrentFrame()->getFrame(); |
| int col = app->getCurrentColumn()->getColumnIndex(); |
| if (col < 0) return; |
| TXsheet *xsh = app->getCurrentXsheet()->getXsheet(); |
| if (!xsh) return; |
| TXshCell cell = xsh->getCell(row, col); |
| currentSl = cell.getSimpleLevel(); |
| currentFrameId = cell.getFrameId(); |
| } |
| if (currentSl == m_level.getPointer() && currentFrameId == m_frameId) { |
| TTool *tool = app->getCurrentTool()->getTool(); |
| if (tool) tool->onImageChanged(); |
| } |
| |
| IconGenerator::instance()->invalidate(m_level.getPointer(), m_frameId); |
| IconGenerator::instance()->invalidateSceneIcon(); |
| |
| if (m_level && m_level->getType() == PLI_XSHLEVEL) { |
| std::string id = m_level->getImageId(m_frameId) + "_rasterized"; |
| ImageManager::instance()->invalidate(id); |
| } |
| } |
| |
| |
| |
| void ToolUtils::TToolUndo::onAdd() { |
| |
| TTool::m_isLevelCreated = false; |
| TTool::m_isFrameCreated = false; |
| TTool::m_isLevelRenumbererd = false; |
| } |
| |
| |
| |
| int ToolUtils::TToolUndo::m_idCount = 0; |
| |
| |
| |
| |
| |
| ToolUtils::TRasterUndo::TRasterUndo(TTileSetCM32 *tiles, TXshSimpleLevel *level, |
| const TFrameId &frameId, bool createdFrame, |
| bool createdLevel, |
| const TPaletteP &oldPalette) |
| : TToolUndo(level, frameId, createdFrame, createdLevel, oldPalette) |
| , m_tiles(tiles) {} |
| |
| |
| |
| ToolUtils::TRasterUndo::~TRasterUndo() { |
| if (m_tiles) delete m_tiles; |
| } |
| |
| |
| |
| TToonzImageP ToolUtils::TRasterUndo::getImage() const { |
| if (m_level->isFid(m_frameId)) |
| return (TToonzImageP)m_level->getFrame(m_frameId, true); |
| return 0; |
| } |
| |
| |
| |
| int ToolUtils::TRasterUndo::getSize() const { |
| int size = sizeof(*this); |
| size += sizeof(*(m_level.getPointer())); |
| size += sizeof(*(m_oldPalette.getPointer())); |
| return m_tiles ? m_tiles->getMemorySize() + size : size; |
| } |
| |
| |
| |
| void ToolUtils::TRasterUndo::undo() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| if (m_tiles && m_tiles->getTileCount() > 0) { |
| TToonzImageP image = getImage(); |
| if (!image) return; |
| |
| ToonzImageUtils::paste(image, m_tiles); |
| ToolUtils::updateSaveBox(m_level, m_frameId); |
| } |
| |
| removeLevelAndFrameIfNeeded(); |
| |
| if (m_level) { |
| m_level->setDirtyFlag(true); |
| } |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| |
| |
| |
| ToolUtils::TFullColorRasterUndo::TFullColorRasterUndo( |
| TTileSetFullColor *tiles, TXshSimpleLevel *level, const TFrameId &frameId, |
| bool createdFrame, bool createdLevel, const TPaletteP &oldPalette) |
| : TToolUndo(level, frameId, createdFrame, createdLevel, oldPalette) |
| , m_tiles(tiles) {} |
| |
| |
| |
| ToolUtils::TFullColorRasterUndo::~TFullColorRasterUndo() { delete m_tiles; } |
| |
| |
| |
| TRasterImageP ToolUtils::TFullColorRasterUndo::getImage() const { |
| if (m_level->isFid(m_frameId)) |
| return (TRasterImageP)m_level->getFrame(m_frameId, true); |
| return 0; |
| } |
| |
| |
| |
| int ToolUtils::TFullColorRasterUndo::getSize() const { |
| int size = sizeof(*this); |
| size += sizeof(*(m_level.getPointer())); |
| size += sizeof(*(m_oldPalette.getPointer())); |
| return m_tiles ? m_tiles->getMemorySize() + size : size; |
| } |
| |
| |
| |
| void ToolUtils::TFullColorRasterUndo::undo() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| if (m_tiles && m_tiles->getTileCount() > 0) { |
| TRasterImageP image = getImage(); |
| if (!image) return; |
| std::vector<TRect> rects = paste(image, m_tiles); |
| int i; |
| TRect resRect = rects[0]; |
| for (i = 1; i < (int)rects.size(); i++) resRect += rects[i]; |
| } |
| |
| removeLevelAndFrameIfNeeded(); |
| |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| |
| std::vector<TRect> ToolUtils::TFullColorRasterUndo::paste( |
| const TRasterImageP &ti, const TTileSetFullColor *tileSet) const { |
| std::vector<TRect> rects; |
| TRasterP raster = ti->getRaster(); |
| for (int i = 0; i < tileSet->getTileCount(); i++) { |
| const TTileSetFullColor::Tile *tile = tileSet->getTile(i); |
| TRasterP ras; |
| tile->getRaster(ras); |
| assert(!!ras); |
| raster->copy(ras, tile->m_rasterBounds.getP00()); |
| rects.push_back(tile->m_rasterBounds); |
| } |
| return rects; |
| } |
| |
| |
| |
| |
| |
| ToolUtils::UndoModifyStroke::UndoModifyStroke(TXshSimpleLevel *level, |
| const TFrameId &frameId, |
| int strokeIndex) |
| : TToolUndo(level, frameId), m_strokeIndex(strokeIndex) { |
| TVectorImageP image = level->getFrame(frameId, true); |
| assert(image); |
| TStroke *stroke = image->getStroke(m_strokeIndex); |
| int n = stroke->getControlPointCount(); |
| for (int i = 0; i < n; i++) m_before.push_back(stroke->getControlPoint(i)); |
| |
| m_selfLoopBefore = stroke->isSelfLoop(); |
| |
| TTool::Application *app = TTool::getApplication(); |
| m_row = app->getCurrentFrame()->getFrame(); |
| m_column = app->getCurrentColumn()->getColumnIndex(); |
| } |
| |
| |
| |
| ToolUtils::UndoModifyStroke::~UndoModifyStroke() {} |
| |
| |
| |
| void ToolUtils::UndoModifyStroke::onAdd() { |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(image); |
| if (!image) return; |
| |
| TStroke *stroke = image->getStroke(m_strokeIndex); |
| assert(stroke); |
| int n = stroke->getControlPointCount(); |
| for (int i = 0; i < n; i++) m_after.push_back(stroke->getControlPoint(i)); |
| |
| m_selfLoopAfter = stroke->isSelfLoop(); |
| } |
| |
| |
| |
| void ToolUtils::UndoModifyStroke::undo() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| if (app->getCurrentFrame()->isEditingScene()) { |
| app->getCurrentColumn()->setColumnIndex(m_column); |
| app->getCurrentFrame()->setFrame(m_row); |
| } else |
| app->getCurrentFrame()->setFid(m_frameId); |
| |
| TSelection *selection = app->getCurrentSelection()->getSelection(); |
| if (selection) selection->selectNone(); |
| |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(image); |
| if (!image) return; |
| QMutexLocker lock(image->getMutex()); |
| TStroke *stroke = 0; |
| if (image->getStrokeCount() == |
| 1) |
| stroke = image->getStroke(0); |
| else |
| stroke = image->getStroke(m_strokeIndex); |
| if (!stroke) return; |
| TStroke *oldStroke = new TStroke(*stroke); |
| stroke->reshape(&m_before[0], m_before.size()); |
| stroke->setSelfLoop(m_selfLoopBefore); |
| image->notifyChangedStrokes(m_strokeIndex, oldStroke); |
| notifyImageChanged(); |
| delete oldStroke; |
| } |
| |
| |
| |
| void ToolUtils::UndoModifyStroke::redo() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| if (app->getCurrentFrame()->isEditingScene()) { |
| app->getCurrentColumn()->setColumnIndex(m_column); |
| app->getCurrentFrame()->setFrame(m_row); |
| } else |
| app->getCurrentFrame()->setFid(m_frameId); |
| |
| TSelection *selection = app->getCurrentSelection()->getSelection(); |
| if (selection) selection->selectNone(); |
| |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(image); |
| if (!image) return; |
| QMutexLocker lock(image->getMutex()); |
| TStroke *stroke = 0; |
| if (image->getStrokeCount() == |
| 1) |
| stroke = image->getStroke(0); |
| else |
| stroke = image->getStroke(m_strokeIndex); |
| if (!stroke) return; |
| TStroke *oldStroke = new TStroke(*stroke); |
| stroke->reshape(&m_after[0], m_after.size()); |
| stroke->setSelfLoop(m_selfLoopAfter); |
| image->notifyChangedStrokes(m_strokeIndex, oldStroke); |
| delete oldStroke; |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| |
| int ToolUtils::UndoModifyStroke::getSize() const { |
| return (m_before.capacity() + m_after.capacity()) * sizeof(TThickPoint) + |
| sizeof(*this) + 500; |
| } |
| |
| |
| |
| ToolUtils::UndoModifyStrokeAndPaint::UndoModifyStrokeAndPaint( |
| TXshSimpleLevel *level, const TFrameId &frameId, int strokeIndex) |
| : UndoModifyStroke(level, frameId, strokeIndex), m_fillInformation(0) { |
| TVectorImageP image = level->getFrame(frameId, true); |
| assert(image); |
| TStroke *stroke = image->getStroke(strokeIndex); |
| m_oldBBox = stroke->getBBox(); |
| } |
| |
| |
| |
| void ToolUtils::UndoModifyStrokeAndPaint::onAdd() { |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(!!image); |
| if (!image) return; |
| |
| UndoModifyStroke::onAdd(); |
| TStroke *stroke = image->getStroke(m_strokeIndex); |
| m_fillInformation = new std::vector<TFilledRegionInf>; |
| ImageUtils::getFillingInformationOverlappingArea( |
| image, *m_fillInformation, m_oldBBox, stroke->getBBox()); |
| } |
| |
| |
| |
| void ToolUtils::UndoModifyStrokeAndPaint::undo() const { |
| TTool::Application *application = TTool::getApplication(); |
| if (!application) return; |
| |
| UndoModifyStroke::undo(); |
| TRegion *reg; |
| UINT size = m_fillInformation->size(); |
| if (!size) { |
| application->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| return; |
| } |
| |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(!!image); |
| if (!image) return; |
| |
| |
| image->findRegions(); |
| for (UINT i = 0; i < size; i++) { |
| reg = image->getRegion((*m_fillInformation)[i].m_regionId); |
| assert(reg); |
| if (reg) reg->setStyle((*m_fillInformation)[i].m_styleId); |
| } |
| |
| application->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| ToolUtils::UndoModifyStrokeAndPaint::~UndoModifyStrokeAndPaint() { |
| delete m_fillInformation; |
| } |
| |
| |
| |
| int ToolUtils::UndoModifyStrokeAndPaint::getSize() const { |
| int size = m_fillInformation |
| ? m_fillInformation->size() * sizeof(TFilledRegionInf) |
| : 0; |
| return UndoModifyStroke::getSize() + size; |
| } |
| |
| |
| |
| ToolUtils::UndoModifyListStroke::UndoModifyListStroke( |
| TXshSimpleLevel *level, const TFrameId &frameId, |
| const std::vector<TStroke *> &strokeVect) |
| : TToolUndo(level, frameId), m_fillInformation(0) { |
| UINT strokeNum = strokeVect.size(); |
| TVectorImageP image = level->getFrame(frameId, true); |
| assert(image); |
| for (UINT i = 0; i < strokeNum; i++) { |
| m_oldBBox += (strokeVect[i])->getBBox(); |
| int strokeIndex = image->getStrokeIndex(strokeVect[i]); |
| m_strokeList.push_back(new UndoModifyStroke(level, frameId, strokeIndex)); |
| } |
| |
| m_beginIt = m_strokeList.begin(); |
| m_endIt = m_strokeList.end(); |
| } |
| |
| |
| |
| ToolUtils::UndoModifyListStroke::~UndoModifyListStroke() { |
| clearPointerContainer(m_strokeList); |
| delete m_fillInformation; |
| } |
| |
| |
| |
| void ToolUtils::UndoModifyListStroke::onAdd() { |
| std::list<UndoModifyStroke *>::iterator it = m_beginIt; |
| TRectD newBBox; |
| |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(!!image); |
| if (!image) return; |
| |
| for (; it != m_endIt; ++it) { |
| TStroke *s = image->getStroke((*it)->m_strokeIndex); |
| (*it)->onAdd(); |
| } |
| m_fillInformation = new std::vector<TFilledRegionInf>; |
| |
| if (m_beginIt != m_endIt) |
| ImageUtils::getFillingInformationOverlappingArea(image, *m_fillInformation, |
| m_oldBBox, newBBox); |
| } |
| |
| |
| |
| void ToolUtils::UndoModifyListStroke::undo() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| std::list<UndoModifyStroke *>::iterator stroke_it = m_beginIt; |
| if (m_beginIt == m_endIt) return; |
| |
| for (; stroke_it != m_endIt; ++stroke_it) { |
| (*stroke_it)->undo(); |
| } |
| |
| UINT size = m_fillInformation->size(); |
| if (!size) { |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| app->getCurrentTool()->getTool()->notifyImageChanged(); |
| return; |
| } |
| |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(!!image); |
| if (!image) return; |
| |
| image->findRegions(); |
| |
| TRegion *reg; |
| for (UINT i = 0; i < size; i++) { |
| reg = image->getRegion((*m_fillInformation)[i].m_regionId); |
| assert(reg); |
| if (reg) reg->setStyle((*m_fillInformation)[i].m_styleId); |
| } |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| |
| void ToolUtils::UndoModifyListStroke::redo() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| std::list<UndoModifyStroke *>::iterator it = m_beginIt; |
| |
| for (; it != m_endIt; ++it) { |
| (*it)->redo(); |
| } |
| |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| |
| int ToolUtils::UndoModifyListStroke::getSize() const { |
| int sum = 0; |
| |
| std::list<UndoModifyStroke *>::iterator it = m_beginIt; |
| |
| for (; it != m_endIt; ++it) { |
| sum += (*it)->getSize(); |
| } |
| |
| if (m_fillInformation) |
| sum += m_fillInformation->capacity() * sizeof(TFilledRegionInf); |
| |
| return sum; |
| } |
| |
| |
| |
| ToolUtils::UndoPencil::UndoPencil( |
| TStroke *stroke, std::vector<TFilledRegionInf> *fillInformation, |
| TXshSimpleLevel *level, const TFrameId &frameId, bool createdFrame, |
| bool createdLevel, bool autogroup, bool autofill) |
| : TToolUndo(level, frameId, createdFrame, createdLevel, 0) |
| , m_strokeId(stroke->getId()) |
| , m_fillInformation(fillInformation) |
| , m_autogroup(autogroup) |
| , m_autofill(autofill) { |
| m_stroke = new TStroke(*stroke); |
| } |
| |
| |
| |
| ToolUtils::UndoPencil::~UndoPencil() { |
| delete m_fillInformation; |
| delete m_stroke; |
| } |
| |
| |
| |
| void ToolUtils::UndoPencil::undo() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(image); |
| if (!image) return; |
| |
| QMutexLocker sl(image->getMutex()); |
| VIStroke *stroke = image->getStrokeById(m_strokeId); |
| if (!stroke) return; |
| image->deleteStroke(stroke); |
| TSelection *selection = app->getCurrentSelection()->getSelection(); |
| StrokeSelection *strokeSelection = dynamic_cast<StrokeSelection *>(selection); |
| if (strokeSelection) strokeSelection->selectNone(); |
| |
| UINT size = m_fillInformation->size(); |
| TRegion *reg; |
| for (UINT i = 0; i < size; i++) { |
| reg = image->getRegion((*m_fillInformation)[i].m_regionId); |
| assert(reg); |
| if (reg) reg->setStyle((*m_fillInformation)[i].m_styleId); |
| } |
| |
| removeLevelAndFrameIfNeeded(); |
| |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| |
| void ToolUtils::UndoPencil::redo() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| insertLevelAndFrameIfNeeded(); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| if (!image) return; |
| |
| QMutexLocker sl(image->getMutex()); |
| TStroke *stroke = new TStroke(*m_stroke); |
| stroke->setId(m_strokeId); |
| image->addStroke(stroke); |
| if (image->isComputedRegionAlmostOnce()) image->findRegions(); |
| |
| if (m_autogroup && stroke->isSelfLoop()) { |
| int index = image->getStrokeCount() - 1; |
| image->group(index, 1); |
| if (m_autofill) { |
| |
| int currentGroup = image->exitGroup(); |
| image->enterGroup(index); |
| image->selectFill(stroke->getBBox().enlarge(1, 1), 0, stroke->getStyle(), |
| false, true, false); |
| if (currentGroup != -1) |
| image->enterGroup(currentGroup); |
| else |
| image->exitGroup(); |
| } |
| } |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| |
| int ToolUtils::UndoPencil::getSize() const { |
| return sizeof(*this) + |
| m_fillInformation->capacity() * sizeof(TFilledRegionInf) + 500; |
| } |
| |
| |
| |
| ToolUtils::UndoRasterPencil::UndoRasterPencil( |
| TXshSimpleLevel *level, const TFrameId &frameId, TStroke *stroke, |
| bool selective, bool filled, bool doAntialias, bool createdFrame, |
| bool createdLevel, std::string primitiveName) |
| : TRasterUndo(0, level, frameId, createdFrame, createdLevel, 0) |
| , m_selective(selective) |
| , m_filled(filled) |
| , m_doAntialias(doAntialias) |
| , m_primitiveName(primitiveName) { |
| TRasterCM32P raster = getImage()->getRaster(); |
| TDimension d = raster->getSize(); |
| m_tiles = new TTileSetCM32(d); |
| TRect rect = |
| convert(stroke->getBBox()) + TPoint((int)(d.lx * 0.5), (int)(d.ly * 0.5)); |
| m_tiles->add(raster, rect.enlarge(2)); |
| m_stroke = new TStroke(*stroke); |
| } |
| |
| |
| |
| ToolUtils::UndoRasterPencil::~UndoRasterPencil() { delete m_stroke; } |
| |
| |
| |
| void ToolUtils::UndoRasterPencil::redo() const { |
| insertLevelAndFrameIfNeeded(); |
| TToonzImageP image = getImage(); |
| if (!image) return; |
| |
| ToonzImageUtils::addInkStroke(image, m_stroke, m_stroke->getStyle(), |
| m_selective, m_filled, TConsts::infiniteRectD, |
| m_doAntialias); |
| ToolUtils::updateSaveBox(); |
| TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| |
| int ToolUtils::UndoRasterPencil::getSize() const { |
| return TRasterUndo::getSize() + |
| m_stroke->getControlPointCount() * sizeof(TThickPoint) + 100 + |
| sizeof(this); |
| } |
| |
| |
| |
| ToolUtils::UndoFullColorPencil::UndoFullColorPencil( |
| TXshSimpleLevel *level, const TFrameId &frameId, TStroke *stroke, |
| double opacity, bool doAntialias, bool createdFrame, bool createdLevel) |
| : TFullColorRasterUndo(0, level, frameId, createdFrame, createdLevel, 0) |
| , m_opacity(opacity) |
| , m_doAntialias(doAntialias) { |
| TRasterP raster = getImage()->getRaster(); |
| TDimension d = raster->getSize(); |
| m_tiles = new TTileSetFullColor(d); |
| TRect rect = |
| convert(stroke->getBBox()) + TPoint((int)(d.lx * 0.5), (int)(d.ly * 0.5)); |
| m_tiles->add(raster, rect.enlarge(2)); |
| m_stroke = new TStroke(*stroke); |
| } |
| |
| |
| |
| ToolUtils::UndoFullColorPencil::~UndoFullColorPencil() { delete m_stroke; } |
| |
| |
| |
| void ToolUtils::UndoFullColorPencil::redo() const { |
| insertLevelAndFrameIfNeeded(); |
| TRasterImageP image = getImage(); |
| if (!image) return; |
| TRasterImageUtils::addStroke(image, m_stroke, TRectD(), m_opacity, |
| m_doAntialias); |
| TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| |
| int ToolUtils::UndoFullColorPencil::getSize() const { |
| return TFullColorRasterUndo::getSize() + |
| m_stroke->getControlPointCount() * sizeof(TThickPoint) + 100 + |
| sizeof(this); |
| } |
| |
| |
| |
| |
| |
| ToolUtils::UndoPath::UndoPath(TStageObjectSpline *spline) : m_spline(spline) { |
| assert(!!m_spline); |
| |
| const TStroke *stroke = m_spline->getStroke(); |
| assert(stroke); |
| int n = stroke->getControlPointCount(); |
| for (int i = 0; i < n; i++) m_before.push_back(stroke->getControlPoint(i)); |
| m_selfLoopBefore = stroke->isSelfLoop(); |
| } |
| |
| ToolUtils::UndoPath::~UndoPath() {} |
| |
| void ToolUtils::UndoPath::onAdd() { |
| assert(!!m_spline); |
| |
| const TStroke *stroke = m_spline->getStroke(); |
| assert(stroke); |
| int n = stroke->getControlPointCount(); |
| for (int i = 0; i < n; i++) m_after.push_back(stroke->getControlPoint(i)); |
| } |
| |
| void ToolUtils::UndoPath::undo() const { |
| assert(!!m_spline); |
| |
| TTool::Application *app = TTool::getApplication(); |
| TSelection *selection = app->getCurrentSelection()->getSelection(); |
| if (selection) selection->selectNone(); |
| |
| TStroke *stroke = new TStroke(*m_spline->getStroke()); |
| stroke->reshape(&m_before[0], m_before.size()); |
| stroke->setSelfLoop(m_selfLoopBefore); |
| m_spline->setStroke(stroke); |
| |
| if (!app->getCurrentObject()->isSpline()) return; |
| |
| TStageObjectId currentObjectId = app->getCurrentObject()->getObjectId(); |
| TStageObject *stageObject = |
| app->getCurrentXsheet()->getXsheet()->getStageObject(currentObjectId); |
| if (stageObject->getSpline()->getId() == m_spline->getId()) |
| app->getCurrentObject()->setSplineObject(m_spline); |
| |
| app->getCurrentTool()->getTool()->notifyImageChanged(); |
| } |
| |
| void ToolUtils::UndoPath::redo() const { |
| assert(!!m_spline); |
| |
| TTool::Application *app = TTool::getApplication(); |
| TSelection *selection = app->getCurrentSelection()->getSelection(); |
| if (selection) selection->selectNone(); |
| |
| TStroke *stroke = new TStroke(*m_spline->getStroke()); |
| stroke->reshape(&m_after[0], m_after.size()); |
| stroke->setSelfLoop(m_selfLoopBefore); |
| m_spline->setStroke(stroke); |
| |
| if (!app->getCurrentObject()->isSpline()) return; |
| |
| TStageObjectId currentObjectId = app->getCurrentObject()->getObjectId(); |
| TStageObject *stageObject = |
| app->getCurrentXsheet()->getXsheet()->getStageObject(currentObjectId); |
| if (stageObject->getSpline()->getId() == m_spline->getId()) |
| app->getCurrentObject()->setSplineObject(m_spline); |
| |
| app->getCurrentTool()->getTool()->notifyImageChanged(); |
| } |
| |
| int ToolUtils::UndoPath::getSize() const { return sizeof(*this) + 500; } |
| |
| |
| |
| |
| |
| |
| ToolUtils::UndoControlPointEditor::UndoControlPointEditor( |
| TXshSimpleLevel *level, const TFrameId &frameId) |
| : TToolUndo(level, frameId), m_isStrokeDelete(false) { |
| TVectorImageP image = level->getFrame(frameId, true); |
| assert(image); |
| if (!image) return; |
| |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| m_row = app->getCurrentFrame()->getFrame(); |
| m_column = app->getCurrentColumn()->getColumnIndex(); |
| } |
| |
| ToolUtils::UndoControlPointEditor::~UndoControlPointEditor() { |
| deleteVIStroke(m_oldStroke.second); |
| if (!m_isStrokeDelete) deleteVIStroke(m_newStroke.second); |
| } |
| |
| void ToolUtils::UndoControlPointEditor::onAdd() { |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(image); |
| if (!image) return; |
| QMutexLocker lock(image->getMutex()); |
| if (m_isStrokeDelete) return; |
| addNewStroke(m_oldStroke.first, image->getVIStroke(m_oldStroke.first)); |
| } |
| |
| void ToolUtils::UndoControlPointEditor::addOldStroke(int index, VIStroke *vs) { |
| VIStroke *s = cloneVIStroke(vs); |
| m_oldStroke = std::make_pair(index, s); |
| } |
| |
| void ToolUtils::UndoControlPointEditor::addNewStroke(int index, VIStroke *vs) { |
| VIStroke *s = cloneVIStroke(vs); |
| m_newStroke = std::make_pair(index, s); |
| } |
| |
| void ToolUtils::UndoControlPointEditor::undo() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| if (app->getCurrentFrame()->isEditingScene()) { |
| app->getCurrentColumn()->setColumnIndex(m_column); |
| app->getCurrentFrame()->setFrame(m_row); |
| } else |
| app->getCurrentFrame()->setFid(m_frameId); |
| |
| TSelection *selection = app->getCurrentSelection()->getSelection(); |
| if (selection) selection->selectNone(); |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(image); |
| if (!image) return; |
| QMutexLocker lock(image->getMutex()); |
| |
| if (!m_isStrokeDelete) image->removeStroke(m_newStroke.first, false); |
| |
| UINT i = 0; |
| VIStroke *s = cloneVIStroke(m_oldStroke.second); |
| image->insertStrokeAt(s, m_oldStroke.first); |
| |
| if (image->isComputedRegionAlmostOnce()) |
| image->findRegions(); |
| |
| |
| |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| void ToolUtils::UndoControlPointEditor::redo() const { |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| if (app->getCurrentFrame()->isEditingScene()) { |
| app->getCurrentColumn()->setColumnIndex(m_column); |
| app->getCurrentFrame()->setFrame(m_row); |
| } else |
| app->getCurrentFrame()->setFid(m_frameId); |
| TSelection *selection = app->getCurrentSelection()->getSelection(); |
| if (selection) selection->selectNone(); |
| TVectorImageP image = m_level->getFrame(m_frameId, true); |
| assert(image); |
| if (!image) return; |
| QMutexLocker lock(image->getMutex()); |
| |
| image->removeStroke(m_oldStroke.first, false); |
| |
| if (!m_isStrokeDelete) { |
| VIStroke *s = cloneVIStroke(m_newStroke.second); |
| image->insertStrokeAt(s, m_newStroke.first); |
| } |
| |
| if (image->isComputedRegionAlmostOnce()) |
| image->findRegions(); |
| |
| |
| |
| app->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| |
| |
| |
| |
| |
| ToolUtils::DragMenu::DragMenu() : QMenu() {} |
| |
| |
| |
| QAction *ToolUtils::DragMenu::exec(const QPoint &p, QAction *action) { |
| QAction *execAct = QMenu::exec(p, action); |
| if (execAct) return execAct; |
| |
| return defaultAction(); |
| } |
| |
| |
| |
| void ToolUtils::DragMenu::mouseReleaseEvent(QMouseEvent *e) { |
| QMenu::mouseReleaseEvent(e); |
| hide(); |
| } |
| |
| |
| |
| |
| |
| |
| ToolUtils::ColumChooserMenu::ColumChooserMenu( |
| TXsheet *xsh, const std::vector<int> &columnIndexes) |
| : DragMenu() { |
| int size = columnIndexes.size(); |
| int i; |
| for (i = size - 1; i >= 0; i--) { |
| int index = columnIndexes[i]; |
| TStageObjectId id = TStageObjectId::ColumnId(index); |
| TStageObject *stageObject = xsh->getStageObject(id); |
| QString cmdStr = "Column " + QString::fromStdString(stageObject->getName()); |
| QAction *act = new QAction(cmdStr, this); |
| act->setData(index); |
| addAction(act); |
| if (size - 1 == i) { |
| setDefaultAction(act); |
| setActiveAction(act); |
| } |
| } |
| } |
| |
| |
| |
| int ToolUtils::ColumChooserMenu::execute() { |
| QAction *executeAct = exec(QCursor::pos()); |
| return (!executeAct) ? -1 : executeAct->data().toInt(); |
| } |
| |
| |
| |
| double ToolUtils::ConeSubVolume::compute(double cover) { |
| double x = (10 * tcrop(cover, -1.0, 1.0)) + 10; |
| assert(0 <= x && x <= 20); |
| int i = tfloor(x); |
| if (i == 20) |
| return m_values[i]; |
| else |
| |
| return (-(x - (i + 1)) * m_values[i]) - (-(x - i) * m_values[i + 1]); |
| } |
| |
| const double ToolUtils::ConeSubVolume::m_values[] = { |
| 1.0, 0.99778, 0.987779, 0.967282, 0.934874, 0.889929, 0.832457, |
| 0.763067, 0.683002, 0.594266, 0.5, 0.405734, 0.316998, 0.236933, |
| 0.167543, 0.110071, 0.0651259, 0.0327182, 0.0122208, 0.00221986, 0.0}; |
| |
| |
| |
| void ToolUtils::drawBalloon(const TPointD &pos, std::string text, |
| const TPixel32 &color, TPoint delta, |
| double pixelSize, bool isPicking, |
| std::vector<TRectD> *otherBalloons) { |
| TTool::Viewer *viewer = |
| TTool::getApplication()->getCurrentTool()->getTool()->getViewer(); |
| int devPixRatio = getDevicePixelRatio(viewer->viewerWidget()); |
| QString qText = QString::fromStdString(text); |
| QFont font("Arial"); |
| font.setPixelSize(13 * devPixRatio); |
| QFontMetrics fm(font); |
| QRect textRect = fm.boundingRect(qText); |
| |
| int baseLine = -textRect.top(); |
| int mrg = 3 * devPixRatio; |
| |
| |
| if (otherBalloons) { |
| QRect tmpTextRect = textRect; |
| int tmpMrg = mrg; |
| |
| if (devPixRatio != 1) { |
| QFont tmpFont = font; |
| tmpFont.setPixelSize(13); |
| QFontMetrics tmpFm(tmpFont); |
| tmpTextRect = tmpFm.boundingRect(qText); |
| tmpMrg = 3; |
| } |
| |
| std::vector<TRectD> &balloons = *otherBalloons; |
| int n = (int)balloons.size(); |
| TDimensionD balloonSize(pixelSize * (tmpTextRect.width() + tmpMrg * 2), |
| pixelSize * (tmpTextRect.height() + tmpMrg * 2)); |
| TRectD balloonRect; |
| for (;;) { |
| balloonRect = TRectD( |
| pos + TPointD(delta.x * pixelSize, delta.y * pixelSize), balloonSize); |
| int i = 0; |
| while (i < n && !balloons[i].overlaps(balloonRect)) i++; |
| if (i == n) break; |
| double y = balloons[i].y0 - balloonSize.ly - 2 * tmpMrg * pixelSize; |
| delta.y = (y - pos.y) / pixelSize; |
| } |
| balloons.push_back(balloonRect); |
| } |
| |
| delta.x *= devPixRatio; |
| delta.y *= devPixRatio; |
| |
| textRect.moveTo(std::max(delta.x, 10 * devPixRatio + mrg), |
| std::max(mrg + 2 * devPixRatio, -delta.y - baseLine)); |
| |
| int y = textRect.top() + baseLine; |
| int x0 = textRect.left() - mrg; |
| int x1 = textRect.right() + mrg; |
| int y0 = textRect.top() - mrg; |
| int y1 = textRect.bottom() + mrg; |
| |
| if (isPicking) { |
| if (viewer->is3DView()) { |
| double x0 = pos.x + textRect.left() * pixelSize, |
| y0 = pos.y + delta.y * pixelSize; |
| double x1 = x0 + pixelSize * textRect.width(); |
| double y1 = y0 + pixelSize * textRect.height(); |
| double d = pixelSize * 5; |
| glRectd(x0 - d, y0 - d, x1 + d, y1 + d); |
| } else { |
| TPointD posBalloon = viewer->worldToPos(pos); |
| |
| double d = 5; |
| double x0 = posBalloon.x + textRect.left() - d; |
| double y0 = posBalloon.y + delta.y - d; |
| double x1 = x0 + textRect.width() + d; |
| double y1 = y0 + textRect.height() + d; |
| |
| TPointD p1(x0, y0); |
| TPointD p2(x1, y0); |
| TPointD p3(x0, y1); |
| TPointD p4(x1, y1); |
| |
| TPointD w1(viewer->winToWorld(p1)); |
| TPointD w2(viewer->winToWorld(p2)); |
| TPointD w3(viewer->winToWorld(p3)); |
| TPointD w4(viewer->winToWorld(p4)); |
| |
| glBegin(GL_QUADS); |
| glVertex2d(w1.x, w1.y); |
| glVertex2d(w2.x, w2.y); |
| glVertex2d(w4.x, w4.y); |
| glVertex2d(w3.x, w3.y); |
| glEnd(); |
| } |
| |
| return; |
| } |
| |
| QSize size(textRect.width() + textRect.left() + mrg, |
| std::max(textRect.bottom() + mrg, y + delta.y) + 3 * devPixRatio); |
| |
| QImage label(size.width(), size.height(), QImage::Format_ARGB32); |
| label.fill(Qt::transparent); |
| |
| QPainter p(&label); |
| p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); |
| p.setBrush(QColor(color.r, color.g, color.b, color.m)); |
| p.setPen(Qt::NoPen); |
| |
| QPainterPath pp; |
| pp.moveTo(x0, y - 8 * devPixRatio); |
| pp.lineTo(0, y + delta.y); |
| pp.lineTo(x0, y); |
| |
| |
| |
| |
| |
| |
| |
| |
| pp.lineTo(x0, y1); |
| pp.lineTo(x1, y1); |
| pp.lineTo(x1, y0); |
| pp.lineTo(x0, y0); |
| |
| pp.closeSubpath(); |
| |
| p.drawPath(pp); |
| |
| p.setPen(Qt::black); |
| p.setFont(font); |
| p.drawText(textRect, Qt::AlignCenter | Qt::TextDontClip, qText); |
| |
| QImage texture = QGLWidget::convertToGLFormat(label); |
| |
| glRasterPos2f(pos.x, pos.y); |
| glBitmap(0, 0, 0, 0, 0, -size.height() + (y + delta.y), NULL); |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| glDrawPixels(texture.width(), texture.height(), GL_RGBA, GL_UNSIGNED_BYTE, |
| texture.bits()); |
| glDisable(GL_BLEND); |
| glColor3d(0, 0, 0); |
| } |
| |
| |
| |
| void ToolUtils::drawHook(const TPointD &pos, ToolUtils::HookType type, |
| bool highlighted, bool onionSkin) { |
| TTool::Viewer *viewer = |
| TTool::getApplication()->getCurrentTool()->getTool()->getViewer(); |
| int devPixRatio = getDevicePixelRatio(viewer->viewerWidget()); |
| int r = 10, d = r + r; |
| QImage image(d * devPixRatio, d * devPixRatio, QImage::Format_ARGB32); |
| image.fill(Qt::transparent); |
| |
| QPainter painter(&image); |
| painter.scale(devPixRatio, devPixRatio); |
| |
| |
| int matte = onionSkin ? 100 : 255; |
| |
| QColor color(0, 0, 0, matte); |
| if (highlighted) color = QColor(0, 175, 175, matte); |
| |
| if (type == NormalHook || type == PassHookA) { |
| painter.setPen(QPen(QColor(255, 255, 255, matte), 3)); |
| painter.drawEllipse(5, 5, d - 10, d - 10); |
| painter.setPen(color); |
| painter.drawEllipse(5, 5, d - 10, d - 10); |
| } else if (type == OtherLevelHook) { |
| QColor color(0, 200, 200, 200); |
| |
| |
| painter.setPen(Qt::white); |
| painter.setBrush(color); |
| painter.drawEllipse(6, 6, d - 12, d - 12); |
| } |
| if (type == NormalHook || type == PassHookB) { |
| painter.setPen(QPen(QColor(255, 255, 255, matte), 3)); |
| painter.drawLine(0, r, d, r); |
| painter.drawLine(r, 0, r, d); |
| painter.setPen(color); |
| painter.drawLine(0, r, d, r); |
| painter.drawLine(r, 0, r, d); |
| } |
| |
| QImage texture = QGLWidget::convertToGLFormat(image); |
| glRasterPos2f(pos.x, pos.y); |
| glBitmap(0, 0, 0, 0, -r * devPixRatio, -r * devPixRatio, NULL); |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| glDrawPixels(texture.width(), texture.height(), GL_RGBA, GL_UNSIGNED_BYTE, |
| texture.bits()); |
| glDisable(GL_BLEND); |
| } |
| |
| |
| |
| bool ToolUtils::isJustCreatedSpline(TImage *image) { |
| TVectorImageP vi = image; |
| if (!vi) return false; |
| if (vi->getStrokeCount() != 1) return false; |
| TStroke *stroke = vi->getStroke(0); |
| if (stroke->getControlPointCount() != 3) return false; |
| TPointD p0 = stroke->getControlPoint(0); |
| TPointD p1 = stroke->getControlPoint(1); |
| TPointD p2 = stroke->getControlPoint(2); |
| double d = 30.0; |
| return p0 == TPointD(-d, 0) && p1 == TPointD(0, 0) && p2 == TPointD(d, 0); |
| } |
| |
| |
| |
| TRectD ToolUtils::interpolateRect(const TRectD &rect1, const TRectD &rect2, |
| double t) { |
| assert(rect1.x0 <= rect1.x1); |
| assert(rect1.y0 <= rect1.y1); |
| assert(rect2.x0 <= rect2.x1); |
| assert(rect2.y0 <= rect2.y1); |
| |
| return TRectD(rect1.x0 + (rect2.x0 - rect1.x0) * t, |
| rect1.y0 + (rect2.y0 - rect1.y0) * t, |
| rect1.x1 + (rect2.x1 - rect1.x1) * t, |
| rect1.y1 + (rect2.y1 - rect1.y1) * t); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| TRectD ToolUtils::getBounds(const std::vector<TThickPoint> &points, |
| double maxThickness) { |
| TThickPoint p = points[0]; |
| double radius = maxThickness == 0 ? p.thick * 0.5 : maxThickness * 0.5; |
| TRectD rect(p - TPointD(radius, radius), p + TPointD(radius, radius)); |
| int i; |
| for (i = 1; i < (int)points.size(); i++) { |
| p = points[i]; |
| radius = maxThickness == 0 ? p.thick * 0.5 : maxThickness * 0.5; |
| rect = |
| rect + TRectD(p - TPointD(radius, radius), p + TPointD(radius, radius)); |
| } |
| return rect; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool ToolUtils::doUpdateXSheet(TXshSimpleLevel *sl, |
| std::vector<TFrameId> oldFids, |
| std::vector<TFrameId> newFids, TXsheet *xsh, |
| std::vector<TXshChildLevel *> &childLevels) { |
| bool ret = false; |
| for (int c = 0; c < xsh->getColumnCount(); ++c) { |
| int r0, r1; |
| int n = xsh->getCellRange(c, r0, r1); |
| if (n > 0) { |
| bool changed = false; |
| std::vector<TXshCell> cells(n); |
| xsh->getCells(r0, c, n, &cells[0]); |
| for (int i = 0; i < n; i++) { |
| TXshCell currCell = cells[i]; |
| |
| if (!cells[i].isEmpty() && |
| cells[i].m_level->getType() == CHILD_XSHLEVEL) { |
| TXshChildLevel *level = cells[i].m_level->getChildLevel(); |
| |
| if (level && std::find(childLevels.begin(), childLevels.end(), |
| level) == childLevels.end()) { |
| childLevels.push_back(level); |
| TXsheet *subXsh = level->getXsheet(); |
| ret |= doUpdateXSheet(sl, oldFids, newFids, subXsh, childLevels); |
| } |
| } |
| for (int j = 0; j < oldFids.size(); j++) { |
| if (oldFids.at(j) == newFids.at(j)) continue; |
| TXshCell tempCell(sl, oldFids.at(j)); |
| bool sameSl = tempCell.getSimpleLevel() == currCell.getSimpleLevel(); |
| bool sameFid = tempCell.getFrameId() == currCell.getFrameId(); |
| if (sameSl && sameFid) { |
| TXshCell newCell(sl, newFids.at(j)); |
| cells[i] = newCell; |
| changed = true; |
| break; |
| } |
| } |
| } |
| if (changed) { |
| xsh->setCells(r0, c, n, &cells[0]); |
| ret = true; |
| |
| } |
| } |
| } |
| return ret; |
| } |
| |
| |
| |
| bool ToolUtils::renumberForInsertFId(TXshSimpleLevel *sl, const TFrameId &fid, |
| const TFrameId &maxFid, TXsheet *xsh) { |
| auto getNextLetter = [](const QString &letter) { |
| if (letter.isEmpty()) return QString('a'); |
| if (letter == 'z' || letter == 'Z') return QString(); |
| QByteArray byteArray = letter.toUtf8(); |
| |
| byteArray.data()[byteArray.size() - 1]++; |
| return QString::fromUtf8(byteArray); |
| }; |
| |
| std::vector<TFrameId> fids; |
| std::vector<TFrameId> oldFrames; |
| sl->getFids(oldFrames); |
| sl->getFids(fids); |
| std::vector<TFrameId>::iterator it = std::find(fids.begin(), fids.end(), fid); |
| if (it == fids.end()) return false; |
| |
| std::set<TFrameId> fidsSet(fids.begin(), fids.end()); |
| QList<TFrameId> fIdsToBeShifted; |
| TFrameId tmpFid = fid; |
| for (auto itr = fidsSet.upper_bound(maxFid); itr != fidsSet.end(); ++itr) { |
| if (*itr > tmpFid) break; |
| fIdsToBeShifted.push_back(*itr); |
| if (!fid.getLetter().isEmpty()) { |
| QString nextLetter = getNextLetter((*itr).getLetter()); |
| if (!nextLetter.isEmpty()) |
| tmpFid = TFrameId((*itr).getNumber(), nextLetter); |
| else |
| tmpFid = TFrameId((*itr).getNumber() + 1); |
| } else |
| tmpFid = TFrameId((*itr).getNumber() + 1, (*itr).getLetter()); |
| } |
| |
| if (fIdsToBeShifted.isEmpty()) return false; |
| |
| for (TFrameId &tmpFid : fids) { |
| if (fIdsToBeShifted.contains(tmpFid)) { |
| if (!fid.getLetter().isEmpty()) { |
| QString nextLetter = getNextLetter(tmpFid.getLetter()); |
| if (!nextLetter.isEmpty()) |
| tmpFid = TFrameId(tmpFid.getNumber(), nextLetter); |
| else |
| tmpFid = TFrameId(tmpFid.getNumber() + 1); |
| } else |
| tmpFid = TFrameId(tmpFid.getNumber() + 1, tmpFid.getLetter()); |
| } |
| } |
| |
| std::vector<TXshChildLevel *> childLevels; |
| doUpdateXSheet(sl, oldFrames, fids, xsh, childLevels); |
| sl->renumber(fids); |
| |
| return true; |
| } |
| |