Tue, 03 Mar 2015 21:54:57 +0200
- rewrote codegen in python and merged updaterevision into it
- renamed editmodes/*.cc to .cpp, forgot about those
--- a/CMakeLists.txt Tue Mar 03 17:42:21 2015 +0200 +++ b/CMakeLists.txt Tue Mar 03 21:54:57 2015 +0200 @@ -6,8 +6,8 @@ ###################################################################### project (ldforge) -add_subdirectory (codegen) cmake_minimum_required (VERSION 2.6) +cmake_policy (SET CMP0020 OLD) option (TRANSPARENT_DIRECT_COLORS "Enables non-standard transparent direct colors" OFF) option (USE_QT5 "Use Qt5 instead of Qt4" OFF) @@ -23,8 +23,6 @@ endif() find_package (OpenGL REQUIRED) - -get_target_property (CODEGEN_EXE codegen LOCATION) include_directories (${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR}) set (LDFORGE_SOURCES @@ -127,11 +125,6 @@ src/ytruder.ui ) -add_custom_target (codegeneration ALL - COMMAND ${CODEGEN_EXE} ${LDFORGE_SOURCES} ${CMAKE_BINARY_DIR}/configuration.inc - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - DEPENDS codegen) - set (LDFORGE_RESOURCES ldforge.qrc) # set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lGLU") @@ -187,11 +180,23 @@ ) endif() -add_dependencies (${PROJECT_NAME} revision_check codegeneration) install (TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) +# +# Code generators +# + add_custom_target (make_hginfo_h COMMAND python - "${CMAKE_SOURCE_DIR}/updaterevision.py" - "${CMAKE_CURRENT_BINARY_DIR}/hginfo.h") -add_dependencies (${PROJECT_NAME} make_hginfo_h) + "${CMAKE_SOURCE_DIR}/codegen.py" "hginfo" + "${CMAKE_CURRENT_BINARY_DIR}/hginfo.h" + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + +add_custom_target (make_config_aux + COMMAND python + "${CMAKE_SOURCE_DIR}/codegen.py" "configaux" + "${CMAKE_CURRENT_BINARY_DIR}/config.aux" + ${LDFORGE_SOURCES} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + +add_dependencies (${PROJECT_NAME} make_hginfo_h make_config_aux)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/codegen.py Tue Mar 03 21:54:57 2015 +0200 @@ -0,0 +1,154 @@ +# +# Copyright 2015 Teemu Piippo +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +import md5 +import re +import sys +import os + +class OutputFile: + def __init__(self, filename): + self.filename = filename + try: + with open (self.filename, "r") as fp: + self.oldsum = fp.readline().replace ('\n', '').replace ('// ', '') + except IOError: + self.oldsum = '' + + self.body = '' + + def write(self, a): + self.body += a + + def save(self): + checksum = md5.new (self.body).hexdigest() + + if checksum == self.oldsum: + print '%s is up to date' % self.filename + return False + + with open (self.filename, "w") as fp: + fp.write ('// %s\n' % checksum) + fp.write (self.body) + return True + +def prog_configaux(): + class CfgEntry: + def __init__(self, type, name, defvalue): + self.type = type + self.name = name + self.defvalue = defvalue + + if len (sys.argv) < 3: + print 'Usage: %s <output> <input1> [input2] [input3] ... [input-n]' % sys.argv[0] + quit(1) + + f = OutputFile (sys.argv[1]) + f.write ('#pragma once\n') + + entries = [] + + for inputname in sys.argv[2:]: + fp = open (inputname, 'r') + + for line in fp.readlines(): + match = re.match (r'CFGENTRY\s*\(([^,]+),\s*([^,]+),\s*([^)]+)\)', line) + if match: + entries.append (CfgEntry (match.group (1), match.group (2), match.group (3))) + + for e in entries: + f.write ('EXTERN_CFGENTRY (%s, %s)\n' % (e.type, e.name)) + + f.write ('\n') + f.write ('static void InitConfigurationEntry (class AbstractConfigEntry* entry);\n') + f.write ('static void SetupConfigurationLists()\n') + f.write ('{\n') + + for e in entries: + f.write ('\tInitConfigurationEntry (new %sConfigEntry (&cfg::%s, \"%s\", %s));\n' + % (e.type, e.name, e.name, e.defvalue)) + + f.write ('}\n') + + if f.save(): + print 'Wrote configuration aux code to %s' % f.filename + +def prog_hginfo(): + import subprocess + from datetime import datetime + + if len (sys.argv) != 2: + print 'usage: %s <output>' % sys.argv[0] + quit (1) + + f = OutputFile (sys.argv[1]) + data = subprocess.check_output (['hg', 'log', '-r.', '--template', + '{node|short} {branch} {date|hgdate}']).replace ('\n', '').split (' ') + + rev = data[0] + branch = data[1] + timestamp = int (data[2]) + date = datetime.utcfromtimestamp (timestamp) + datestring = date.strftime ('%y%m%d-%H%M') if date.year >= 2000 else '000000-0000' + + if len(rev) > 7: + rev = rev[0:7] + + if subprocess.check_output (['hg', 'id', '-n']).replace ('\n', '')[-1] == '+': + rev += '+' + + f.write ('#define HG_NODE "%s"\n' % rev) + f.write ('#define HG_BRANCH "%s"\n' % branch) + f.write ('#define HG_DATE_VERSION "%s"\n' % datestring) + f.write ('#define HG_DATE_STRING "%s"\n' % date.strftime ('%d %b %Y')) + f.write ('#define HG_DATE_TIME %d\n' % int (timestamp)) + if f.save(): + print '%s updated to %s' % (f.filename, rev) + +def main(): + if len(sys.argv) < 2: + print 'You must give a program name' + quit(1) + + progname = sys.argv[1] + sys.argv[0] = '%s %s' % (sys.argv[0], sys.argv[1]) + sys.argv.pop (1) + + impl = globals().copy() + impl.update (locals()) + method = impl.get ('prog_' + progname) + + if method: + method() + else: + print 'no such program %s' % progname + +if __name__ == '__main__': + main() \ No newline at end of file
--- a/codegen/CMakeLists.txt Tue Mar 03 17:42:21 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -cmake_minimum_required (VERSION 2.4) -add_executable (codegen codegen.cpp) - -# -# LDForge uses alternative operators. GCC and Clang use these by default but MSVC does not. -# So we'll have to tell MSVC to use these alternative operators -# -if (MSVC) - set_target_properties (codegen PROPERTIES COMPILE_FLAGS "/Za") -endif()
--- a/codegen/codegen.cpp Tue Mar 03 17:42:21 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013, 2014 Teemu Piippo - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <sstream> -#include <fstream> -#include <string> -#include <cstring> -#include <iostream> -#include <vector> -#include <algorithm> - -using std::string; -using std::vector; -using std::ifstream; -using std::size_t; -using std::strncmp; -using std::getline; -using std::cout; -using std::endl; - -struct EntryType -{ - string name; - string type; - string defvalue; - - inline bool operator< (EntryType const& other) const - { - return name < other.name; - } - - inline bool operator== (EntryType const& other) const - { - return name == other.name and type == other.type; - } - - inline bool operator!= (EntryType const& other) const - { - return not operator== (other); - } -}; - -char AdvancePointer (char const*& ptr) -{ - char a = *ptr++; - - if (*ptr == '\0') - throw false; - - return a; -} - -void ReadConfigEntries (string const& filename, vector<EntryType>& entries, const char* macroname) -{ - ifstream is (filename.c_str()); - string line; - size_t const macrolen = strlen (macroname); - - while (getline (is, line)) - { - try - { - if (strncmp (line.c_str(), macroname, macrolen) != 0) - continue; - - char const* ptr = &line[macrolen]; - EntryType entry; - - // Skip to paren - while (*ptr != '(') - AdvancePointer (ptr); - - // Skip whitespace - while (isspace (*ptr)) - AdvancePointer (ptr); - - // Skip paren - AdvancePointer (ptr); - - // Read type - while (*ptr != ',') - entry.type += AdvancePointer (ptr); - - // Skip comma and whitespace - for (AdvancePointer (ptr); isspace (*ptr); AdvancePointer (ptr)) - ; - - // Read name - while (*ptr != ',') - entry.name += AdvancePointer (ptr); - - // Skip comma and whitespace - for (AdvancePointer (ptr); isspace (*ptr); AdvancePointer (ptr)) - ; - - // Read default - while (*ptr != ')') - entry.defvalue += AdvancePointer (ptr); - - entries.push_back (entry); - } - catch (bool) {} - } -} - -bool CheckEquality (vector<EntryType> a, vector<EntryType> b) -{ - if (a.size() != b.size()) - return false; - - std::sort (a.begin(), a.end()); - std::sort (b.begin(), b.end()); - - for (size_t i = 0; i < a.size(); ++i) - { - if (a[i] != b[i]) - return false; - } - - return true; -} - -int main (int argc, char* argv[]) -{ - vector<EntryType> entries; - vector<EntryType> oldentries; - ReadConfigEntries (argv[argc - 1], oldentries, "CODEGEN_CACHE"); - - for (int arg = 1; arg < argc - 1; ++arg) - ReadConfigEntries (argv[arg], entries, "CFGENTRY"); - - if (CheckEquality (entries, oldentries)) - { - cout << "Configuration options unchanged" << endl; - return 0; - } - - std::ofstream os (argv[argc - 1]); - os << "#pragma once" << endl; - os << "#define CODEGEN_CACHE(A,B,C)" << endl; - - for (vector<EntryType>::const_iterator it = entries.begin(); it != entries.end(); ++it) - { - os << "CODEGEN_CACHE (" << it->type << ", " << it->name << ", " << - it->defvalue << ")" << endl; - } - - os << endl; - for (vector<EntryType>::const_iterator it = entries.begin(); it != entries.end(); ++it) - os << "EXTERN_CFGENTRY (" << it->type << ", " << it->name << ")" << endl; - - os << endl; - os << "static void InitConfigurationEntry (AbstractConfigEntry* entry);" << endl; - os << "static void SetupConfigurationLists()" << endl; - os << "{" << endl; - - for (vector<EntryType>::const_iterator it = entries.begin(); it != entries.end(); ++it) - { - os << "\tInitConfigurationEntry (new " << it->type << "ConfigEntry (&cfg::" << - it->name << ", \"" << it->name << "\", " << it->defvalue << "));" << endl; - } - - os << "}" << endl; - cout << "Wrote configuration options list to " << argv[argc - 1] << "." << endl; - return 0; -}
--- a/src/configuration.cpp Tue Mar 03 17:42:21 2015 +0200 +++ b/src/configuration.cpp Tue Mar 03 21:54:57 2015 +0200 @@ -32,7 +32,7 @@ #include "mainWindow.h" #include "ldDocument.h" #include "glRenderer.h" -#include "configuration.inc" +#include "config.aux" #ifdef _WIN32 # define EXTENSION ".ini"
--- a/src/editmodes/abstractEditMode.cc Tue Mar 03 17:42:21 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,263 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 - 2015 Teemu Piippo - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <QMouseEvent> -#include <stdexcept> -#include "abstractEditMode.h" -#include "selectMode.h" -#include "drawMode.h" -#include "rectangleMode.h" -#include "circleMode.h" -#include "magicWandMode.h" -#include "linePathMode.h" -#include "../mainWindow.h" -#include "../glRenderer.h" - -CFGENTRY (Bool, DrawLineLengths, true) -CFGENTRY (Bool, DrawAngles, false) - -AbstractEditMode::AbstractEditMode (GLRenderer* renderer) : - m_renderer (renderer) {} - -AbstractEditMode::~AbstractEditMode() {} - -AbstractEditMode* AbstractEditMode::createByType (GLRenderer* renderer, EditModeType type) -{ - switch (type) - { - case EditModeType::Select: return new SelectMode (renderer); - case EditModeType::Draw: return new DrawMode (renderer); - case EditModeType::Rectangle: return new RectangleMode (renderer); - case EditModeType::Circle: return new CircleMode (renderer); - case EditModeType::MagicWand: return new MagicWandMode (renderer); - case EditModeType::LinePath: return new LinePathMode (renderer); - } - - throw std::logic_error ("bad type given to AbstractEditMode::createByType"); -} - -GLRenderer* AbstractEditMode::renderer() const -{ - return m_renderer; -} - -AbstractDrawMode::AbstractDrawMode (GLRenderer* renderer) : - AbstractEditMode (renderer), - m_polybrush (QBrush (QColor (64, 192, 0, 128))) -{ - // Disable the context menu - we need the right mouse button - // for removing vertices. - renderer->setContextMenuPolicy (Qt::NoContextMenu); - - // Use the crosshair cursor when drawing. - renderer->setCursor (Qt::CrossCursor); - - // Clear the selection when beginning to draw. - CurrentDocument()->clearSelection(); - - g_win->updateSelection(); - m_drawedVerts.clear(); -} - -AbstractSelectMode::AbstractSelectMode (GLRenderer* renderer) : - AbstractEditMode (renderer) -{ - renderer->unsetCursor(); - renderer->setContextMenuPolicy (Qt::DefaultContextMenu); -} - -// ============================================================================= -// -void AbstractDrawMode::addDrawnVertex (Vertex const& pos) -{ - if (preAddVertex (pos)) - return; - - m_drawedVerts << pos; -} - -bool AbstractDrawMode::mouseReleased (MouseEventData const& data) -{ - if (Super::mouseReleased (data)) - return true; - - if ((data.releasedButtons & Qt::MidButton) and (m_drawedVerts.size() < 4) and (not data.mouseMoved)) - { - // Find the closest vertex to our cursor - double minimumDistance = 1024.0; - const Vertex* closest = null; - Vertex cursorPosition = renderer()->coordconv2_3 (data.ev->pos(), false); - QPoint cursorPosition2D (data.ev->pos()); - const Axis relZ = renderer()->getRelativeZ(); - QVector<Vertex> vertices = renderer()->document()->inlineVertices(); - - // Sort the vertices in order of distance to camera - std::sort (vertices.begin(), vertices.end(), [&](const Vertex& a, const Vertex& b) -> bool - { - if (renderer()->getFixedCamera (renderer()->camera()).negatedDepth) - return a[relZ] > b[relZ]; - - return a[relZ] < b[relZ]; - }); - - for (const Vertex& vrt : vertices) - { - // If the vertex in 2d space is very close to the cursor then we use - // it regardless of depth. - QPoint vect2d = renderer()->coordconv3_2 (vrt) - cursorPosition2D; - const double distance2DSquared = std::pow (vect2d.x(), 2) + std::pow (vect2d.y(), 2); - if (distance2DSquared < 16.0 * 16.0) - { - closest = &vrt; - break; - } - - // Check if too far away from the cursor. - if (distance2DSquared > 64.0 * 64.0) - continue; - - // Not very close to the cursor. Compare using true distance, - // including depth. - const double distanceSquared = (vrt - cursorPosition).lengthSquared(); - - if (distanceSquared < minimumDistance) - { - minimumDistance = distanceSquared; - closest = &vrt; - } - } - - if (closest != null) - addDrawnVertex (*closest); - - return true; - } - - if ((data.releasedButtons & Qt::RightButton) and (not m_drawedVerts.isEmpty())) - { - // Remove the last vertex - m_drawedVerts.removeLast(); - - return true; - } - - return false; -} - -void AbstractDrawMode::finishDraw (LDObjectList const& objs) -{ - int pos = g_win->getInsertionPoint(); - - if (objs.size() > 0) - { - for (LDObjectPtr obj : objs) - { - renderer()->document()->insertObj (pos++, obj); - renderer()->compileObject (obj); - } - - g_win->refresh(); - g_win->endAction(); - } - - m_drawedVerts.clear(); -} - -void AbstractDrawMode::drawLength (QPainter &painter, const Vertex &v0, const Vertex &v1, - const QPointF& v0p, const QPointF& v1p) const -{ - if (not cfg::DrawLineLengths) - return; - - const QString label = QString::number ((v1 - v0).length()); - QPoint origin = QLineF (v0p, v1p).pointAt (0.5).toPoint(); - painter.drawText (origin, label); -} - -void AbstractDrawMode::renderPolygon (QPainter& painter, const QVector<Vertex>& poly3d, - bool withlengths, bool withangles) const -{ - QVector<QPoint> poly (poly3d.size()); - QFontMetrics metrics = QFontMetrics (QFont()); - - // Convert to 2D - for (int i = 0; i < poly3d.size(); ++i) - poly[i] = renderer()->coordconv3_2 (poly3d[i]); - - // Draw the polygon-to-be - painter.setBrush (m_polybrush); - painter.drawPolygon (QPolygonF (poly)); - - // Draw vertex blips - for (int i = 0; i < poly3d.size(); ++i) - { - QPoint& blip = poly[i]; - painter.setPen (renderer()->linePen()); - renderer()->drawBlip (painter, blip); - - // Draw their coordinates - painter.setPen (renderer()->textPen()); - painter.drawText (blip.x(), blip.y() - 8, poly3d[i].toString (true)); - } - - // Draw line lenghts and angle info if appropriate - if (poly3d.size() >= 2 and (withlengths or withangles)) - { - painter.setPen (renderer()->textPen()); - - for (int i = 0; i < poly3d.size(); ++i) - { - const int j = (i + 1) % poly3d.size(); - const int h = (i - 1 >= 0) ? (i - 1) : (poly3d.size() - 1); - - if (withlengths) - drawLength (painter, poly3d[i], poly3d[j], poly[i], poly[j]); - - if (withangles and cfg::DrawAngles) - { - QLineF l0 (poly[h], poly[i]), - l1 (poly[i], poly[j]); - - double angle = 180 - l0.angleTo (l1); - - if (angle < 0) - angle = 180 - l1.angleTo (l0); - - QString label = QString::number (angle) + QString::fromUtf8 (QByteArray ("\302\260")); - QPoint pos = poly[i]; - pos.setY (pos.y() + metrics.height()); - - painter.drawText (pos, label); - } - } - } -} - -bool AbstractDrawMode::keyReleased (QKeyEvent *ev) -{ - if (Super::keyReleased (ev)) - return true; - - if (not m_drawedVerts.isEmpty() and ev->key() == Qt::Key_Backspace) - { - m_drawedVerts.removeLast(); - return true; - } - - return false; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/abstractEditMode.cpp Tue Mar 03 21:54:57 2015 +0200 @@ -0,0 +1,263 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2015 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <QMouseEvent> +#include <stdexcept> +#include "abstractEditMode.h" +#include "selectMode.h" +#include "drawMode.h" +#include "rectangleMode.h" +#include "circleMode.h" +#include "magicWandMode.h" +#include "linePathMode.h" +#include "../mainWindow.h" +#include "../glRenderer.h" + +CFGENTRY (Bool, DrawLineLengths, true) +CFGENTRY (Bool, DrawAngles, false) + +AbstractEditMode::AbstractEditMode (GLRenderer* renderer) : + m_renderer (renderer) {} + +AbstractEditMode::~AbstractEditMode() {} + +AbstractEditMode* AbstractEditMode::createByType (GLRenderer* renderer, EditModeType type) +{ + switch (type) + { + case EditModeType::Select: return new SelectMode (renderer); + case EditModeType::Draw: return new DrawMode (renderer); + case EditModeType::Rectangle: return new RectangleMode (renderer); + case EditModeType::Circle: return new CircleMode (renderer); + case EditModeType::MagicWand: return new MagicWandMode (renderer); + case EditModeType::LinePath: return new LinePathMode (renderer); + } + + throw std::logic_error ("bad type given to AbstractEditMode::createByType"); +} + +GLRenderer* AbstractEditMode::renderer() const +{ + return m_renderer; +} + +AbstractDrawMode::AbstractDrawMode (GLRenderer* renderer) : + AbstractEditMode (renderer), + m_polybrush (QBrush (QColor (64, 192, 0, 128))) +{ + // Disable the context menu - we need the right mouse button + // for removing vertices. + renderer->setContextMenuPolicy (Qt::NoContextMenu); + + // Use the crosshair cursor when drawing. + renderer->setCursor (Qt::CrossCursor); + + // Clear the selection when beginning to draw. + CurrentDocument()->clearSelection(); + + g_win->updateSelection(); + m_drawedVerts.clear(); +} + +AbstractSelectMode::AbstractSelectMode (GLRenderer* renderer) : + AbstractEditMode (renderer) +{ + renderer->unsetCursor(); + renderer->setContextMenuPolicy (Qt::DefaultContextMenu); +} + +// ============================================================================= +// +void AbstractDrawMode::addDrawnVertex (Vertex const& pos) +{ + if (preAddVertex (pos)) + return; + + m_drawedVerts << pos; +} + +bool AbstractDrawMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + if ((data.releasedButtons & Qt::MidButton) and (m_drawedVerts.size() < 4) and (not data.mouseMoved)) + { + // Find the closest vertex to our cursor + double minimumDistance = 1024.0; + const Vertex* closest = null; + Vertex cursorPosition = renderer()->coordconv2_3 (data.ev->pos(), false); + QPoint cursorPosition2D (data.ev->pos()); + const Axis relZ = renderer()->getRelativeZ(); + QVector<Vertex> vertices = renderer()->document()->inlineVertices(); + + // Sort the vertices in order of distance to camera + std::sort (vertices.begin(), vertices.end(), [&](const Vertex& a, const Vertex& b) -> bool + { + if (renderer()->getFixedCamera (renderer()->camera()).negatedDepth) + return a[relZ] > b[relZ]; + + return a[relZ] < b[relZ]; + }); + + for (const Vertex& vrt : vertices) + { + // If the vertex in 2d space is very close to the cursor then we use + // it regardless of depth. + QPoint vect2d = renderer()->coordconv3_2 (vrt) - cursorPosition2D; + const double distance2DSquared = std::pow (vect2d.x(), 2) + std::pow (vect2d.y(), 2); + if (distance2DSquared < 16.0 * 16.0) + { + closest = &vrt; + break; + } + + // Check if too far away from the cursor. + if (distance2DSquared > 64.0 * 64.0) + continue; + + // Not very close to the cursor. Compare using true distance, + // including depth. + const double distanceSquared = (vrt - cursorPosition).lengthSquared(); + + if (distanceSquared < minimumDistance) + { + minimumDistance = distanceSquared; + closest = &vrt; + } + } + + if (closest != null) + addDrawnVertex (*closest); + + return true; + } + + if ((data.releasedButtons & Qt::RightButton) and (not m_drawedVerts.isEmpty())) + { + // Remove the last vertex + m_drawedVerts.removeLast(); + + return true; + } + + return false; +} + +void AbstractDrawMode::finishDraw (LDObjectList const& objs) +{ + int pos = g_win->getInsertionPoint(); + + if (objs.size() > 0) + { + for (LDObjectPtr obj : objs) + { + renderer()->document()->insertObj (pos++, obj); + renderer()->compileObject (obj); + } + + g_win->refresh(); + g_win->endAction(); + } + + m_drawedVerts.clear(); +} + +void AbstractDrawMode::drawLength (QPainter &painter, const Vertex &v0, const Vertex &v1, + const QPointF& v0p, const QPointF& v1p) const +{ + if (not cfg::DrawLineLengths) + return; + + const QString label = QString::number ((v1 - v0).length()); + QPoint origin = QLineF (v0p, v1p).pointAt (0.5).toPoint(); + painter.drawText (origin, label); +} + +void AbstractDrawMode::renderPolygon (QPainter& painter, const QVector<Vertex>& poly3d, + bool withlengths, bool withangles) const +{ + QVector<QPoint> poly (poly3d.size()); + QFontMetrics metrics = QFontMetrics (QFont()); + + // Convert to 2D + for (int i = 0; i < poly3d.size(); ++i) + poly[i] = renderer()->coordconv3_2 (poly3d[i]); + + // Draw the polygon-to-be + painter.setBrush (m_polybrush); + painter.drawPolygon (QPolygonF (poly)); + + // Draw vertex blips + for (int i = 0; i < poly3d.size(); ++i) + { + QPoint& blip = poly[i]; + painter.setPen (renderer()->linePen()); + renderer()->drawBlip (painter, blip); + + // Draw their coordinates + painter.setPen (renderer()->textPen()); + painter.drawText (blip.x(), blip.y() - 8, poly3d[i].toString (true)); + } + + // Draw line lenghts and angle info if appropriate + if (poly3d.size() >= 2 and (withlengths or withangles)) + { + painter.setPen (renderer()->textPen()); + + for (int i = 0; i < poly3d.size(); ++i) + { + const int j = (i + 1) % poly3d.size(); + const int h = (i - 1 >= 0) ? (i - 1) : (poly3d.size() - 1); + + if (withlengths) + drawLength (painter, poly3d[i], poly3d[j], poly[i], poly[j]); + + if (withangles and cfg::DrawAngles) + { + QLineF l0 (poly[h], poly[i]), + l1 (poly[i], poly[j]); + + double angle = 180 - l0.angleTo (l1); + + if (angle < 0) + angle = 180 - l1.angleTo (l0); + + QString label = QString::number (angle) + QString::fromUtf8 (QByteArray ("\302\260")); + QPoint pos = poly[i]; + pos.setY (pos.y() + metrics.height()); + + painter.drawText (pos, label); + } + } + } +} + +bool AbstractDrawMode::keyReleased (QKeyEvent *ev) +{ + if (Super::keyReleased (ev)) + return true; + + if (not m_drawedVerts.isEmpty() and ev->key() == Qt::Key_Backspace) + { + m_drawedVerts.removeLast(); + return true; + } + + return false; +}
--- a/src/editmodes/circleMode.cc Tue Mar 03 17:42:21 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,315 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 - 2015 Teemu Piippo - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <QPainter> -#include "circleMode.h" -#include "../miscallenous.h" -#include "../ldObject.h" -#include "../ldDocument.h" -#include "../ringFinder.h" -#include "../primitives.h" -#include "../glRenderer.h" -#include "../mainWindow.h" -#include "../ldObjectMath.h" - -CircleMode::CircleMode (GLRenderer* renderer) : - Super (renderer) {} - -EditModeType CircleMode::type() const -{ - return EditModeType::Circle; -} - -double CircleMode::getCircleDrawDist (int pos) const -{ - assert (m_drawedVerts.size() >= pos + 1); - Vertex v1 = (m_drawedVerts.size() >= pos + 2) ? m_drawedVerts[pos + 1] : - renderer()->coordconv2_3 (renderer()->mousePosition(), false); - Axis localx, localy; - renderer()->getRelativeAxes (localx, localy); - double dx = m_drawedVerts[0][localx] - v1[localx]; - double dy = m_drawedVerts[0][localy] - v1[localy]; - return Grid::Snap (sqrt ((dx * dx) + (dy * dy)), Grid::Coordinate); -} - -Matrix CircleMode::getCircleDrawMatrix (double scale) -{ - // Matrix templates. 2 is substituted with the scale value, 1 is inverted to -1 if needed. - static const Matrix templates[3] = - { - { 2, 0, 0, 0, 1, 0, 0, 0, 2 }, - { 2, 0, 0, 0, 0, 2, 0, 1, 0 }, - { 0, 1, 0, 2, 0, 0, 0, 0, 2 }, - }; - - Matrix transform = templates[renderer()->camera() % 3]; - - for (int i = 0; i < 9; ++i) - { - if (transform[i] == 2) - transform[i] = scale; - elif (transform[i] == 1 and renderer()->camera() >= 3) - transform[i] = -1; - } - - return transform; -} - -void CircleMode::buildCircle() -{ - LDObjectList objs; - const int segments (g_win->ringToolSegments()); - const int divisions (g_win->ringToolHiRes() ? HighResolution : LowResolution); - double dist0 (getCircleDrawDist (0)); - double dist1 (getCircleDrawDist (1)); - LDDocumentPtr refFile; - Matrix transform; - bool circleOrDisc = false; - - if (dist1 < dist0) - qSwap (dist0, dist1); - - if (dist0 == dist1) - { - // If the radii are the same, there's no ring space to fill. Use a circle. - refFile = GetPrimitive (::Circle, segments, divisions, 0); - transform = getCircleDrawMatrix (dist0); - circleOrDisc = true; - } - elif (dist0 == 0 or dist1 == 0) - { - // If either radii is 0, use a disc. - refFile = GetPrimitive (::Disc, segments, divisions, 0); - transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1); - circleOrDisc = true; - } - elif (g_RingFinder.findRings (dist0, dist1)) - { - // The ring finder found a solution, use that. Add the component rings to the file. - for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents()) - { - refFile = GetPrimitive (::Ring, segments, divisions, cmp.num); - LDSubfilePtr ref = LDSpawn<LDSubfile>(); - ref->setFileInfo (refFile); - ref->setTransform (getCircleDrawMatrix (cmp.scale)); - ref->setPosition (m_drawedVerts[0]); - ref->setColor (MainColor()); - objs << ref; - } - } - else - { - // Ring finder failed, last resort: draw the ring with quads - QList<QLineF> c0, c1; - Axis localx, localy, localz; - renderer()->getRelativeAxes (localx, localy); - localz = (Axis) (3 - localx - localy); - double x0 (m_drawedVerts[0][localx]); - double y0 (m_drawedVerts[0][localy]); - - Vertex templ; - templ.setCoordinate (localx, x0); - templ.setCoordinate (localy, y0); - templ.setCoordinate (localz, renderer()->getDepthValue()); - - // Calculate circle coords - MakeCircle (segments, divisions, dist0, c0); - MakeCircle (segments, divisions, dist1, c1); - - for (int i = 0; i < segments; ++i) - { - Vertex v0, v1, v2, v3; - v0 = v1 = v2 = v3 = templ; - v0.setCoordinate (localx, v0[localx] + c0[i].x1()); - v0.setCoordinate (localy, v0[localy] + c0[i].y1()); - v1.setCoordinate (localx, v1[localx] + c0[i].x2()); - v1.setCoordinate (localy, v1[localy] + c0[i].y2()); - v2.setCoordinate (localx, v2[localx] + c1[i].x2()); - v2.setCoordinate (localy, v2[localy] + c1[i].y2()); - v3.setCoordinate (localx, v3[localx] + c1[i].x1()); - v3.setCoordinate (localy, v3[localy] + c1[i].y1()); - - LDQuadPtr quad (LDSpawn<LDQuad> (v0, v1, v2, v3)); - quad->setColor (MainColor()); - - // Ensure the quads always are BFC-front towards the camera - if (renderer()->camera() % 3 <= 0) - quad->invert(); - - objs << quad; - } - } - - if (circleOrDisc and refFile != null) - { - LDSubfilePtr ref = LDSpawn<LDSubfile>(); - ref->setFileInfo (refFile); - ref->setTransform (transform); - ref->setPosition (m_drawedVerts[0]); - ref->setColor (MainColor()); - objs << ref; - } - - unless (objs.isEmpty()) - { - Axis relZ = renderer()->getRelativeZ();; - const int l (relZ == X ? 1 : 0); - const int m (relZ == Y ? 1 : 0); - const int n (relZ == Z ? 1 : 0); - RotateObjects (l, m, n, -m_angleOffset, objs); - } - - finishDraw (objs); -} - -double CircleMode::getAngleOffset() const -{ - if (m_drawedVerts.isEmpty()) - return 0.0; - - const int divisions (g_win->ringToolHiRes() ? HighResolution : LowResolution); - QPointF originspot (renderer()->coordconv3_2 (m_drawedVerts.first())); - QLineF bearing (originspot, renderer()->mousePositionF()); - QLineF bearing2 (originspot, QPointF (originspot.x(), 0.0)); - double angleoffset (-bearing.angleTo (bearing2) + 90); - angleoffset /= (360.0 / divisions); // convert angle to 0-16 scale - angleoffset = round (angleoffset); // round to nearest 16th - angleoffset *= ((2 * Pi) / divisions); // convert to radians - angleoffset *= renderer()->depthNegateFactor(); // negate based on camera - return angleoffset; -} - -void CircleMode::render (QPainter& painter) const -{ - QFontMetrics metrics = QFontMetrics (QFont()); - - // If we have not specified the center point of the circle yet, preview it on the screen. - if (m_drawedVerts.isEmpty()) - { - renderer()->drawBlip (painter, renderer()->coordconv3_2 (renderer()->position3D())); - return; - } - - QVector<Vertex> innerverts, outerverts; - QVector<QPointF> innerverts2d, outerverts2d; - const double innerdistance (getCircleDrawDist (0)); - const double outerdistance (m_drawedVerts.size() >= 2 ? getCircleDrawDist (1) : -1); - const int divisions (g_win->ringToolHiRes() ? HighResolution : LowResolution); - const int segments (g_win->ringToolSegments()); - const double angleUnit (2 * Pi / divisions); - Axis relX, relY; - renderer()->getRelativeAxes (relX, relY); - const double angleoffset (m_drawedVerts.size() < 3 ? getAngleOffset() : m_angleOffset); - - // Calculate the preview positions of vertices - for (int i = 0; i < segments + 1; ++i) - { - const double sinangle (sin (angleoffset + i * angleUnit)); - const double cosangle (cos (angleoffset + i * angleUnit)); - Vertex v (Origin); - v.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * innerdistance)); - v.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * innerdistance)); - innerverts << v; - innerverts2d << renderer()->coordconv3_2 (v); - - if (outerdistance != -1) - { - v.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * outerdistance)); - v.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * outerdistance)); - outerverts << v; - outerverts2d << renderer()->coordconv3_2 (v); - } - } - - QVector<QLineF> lines (segments); - - if (outerdistance != -1 and outerdistance != innerdistance) - { - painter.setBrush (m_polybrush); - painter.setPen (Qt::NoPen); - - // Compile polygons - for (int i = 0; i < segments; ++i) - { - QVector<QPointF> points; - points << innerverts2d[i] - << innerverts2d[i + 1] - << outerverts2d[i + 1] - << outerverts2d[i]; - painter.drawPolygon (QPolygonF (points)); - lines << QLineF (innerverts2d[i], innerverts2d[i + 1]); - lines << QLineF (outerverts2d[i], outerverts2d[i + 1]); - } - - // Add bordering edges for unclosed rings/discs - if (segments != divisions) - { - lines << QLineF (innerverts2d.first(), outerverts2d.first()); - lines << QLineF (innerverts2d.last(), outerverts2d.last()); - } - } - else - { - for (int i = 0; i < segments; ++i) - lines << QLineF (innerverts2d[i], innerverts2d[i + 1]); - } - - // Draw a green blips at where the points are - for (QPointF const& point : innerverts2d + outerverts2d) - renderer()->drawBlip (painter, point); - - // Draw edge lines - painter.setPen (renderer()->linePen()); - painter.drawLines (lines); - - // Draw the current radius in the middle of the circle. - QPoint origin = renderer()->coordconv3_2 (m_drawedVerts[0]); - QString label = QString::number (innerdistance); - painter.setPen (renderer()->textPen()); - painter.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label); - - if (m_drawedVerts.size() >= 2) - { - painter.drawText (origin.x() - (metrics.width (label) / 2), - origin.y() + metrics.height(), QString::number (outerdistance)); - } -} - -bool CircleMode::mouseReleased (MouseEventData const& data) -{ - if (Super::mouseReleased (data)) - return true; - - if (data.releasedButtons & Qt::LeftButton) - { - if (m_drawedVerts.size() < 3) - addDrawnVertex (renderer()->position3D()); - else - buildCircle(); - - return true; - } - - return false; -} - -bool CircleMode::preAddVertex (const Vertex&) -{ - m_angleOffset = getAngleOffset(); - return false; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/circleMode.cpp Tue Mar 03 21:54:57 2015 +0200 @@ -0,0 +1,315 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2015 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <QPainter> +#include "circleMode.h" +#include "../miscallenous.h" +#include "../ldObject.h" +#include "../ldDocument.h" +#include "../ringFinder.h" +#include "../primitives.h" +#include "../glRenderer.h" +#include "../mainWindow.h" +#include "../ldObjectMath.h" + +CircleMode::CircleMode (GLRenderer* renderer) : + Super (renderer) {} + +EditModeType CircleMode::type() const +{ + return EditModeType::Circle; +} + +double CircleMode::getCircleDrawDist (int pos) const +{ + assert (m_drawedVerts.size() >= pos + 1); + Vertex v1 = (m_drawedVerts.size() >= pos + 2) ? m_drawedVerts[pos + 1] : + renderer()->coordconv2_3 (renderer()->mousePosition(), false); + Axis localx, localy; + renderer()->getRelativeAxes (localx, localy); + double dx = m_drawedVerts[0][localx] - v1[localx]; + double dy = m_drawedVerts[0][localy] - v1[localy]; + return Grid::Snap (sqrt ((dx * dx) + (dy * dy)), Grid::Coordinate); +} + +Matrix CircleMode::getCircleDrawMatrix (double scale) +{ + // Matrix templates. 2 is substituted with the scale value, 1 is inverted to -1 if needed. + static const Matrix templates[3] = + { + { 2, 0, 0, 0, 1, 0, 0, 0, 2 }, + { 2, 0, 0, 0, 0, 2, 0, 1, 0 }, + { 0, 1, 0, 2, 0, 0, 0, 0, 2 }, + }; + + Matrix transform = templates[renderer()->camera() % 3]; + + for (int i = 0; i < 9; ++i) + { + if (transform[i] == 2) + transform[i] = scale; + elif (transform[i] == 1 and renderer()->camera() >= 3) + transform[i] = -1; + } + + return transform; +} + +void CircleMode::buildCircle() +{ + LDObjectList objs; + const int segments (g_win->ringToolSegments()); + const int divisions (g_win->ringToolHiRes() ? HighResolution : LowResolution); + double dist0 (getCircleDrawDist (0)); + double dist1 (getCircleDrawDist (1)); + LDDocumentPtr refFile; + Matrix transform; + bool circleOrDisc = false; + + if (dist1 < dist0) + qSwap (dist0, dist1); + + if (dist0 == dist1) + { + // If the radii are the same, there's no ring space to fill. Use a circle. + refFile = GetPrimitive (::Circle, segments, divisions, 0); + transform = getCircleDrawMatrix (dist0); + circleOrDisc = true; + } + elif (dist0 == 0 or dist1 == 0) + { + // If either radii is 0, use a disc. + refFile = GetPrimitive (::Disc, segments, divisions, 0); + transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1); + circleOrDisc = true; + } + elif (g_RingFinder.findRings (dist0, dist1)) + { + // The ring finder found a solution, use that. Add the component rings to the file. + for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents()) + { + refFile = GetPrimitive (::Ring, segments, divisions, cmp.num); + LDSubfilePtr ref = LDSpawn<LDSubfile>(); + ref->setFileInfo (refFile); + ref->setTransform (getCircleDrawMatrix (cmp.scale)); + ref->setPosition (m_drawedVerts[0]); + ref->setColor (MainColor()); + objs << ref; + } + } + else + { + // Ring finder failed, last resort: draw the ring with quads + QList<QLineF> c0, c1; + Axis localx, localy, localz; + renderer()->getRelativeAxes (localx, localy); + localz = (Axis) (3 - localx - localy); + double x0 (m_drawedVerts[0][localx]); + double y0 (m_drawedVerts[0][localy]); + + Vertex templ; + templ.setCoordinate (localx, x0); + templ.setCoordinate (localy, y0); + templ.setCoordinate (localz, renderer()->getDepthValue()); + + // Calculate circle coords + MakeCircle (segments, divisions, dist0, c0); + MakeCircle (segments, divisions, dist1, c1); + + for (int i = 0; i < segments; ++i) + { + Vertex v0, v1, v2, v3; + v0 = v1 = v2 = v3 = templ; + v0.setCoordinate (localx, v0[localx] + c0[i].x1()); + v0.setCoordinate (localy, v0[localy] + c0[i].y1()); + v1.setCoordinate (localx, v1[localx] + c0[i].x2()); + v1.setCoordinate (localy, v1[localy] + c0[i].y2()); + v2.setCoordinate (localx, v2[localx] + c1[i].x2()); + v2.setCoordinate (localy, v2[localy] + c1[i].y2()); + v3.setCoordinate (localx, v3[localx] + c1[i].x1()); + v3.setCoordinate (localy, v3[localy] + c1[i].y1()); + + LDQuadPtr quad (LDSpawn<LDQuad> (v0, v1, v2, v3)); + quad->setColor (MainColor()); + + // Ensure the quads always are BFC-front towards the camera + if (renderer()->camera() % 3 <= 0) + quad->invert(); + + objs << quad; + } + } + + if (circleOrDisc and refFile != null) + { + LDSubfilePtr ref = LDSpawn<LDSubfile>(); + ref->setFileInfo (refFile); + ref->setTransform (transform); + ref->setPosition (m_drawedVerts[0]); + ref->setColor (MainColor()); + objs << ref; + } + + unless (objs.isEmpty()) + { + Axis relZ = renderer()->getRelativeZ();; + const int l (relZ == X ? 1 : 0); + const int m (relZ == Y ? 1 : 0); + const int n (relZ == Z ? 1 : 0); + RotateObjects (l, m, n, -m_angleOffset, objs); + } + + finishDraw (objs); +} + +double CircleMode::getAngleOffset() const +{ + if (m_drawedVerts.isEmpty()) + return 0.0; + + const int divisions (g_win->ringToolHiRes() ? HighResolution : LowResolution); + QPointF originspot (renderer()->coordconv3_2 (m_drawedVerts.first())); + QLineF bearing (originspot, renderer()->mousePositionF()); + QLineF bearing2 (originspot, QPointF (originspot.x(), 0.0)); + double angleoffset (-bearing.angleTo (bearing2) + 90); + angleoffset /= (360.0 / divisions); // convert angle to 0-16 scale + angleoffset = round (angleoffset); // round to nearest 16th + angleoffset *= ((2 * Pi) / divisions); // convert to radians + angleoffset *= renderer()->depthNegateFactor(); // negate based on camera + return angleoffset; +} + +void CircleMode::render (QPainter& painter) const +{ + QFontMetrics metrics = QFontMetrics (QFont()); + + // If we have not specified the center point of the circle yet, preview it on the screen. + if (m_drawedVerts.isEmpty()) + { + renderer()->drawBlip (painter, renderer()->coordconv3_2 (renderer()->position3D())); + return; + } + + QVector<Vertex> innerverts, outerverts; + QVector<QPointF> innerverts2d, outerverts2d; + const double innerdistance (getCircleDrawDist (0)); + const double outerdistance (m_drawedVerts.size() >= 2 ? getCircleDrawDist (1) : -1); + const int divisions (g_win->ringToolHiRes() ? HighResolution : LowResolution); + const int segments (g_win->ringToolSegments()); + const double angleUnit (2 * Pi / divisions); + Axis relX, relY; + renderer()->getRelativeAxes (relX, relY); + const double angleoffset (m_drawedVerts.size() < 3 ? getAngleOffset() : m_angleOffset); + + // Calculate the preview positions of vertices + for (int i = 0; i < segments + 1; ++i) + { + const double sinangle (sin (angleoffset + i * angleUnit)); + const double cosangle (cos (angleoffset + i * angleUnit)); + Vertex v (Origin); + v.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * innerdistance)); + v.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * innerdistance)); + innerverts << v; + innerverts2d << renderer()->coordconv3_2 (v); + + if (outerdistance != -1) + { + v.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * outerdistance)); + v.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * outerdistance)); + outerverts << v; + outerverts2d << renderer()->coordconv3_2 (v); + } + } + + QVector<QLineF> lines (segments); + + if (outerdistance != -1 and outerdistance != innerdistance) + { + painter.setBrush (m_polybrush); + painter.setPen (Qt::NoPen); + + // Compile polygons + for (int i = 0; i < segments; ++i) + { + QVector<QPointF> points; + points << innerverts2d[i] + << innerverts2d[i + 1] + << outerverts2d[i + 1] + << outerverts2d[i]; + painter.drawPolygon (QPolygonF (points)); + lines << QLineF (innerverts2d[i], innerverts2d[i + 1]); + lines << QLineF (outerverts2d[i], outerverts2d[i + 1]); + } + + // Add bordering edges for unclosed rings/discs + if (segments != divisions) + { + lines << QLineF (innerverts2d.first(), outerverts2d.first()); + lines << QLineF (innerverts2d.last(), outerverts2d.last()); + } + } + else + { + for (int i = 0; i < segments; ++i) + lines << QLineF (innerverts2d[i], innerverts2d[i + 1]); + } + + // Draw a green blips at where the points are + for (QPointF const& point : innerverts2d + outerverts2d) + renderer()->drawBlip (painter, point); + + // Draw edge lines + painter.setPen (renderer()->linePen()); + painter.drawLines (lines); + + // Draw the current radius in the middle of the circle. + QPoint origin = renderer()->coordconv3_2 (m_drawedVerts[0]); + QString label = QString::number (innerdistance); + painter.setPen (renderer()->textPen()); + painter.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label); + + if (m_drawedVerts.size() >= 2) + { + painter.drawText (origin.x() - (metrics.width (label) / 2), + origin.y() + metrics.height(), QString::number (outerdistance)); + } +} + +bool CircleMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + if (data.releasedButtons & Qt::LeftButton) + { + if (m_drawedVerts.size() < 3) + addDrawnVertex (renderer()->position3D()); + else + buildCircle(); + + return true; + } + + return false; +} + +bool CircleMode::preAddVertex (const Vertex&) +{ + m_angleOffset = getAngleOffset(); + return false; +}
--- a/src/editmodes/drawMode.cc Tue Mar 03 17:42:21 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 - 2015 Teemu Piippo - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <QPainter> -#include <QMouseEvent> -#include "drawMode.h" -#include "../ldObject.h" -#include "../glRenderer.h" -#include "../miscallenous.h" - -DrawMode::DrawMode (GLRenderer* renderer) : - Super (renderer) {} - -EditModeType DrawMode::type() const -{ - return EditModeType::Draw; -} - -void DrawMode::render (QPainter& painter) const -{ - QVector<Vertex> poly; - QFontMetrics metrics = QFontMetrics (QFont()); - - for (Vertex const& vert : m_drawedVerts) - poly << vert; - - // Draw the cursor vertex as the last one in the list. - if (poly.size() < 4) - poly << getCursorVertex(); - - renderPolygon (painter, poly, true, true); -} - -bool DrawMode::preAddVertex (Vertex const& pos) -{ - // If we picked an already-existing vertex, stop drawing - for (Vertex& vert : m_drawedVerts) - { - if (vert == pos) - { - endDraw(); - return true; - } - } - - return false; -} - -bool DrawMode::mouseReleased (MouseEventData const& data) -{ - if (Super::mouseReleased (data)) - return true; - - if (data.releasedButtons & Qt::LeftButton) - { - // If we have 4 verts, stop drawing. - if (m_drawedVerts.size() >= 4) - { - endDraw(); - return true; - } - - addDrawnVertex (getCursorVertex()); - return true; - } - - return false; -} - -void DrawMode::endDraw() -{ - // Clean the selection and create the object - QList<Vertex>& verts = m_drawedVerts; - LDObjectList objs; - - switch (verts.size()) - { - case 1: - return; - - case 2: - { - // 2 verts - make a line - LDLinePtr obj = LDSpawn<LDLine> (verts[0], verts[1]); - obj->setColor (EdgeColor()); - objs << obj; - break; - } - - case 3: - case 4: - { - LDObjectPtr obj = (verts.size() == 3) ? - static_cast<LDObjectPtr> (LDSpawn<LDTriangle>()) : - static_cast<LDObjectPtr> (LDSpawn<LDQuad>()); - - obj->setColor (MainColor()); - - for (int i = 0; i < verts.size(); ++i) - obj->setVertex (i, verts[i]); - - objs << obj; - break; - } - } - - finishDraw (objs); -} - -template<typename _Type> -_Type IntervalClamp (_Type a, _Type interval) -{ - _Type remainder = a % interval; - - if (remainder >= float (interval / 2)) - a += interval; - - a -= remainder; - return a; -} - -Vertex DrawMode::getCursorVertex() const -{ - Vertex result = renderer()->position3D(); - - if (renderer()->keyboardModifiers() & Qt::ControlModifier - and not m_drawedVerts.isEmpty()) - { - Vertex const& v0 = m_drawedVerts.last(); - Vertex const& v1 = result; - Axis relX, relY; - - renderer()->getRelativeAxes (relX, relY); - QLineF ln (v0[relX], v0[relY], v1[relX], v1[relY]); - ln.setAngle (IntervalClamp<int> (ln.angle(), 45)); - result.setCoordinate (relX, Grid::Snap (ln.x2(), Grid::Coordinate)); - result.setCoordinate (relY, Grid::Snap (ln.y2(), Grid::Coordinate)); - } - - return result; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/drawMode.cpp Tue Mar 03 21:54:57 2015 +0200 @@ -0,0 +1,156 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2015 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <QPainter> +#include <QMouseEvent> +#include "drawMode.h" +#include "../ldObject.h" +#include "../glRenderer.h" +#include "../miscallenous.h" + +DrawMode::DrawMode (GLRenderer* renderer) : + Super (renderer) {} + +EditModeType DrawMode::type() const +{ + return EditModeType::Draw; +} + +void DrawMode::render (QPainter& painter) const +{ + QVector<Vertex> poly; + QFontMetrics metrics = QFontMetrics (QFont()); + + for (Vertex const& vert : m_drawedVerts) + poly << vert; + + // Draw the cursor vertex as the last one in the list. + if (poly.size() < 4) + poly << getCursorVertex(); + + renderPolygon (painter, poly, true, true); +} + +bool DrawMode::preAddVertex (Vertex const& pos) +{ + // If we picked an already-existing vertex, stop drawing + for (Vertex& vert : m_drawedVerts) + { + if (vert == pos) + { + endDraw(); + return true; + } + } + + return false; +} + +bool DrawMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + if (data.releasedButtons & Qt::LeftButton) + { + // If we have 4 verts, stop drawing. + if (m_drawedVerts.size() >= 4) + { + endDraw(); + return true; + } + + addDrawnVertex (getCursorVertex()); + return true; + } + + return false; +} + +void DrawMode::endDraw() +{ + // Clean the selection and create the object + QList<Vertex>& verts = m_drawedVerts; + LDObjectList objs; + + switch (verts.size()) + { + case 1: + return; + + case 2: + { + // 2 verts - make a line + LDLinePtr obj = LDSpawn<LDLine> (verts[0], verts[1]); + obj->setColor (EdgeColor()); + objs << obj; + break; + } + + case 3: + case 4: + { + LDObjectPtr obj = (verts.size() == 3) ? + static_cast<LDObjectPtr> (LDSpawn<LDTriangle>()) : + static_cast<LDObjectPtr> (LDSpawn<LDQuad>()); + + obj->setColor (MainColor()); + + for (int i = 0; i < verts.size(); ++i) + obj->setVertex (i, verts[i]); + + objs << obj; + break; + } + } + + finishDraw (objs); +} + +template<typename _Type> +_Type IntervalClamp (_Type a, _Type interval) +{ + _Type remainder = a % interval; + + if (remainder >= float (interval / 2)) + a += interval; + + a -= remainder; + return a; +} + +Vertex DrawMode::getCursorVertex() const +{ + Vertex result = renderer()->position3D(); + + if (renderer()->keyboardModifiers() & Qt::ControlModifier + and not m_drawedVerts.isEmpty()) + { + Vertex const& v0 = m_drawedVerts.last(); + Vertex const& v1 = result; + Axis relX, relY; + + renderer()->getRelativeAxes (relX, relY); + QLineF ln (v0[relX], v0[relY], v1[relX], v1[relY]); + ln.setAngle (IntervalClamp<int> (ln.angle(), 45)); + result.setCoordinate (relX, Grid::Snap (ln.x2(), Grid::Coordinate)); + result.setCoordinate (relY, Grid::Snap (ln.y2(), Grid::Coordinate)); + } + + return result; +}
--- a/src/editmodes/magicWandMode.cc Tue Mar 03 17:42:21 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,220 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 - 2015 Teemu Piippo - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <QMouseEvent> -#include "magicWandMode.h" -#include "../ldDocument.h" -#include "../mainWindow.h" -#include "../glRenderer.h" - -MagicWandMode::MagicWandMode (GLRenderer* renderer) : - Super (renderer) -{ - // Get vertex<->object data - for (LDObjectPtr obj : CurrentDocument()->objects()) - { - // Note: this deliberately only takes vertex-objects into account. - // The magic wand does not process subparts. - for (int i = 0; i < obj->numVertices(); ++i) - m_vertices[obj->vertex (i)] << obj; - } -} - -EditModeType MagicWandMode::type() const -{ - return EditModeType::MagicWand; -} - -void MagicWandMode::fillBoundaries (LDObjectPtr obj, QVector<BoundaryType>& boundaries, QVector<LDObjectPtr>& candidates) -{ - // All boundaries obviously share vertices with the object, therefore they're all in the list - // of candidates. - for (auto it = candidates.begin(); it != candidates.end(); ++it) - { - if (not Eq ((*it)->type(), OBJ_Line, OBJ_CondLine) or (*it)->vertex (0) == (*it)->vertex (1)) - continue; - - int matches = 0; - - for (int i = 0; i < obj->numVertices(); ++i) - { - if (not Eq (obj->vertex (i), (*it)->vertex (0), (*it)->vertex (1))) - continue; - - if (++matches == 2) - { - // Boundary found. If it's an edgeline, add it to the boundaries list, if a - // conditional line, select it. - if ((*it)->type() == OBJ_CondLine) - m_selection << *it; - else - boundaries.append (std::make_tuple ((*it)->vertex (0), (*it)->vertex (1))); - - break; - } - } - } -} - -void MagicWandMode::doMagic (LDObjectPtr obj, MagicWandMode::MagicType type) -{ - if (obj == null) - { - if (type == Set) - { - CurrentDocument()->clearSelection(); - g_win->buildObjList(); - } - - return; - } - - int matchesneeded = 0; - QVector<BoundaryType> boundaries; - LDObjectType objtype = obj->type(); - - if (type != InternalRecursion) - { - m_selection.clear(); - m_selection.append (obj); - } - - switch (obj->type()) - { - case OBJ_Line: - case OBJ_CondLine: - matchesneeded = 1; - break; - - case OBJ_Triangle: - case OBJ_Quad: - matchesneeded = 2; - break; - - default: - return; - } - - QVector<LDObjectPtr> candidates; - - // Get the list of objects that touch this object, i.e. share a vertex - // with this. - for (int i = 0; i < obj->numVertices(); ++i) - candidates += m_vertices[obj->vertex (i)]; - - RemoveDuplicates (candidates); - - // If we're dealing with surfaces, get a list of boundaries. - if (matchesneeded > 1) - fillBoundaries (obj, boundaries, candidates); - - for (LDObjectPtr candidate : candidates) - { - try - { - // If we're doing this on lines, we need exact type match. Surface types (quads and - // triangles) can be mixed. Also don't consider self a candidate, and don't consider - // objects we have already processed. - if ((candidate == obj) or - (candidate->color() != obj->color()) or - (m_selection.contains (candidate)) or - (matchesneeded == 1 and (candidate->type() != objtype)) or - ((candidate->numVertices() > 2) ^ (matchesneeded == 2))) - { - throw 0; - } - - // Now ensure the two objects share enough vertices. - QVector<Vertex> matches; - - for (int i = 0; i < obj->numVertices(); ++i) - { - for (int j = 0; j < candidate->numVertices(); ++j) - { - if (obj->vertex(i) == candidate->vertex(j)) - { - matches << obj->vertex(i); - break; - } - } - } - - if (matches.size() < matchesneeded) - throw 0; // Not enough matches. - - // Check if a boundary gets in between the objects. - for (auto boundary : boundaries) - { - if (Eq (matches[0], std::get<0> (boundary), std::get<1> (boundary)) and - Eq (matches[1], std::get<0> (boundary), std::get<1> (boundary))) - { - throw 0; - } - } - - m_selection.append (candidate); - doMagic (candidate, InternalRecursion); - } - catch (int&) - { - continue; - } - } - - switch (type) - { - case Set: - CurrentDocument()->clearSelection(); - case Additive: - for (LDObjectPtr obj : m_selection) - obj->select(); - break; - - case Subtractive: - for (LDObjectPtr obj : m_selection) - obj->deselect(); - break; - - case InternalRecursion: - break; - } - - if (type != InternalRecursion) - g_win->buildObjList(); -} - -bool MagicWandMode::mouseReleased (MouseEventData const& data) -{ - if (Super::mouseReleased (data)) - return true; - - if (data.releasedButtons & Qt::LeftButton and not data.mouseMoved) - { - MagicType wandtype = MagicWandMode::Set; - - if (data.keymods & Qt::ShiftModifier) - wandtype = MagicWandMode::Additive; - elif (data.keymods & Qt::ControlModifier) - wandtype = MagicWandMode::Subtractive; - - doMagic (renderer()->pickOneObject (data.ev->x(), data.ev->y()), wandtype); - return true; - } - - return false; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/magicWandMode.cpp Tue Mar 03 21:54:57 2015 +0200 @@ -0,0 +1,220 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2015 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <QMouseEvent> +#include "magicWandMode.h" +#include "../ldDocument.h" +#include "../mainWindow.h" +#include "../glRenderer.h" + +MagicWandMode::MagicWandMode (GLRenderer* renderer) : + Super (renderer) +{ + // Get vertex<->object data + for (LDObjectPtr obj : CurrentDocument()->objects()) + { + // Note: this deliberately only takes vertex-objects into account. + // The magic wand does not process subparts. + for (int i = 0; i < obj->numVertices(); ++i) + m_vertices[obj->vertex (i)] << obj; + } +} + +EditModeType MagicWandMode::type() const +{ + return EditModeType::MagicWand; +} + +void MagicWandMode::fillBoundaries (LDObjectPtr obj, QVector<BoundaryType>& boundaries, QVector<LDObjectPtr>& candidates) +{ + // All boundaries obviously share vertices with the object, therefore they're all in the list + // of candidates. + for (auto it = candidates.begin(); it != candidates.end(); ++it) + { + if (not Eq ((*it)->type(), OBJ_Line, OBJ_CondLine) or (*it)->vertex (0) == (*it)->vertex (1)) + continue; + + int matches = 0; + + for (int i = 0; i < obj->numVertices(); ++i) + { + if (not Eq (obj->vertex (i), (*it)->vertex (0), (*it)->vertex (1))) + continue; + + if (++matches == 2) + { + // Boundary found. If it's an edgeline, add it to the boundaries list, if a + // conditional line, select it. + if ((*it)->type() == OBJ_CondLine) + m_selection << *it; + else + boundaries.append (std::make_tuple ((*it)->vertex (0), (*it)->vertex (1))); + + break; + } + } + } +} + +void MagicWandMode::doMagic (LDObjectPtr obj, MagicWandMode::MagicType type) +{ + if (obj == null) + { + if (type == Set) + { + CurrentDocument()->clearSelection(); + g_win->buildObjList(); + } + + return; + } + + int matchesneeded = 0; + QVector<BoundaryType> boundaries; + LDObjectType objtype = obj->type(); + + if (type != InternalRecursion) + { + m_selection.clear(); + m_selection.append (obj); + } + + switch (obj->type()) + { + case OBJ_Line: + case OBJ_CondLine: + matchesneeded = 1; + break; + + case OBJ_Triangle: + case OBJ_Quad: + matchesneeded = 2; + break; + + default: + return; + } + + QVector<LDObjectPtr> candidates; + + // Get the list of objects that touch this object, i.e. share a vertex + // with this. + for (int i = 0; i < obj->numVertices(); ++i) + candidates += m_vertices[obj->vertex (i)]; + + RemoveDuplicates (candidates); + + // If we're dealing with surfaces, get a list of boundaries. + if (matchesneeded > 1) + fillBoundaries (obj, boundaries, candidates); + + for (LDObjectPtr candidate : candidates) + { + try + { + // If we're doing this on lines, we need exact type match. Surface types (quads and + // triangles) can be mixed. Also don't consider self a candidate, and don't consider + // objects we have already processed. + if ((candidate == obj) or + (candidate->color() != obj->color()) or + (m_selection.contains (candidate)) or + (matchesneeded == 1 and (candidate->type() != objtype)) or + ((candidate->numVertices() > 2) ^ (matchesneeded == 2))) + { + throw 0; + } + + // Now ensure the two objects share enough vertices. + QVector<Vertex> matches; + + for (int i = 0; i < obj->numVertices(); ++i) + { + for (int j = 0; j < candidate->numVertices(); ++j) + { + if (obj->vertex(i) == candidate->vertex(j)) + { + matches << obj->vertex(i); + break; + } + } + } + + if (matches.size() < matchesneeded) + throw 0; // Not enough matches. + + // Check if a boundary gets in between the objects. + for (auto boundary : boundaries) + { + if (Eq (matches[0], std::get<0> (boundary), std::get<1> (boundary)) and + Eq (matches[1], std::get<0> (boundary), std::get<1> (boundary))) + { + throw 0; + } + } + + m_selection.append (candidate); + doMagic (candidate, InternalRecursion); + } + catch (int&) + { + continue; + } + } + + switch (type) + { + case Set: + CurrentDocument()->clearSelection(); + case Additive: + for (LDObjectPtr obj : m_selection) + obj->select(); + break; + + case Subtractive: + for (LDObjectPtr obj : m_selection) + obj->deselect(); + break; + + case InternalRecursion: + break; + } + + if (type != InternalRecursion) + g_win->buildObjList(); +} + +bool MagicWandMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + if (data.releasedButtons & Qt::LeftButton and not data.mouseMoved) + { + MagicType wandtype = MagicWandMode::Set; + + if (data.keymods & Qt::ShiftModifier) + wandtype = MagicWandMode::Additive; + elif (data.keymods & Qt::ControlModifier) + wandtype = MagicWandMode::Subtractive; + + doMagic (renderer()->pickOneObject (data.ev->x(), data.ev->y()), wandtype); + return true; + } + + return false; +}
--- a/src/editmodes/rectangleMode.cc Tue Mar 03 17:42:21 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 - 2015 Teemu Piippo - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <QPainter> -#include <QMouseEvent> -#include "rectangleMode.h" -#include "../ldObject.h" -#include "../glRenderer.h" - -RectangleMode::RectangleMode (GLRenderer* renderer) : - Super (renderer), - m_rectangleVerts (QVector<Vertex>(4)) {} - -EditModeType RectangleMode::type() const -{ - return EditModeType::Rectangle; -} - -void RectangleMode::render (QPainter& painter) const -{ - renderPolygon (painter, (m_drawedVerts.size() > 0) ? m_rectangleVerts : - QVector<Vertex> ({renderer()->position3D()}), true, false); -} - -bool RectangleMode::mouseReleased (MouseEventData const& data) -{ - if (Super::mouseReleased (data)) - return true; - - if (data.releasedButtons & Qt::LeftButton) - { - if (m_drawedVerts.size() == 2) - { - LDQuadPtr quad (LDSpawn<LDQuad>()); - updateRectVerts(); - - for (int i = 0; i < quad->numVertices(); ++i) - quad->setVertex (i, m_rectangleVerts[i]); - - quad->setColor (MainColor()); - finishDraw (LDObjectList ({quad})); - return true; - } - - addDrawnVertex (renderer()->position3D()); - return true; - } - - return false; -} - -// -// Update rect vertices when the mouse moves since the 3d position likely has changed -// -bool RectangleMode::mouseMoved (QMouseEvent*) -{ - updateRectVerts(); - return false; -} - -void RectangleMode::updateRectVerts() -{ - if (m_drawedVerts.isEmpty()) - { - for (int i = 0; i < 4; ++i) - m_rectangleVerts[i] = renderer()->position3D(); - - return; - } - - Vertex v0 = m_drawedVerts[0], - v1 = (m_drawedVerts.size() >= 2) ? m_drawedVerts[1] : renderer()->position3D(); - - const Axis localx = renderer()->getCameraAxis (false), - localy = renderer()->getCameraAxis (true), - localz = (Axis) (3 - localx - localy); - - for (int i = 0; i < 4; ++i) - m_rectangleVerts[i].setCoordinate (localz, renderer()->getDepthValue()); - - m_rectangleVerts[0].setCoordinate (localx, v0[localx]); - m_rectangleVerts[0].setCoordinate (localy, v0[localy]); - m_rectangleVerts[1].setCoordinate (localx, v1[localx]); - m_rectangleVerts[1].setCoordinate (localy, v0[localy]); - m_rectangleVerts[2].setCoordinate (localx, v1[localx]); - m_rectangleVerts[2].setCoordinate (localy, v1[localy]); - m_rectangleVerts[3].setCoordinate (localx, v0[localx]); - m_rectangleVerts[3].setCoordinate (localy, v1[localy]); -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/rectangleMode.cpp Tue Mar 03 21:54:57 2015 +0200 @@ -0,0 +1,104 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2015 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <QPainter> +#include <QMouseEvent> +#include "rectangleMode.h" +#include "../ldObject.h" +#include "../glRenderer.h" + +RectangleMode::RectangleMode (GLRenderer* renderer) : + Super (renderer), + m_rectangleVerts (QVector<Vertex>(4)) {} + +EditModeType RectangleMode::type() const +{ + return EditModeType::Rectangle; +} + +void RectangleMode::render (QPainter& painter) const +{ + renderPolygon (painter, (m_drawedVerts.size() > 0) ? m_rectangleVerts : + QVector<Vertex> ({renderer()->position3D()}), true, false); +} + +bool RectangleMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + if (data.releasedButtons & Qt::LeftButton) + { + if (m_drawedVerts.size() == 2) + { + LDQuadPtr quad (LDSpawn<LDQuad>()); + updateRectVerts(); + + for (int i = 0; i < quad->numVertices(); ++i) + quad->setVertex (i, m_rectangleVerts[i]); + + quad->setColor (MainColor()); + finishDraw (LDObjectList ({quad})); + return true; + } + + addDrawnVertex (renderer()->position3D()); + return true; + } + + return false; +} + +// +// Update rect vertices when the mouse moves since the 3d position likely has changed +// +bool RectangleMode::mouseMoved (QMouseEvent*) +{ + updateRectVerts(); + return false; +} + +void RectangleMode::updateRectVerts() +{ + if (m_drawedVerts.isEmpty()) + { + for (int i = 0; i < 4; ++i) + m_rectangleVerts[i] = renderer()->position3D(); + + return; + } + + Vertex v0 = m_drawedVerts[0], + v1 = (m_drawedVerts.size() >= 2) ? m_drawedVerts[1] : renderer()->position3D(); + + const Axis localx = renderer()->getCameraAxis (false), + localy = renderer()->getCameraAxis (true), + localz = (Axis) (3 - localx - localy); + + for (int i = 0; i < 4; ++i) + m_rectangleVerts[i].setCoordinate (localz, renderer()->getDepthValue()); + + m_rectangleVerts[0].setCoordinate (localx, v0[localx]); + m_rectangleVerts[0].setCoordinate (localy, v0[localy]); + m_rectangleVerts[1].setCoordinate (localx, v1[localx]); + m_rectangleVerts[1].setCoordinate (localy, v0[localy]); + m_rectangleVerts[2].setCoordinate (localx, v1[localx]); + m_rectangleVerts[2].setCoordinate (localy, v1[localy]); + m_rectangleVerts[3].setCoordinate (localx, v0[localx]); + m_rectangleVerts[3].setCoordinate (localy, v1[localy]); +}
--- a/src/editmodes/selectMode.cc Tue Mar 03 17:42:21 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,137 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 - 2015 Teemu Piippo - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <QMouseEvent> -#include "selectMode.h" -#include "../glRenderer.h" -#include "../addObjectDialog.h" -#include "../mainWindow.h" -#include "../glRenderer.h" - -SelectMode::SelectMode (GLRenderer* renderer) : - Super (renderer), - m_rangepick (false) {} - -EditModeType SelectMode::type() const -{ - return EditModeType::Select; -} - -void SelectMode::render (QPainter& painter) const -{ - // If we're range-picking, draw a rectangle encompassing the selection area. - if (m_rangepick) - { - int x0 = m_rangeStart.x(), - y0 = m_rangeStart.y(), - x1 = renderer()->mousePosition().x(), - y1 = renderer()->mousePosition().y(); - - QRect rect (x0, y0, x1 - x0, y1 - y0); - QColor fillColor = (m_addpick ? "#40FF00" : "#00CCFF"); - fillColor.setAlphaF (0.2f); - painter.setPen (QPen (QColor (0, 0, 0, 208), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); - painter.setBrush (QBrush (fillColor)); - painter.drawRect (rect); - } -} - -bool SelectMode::mouseReleased (MouseEventData const& data) -{ - if (Super::mouseReleased (data)) - return true; - - if (data.releasedButtons & Qt::LeftButton) - { - if (not data.mouseMoved) - m_rangepick = false; - - if (not m_rangepick) - m_addpick = (data.keymods & Qt::ControlModifier); - - if (not data.mouseMoved or m_rangepick) - { - QRect area; - int const mx = data.ev->x(); - int const my = data.ev->y(); - - if (not m_rangepick) - { - area = QRect (mx, my, 1, 1); - } - else - { - int const x = Min (m_rangeStart.x(), mx); - int const y = Min (m_rangeStart.y(), my); - int const width = Abs (m_rangeStart.x() - mx); - int const height = Abs (m_rangeStart.y() - my); - area = QRect (x, y, width, height); - } - - renderer()->pick (area, m_addpick); - } - - m_rangepick = false; - return true; - } - - return false; -} - -bool SelectMode::mousePressed (QMouseEvent* ev) -{ - if (Super::mousePressed (ev)) - return true; - - if (ev->modifiers() & Qt::ControlModifier) - { - m_rangepick = true; - m_rangeStart.setX (ev->x()); - m_rangeStart.setY (ev->y()); - m_addpick = (ev->modifiers() & Qt::AltModifier); - return true; - } - - return false; -} - -bool SelectMode::mouseDoubleClicked (QMouseEvent* ev) -{ - if (Super::mouseDoubleClicked (ev)) - return true; - - if (ev->buttons() & Qt::LeftButton) - { - renderer()->document()->clearSelection(); - LDObjectPtr obj = renderer()->pickOneObject (ev->x(), ev->y()); - - if (obj != null) - { - AddObjectDialog::staticDialog (obj->type(), obj); - g_win->endAction(); - return true; - } - } - - return false; -} - -bool SelectMode::mouseMoved (QMouseEvent*) -{ - return m_rangepick; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/selectMode.cpp Tue Mar 03 21:54:57 2015 +0200 @@ -0,0 +1,137 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2015 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <QMouseEvent> +#include "selectMode.h" +#include "../glRenderer.h" +#include "../addObjectDialog.h" +#include "../mainWindow.h" +#include "../glRenderer.h" + +SelectMode::SelectMode (GLRenderer* renderer) : + Super (renderer), + m_rangepick (false) {} + +EditModeType SelectMode::type() const +{ + return EditModeType::Select; +} + +void SelectMode::render (QPainter& painter) const +{ + // If we're range-picking, draw a rectangle encompassing the selection area. + if (m_rangepick) + { + int x0 = m_rangeStart.x(), + y0 = m_rangeStart.y(), + x1 = renderer()->mousePosition().x(), + y1 = renderer()->mousePosition().y(); + + QRect rect (x0, y0, x1 - x0, y1 - y0); + QColor fillColor = (m_addpick ? "#40FF00" : "#00CCFF"); + fillColor.setAlphaF (0.2f); + painter.setPen (QPen (QColor (0, 0, 0, 208), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + painter.setBrush (QBrush (fillColor)); + painter.drawRect (rect); + } +} + +bool SelectMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + if (data.releasedButtons & Qt::LeftButton) + { + if (not data.mouseMoved) + m_rangepick = false; + + if (not m_rangepick) + m_addpick = (data.keymods & Qt::ControlModifier); + + if (not data.mouseMoved or m_rangepick) + { + QRect area; + int const mx = data.ev->x(); + int const my = data.ev->y(); + + if (not m_rangepick) + { + area = QRect (mx, my, 1, 1); + } + else + { + int const x = Min (m_rangeStart.x(), mx); + int const y = Min (m_rangeStart.y(), my); + int const width = Abs (m_rangeStart.x() - mx); + int const height = Abs (m_rangeStart.y() - my); + area = QRect (x, y, width, height); + } + + renderer()->pick (area, m_addpick); + } + + m_rangepick = false; + return true; + } + + return false; +} + +bool SelectMode::mousePressed (QMouseEvent* ev) +{ + if (Super::mousePressed (ev)) + return true; + + if (ev->modifiers() & Qt::ControlModifier) + { + m_rangepick = true; + m_rangeStart.setX (ev->x()); + m_rangeStart.setY (ev->y()); + m_addpick = (ev->modifiers() & Qt::AltModifier); + return true; + } + + return false; +} + +bool SelectMode::mouseDoubleClicked (QMouseEvent* ev) +{ + if (Super::mouseDoubleClicked (ev)) + return true; + + if (ev->buttons() & Qt::LeftButton) + { + renderer()->document()->clearSelection(); + LDObjectPtr obj = renderer()->pickOneObject (ev->x(), ev->y()); + + if (obj != null) + { + AddObjectDialog::staticDialog (obj->type(), obj); + g_win->endAction(); + return true; + } + } + + return false; +} + +bool SelectMode::mouseMoved (QMouseEvent*) +{ + return m_rangepick; +}
--- a/updaterevision.py Tue Mar 03 17:42:21 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -# -# Copyright 2014 Teemu Piippo -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER -# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -import sys -import subprocess -from datetime import datetime - -if len (sys.argv) != 2: - print 'usage: %s <output>' % sys.argv[0] - quit (1) - -oldrev = '' - -try: - with open (sys.argv[1]) as fp: - oldrev = fp.readline().replace ('\n', '').replace ('// ', '') -except IOError: - pass - -data = subprocess.check_output (['hg', 'log', '-r.', '--template', - '{node|short} {branch} {date|hgdate}']).replace ('\n', '').split (' ') - -rev = data[0] -branch = data[1] -timestamp = int (data[2]) -date = datetime.utcfromtimestamp (timestamp) -datestring = date.strftime ('%y%m%d-%H%M') if date.year >= 2000 else '000000-0000' - -if len(rev) > 7: - rev = rev[0:7] - -if subprocess.check_output (['hg', 'id', '-n']).replace ('\n', '')[-1] == '+': - rev += '+' - -if rev == oldrev: - print "%s is up to date at %s" % (sys.argv[1], rev) - quit (0) - -with open (sys.argv[1], 'w') as fp: - fp.write ('// %s\n' % rev) - fp.write ('#define HG_NODE "%s"\n' % rev) - fp.write ('#define HG_BRANCH "%s"\n' % branch) - fp.write ('#define HG_DATE_VERSION "%s"\n' % datestring) - fp.write ('#define HG_DATE_STRING "%s"\n' % date.strftime ('%d %b %Y')) - fp.write ('#define HG_DATE_TIME %d\n' % int (timestamp)) - print '%s updated to %s' % (sys.argv[1], rev)