Sun, 07 Jul 2013 17:45:48 +0300
more restyle/refactor
/* * LDForge: LDraw parts authoring CAD * Copyright (C) 2013 Santeri 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 "common.h" #include "ldtypes.h" #include "file.h" #include "misc.h" #include "gui.h" #include "history.h" #include "gldraw.h" // List of all LDObjects vector<LDObject*> g_LDObjects; // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= // LDObject constructors LDObject::LDObject() { qObjListEntry = null; setParent( null ); m_hidden = false; m_selected = false; m_glinit = false; // Determine ID int id = 1; // 0 is invalid for( LDObject * obj : g_LDObjects ) if( obj->id() >= id ) id = obj->id() + 1; setID( id ); g_LDObjects << this; } LDGibberish::LDGibberish() {} LDGibberish::LDGibberish( str contents, str reason ) : contents( contents ), reason( reason ) {} // ============================================================================= str LDComment::raw() { return fmt( "0 %1", text ); } str LDSubfile::raw() { str val = fmt( "1 %1 %2 ", color(), position() ); val += transform().stringRep(); val += ' '; val += fileInfo()->name(); return val; } str LDLine::raw() { str val = fmt( "2 %1", color() ); for( ushort i = 0; i < 2; ++i ) val += fmt( " %1", getVertex( i ) ); return val; } str LDTriangle::raw() { str val = fmt( "3 %1", color() ); for( ushort i = 0; i < 3; ++i ) val += fmt( " %1", getVertex( i ) ); return val; } str LDQuad::raw() { str val = fmt( "4 %1", color() ); for( ushort i = 0; i < 4; ++i ) val += fmt( " %1", getVertex( i ) ); return val; } str LDCondLine::raw() { str val = fmt( "5 %1", color() ); // Add the coordinates for( ushort i = 0; i < 4; ++i ) val += fmt( " %1", getVertex( i ) ); return val; } str LDGibberish::raw() { return contents; } str LDVertex::raw() { return fmt( "0 !LDFORGE VERTEX %1 %2", color(), pos ); } str LDEmpty::raw() { return ""; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= const char* LDBFC::statements[] = { "CERTIFY CCW", "CCW", "CERTIFY CW", "CW", "NOCERTIFY", "INVERTNEXT", }; str LDBFC::raw() { return fmt( "0 BFC %1", LDBFC::statements[type] ); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= vector<LDTriangle*> LDQuad::splitToTriangles() { // Create the two triangles based on this quadrilateral: // 0---3 0---3 3 // | | | / /| // | | = | / / | // | | |/ / | // 1---2 1 1---2 LDTriangle* tri1 = new LDTriangle( getVertex( 0 ), getVertex( 1 ), getVertex( 3 ) ); LDTriangle* tri2 = new LDTriangle( getVertex( 1 ), getVertex( 2 ), getVertex( 3 ) ); // The triangles also inherit the quad's color tri1->setColor( color() ); tri2->setColor( color() ); vector<LDTriangle*> triangles; triangles << tri1; triangles << tri2; return triangles; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void LDObject::replace( LDObject* replacement ) { long idx = getIndex( g_curfile ); assert( idx != -1 ); // Replace the instance of the old object with the new object g_curfile->setObject( idx, replacement ); // Remove the old object delete this; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void LDObject::swap( LDObject* other ) { for( LDObject*& obj : *g_curfile ) { if( obj == this ) obj = other; elif( obj == other ) obj = this; } } LDLine::LDLine( vertex v1, vertex v2 ) { setVertex( 0, v1 ); setVertex( 1, v2 ); } LDObject::~LDObject() { // Remove this object from the selection array if it is there. for( ulong i = 0; i < g_win->sel().size(); ++i ) if( g_win->sel()[i] == this ) g_win->sel().erase( i ); // Delete the GL lists GL::deleteLists( this ); // Remove this object from the list of LDObjects ulong pos = g_LDObjects.find( this ); if( pos < g_LDObjects.size() ) g_LDObjects.erase( pos ); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= static void transformObject( LDObject* obj, matrix transform, vertex pos, short parentcolor ) { switch( obj->getType() ) { case LDObject::Line: case LDObject::CondLine: case LDObject::Triangle: case LDObject::Quad: for( short i = 0; i < obj->vertices(); ++i ) { vertex v = obj->getVertex( i ); v.transform( transform, pos ); obj->setVertex( i, v ); } break; case LDObject::Subfile: { LDSubfile* ref = static_cast<LDSubfile*>( obj ); matrix newMatrix = transform * ref->transform(); vertex newpos = ref->position(); newpos.transform( transform, pos ); ref->setPosition( newpos ); ref->setTransform( newMatrix ); } break; default: break; } if( obj->color() == maincolor ) obj->setColor( parentcolor ); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= vector<LDObject*> LDSubfile::inlineContents( bool deep, bool cache ) { vector<LDObject*> objs, objcache; // If we have this cached, just clone that if( deep && fileInfo()->cache().size() ) { for( LDObject * obj : fileInfo()->cache() ) objs << obj->clone(); } else { if( !deep ) cache = false; for( LDObject * obj : *fileInfo() ) { // Skip those without scemantic meaning if( !obj->isScemantic() ) continue; // Got another sub-file reference, inline it if we're deep-inlining. If not, // just add it into the objects normally. Also, we only cache immediate // subfiles and this is not one. Yay, recursion! if( deep && obj->getType() == LDObject::Subfile ) { LDSubfile* ref = static_cast<LDSubfile*>( obj ); vector<LDObject*> otherobjs = ref->inlineContents( true, false ); for( LDObject * otherobj : otherobjs ) { // Cache this object, if desired if( cache ) objcache << otherobj->clone(); objs << otherobj; } } else { if( cache ) objcache << obj->clone(); objs << obj->clone(); } } if( cache ) fileInfo()->setCache( objcache ); } // Transform the objects for( LDObject * obj : objs ) { // Set the parent now so we know what inlined this. obj->setParent( this ); transformObject( obj, transform(), position(), color() ); } return objs; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= long LDObject::getIndex( LDOpenFile* file ) const { for( ulong i = 0; i < file->numObjs(); ++i ) if( file->obj( i ) == this ) return i; return -1; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void LDObject::moveObjects( vector<LDObject*> objs, const bool up ) { // If we move down, we need to iterate the array in reverse order. const long start = up ? 0 : ( objs.size() - 1 ); const long end = up ? objs.size() : -1; const long incr = up ? 1 : -1; vector<LDObject*> objsToCompile; for( long i = start; i != end; i += incr ) { LDObject* obj = objs[i]; const long idx = obj->getIndex( g_curfile ), target = idx + ( up ? -1 : 1 ); if(( up && idx == 0 ) || ( !up && idx == (long) ( g_curfile->objs().size() - 1 )) ) { // One of the objects hit the extrema. If this happens, this should be the first // object to be iterated on. Thus, nothing has changed yet and it's safe to just // abort the entire operation. assert( i == start ); return; } objsToCompile << obj; objsToCompile << g_curfile->obj( target ); obj->swap( g_curfile->obj( target ) ); } objsToCompile.makeUnique(); // The objects need to be recompiled, otherwise their pick lists are left with // the wrong index colors which messes up selection. for( LDObject * obj : objsToCompile ) g_win->R()->compileObject( obj ); } str LDObject::typeName( LDObject::Type type ) { LDObject* obj = LDObject::getDefault( type ); str name = obj->typeName(); delete obj; return name; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= str LDObject::objectListContents( const vector<LDObject*>& objs ) { bool firstDetails = true; str text = ""; if( objs.size() == 0 ) return "nothing"; // :) for( long i = 0; i < LDObject::NumTypes; ++i ) { LDObject::Type objType = ( LDObject::Type ) i; ulong objCount = 0; for( LDObject * obj : objs ) if( obj->getType() == objType ) objCount++; if( objCount == 0 ) continue; if( !firstDetails ) text += ", "; str noun = fmt( "%1%2", typeName( objType ), plural( objCount ) ); // Plural of "vertex" is "vertices". Stupid English. if( objType == LDObject::Vertex && objCount != 1 ) noun = "vertices"; text += fmt( "%1 %2", objCount, noun ); firstDetails = false; } return text; } // ============================================================================= LDObject* LDObject::topLevelParent() { if( !parent() ) return this; LDObject* it = this; while( it->parent() ) it = it->parent(); return it; } // ============================================================================= LDObject* LDObject::next() const { long idx = getIndex( g_curfile ); assert( idx != -1 ); if( idx == ( long ) g_curfile->numObjs() - 1 ) return null; return g_curfile->obj( idx + 1 ); } // ============================================================================= LDObject* LDObject::prev() const { long idx = getIndex( g_curfile ); assert( idx != -1 ); if( idx == 0 ) return null; return g_curfile->obj( idx - 1 ); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void LDObject::move( vertex vect ) { (void) vect; } void LDEmpty::move( vertex vect ) { (void) vect; } void LDBFC::move( vertex vect ) { (void) vect; } void LDComment::move( vertex vect ) { (void) vect; } void LDGibberish::move( vertex vect ) { (void) vect; } void LDVertex::move( vertex vect ) { pos += vect; } void LDSubfile::move( vertex vect ) { setPosition( position() + vect ); } void LDLine::move( vertex vect ) { for( short i = 0; i < 2; ++i ) setVertex( i, getVertex( i ) + vect ); } void LDTriangle::move( vertex vect ) { for( short i = 0; i < 3; ++i ) setVertex( i, getVertex( i ) + vect ); } void LDQuad::move( vertex vect ) { for( short i = 0; i < 4; ++i ) setVertex( i, getVertex( i ) + vect ); } void LDCondLine::move( vertex vect ) { for( short i = 0; i < 4; ++i ) setVertex( i, getVertex( i ) + vect ); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= #define CHECK_FOR_OBJ(N) \ if( type == LDObject::N ) \ return new LD##N; LDObject* LDObject::getDefault( const LDObject::Type type ) { CHECK_FOR_OBJ( Comment ) CHECK_FOR_OBJ( BFC ) CHECK_FOR_OBJ( Line ) CHECK_FOR_OBJ( CondLine ) CHECK_FOR_OBJ( Subfile ) CHECK_FOR_OBJ( Triangle ) CHECK_FOR_OBJ( Quad ) CHECK_FOR_OBJ( Empty ) CHECK_FOR_OBJ( BFC ) CHECK_FOR_OBJ( Gibberish ) CHECK_FOR_OBJ( Vertex ) CHECK_FOR_OBJ( Overlay ) return null; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void LDObject::invert() {} void LDBFC::invert() {} void LDEmpty::invert() {} void LDComment::invert() {} void LDGibberish::invert() {} void LDTriangle::invert() { // Triangle goes 0 -> 1 -> 2, reversed: 0 -> 2 -> 1. // Thus, we swap 1 and 2. vertex tmp = getVertex( 1 ); setVertex( 1, getVertex( 2 ) ); setVertex( 2, tmp ); return; } void LDQuad::invert() { // Quad: 0 -> 1 -> 2 -> 3 // rev: 0 -> 3 -> 2 -> 1 // Thus, we swap 1 and 3. vertex tmp = getVertex( 1 ); setVertex( 1, getVertex( 3 ) ); setVertex( 3, tmp ); } void LDSubfile::invert() { // Subfiles are inverted when they're prefixed with // a BFC INVERTNEXT statement. Thus we need to toggle this status. // For flat primitives it's sufficient that the determinant is // flipped but I don't have a method for checking flatness yet. // Food for thought... ulong idx = getIndex( g_curfile ); if( idx > 0 ) { LDBFC* bfc = dynamic_cast<LDBFC*>( prev() ); if( bfc && bfc->type == LDBFC::InvertNext ) { // This is prefixed with an invertnext, thus remove it. g_curfile->forgetObject( bfc ); delete bfc; return; } } // Not inverted, thus prefix it with a new invertnext. LDBFC* bfc = new LDBFC( LDBFC::InvertNext ); g_curfile->insertObj( idx, bfc ); } static void invertLine( LDObject* line ) { // For lines, we swap the vertices. I don't think that a // cond-line's control points need to be swapped, do they? vertex tmp = line->getVertex( 0 ); line->setVertex( 0, line->getVertex( 1 ) ); line->setVertex( 1, tmp ); } void LDLine::invert() { invertLine( this ); } void LDCondLine::invert() { invertLine( this ); } void LDVertex::invert() {} // ============================================================================= LDLine* LDCondLine::demote() { LDLine* repl = new LDLine; for( int i = 0; i < repl->vertices(); ++i ) repl->setVertex( i, getVertex( i ) ); repl->setColor( color() ); replace( repl ); return repl; } LDObject* LDObject::fromID( int id ) { for( LDObject * obj : g_LDObjects ) if( obj->id() == id ) return obj; return null; } // ============================================================================= str LDOverlay::raw() { return fmt( "0 !LDFORGE OVERLAY %1 %2 %3 %4 %5 %6", filename(), camera(), x(), y(), width(), height() ); } void LDOverlay::move( vertex vect ) { Q_UNUSED( vect ) } void LDOverlay::invert() {} // ============================================================================= template<class T> void changeProperty( LDObject* obj, T* ptr, const T& val ) { long idx; if(( idx = obj->getIndex( g_curfile )) != -1 ) { str before = obj->raw(); *ptr = val; str after = obj->raw(); g_curfile->addToHistory( new EditHistory( idx, before, after ) ); } else *ptr = val; } READ_ACCESSOR( short, LDObject::color ) { return m_color; } SET_ACCESSOR( short, LDObject::setColor ) { changeProperty( this, &m_color, val ); } const vertex& LDObject::getVertex( int i ) const { return m_coords[i]; } void LDObject::setVertex( int i, const vertex& vert ) { changeProperty( this, &m_coords[i], vert ); } READ_ACCESSOR( vertex, LDMatrixObject::position ) { return m_position; } SET_ACCESSOR( vertex, LDMatrixObject::setPosition ) { changeProperty( linkPointer(), &m_position, val ); } READ_ACCESSOR( matrix, LDMatrixObject::transform ) { return m_transform; } SET_ACCESSOR( matrix, LDMatrixObject::setTransform ) { changeProperty( linkPointer(), &m_transform, val ); }