1 /*******************************************************************************
2  *  Project: libopencad
3  *  Purpose: OpenSource CAD formats support library
4  *  Author: Alexandr Borzykh, mush3d at gmail.com
5  *  Author: Dmitry Baryshnikov, bishop.dev@gmail.com
6  *  Language: C++
7  *******************************************************************************
8  *  The MIT License (MIT)
9  *
10  *  Copyright (c) 2016 Alexandr Borzykh
11  *  Copyright (c) 2016-2018 NextGIS, <info@nextgis.com>
12  *
13  *  Permission is hereby granted, free of charge, to any person obtaining a copy
14  *  of this software and associated documentation files (the "Software"), to deal
15  *  in the Software without restriction, including without limitation the rights
16  *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17  *  copies of the Software, and to permit persons to whom the Software is
18  *  furnished to do so, subject to the following conditions:
19  *
20  *  The above copyright notice and this permission notice shall be included in all
21  *  copies or substantial portions of the Software.
22  *
23  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29  *  SOFTWARE.
30  *******************************************************************************/
31 #include "cadtables.h"
32 #include "opencad_api.h"
33 
34 #include <cassert>
35 #include <iostream>
36 #include <memory>
37 #include <set>
38 
39 using namespace std;
40 
CADTables()41 CADTables::CADTables()
42 {
43 }
44 
AddTable(TableType eType,const CADHandle & hHandle)45 void CADTables::AddTable( TableType eType, const CADHandle& hHandle )
46 {
47     mapTables[eType] = hHandle;
48 }
49 
ReadTable(CADFile * const pCADFile,CADTables::TableType eType)50 int CADTables::ReadTable( CADFile * const pCADFile, CADTables::TableType eType )
51 {
52     auto iterAskedTable = mapTables.find( eType );
53     if( iterAskedTable == mapTables.end() )
54         return CADErrorCodes::TABLE_READ_FAILED;
55 
56     // TODO: read different tables
57     switch( eType )
58     {
59         case LayersTable:
60             return ReadLayersTable( pCADFile, iterAskedTable->second.getAsLong() );
61         default:
62             std::cerr << "Unsupported table.";
63             break;
64     }
65 
66     return CADErrorCodes::SUCCESS;
67 }
68 
GetLayerCount() const69 size_t CADTables::GetLayerCount() const
70 {
71     return aLayers.size();
72 }
73 
GetLayer(size_t iIndex)74 CADLayer& CADTables::GetLayer( size_t iIndex )
75 {
76     return aLayers[iIndex];
77 }
78 
GetTableHandle(enum TableType eType)79 CADHandle CADTables::GetTableHandle( enum TableType eType )
80 {
81     // FIXME: need to add try/catch to prevent crashes on not found elem.
82     return mapTables[eType];
83 }
84 
ReadLayersTable(CADFile * const pCADFile,long dLayerControlHandle)85 int CADTables::ReadLayersTable( CADFile * const pCADFile, long dLayerControlHandle )
86 {
87     // Reading Layer Control obj, and aLayers.
88     unique_ptr<CADObject> pCADObject( pCADFile->GetObject( dLayerControlHandle ) );
89 
90     CADLayerControlObject* spLayerControl =
91             dynamic_cast<CADLayerControlObject *>(pCADObject.get());
92     if( !spLayerControl )
93     {
94         return CADErrorCodes::TABLE_READ_FAILED;
95     }
96 
97     for( size_t i = 0; i < spLayerControl->hLayers.size(); ++i )
98     {
99         if( !spLayerControl->hLayers[i].isNull() )
100         {
101             CADLayer oCADLayer( pCADFile );
102 
103             // Init CADLayer from CADLayerObject properties
104             CADObject* pCADLayerObject = pCADFile->GetObject(
105                         spLayerControl->hLayers[i].getAsLong() );
106             unique_ptr<CADLayerObject> oCADLayerObj(
107                     dynamic_cast<CADLayerObject *>( pCADLayerObject ) );
108 
109             if(oCADLayerObj)
110             {
111                 oCADLayer.setName( oCADLayerObj->sLayerName );
112                 oCADLayer.setFrozen( oCADLayerObj->bFrozen );
113                 oCADLayer.setOn( oCADLayerObj->bOn );
114                 oCADLayer.setFrozenByDefault( oCADLayerObj->bFrozenInNewVPORT );
115                 oCADLayer.setLocked( oCADLayerObj->bLocked );
116                 oCADLayer.setLineWeight( oCADLayerObj->dLineWeight );
117                 oCADLayer.setColor( oCADLayerObj->dCMColor );
118                 oCADLayer.setId( aLayers.size() + 1 );
119                 oCADLayer.setHandle( oCADLayerObj->hObjectHandle.getAsLong() );
120 
121                 aLayers.push_back( oCADLayer );
122             }
123             else
124             {
125                 delete pCADLayerObject;
126             }
127         }
128     }
129 
130     auto iterBlockMS = mapTables.find( BlockRecordModelSpace );
131     if( iterBlockMS == mapTables.end() )
132         return CADErrorCodes::TABLE_READ_FAILED;
133 
134     CADObject* pCADBlockObject = pCADFile->GetObject(
135                 iterBlockMS->second.getAsLong() );
136     unique_ptr<CADBlockHeaderObject> spModelSpace(
137             dynamic_cast<CADBlockHeaderObject *>( pCADBlockObject ) );
138     if(!spModelSpace)
139     {
140         delete pCADBlockObject;
141         return CADErrorCodes::TABLE_READ_FAILED;
142     }
143 
144     if(spModelSpace->hEntities.size() < 2)
145     {
146         return CADErrorCodes::TABLE_READ_FAILED;
147     }
148 
149     auto dCurrentEntHandle = spModelSpace->hEntities[0].getAsLong();
150     auto dLastEntHandle    = spModelSpace->hEntities[1].getAsLong();
151     // To avoid infinite loops
152     std::set<long> oVisitedHandles;
153     while( dCurrentEntHandle != 0 &&
154            oVisitedHandles.find(dCurrentEntHandle) == oVisitedHandles.end() )
155     {
156         oVisitedHandles.insert(dCurrentEntHandle);
157 
158         CADObject* pCADEntityObject = pCADFile->GetObject( dCurrentEntHandle, true );
159         unique_ptr<CADEntityObject> spEntityObj(
160                     dynamic_cast<CADEntityObject *>( pCADEntityObject ) );
161 
162         if( !spEntityObj )
163         {
164             delete pCADEntityObject;
165             DebugMsg( "Entity object is null\n" );
166             break;
167         }
168         else if ( dCurrentEntHandle == dLastEntHandle )
169         {
170             FillLayer( spEntityObj.get() );
171             break;
172         }
173 
174         FillLayer( spEntityObj.get() );
175 
176         if( spEntityObj->stCed.bNoLinks )
177         {
178             ++dCurrentEntHandle;
179         }
180         else
181         {
182             dCurrentEntHandle = spEntityObj->stChed.hNextEntity.getAsLong( spEntityObj->stCed.hObjectHandle );
183         }
184     }
185 
186     DebugMsg( "Read aLayers using LayerControl object count: %d\n",
187               static_cast<int>(aLayers.size()) );
188 
189     return CADErrorCodes::SUCCESS;
190 }
191 
FillLayer(const CADEntityObject * pEntityObject)192 void CADTables::FillLayer( const CADEntityObject * pEntityObject )
193 {
194     if(nullptr == pEntityObject)
195     {
196         return;
197     }
198 
199     for( CADLayer& oLayer : aLayers )
200     {
201         if( pEntityObject->stChed.hLayer.getAsLong(
202                     pEntityObject->stCed.hObjectHandle ) == oLayer.getHandle() )
203         {
204             DebugMsg( "Object with type: %s is attached to layer named: %s\n",
205                       getNameByType( pEntityObject->getType() ).c_str(),
206                       oLayer.getName().c_str() );
207 
208             oLayer.addHandle( pEntityObject->stCed.hObjectHandle.getAsLong(),
209                               pEntityObject->getType() );
210             break;
211         }
212     }
213 }
214