/* * Copyright (c) 2015 Tricoire Sebastien 3dsman@free.fr * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * */ #include "OE_interfaceDisplay.h" #include "OE_utils.h" #include "OE_controller.h" #include #include #include #include #include #include #include #include #include #include "actions/OE_actionsCurves.h" #include "actions/OE_actionsStitchs.h" #include "actions/OE_actionsLineStitchs.h" #include "actions/OE_actionsBirailStitchs.h" #include "actions/OE_actionsFillStitchs.h" #include "actions/OE_actionsLinkStitchs.h" #include "actions/OE_actionsSelection.h" #include "actions/OE_actionsThreads.h" OE_interfaceDisplay::OE_interfaceDisplay(QWidget *parent, Qt::WindowFlags f) : OE_display(parent, f) { editStyle = new OE_display::OE_displayStyle(); commandStyle = new OE_display::OE_displayStyle(); commandStyle->drawGrid = false; commandStyle->drawCurves = false; commandStyle->drawStitches = false; commandStyle->drawCommands = true; setDisplayStyle(editStyle); setDocument(nullptr); modState = Qt::KeyboardModifier::NoModifier; } OE_interfaceDisplay::~OE_interfaceDisplay() { //delete curDisplay; } bool OE_interfaceDisplay::setDocument(OE_document* document) { this->document = document; return true; } bool OE_interfaceDisplay::setController(OE_controller* controller) { this->controller = controller; return true; } void OE_interfaceDisplay::showAll() { OE_display::showAll(); } bool OE_interfaceDisplay::mouse_Pos(double x, double y) { bool redraw = false; redraw |= OE_display::mouse_Pos(x, y); vector_2dt oldClosestPoint = editClosestPoint; vector_2d absMove = vector_2d((mouse.x - clicOldMouse.x)*zoom*2, (mouse.y - clicOldMouse.y)*zoom*2); if (editionState == MoveSelection) //if the move action is enabled { controller->editActionMoveSelection(vector_2d((mouse.x - clicOldMouse.x)*zoom*2, (mouse.y - clicOldMouse.y)*zoom*2)); return true; } if (editionState == ScaleSelection) //if the scale action is enabled { vector_2d bbCenter = controller->getSelectionBoundingBox().getCenter(); float scale = (absMouse-bbCenter).len()/(screenToDocument(clicOldMouse)-bbCenter).len(); controller->editActionScaleSelection(vector_2d(scale,scale)); return true; } if (editionState == NewSubcurve) //if we must find the closest subcurve (get the closest point on it too) { closestCurve = controller->getClosestCurve(absMouse, editClosestPoint); redraw = true; } else if (editSubcurve) //if we are working on a defined subcurve get the closest point on it { editClosestPoint = controller->getClosestPoint(editSubcurve->getCurve(), absMouse); redraw = true; } if (editionState == MovePointcurve && selControlCurve && selControlType==0) //if we're moving a curve control point { controller->editActionMovePointCurve(vector_2d((mouse.x - clicOldMouse.x)*zoom*2, (mouse.y - clicOldMouse.y)*zoom*2)); return true; } if (editionState == MovePointstitch) //if we're moving a linkstitch control point { controller->editActionMovePointLinkStitch(absMove); controller->editActionMoveGridPointFillStitch(absMove); return true; } //if we're editing the scaleWidth of a linestitch if (editionState == OffsetWidthLinestitch) { controller->editActionSetLinestitchWidth((mouse.x - clicOldMouse.x)/100.0f, true); return true; } //if we're define a new subcurve and the reference curve is already picked if ((editionState == NewSubcurve)&&(selControlStitch)) { editionState = TraceSubcurve; if (oldClosestPoint.t>editClosestPoint.t) controller->editActionAddSubcurvePosSwitchDir(); redraw = true; } if (editionState == TraceSubcurve) { controller->editActionAddSubcurvePosEnd(editClosestPoint.t); redraw = true; } else if (editionState == MoveSubcurve) { controller->editActionSetSubcurvePos(editClosestPoint.t); redraw = true; } return redraw; } bool OE_interfaceDisplay::mouse_Button(QMouseEvent* event) { bool redraw = false; redraw |= OE_display::mouse_Button(event); if (curScreen == edit && event->button() == Qt::LeftButton && event->type() == QEvent::MouseButtonPress) { redraw = true; if (editionState == NewSubcurve) { if (!closestCurve) { editionState = None; } else { controller->addAction(new OE_actionJoincurveAddSubCurve(document, curJoincurve, subcurve_id, closestCurve, editClosestPoint.t, editClosestPoint.t, false)); //subcurve_id = -1; selControlStitch = controller->getStitch(-1); selControlType = 0; //editSubcurve = curJoincurve->getCurve(-1); editSubcurve = curJoincurve->getCurve(subcurve_id); } } else { clicOldMouse = mouse; if (editionState == None) { drawPicking(); } if (editionState == None) { editionState = Selection; } } return redraw; } if (curScreen == edit && event->button() == Qt::LeftButton && event->type() == QEvent::MouseButtonRelease) { redraw = true; selControlCurve = nullptr; selControlStitch = nullptr; if (editionState == AddSubcurve) { //curJoincurve = controller->getLineStitch(-1)->getJoincurve(); //closestCurve = controller->getClosestCurve(absMouse, editClosestPoint); editionState = NewSubcurve; joincurve_id = 0; return true; } if (editionState == Selection) { if ((mouse-clicOldMouse).len()>5) { selectBox(); } selectApply(selectedCurves, selectedStitches); editionState = None; } if ((editionState == NewSubcurve)||(editionState == TraceSubcurve)) { if (joincurve_id == 0) { editionState = None; editSubcurve = nullptr; } else { editionState = NewSubcurve; curJoincurve = controller->getBirailStitch(-1)->getJoincurve2(); closestCurve = controller->getClosestCurve(absMouse, editClosestPoint); joincurve_id -= 1; } return true; } editionState = None; return redraw; } return redraw; } void OE_interfaceDisplay::scroll(double xoffset, double yoffset) { OE_display::scroll(xoffset, yoffset); } void OE_interfaceDisplay::resize(int width, int height) { OE_display::resize(width, height); } void OE_interfaceDisplay::key(QKeyEvent* event) { static QString writeFilters = QString::fromStdString(OE_document::getWriteDialogFilters()); static QString readFilters = QString::fromStdString(OE_document::getReadDialogFilters()); if (event->type() == QEvent::KeyPress) modState = event->modifiers(); else modState = Qt::KeyboardModifier::NoModifier; if (event->type() == QEvent::KeyPress && curScreen == edit) { if (event->key() == Qt::Key_R && event->modifiers() == Qt::NoModifier) { if (editionState == TraceSubcurve) { controller->editActionAddSubcurvePosSwitchDir(); update(); } else if (editionState == MoveSubcurve) { controller->editActionSetSubcurvePosSwitchDir(); update(); } } if(editionState==None) { if (event->key() == Qt::Key_Z && event->modifiers() == Qt::ControlModifier) { if (controller) { controller->undoAction(); update(); } } else if (event->key() == Qt::Key_S && event->modifiers() == Qt::ControlModifier) { if (document) { QString fileName = QFileDialog::getSaveFileName(this, "Save design", "", writeFilters); if (!fileName.isEmpty()) { document->saveToFile(fileName.toStdString()); } } } else if (event->key() == Qt::Key_O && event->modifiers() == Qt::ControlModifier) { QString type; QString fileName = QFileDialog::getOpenFileName(this, "Open design", "", readFilters, &type); if (!fileName.isEmpty()) { loadFile(fileName.toStdString()); update(); } } else if (event->key() == Qt::Key_Z && event->modifiers() == (Qt::ControlModifier|Qt::ShiftModifier)) { if (controller) { controller->redoAction(); update(); } } else if (event->modifiers() == Qt::NoModifier) { if (event->key() == Qt::Key_C) { if (controller) { controller->toggleCloseSelectedCurve(); update(); } } else if (event->key() == Qt::Key_Delete) { //TODO create an action to delete all selected stitches std::list::iterator it=selectedStitches.begin(); controller->addAction(new OE_actionDelStitch(document, *it)); update(); } else if (event->key() == Qt::Key_Right) { std::list::iterator it=selectedStitches.begin(); if (it != selectedStitches.end()) { it = std::find (document->stitchs.begin(), document->stitchs.end(), *it); it++; if (it != document->stitchs.end()) { selectedStitches.clear(); selectedStitches.push_back(*it); controller->clearSelection(); controller->selectStitches(selectedStitches,true); update(); } } } else if (event->key() == Qt::Key_Left) { std::list::iterator it=selectedStitches.begin(); if (it != selectedStitches.end()) { it = std::find (document->stitchs.begin(), document->stitchs.end(), *it); if (it != document->stitchs.begin()) { it--; selectedStitches.clear(); selectedStitches.push_back(*it); controller->clearSelection(); controller->selectStitches(selectedStitches,true); update(); } } } } } } if (event->type() == QEvent::KeyRelease) { if (event->key() == Qt::Key_R) { if (editionState == TraceSubcurve) { controller->editActionAddSubcurvePosSwitchDir(); update(); } else if (editionState == MoveSubcurve) { controller->editActionSetSubcurvePosSwitchDir(); update(); } } if(editionState==None) { if (curScreen == command && event->key() == Qt::Key_Space) { run=true; update(); } if (event->key() == Qt::Key_F12) { if (curScreen == edit) { curScreen = command; setDisplayStyle(commandStyle); update(); } else { curScreen = edit; setDisplayStyle(editStyle); update(); } } else if (event->key() == Qt::Key_L) { if (controller) { controller->newLineStitch(2.5, 2.5, controller->getPattern(0)); document->selectedStitchs.push_back(controller->getLineStitch(-1)); curJoincurve = controller->getLineStitch(-1)->getJoincurve(); closestCurve = controller->getClosestCurve(absMouse, editClosestPoint); editionState = NewSubcurve; joincurve_id = 0; subcurve_id = -1; update(); } } else if (event->key() == Qt::Key_B) { if (controller) { controller->newBirailStitch(false, false, 0, 0, 0.7f); document->selectedStitchs.push_back(controller->getBirailStitch(-1)); curJoincurve = controller->getBirailStitch(-1)->getJoincurve1(); closestCurve = controller->getClosestCurve(absMouse, editClosestPoint); editionState = NewSubcurve; joincurve_id = 1; subcurve_id = -1; update(); } } else if (event->key() == Qt::Key_F) { if (controller) { controller->newFillStitch(1.5f, 0.3f, controller->getPattern(0)); document->selectedStitchs.push_back(controller->getFillStitch(-1)); curJoincurve = controller->getFillStitch(-1)->getJoincurve(); closestCurve = controller->getClosestCurve(absMouse, editClosestPoint); editionState = NewSubcurve; joincurve_id = 0; subcurve_id = -1; update(); } } /*else if (event->key() == Qt::Key_Enter) { if (editionState == NewSubcurve) { if (joincurve_id == 0) { editionState = None; curJoincurve = 0; } else { curJoincurve = controller->getBirailStitch(-1)->getJoincurve2(); closestCurve = controller->getClosestCurve(absMouse, editClosestPoint); joincurve_id -= 1; } } }*/ else if (event->key() == Qt::Key_Plus && event->modifiers() & Qt::KeypadModifier) { if (event->modifiers() & Qt::ControlModifier) { controller->addAction(new OE_actionScaleSelectedStitchLen(document, 1.1f)); update(); } else { controller->addAction(new OE_actionScaleSelectedStitchWidth(document, 1.1f)); update(); } } else if (event->key() == Qt::Key_Minus && event->modifiers() & Qt::KeypadModifier) { if (event->modifiers() & Qt::ControlModifier) { controller->addAction(new OE_actionScaleSelectedStitchLen(document, 1.f/1.1f)); update(); } else { controller->addAction(new OE_actionScaleSelectedStitchWidth(document, 1.f/1.1f)); update(); } } else if (event->key() == Qt::Key_1 && event->modifiers() & Qt::KeypadModifier) { controller->addAction(new OE_actionSetSelectedStitchPattern(document, controller->getPattern(0))); update(); } else if (event->key() == Qt::Key_2 && event->modifiers() & Qt::KeypadModifier) { controller->addAction(new OE_actionSetSelectedStitchPattern(document, controller->getPattern(1))); update(); } else if (event->key() == Qt::Key_3 && event->modifiers() & Qt::KeypadModifier) { controller->addAction(new OE_actionSetSelectedStitchPattern(document, controller->getPattern(2))); update(); } else if (event->key() == Qt::Key_4 && event->modifiers() & Qt::KeypadModifier) { controller->addAction(new OE_actionSetSelectedStitchPattern(document, controller->getPattern(3))); update(); } else if (event->key() == Qt::Key_5 && event->modifiers() & Qt::KeypadModifier) { controller->addAction(new OE_actionSetSelectedStitchPattern(document, controller->getPattern(4))); update(); } else if (event->key() == Qt::Key_6 && event->modifiers() & Qt::KeypadModifier) { controller->addAction(new OE_actionSetSelectedStitchPattern(document, controller->getPattern(5))); update(); } else if (event->key() == Qt::Key_7 && event->modifiers() & Qt::KeypadModifier) { controller->addAction(new OE_actionSetSelectedStitchPattern(document, controller->getPattern(6))); update(); } else if (event->key() == Qt::Key_8 && event->modifiers() & Qt::KeypadModifier) { controller->addAction(new OE_actionSetSelectedStitchPattern(document, controller->getPattern(7))); update(); } else if (event->key() == Qt::Key_9 && event->modifiers() & Qt::KeypadModifier) { controller->addAction(new OE_actionSetSelectedStitchPattern(document, controller->getPattern(8))); update(); } if (event->key() == Qt::Key_Home) { zoomSelection(); update(); } } } } void OE_interfaceDisplay::selectBox() { selectedCurves.clear(); selectedStitches.clear(); vector_2d clicAbsMouse = viewPos+(clicOldMouse-vector_2d(width,height)/2)*zoom*2; std::list::iterator curve = document->curves.begin(); while (curve != document->curves.end()) { BoundingBox box = (*curve)->getBound(); if ((box.getMin().x > std::min(clicAbsMouse.x, absMouse.x)) &&(box.getMax().x < std::max(clicAbsMouse.x, absMouse.x)) &&(box.getMin().y > std::min(clicAbsMouse.y, absMouse.y)) &&(box.getMax().y < std::max(clicAbsMouse.y, absMouse.y))) { selectedCurves.push_back(*curve); } curve++; } std::list::iterator stitch = document->stitchs.begin(); while (stitch != document->stitchs.end()) { BoundingBox box = (*stitch)->getBound(); if ((box.getMin().x > std::min(clicAbsMouse.x, absMouse.x)) &&(box.getMax().x < std::max(clicAbsMouse.x, absMouse.x)) &&(box.getMin().y > std::min(clicAbsMouse.y, absMouse.y)) &&(box.getMax().y < std::max(clicAbsMouse.y, absMouse.y))) { selectedStitches.push_back(*stitch); } stitch++; } } bool OE_interfaceDisplay::checkJoinCurveControl(OE_joincurve* joinCurve, int &index) { if (selControlType == index) { // get the corresponding subcurve editSubcurve = joinCurve->getCurve(selControlIndex/2); if (editSubcurve) { editionState = MoveSubcurve; // get the closest point t value vector_2dt pos = editSubcurve->getCurve()->closestPoint(absMouse); // and add an action to move the point controller->addAction(new OE_actionSetSubcurvePos(document, editSubcurve, selControlIndex%2, pos.t)); return true; } return false; } index++; if (selControlType == index) { editionState = AddSubcurve; subcurve_id = selControlIndex; curJoincurve = joinCurve; return true; } index++; return false; } bool OE_interfaceDisplay::pickingTool(int toolType, int toolIndex) { if (toolType == ZEROID) //zero { } if (toolType == TRANSTOOLID) //one of the transform selection tools { if (toolIndex == MOVETOOLID) //move selection tools { return pickingMoveTool(); } if (toolIndex == SCALETOOLID) //scale selection tools { return pickingScaleTool(); } } return false; } bool OE_interfaceDisplay::pickingMoveTool() { editionState = MoveSelection; controller->addAction(new OE_actionMoveSelection(document, vector_2d(0,0))); return true; } bool OE_interfaceDisplay::pickingScaleTool() { editionState = ScaleSelection; controller->addAction(new OE_actionScaleSelection(document, vector_2d(1,1), controller->getSelectionBoundingBox().getCenter())); return true; } bool OE_interfaceDisplay::pickingStitch(int stitchIndex) { if (stitchIndex >= document->stitchs.size()) { return false; } std::list::iterator it = document->stitchs.begin(); std::advance(it, stitchIndex); selectedStitches.push_back(*it); return true; } bool OE_interfaceDisplay::pickingStitchControl(int stitchIndex, int ctrlType, int ctrlIndex) { if (stitchIndex >= document->selectedStitchs.size()) { return false; } std::list::iterator it = document->selectedStitchs.begin(); std::advance(it, stitchIndex); selControlStitch = *it; selControlType = ctrlType; selControlIndex = ctrlIndex; int ControlTypeIndex = 0; OE_linestitch* tmpLineStitch = dynamic_cast(selControlStitch); if (tmpLineStitch) { // check generic joinCurve controls if (checkJoinCurveControl(tmpLineStitch->getJoincurve(), ControlTypeIndex)) return true; //check picking for linestitch width controller if (selControlType == ControlTypeIndex && selControlIndex == 0) { editionState = OffsetWidthLinestitch; controller->addAction(new OE_actionSetLineStitchWidth(document, tmpLineStitch, tmpLineStitch->getWidth())); return true; } } OE_birailstitch* tmpBirailStitch = dynamic_cast(selControlStitch); if (tmpBirailStitch) { // check generic joinCurve controls for each birail's joincurve if (checkJoinCurveControl(tmpBirailStitch->getJoincurve1(), ControlTypeIndex)) return true; if (checkJoinCurveControl(tmpBirailStitch->getJoincurve2(), ControlTypeIndex)) return true; return false; } OE_fillstitch* tmpFillStitch = dynamic_cast(selControlStitch); if (tmpFillStitch) { // check generic joinCurve controls if (checkJoinCurveControl(tmpFillStitch->getJoincurve(), ControlTypeIndex)) return true; // check picking for grid's controller points else if (selControlType == ControlTypeIndex) { editionState = MovePointstitch; //edit gridpoint controller->addAction(new OE_actionMovePointFillStitch(document, tmpFillStitch, selControlIndex, vector_2d(0,0))); return true; } return false; } OE_linkstitch* tmpLinkStitch = dynamic_cast(selControlStitch); if (tmpLinkStitch) { //check picking of control point, add or delete if (selControlType == 0) { editionState = MovePointstitch; controller->addAction(new OE_actionMovePointLinkStitch(document, tmpLinkStitch, selControlIndex, vector_2d())); return true; } else if (selControlType == 1) { editionState = MovePointstitch; controller->addAction(new OE_actionAddPointLinkStitch(document, tmpLinkStitch, selControlIndex, absMouse)); return true; } else if (selControlType == 2) { editionState = DelPointstitch; controller->addAction(new OE_actionDelPointLinkStitch(document, tmpLinkStitch, selControlIndex)); return true; } return false; } selControlStitch = nullptr; return false; } bool OE_interfaceDisplay::pickingCurve(int curveIndex) { if (curveIndex >= document->curves.size()) { return false; } std::list::iterator it = document->curves.begin(); std::advance(it, curveIndex); selectedCurves.push_back(*it); return true; } bool OE_interfaceDisplay::pickingCurveControl(int curveIndex, int ctrlType, int ctrlIndex) { if (curveIndex >= document->selectedCurves.size()) { return false; } std::list::iterator it = document->selectedCurves.begin(); std::advance(it, curveIndex); selControlCurve = *it; selControlType = ctrlType; selControlIndex = ctrlIndex; OE_pointcurve* tmpPointCurve = dynamic_cast(selControlCurve); if (tmpPointCurve && selControlType == 0) { editionState = MovePointcurve; controller->addAction(new OE_actionMovePointCurve(document, tmpPointCurve, selControlIndex+1, vector_2d(0,0))); return true; } selControlCurve = nullptr; return false; } /** \brief set selection * * \return true if all is ok * */ bool OE_interfaceDisplay::drawPicking() { GLuint selectBuf[64]; glSelectBuffer(64, selectBuf); OE_display::pick(document->selectedCurves, document->selectedStitchs); //get the hits of the picking //hits are represented by an array of GLuint* // first value give us the number of items on the name stack at the time of the hit // next 2 values are deep value of the picked object (we don't use it) // then the items we need arrived // we format it like that: // - type of the picked object (tool, stitch or curve) // - index of the picked object (list index for stitch or curve, index of the tool for the tools) // - if we picked a sub element of a curve, stich or tool here come his type (handle, startpoint,...) // - then his index // // so a hit can be defined by // 2 values in case a curve of a stitch was picked directly // 3 values in case of picking a transform tool like move // or 4 values if we picked a sub element of a curve or stitch GLint hits = glRenderMode(GL_RENDER); selectedCurves.clear(); selectedStitches.clear(); GLuint *ptr; ptr = static_cast(selectBuf); for (GLint i=0; i selectedCurves, std::list selectedStitches) { if (selectedCurves.size()) { if (modState.testFlag(Qt::ControlModifier)) { controller->unselectCurves(selectedCurves); } else { controller->selectCurves(selectedCurves, !modState.testFlag(Qt::ShiftModifier)); } } if (selectedStitches.size()) { if (modState.testFlag(Qt::ControlModifier)) { controller->unselectStitches(selectedStitches); } else { controller->selectStitches(selectedStitches, !modState.testFlag(Qt::ShiftModifier)); } } if (!selectedStitches.size() && !selectedCurves.size()) { controller->clearSelection(); } return true; } /** \brief draw the document on screen * * \return true if all is ok * */ bool OE_interfaceDisplay::draw() { if (!document) { return false; } if (run) { //TODO remove controller->sendInstPoint(); } OE_display::draw(); //draw the closest point if (editionState==NewSubcurve) { // Points glPointSize(5.0f); glBegin(GL_POINTS); if (joincurve_id == 0) { glColor4f(0.8f, 0.0f, 0.0f, 1.0f); } else { glColor4f(0.0f, 0.8f, 0.0f, 1.0f); } glVertex2f(editClosestPoint.v.x,editClosestPoint.v.y); glEnd(); } // draw the cursor cross glLineWidth(1.5); glBegin(GL_LINES); if (editionState) { if (joincurve_id == 0) { glColor4f(0.8f, 0.0f, 0.0f, 0.5f); } else { glColor4f(0.0f, 0.8f, 0.0f, 0.5f); } } else { glColor4fv(style->cursorColor.rgba); } glVertex2f(absMouse.x,-10000); glVertex2f(absMouse.x,10000); glVertex2f(-10000,absMouse.y); glVertex2f(10000,absMouse.y); glEnd(); // draw the cursor picking zone glLineWidth(2); glBegin(GL_LINE_LOOP); glColor4fv(style->cursorColor.rgba); glVertex2f(absMouse.x-5*zoom,absMouse.y+5*zoom); glVertex2f(absMouse.x-5*zoom,absMouse.y-5*zoom); glVertex2f(absMouse.x+5*zoom,absMouse.y-5*zoom); glVertex2f(absMouse.x+5*zoom,absMouse.y+5*zoom); glEnd(); if (editionState == Selection) { vector_2d clicAbsMouse = viewPos+(clicOldMouse-vector_2d(width,height)/2)*zoom*2; glLineWidth(2); glBegin(GL_LINE_STRIP); glColor4fv(style->cursorColor.rgba); glVertex2f(absMouse.x,clicAbsMouse.y); glVertex2f(absMouse.x,absMouse.y); glVertex2f(clicAbsMouse.x,absMouse.y); glVertex2f(clicAbsMouse.x,clicAbsMouse.y); glVertex2f(absMouse.x,clicAbsMouse.y); glEnd(); } return true; }